opendevbrowser 0.0.12 → 0.0.16
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 +425 -42
- package/dist/annotate/direct-annotator.d.ts +22 -0
- package/dist/annotate/direct-annotator.d.ts.map +1 -0
- package/dist/annotate/output.d.ts +10 -0
- package/dist/annotate/output.d.ts.map +1 -0
- package/dist/browser/annotation-manager.d.ts +30 -0
- package/dist/browser/annotation-manager.d.ts.map +1 -0
- package/dist/browser/browser-manager.d.ts +397 -0
- package/dist/browser/browser-manager.d.ts.map +1 -0
- package/dist/browser/fingerprint/adapters.d.ts +26 -0
- package/dist/browser/fingerprint/adapters.d.ts.map +1 -0
- package/dist/browser/fingerprint/canary.d.ts +25 -0
- package/dist/browser/fingerprint/canary.d.ts.map +1 -0
- package/dist/browser/fingerprint/profiles.d.ts +16 -0
- package/dist/browser/fingerprint/profiles.d.ts.map +1 -0
- package/dist/browser/fingerprint/tier1-coherence.d.ts +36 -0
- package/dist/browser/fingerprint/tier1-coherence.d.ts.map +1 -0
- package/dist/browser/fingerprint/tier2-runtime.d.ts +40 -0
- package/dist/browser/fingerprint/tier2-runtime.d.ts.map +1 -0
- package/dist/browser/fingerprint/tier3-adaptive.d.ts +30 -0
- package/dist/browser/fingerprint/tier3-adaptive.d.ts.map +1 -0
- package/dist/browser/manager-types.d.ts +3 -0
- package/dist/browser/manager-types.d.ts.map +1 -0
- package/dist/browser/ops-browser-manager.d.ts +131 -0
- package/dist/browser/ops-browser-manager.d.ts.map +1 -0
- package/dist/browser/ops-client.d.ts +56 -0
- package/dist/browser/ops-client.d.ts.map +1 -0
- package/dist/browser/parallelism-governor.d.ts +31 -0
- package/dist/browser/parallelism-governor.d.ts.map +1 -0
- package/dist/browser/script-runner.d.ts +23 -0
- package/dist/browser/script-runner.d.ts.map +1 -0
- package/dist/browser/session-store.d.ts +63 -0
- package/dist/browser/session-store.d.ts.map +1 -0
- package/dist/browser/target-manager.d.ts +36 -0
- package/dist/browser/target-manager.d.ts.map +1 -0
- package/dist/cache/chrome-locator.d.ts +2 -0
- package/dist/cache/chrome-locator.d.ts.map +1 -0
- package/dist/cache/downloader.d.ts +6 -0
- package/dist/cache/downloader.d.ts.map +1 -0
- package/dist/cache/paths.d.ts +9 -0
- package/dist/cache/paths.d.ts.map +1 -0
- package/dist/chunk-7W3SPXIB.js +166 -0
- package/dist/chunk-7W3SPXIB.js.map +1 -0
- package/dist/chunk-ST7CO5FA.js +18668 -0
- package/dist/chunk-ST7CO5FA.js.map +1 -0
- package/dist/cli/args.d.ts +25 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/client.d.ts +2 -0
- package/dist/cli/client.d.ts.map +1 -0
- package/dist/cli/commands/annotate.d.ts +27 -0
- package/dist/cli/commands/annotate.d.ts.map +1 -0
- package/dist/cli/commands/artifacts.d.ts +24 -0
- package/dist/cli/commands/artifacts.d.ts.map +1 -0
- package/dist/cli/commands/daemon.d.ts +35 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -0
- package/dist/cli/commands/devtools/console-poll.d.ts +7 -0
- package/dist/cli/commands/devtools/console-poll.d.ts.map +1 -0
- package/dist/cli/commands/devtools/debug-trace-snapshot.d.ts +20 -0
- package/dist/cli/commands/devtools/debug-trace-snapshot.d.ts.map +1 -0
- package/dist/cli/commands/devtools/network-poll.d.ts +7 -0
- package/dist/cli/commands/devtools/network-poll.d.ts.map +1 -0
- package/dist/cli/commands/devtools/perf.d.ts +7 -0
- package/dist/cli/commands/devtools/perf.d.ts.map +1 -0
- package/dist/cli/commands/devtools/screenshot.d.ts +17 -0
- package/dist/cli/commands/devtools/screenshot.d.ts.map +1 -0
- package/dist/cli/commands/dom/attr.d.ts +7 -0
- package/dist/cli/commands/dom/attr.d.ts.map +1 -0
- package/dist/cli/commands/dom/checked.d.ts +7 -0
- package/dist/cli/commands/dom/checked.d.ts.map +1 -0
- package/dist/cli/commands/dom/enabled.d.ts +7 -0
- package/dist/cli/commands/dom/enabled.d.ts.map +1 -0
- package/dist/cli/commands/dom/html.d.ts +7 -0
- package/dist/cli/commands/dom/html.d.ts.map +1 -0
- package/dist/cli/commands/dom/text.d.ts +7 -0
- package/dist/cli/commands/dom/text.d.ts.map +1 -0
- package/dist/cli/commands/dom/value.d.ts +7 -0
- package/dist/cli/commands/dom/value.d.ts.map +1 -0
- package/dist/cli/commands/dom/visible.d.ts +7 -0
- package/dist/cli/commands/dom/visible.d.ts.map +1 -0
- package/dist/cli/commands/export/clone-component.d.ts +7 -0
- package/dist/cli/commands/export/clone-component.d.ts.map +1 -0
- package/dist/cli/commands/export/clone-page.d.ts +7 -0
- package/dist/cli/commands/export/clone-page.d.ts.map +1 -0
- package/dist/cli/commands/interact/check.d.ts +7 -0
- package/dist/cli/commands/interact/check.d.ts.map +1 -0
- package/dist/cli/commands/interact/click.d.ts +7 -0
- package/dist/cli/commands/interact/click.d.ts.map +1 -0
- package/dist/cli/commands/interact/hover.d.ts +7 -0
- package/dist/cli/commands/interact/hover.d.ts.map +1 -0
- package/dist/cli/commands/interact/press.d.ts +7 -0
- package/dist/cli/commands/interact/press.d.ts.map +1 -0
- package/dist/cli/commands/interact/scroll-into-view.d.ts +7 -0
- package/dist/cli/commands/interact/scroll-into-view.d.ts.map +1 -0
- package/dist/cli/commands/interact/scroll.d.ts +7 -0
- package/dist/cli/commands/interact/scroll.d.ts.map +1 -0
- package/dist/cli/commands/interact/select.d.ts +7 -0
- package/dist/cli/commands/interact/select.d.ts.map +1 -0
- package/dist/cli/commands/interact/type.d.ts +7 -0
- package/dist/cli/commands/interact/type.d.ts.map +1 -0
- package/dist/cli/commands/interact/uncheck.d.ts +7 -0
- package/dist/cli/commands/interact/uncheck.d.ts.map +1 -0
- package/dist/cli/commands/macro-resolve.d.ts +18 -0
- package/dist/cli/commands/macro-resolve.d.ts.map +1 -0
- package/dist/cli/commands/native.d.ts +82 -0
- package/dist/cli/commands/native.d.ts.map +1 -0
- package/dist/cli/commands/nav/goto.d.ts +7 -0
- package/dist/cli/commands/nav/goto.d.ts.map +1 -0
- package/dist/cli/commands/nav/snapshot.d.ts +7 -0
- package/dist/cli/commands/nav/snapshot.d.ts.map +1 -0
- package/dist/cli/commands/nav/wait.d.ts +7 -0
- package/dist/cli/commands/nav/wait.d.ts.map +1 -0
- package/dist/cli/commands/pages/close.d.ts +6 -0
- package/dist/cli/commands/pages/close.d.ts.map +1 -0
- package/dist/cli/commands/pages/list.d.ts +7 -0
- package/dist/cli/commands/pages/list.d.ts.map +1 -0
- package/dist/cli/commands/pages/open.d.ts +7 -0
- package/dist/cli/commands/pages/open.d.ts.map +1 -0
- package/dist/cli/commands/product-video.d.ts +25 -0
- package/dist/cli/commands/product-video.d.ts.map +1 -0
- package/dist/cli/commands/registry.d.ts +5 -0
- package/dist/cli/commands/registry.d.ts.map +1 -0
- package/dist/cli/commands/research.d.ts +27 -0
- package/dist/cli/commands/research.d.ts.map +1 -0
- package/dist/cli/commands/rpc.d.ts +28 -0
- package/dist/cli/commands/rpc.d.ts.map +1 -0
- package/dist/cli/commands/run.d.ts +17 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/serve.d.ts +59 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/session/connect.d.ts +9 -0
- package/dist/cli/commands/session/connect.d.ts.map +1 -0
- package/dist/cli/commands/session/cookie-import.d.ts +31 -0
- package/dist/cli/commands/session/cookie-import.d.ts.map +1 -0
- package/dist/cli/commands/session/cookie-list.d.ts +17 -0
- package/dist/cli/commands/session/cookie-list.d.ts.map +1 -0
- package/dist/cli/commands/session/disconnect.d.ts +6 -0
- package/dist/cli/commands/session/disconnect.d.ts.map +1 -0
- package/dist/cli/commands/session/launch.d.ts +29 -0
- package/dist/cli/commands/session/launch.d.ts.map +1 -0
- package/dist/cli/commands/session/status.d.ts +7 -0
- package/dist/cli/commands/session/status.d.ts.map +1 -0
- package/dist/cli/commands/shopping.d.ts +25 -0
- package/dist/cli/commands/shopping.d.ts.map +1 -0
- package/dist/cli/commands/status.d.ts +19 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/targets/close.d.ts +6 -0
- package/dist/cli/commands/targets/close.d.ts.map +1 -0
- package/dist/cli/commands/targets/list.d.ts +7 -0
- package/dist/cli/commands/targets/list.d.ts.map +1 -0
- package/dist/cli/commands/targets/new.d.ts +7 -0
- package/dist/cli/commands/targets/new.d.ts.map +1 -0
- package/dist/cli/commands/targets/use.d.ts +7 -0
- package/dist/cli/commands/targets/use.d.ts.map +1 -0
- package/dist/cli/commands/types.d.ts +13 -0
- package/dist/cli/commands/types.d.ts.map +1 -0
- package/dist/cli/commands/uninstall.d.ts +14 -0
- package/dist/cli/commands/uninstall.d.ts.map +1 -0
- package/dist/cli/commands/update.d.ts +7 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/daemon-autostart.d.ts +46 -0
- package/dist/cli/daemon-autostart.d.ts.map +1 -0
- package/dist/cli/daemon-client.d.ts +33 -0
- package/dist/cli/daemon-client.d.ts.map +1 -0
- package/dist/cli/daemon-commands.d.ts +7 -0
- package/dist/cli/daemon-commands.d.ts.map +1 -0
- package/dist/cli/daemon-state.d.ts +56 -0
- package/dist/cli/daemon-state.d.ts.map +1 -0
- package/dist/cli/daemon-status.d.ts +19 -0
- package/dist/cli/daemon-status.d.ts.map +1 -0
- package/dist/cli/daemon.d.ts +29 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/errors.d.ts +20 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/help.d.ts +28 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +4604 -769
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/installers/global.d.ts +9 -0
- package/dist/cli/installers/global.d.ts.map +1 -0
- package/dist/cli/installers/local.d.ts +9 -0
- package/dist/cli/installers/local.d.ts.map +1 -0
- package/dist/cli/installers/skills.d.ts +19 -0
- package/dist/cli/installers/skills.d.ts.map +1 -0
- package/dist/cli/output.d.ts +7 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/remote-manager.d.ts +96 -0
- package/dist/cli/remote-manager.d.ts.map +1 -0
- package/dist/cli/remote-relay.d.ts +17 -0
- package/dist/cli/remote-relay.d.ts.map +1 -0
- package/dist/cli/templates/config.d.ts +7 -0
- package/dist/cli/templates/config.d.ts.map +1 -0
- package/dist/cli/utils/config.d.ts +20 -0
- package/dist/cli/utils/config.d.ts.map +1 -0
- package/dist/cli/utils/http.d.ts +5 -0
- package/dist/cli/utils/http.d.ts.map +1 -0
- package/dist/cli/utils/parse.d.ts +8 -0
- package/dist/cli/utils/parse.d.ts.map +1 -0
- package/dist/cli/utils/skills.d.ts +12 -0
- package/dist/cli/utils/skills.d.ts.map +1 -0
- package/dist/config.d.ts +208 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/core/bootstrap.d.ts +3 -0
- package/dist/core/bootstrap.d.ts.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/logging.d.ts +34 -0
- package/dist/core/logging.d.ts.map +1 -0
- package/dist/core/types.d.ts +34 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/devtools/console-tracker.d.ts +44 -0
- package/dist/devtools/console-tracker.d.ts.map +1 -0
- package/dist/devtools/exception-tracker.d.ts +42 -0
- package/dist/devtools/exception-tracker.d.ts.map +1 -0
- package/dist/devtools/network-tracker.d.ts +34 -0
- package/dist/devtools/network-tracker.d.ts.map +1 -0
- package/dist/export/css-extract.d.ts +5 -0
- package/dist/export/css-extract.d.ts.map +1 -0
- package/dist/export/dom-capture.d.ts +15 -0
- package/dist/export/dom-capture.d.ts.map +1 -0
- package/dist/export/react-emitter.d.ts +11 -0
- package/dist/export/react-emitter.d.ts.map +1 -0
- package/dist/extension-extractor.d.ts +3 -0
- package/dist/extension-extractor.d.ts.map +1 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1905 -262
- package/dist/index.js.map +1 -1
- package/dist/macros/execute.d.ts +44 -0
- package/dist/macros/execute.d.ts.map +1 -0
- package/dist/macros/index.d.ts +9 -0
- package/dist/macros/index.d.ts.map +1 -0
- package/dist/macros/packs/core.d.ts +3 -0
- package/dist/macros/packs/core.d.ts.map +1 -0
- package/dist/macros/registry.d.ts +48 -0
- package/dist/macros/registry.d.ts.map +1 -0
- package/dist/macros-NUBRM44Y.js +399 -0
- package/dist/macros-NUBRM44Y.js.map +1 -0
- package/dist/opendevbrowser.d.ts +3 -4
- package/dist/opendevbrowser.d.ts.map +1 -0
- package/dist/opendevbrowser.js +1905 -262
- package/dist/opendevbrowser.js.map +1 -1
- package/dist/providers/adaptive-concurrency.d.ts +42 -0
- package/dist/providers/adaptive-concurrency.d.ts.map +1 -0
- package/dist/providers/artifacts.d.ts +34 -0
- package/dist/providers/artifacts.d.ts.map +1 -0
- package/dist/providers/blocker.d.ts +47 -0
- package/dist/providers/blocker.d.ts.map +1 -0
- package/dist/providers/community/index.d.ts +44 -0
- package/dist/providers/community/index.d.ts.map +1 -0
- package/dist/providers/enrichment.d.ts +33 -0
- package/dist/providers/enrichment.d.ts.map +1 -0
- package/dist/providers/errors.d.ts +41 -0
- package/dist/providers/errors.d.ts.map +1 -0
- package/dist/providers/index.d.ts +121 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/normalize.d.ts +39 -0
- package/dist/providers/normalize.d.ts.map +1 -0
- package/dist/providers/policy.d.ts +5 -0
- package/dist/providers/policy.d.ts.map +1 -0
- package/dist/providers/registry.d.ts +22 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/renderer.d.ts +49 -0
- package/dist/providers/renderer.d.ts.map +1 -0
- package/dist/providers/runtime-factory.d.ts +20 -0
- package/dist/providers/runtime-factory.d.ts.map +1 -0
- package/dist/providers/safety/prompt-guard.d.ts +34 -0
- package/dist/providers/safety/prompt-guard.d.ts.map +1 -0
- package/dist/providers/shared/anti-bot-policy.d.ts +51 -0
- package/dist/providers/shared/anti-bot-policy.d.ts.map +1 -0
- package/dist/providers/shared/post-policy.d.ts +31 -0
- package/dist/providers/shared/post-policy.d.ts.map +1 -0
- package/dist/providers/shared/request-headers.d.ts +5 -0
- package/dist/providers/shared/request-headers.d.ts.map +1 -0
- package/dist/providers/shared/traversal-url.d.ts +2 -0
- package/dist/providers/shared/traversal-url.d.ts.map +1 -0
- package/dist/providers/shopping/index.d.ts +63 -0
- package/dist/providers/shopping/index.d.ts.map +1 -0
- package/dist/providers/social/bluesky.d.ts +3 -0
- package/dist/providers/social/bluesky.d.ts.map +1 -0
- package/dist/providers/social/facebook.d.ts +3 -0
- package/dist/providers/social/facebook.d.ts.map +1 -0
- package/dist/providers/social/index.d.ts +31 -0
- package/dist/providers/social/index.d.ts.map +1 -0
- package/dist/providers/social/instagram.d.ts +3 -0
- package/dist/providers/social/instagram.d.ts.map +1 -0
- package/dist/providers/social/linkedin.d.ts +3 -0
- package/dist/providers/social/linkedin.d.ts.map +1 -0
- package/dist/providers/social/platform.d.ts +40 -0
- package/dist/providers/social/platform.d.ts.map +1 -0
- package/dist/providers/social/reddit.d.ts +3 -0
- package/dist/providers/social/reddit.d.ts.map +1 -0
- package/dist/providers/social/threads.d.ts +3 -0
- package/dist/providers/social/threads.d.ts.map +1 -0
- package/dist/providers/social/tiktok.d.ts +3 -0
- package/dist/providers/social/tiktok.d.ts.map +1 -0
- package/dist/providers/social/x.d.ts +3 -0
- package/dist/providers/social/x.d.ts.map +1 -0
- package/dist/providers/social/youtube-resolver.d.ts +78 -0
- package/dist/providers/social/youtube-resolver.d.ts.map +1 -0
- package/dist/providers/social/youtube.d.ts +34 -0
- package/dist/providers/social/youtube.d.ts.map +1 -0
- package/dist/providers/tier-router.d.ts +30 -0
- package/dist/providers/tier-router.d.ts.map +1 -0
- package/dist/providers/timebox.d.ts +20 -0
- package/dist/providers/timebox.d.ts.map +1 -0
- package/dist/providers/types.d.ts +344 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/web/crawl-worker.d.ts +36 -0
- package/dist/providers/web/crawl-worker.d.ts.map +1 -0
- package/dist/providers/web/crawler.d.ts +101 -0
- package/dist/providers/web/crawler.d.ts.map +1 -0
- package/dist/providers/web/extract.d.ts +11 -0
- package/dist/providers/web/extract.d.ts.map +1 -0
- package/dist/providers/web/index.d.ts +24 -0
- package/dist/providers/web/index.d.ts.map +1 -0
- package/dist/providers/web/policy.d.ts +14 -0
- package/dist/providers/web/policy.d.ts.map +1 -0
- package/dist/providers/workflows.d.ts +67 -0
- package/dist/providers/workflows.d.ts.map +1 -0
- package/dist/relay/protocol.d.ts +317 -0
- package/dist/relay/protocol.d.ts.map +1 -0
- package/dist/relay/relay-endpoints.d.ts +16 -0
- package/dist/relay/relay-endpoints.d.ts.map +1 -0
- package/dist/relay/relay-server.d.ts +111 -0
- package/dist/relay/relay-server.d.ts.map +1 -0
- package/dist/relay/relay-types.d.ts +9 -0
- package/dist/relay/relay-types.d.ts.map +1 -0
- package/dist/skills/continuity-nudge.d.ts +12 -0
- package/dist/skills/continuity-nudge.d.ts.map +1 -0
- package/dist/skills/skill-loader.d.ts +20 -0
- package/dist/skills/skill-loader.d.ts.map +1 -0
- package/dist/skills/skill-nudge.d.ts +18 -0
- package/dist/skills/skill-nudge.d.ts.map +1 -0
- package/dist/skills/types.d.ts +12 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/snapshot/ops-snapshot.d.ts +16 -0
- package/dist/snapshot/ops-snapshot.d.ts.map +1 -0
- package/dist/snapshot/refs.d.ts +23 -0
- package/dist/snapshot/refs.d.ts.map +1 -0
- package/dist/snapshot/snapshotter.d.ts +27 -0
- package/dist/snapshot/snapshotter.d.ts.map +1 -0
- package/dist/tools/annotate.d.ts +4 -0
- package/dist/tools/annotate.d.ts.map +1 -0
- package/dist/tools/check.d.ts +4 -0
- package/dist/tools/check.d.ts.map +1 -0
- package/dist/tools/click.d.ts +4 -0
- package/dist/tools/click.d.ts.map +1 -0
- package/dist/tools/clone_component.d.ts +4 -0
- package/dist/tools/clone_component.d.ts.map +1 -0
- package/dist/tools/clone_page.d.ts +4 -0
- package/dist/tools/clone_page.d.ts.map +1 -0
- package/dist/tools/close.d.ts +4 -0
- package/dist/tools/close.d.ts.map +1 -0
- package/dist/tools/connect.d.ts +4 -0
- package/dist/tools/connect.d.ts.map +1 -0
- package/dist/tools/console_poll.d.ts +4 -0
- package/dist/tools/console_poll.d.ts.map +1 -0
- package/dist/tools/cookie_import.d.ts +25 -0
- package/dist/tools/cookie_import.d.ts.map +1 -0
- package/dist/tools/cookie_list.d.ts +9 -0
- package/dist/tools/cookie_list.d.ts.map +1 -0
- package/dist/tools/debug_trace_snapshot.d.ts +4 -0
- package/dist/tools/debug_trace_snapshot.d.ts.map +1 -0
- package/dist/tools/deps.d.ts +26 -0
- package/dist/tools/deps.d.ts.map +1 -0
- package/dist/tools/disconnect.d.ts +4 -0
- package/dist/tools/disconnect.d.ts.map +1 -0
- package/dist/tools/dom_get_html.d.ts +4 -0
- package/dist/tools/dom_get_html.d.ts.map +1 -0
- package/dist/tools/dom_get_text.d.ts +4 -0
- package/dist/tools/dom_get_text.d.ts.map +1 -0
- package/dist/tools/get_attr.d.ts +4 -0
- package/dist/tools/get_attr.d.ts.map +1 -0
- package/dist/tools/get_value.d.ts +4 -0
- package/dist/tools/get_value.d.ts.map +1 -0
- package/dist/tools/goto.d.ts +4 -0
- package/dist/tools/goto.d.ts.map +1 -0
- package/dist/tools/hover.d.ts +4 -0
- package/dist/tools/hover.d.ts.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/is_checked.d.ts +4 -0
- package/dist/tools/is_checked.d.ts.map +1 -0
- package/dist/tools/is_enabled.d.ts +4 -0
- package/dist/tools/is_enabled.d.ts.map +1 -0
- package/dist/tools/is_visible.d.ts +4 -0
- package/dist/tools/is_visible.d.ts.map +1 -0
- package/dist/tools/launch.d.ts +4 -0
- package/dist/tools/launch.d.ts.map +1 -0
- package/dist/tools/list.d.ts +4 -0
- package/dist/tools/list.d.ts.map +1 -0
- package/dist/tools/macro_resolve.d.ts +25 -0
- package/dist/tools/macro_resolve.d.ts.map +1 -0
- package/dist/tools/network_poll.d.ts +4 -0
- package/dist/tools/network_poll.d.ts.map +1 -0
- package/dist/tools/page.d.ts +4 -0
- package/dist/tools/page.d.ts.map +1 -0
- package/dist/tools/perf.d.ts +4 -0
- package/dist/tools/perf.d.ts.map +1 -0
- package/dist/tools/press.d.ts +4 -0
- package/dist/tools/press.d.ts.map +1 -0
- package/dist/tools/product_video_run.d.ts +4 -0
- package/dist/tools/product_video_run.d.ts.map +1 -0
- package/dist/tools/prompting_guide.d.ts +4 -0
- package/dist/tools/prompting_guide.d.ts.map +1 -0
- package/dist/tools/research_run.d.ts +4 -0
- package/dist/tools/research_run.d.ts.map +1 -0
- package/dist/tools/response.d.ts +16 -0
- package/dist/tools/response.d.ts.map +1 -0
- package/dist/tools/run.d.ts +4 -0
- package/dist/tools/run.d.ts.map +1 -0
- package/dist/tools/screenshot.d.ts +4 -0
- package/dist/tools/screenshot.d.ts.map +1 -0
- package/dist/tools/scroll.d.ts +4 -0
- package/dist/tools/scroll.d.ts.map +1 -0
- package/dist/tools/scroll_into_view.d.ts +4 -0
- package/dist/tools/scroll_into_view.d.ts.map +1 -0
- package/dist/tools/select.d.ts +4 -0
- package/dist/tools/select.d.ts.map +1 -0
- package/dist/tools/shopping_run.d.ts +4 -0
- package/dist/tools/shopping_run.d.ts.map +1 -0
- package/dist/tools/skill_list.d.ts +4 -0
- package/dist/tools/skill_list.d.ts.map +1 -0
- package/dist/tools/skill_load.d.ts +4 -0
- package/dist/tools/skill_load.d.ts.map +1 -0
- package/dist/tools/snapshot.d.ts +4 -0
- package/dist/tools/snapshot.d.ts.map +1 -0
- package/dist/tools/status.d.ts +4 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/target_close.d.ts +4 -0
- package/dist/tools/target_close.d.ts.map +1 -0
- package/dist/tools/target_new.d.ts +4 -0
- package/dist/tools/target_new.d.ts.map +1 -0
- package/dist/tools/target_use.d.ts +4 -0
- package/dist/tools/target_use.d.ts.map +1 -0
- package/dist/tools/targets_list.d.ts +4 -0
- package/dist/tools/targets_list.d.ts.map +1 -0
- package/dist/tools/type.d.ts +4 -0
- package/dist/tools/type.d.ts.map +1 -0
- package/dist/tools/uncheck.d.ts +4 -0
- package/dist/tools/uncheck.d.ts.map +1 -0
- package/dist/tools/wait.d.ts +4 -0
- package/dist/tools/wait.d.ts.map +1 -0
- package/dist/tools/workflow-runtime.d.ts +4 -0
- package/dist/tools/workflow-runtime.d.ts.map +1 -0
- package/dist/utils/crypto.d.ts +2 -0
- package/dist/utils/crypto.d.ts.map +1 -0
- package/dist/utils/endpoint-validation.d.ts +2 -0
- package/dist/utils/endpoint-validation.d.ts.map +1 -0
- package/dist/utils/fs.d.ts +5 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/hub-enabled.d.ts +3 -0
- package/dist/utils/hub-enabled.d.ts.map +1 -0
- package/extension/dist/annotate-content.css +237 -0
- package/extension/dist/annotate-content.js +934 -0
- package/extension/dist/background.js +1203 -35
- package/extension/dist/logging.js +50 -0
- package/extension/dist/ops/dom-bridge.js +355 -0
- package/extension/dist/ops/ops-runtime.js +1751 -0
- package/extension/dist/ops/ops-session-store.js +203 -0
- package/extension/dist/ops/parallelism-governor.js +117 -0
- package/extension/dist/ops/redaction.js +52 -0
- package/extension/dist/ops/snapshot-builder.js +4 -0
- package/extension/dist/ops/snapshot-shared.js +236 -0
- package/extension/dist/popup.js +370 -25
- package/extension/dist/relay-settings.js +1 -0
- package/extension/dist/services/CDPRouter.js +567 -104
- package/extension/dist/services/ConnectionManager.js +469 -60
- package/extension/dist/services/NativePortManager.js +182 -0
- package/extension/dist/services/RelayClient.js +227 -26
- package/extension/dist/services/TabManager.js +81 -0
- package/extension/dist/services/TargetSessionMap.js +146 -0
- package/extension/dist/services/cdp-router-commands.js +203 -0
- package/extension/dist/services/url-restrictions.js +41 -0
- package/extension/dist/types.js +3 -1
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon32.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +17 -3
- package/extension/popup.html +144 -0
- package/package.json +26 -17
- package/scripts/native/host.cjs +230 -0
- package/scripts/native/install.ps1 +73 -0
- package/scripts/native/install.sh +66 -0
- package/scripts/native/uninstall.ps1 +25 -0
- package/scripts/native/uninstall.sh +26 -0
- package/skills/AGENTS.md +47 -66
- package/skills/opendevbrowser-best-practices/SKILL.md +196 -49
- package/skills/opendevbrowser-best-practices/artifacts/browser-agent-known-issues-matrix.md +44 -0
- package/skills/opendevbrowser-best-practices/artifacts/command-channel-reference.md +95 -0
- package/skills/opendevbrowser-best-practices/artifacts/debug-trace-playbook.md +36 -0
- package/skills/opendevbrowser-best-practices/artifacts/fingerprint-tiers.md +36 -0
- package/skills/opendevbrowser-best-practices/artifacts/macro-workflows.md +43 -0
- package/skills/opendevbrowser-best-practices/artifacts/parity-gates.md +36 -0
- package/skills/opendevbrowser-best-practices/artifacts/provider-workflows.md +89 -0
- package/skills/opendevbrowser-best-practices/assets/templates/cdp-forward-envelope.json +11 -0
- package/skills/opendevbrowser-best-practices/assets/templates/mode-flag-matrix.json +56 -0
- package/skills/opendevbrowser-best-practices/assets/templates/ops-request-envelope.json +9 -0
- package/skills/opendevbrowser-best-practices/assets/templates/robustness-checklist.json +79 -0
- package/skills/opendevbrowser-best-practices/assets/templates/surface-audit-checklist.json +24 -0
- package/skills/opendevbrowser-best-practices/scripts/odb-workflow.sh +144 -0
- package/skills/opendevbrowser-best-practices/scripts/run-robustness-audit.sh +83 -0
- package/skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh +93 -0
- package/skills/opendevbrowser-continuity-ledger/SKILL.md +67 -23
- package/skills/opendevbrowser-data-extraction/SKILL.md +126 -0
- package/skills/opendevbrowser-data-extraction/artifacts/extraction-workflows.md +31 -0
- package/skills/opendevbrowser-data-extraction/assets/templates/compliance-checklist.md +7 -0
- package/skills/opendevbrowser-data-extraction/assets/templates/extraction-schema.json +17 -0
- package/skills/opendevbrowser-data-extraction/assets/templates/pagination-state.json +11 -0
- package/skills/opendevbrowser-data-extraction/assets/templates/quality-gates.json +10 -0
- package/skills/opendevbrowser-data-extraction/examples/sample-schema.json +19 -0
- package/skills/opendevbrowser-data-extraction/scripts/run-extraction-workflow.sh +83 -0
- package/skills/opendevbrowser-data-extraction/scripts/validate-skill-assets.sh +49 -0
- package/skills/opendevbrowser-form-testing/SKILL.md +143 -0
- package/skills/opendevbrowser-form-testing/artifacts/form-workflows.md +37 -0
- package/skills/opendevbrowser-form-testing/assets/templates/a11y-assertions.md +7 -0
- package/skills/opendevbrowser-form-testing/assets/templates/challenge-decision-tree.json +16 -0
- package/skills/opendevbrowser-form-testing/assets/templates/multi-step-state.json +11 -0
- package/skills/opendevbrowser-form-testing/assets/templates/validation-matrix.json +24 -0
- package/skills/opendevbrowser-form-testing/examples/sample-validation-matrix.json +29 -0
- package/skills/opendevbrowser-form-testing/scripts/run-form-workflow.sh +82 -0
- package/skills/opendevbrowser-form-testing/scripts/validate-skill-assets.sh +49 -0
- package/skills/opendevbrowser-login-automation/SKILL.md +159 -0
- package/skills/opendevbrowser-login-automation/artifacts/login-workflows.md +39 -0
- package/skills/opendevbrowser-login-automation/assets/templates/auth-signals.json +21 -0
- package/skills/opendevbrowser-login-automation/assets/templates/challenge-checkpoint.md +10 -0
- package/skills/opendevbrowser-login-automation/assets/templates/login-scenario-matrix.json +26 -0
- package/skills/opendevbrowser-login-automation/examples/sample-auth-signals.json +14 -0
- package/skills/opendevbrowser-login-automation/scripts/record-auth-signals.sh +18 -0
- package/skills/opendevbrowser-login-automation/scripts/run-login-workflow.sh +99 -0
- package/skills/opendevbrowser-login-automation/scripts/validate-skill-assets.sh +50 -0
- package/skills/opendevbrowser-product-presentation-asset/SKILL.md +98 -0
- package/skills/opendevbrowser-product-presentation-asset/artifacts/asset-pack-assembly.md +23 -0
- package/skills/opendevbrowser-product-presentation-asset/artifacts/ugc-creative-guide.md +21 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/claims-evidence-map.md +5 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/copy.md +5 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/features.md +4 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/manifest.schema.json +14 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/shot-list.md +7 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/ugc-concepts.md +17 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/user-actions.md +7 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/video-assembly.md +18 -0
- package/skills/opendevbrowser-product-presentation-asset/examples/sample-input.json +6 -0
- package/skills/opendevbrowser-product-presentation-asset/examples/sample-manifest.json +18 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/capture-screenshots.sh +9 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/collect-product.sh +14 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/download-images.sh +9 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/render-video-brief.sh +96 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/validate-skill-assets.sh +56 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/write-manifest.sh +43 -0
- package/skills/opendevbrowser-research/SKILL.md +73 -0
- package/skills/opendevbrowser-research/artifacts/research-workflows.md +29 -0
- package/skills/opendevbrowser-research/assets/templates/compact.md +7 -0
- package/skills/opendevbrowser-research/assets/templates/context.json +18 -0
- package/skills/opendevbrowser-research/assets/templates/report.md +9 -0
- package/skills/opendevbrowser-research/examples/sample-input.json +6 -0
- package/skills/opendevbrowser-research/examples/sample-output.md +4 -0
- package/skills/opendevbrowser-research/scripts/render-output.sh +12 -0
- package/skills/opendevbrowser-research/scripts/run-research.sh +23 -0
- package/skills/opendevbrowser-research/scripts/validate-skill-assets.sh +48 -0
- package/skills/opendevbrowser-research/scripts/write-artifacts.sh +29 -0
- package/skills/opendevbrowser-shopping/SKILL.md +118 -0
- package/skills/opendevbrowser-shopping/artifacts/deal-hunting-workflows.md +37 -0
- package/skills/opendevbrowser-shopping/assets/templates/deal-thresholds.json +8 -0
- package/skills/opendevbrowser-shopping/assets/templates/deals-context.json +9 -0
- package/skills/opendevbrowser-shopping/assets/templates/deals-table.md +4 -0
- package/skills/opendevbrowser-shopping/assets/templates/market-analysis.json +30 -0
- package/skills/opendevbrowser-shopping/examples/sample-deals.md +4 -0
- package/skills/opendevbrowser-shopping/examples/sample-query.json +5 -0
- package/skills/opendevbrowser-shopping/scripts/analyze-market.sh +307 -0
- package/skills/opendevbrowser-shopping/scripts/normalize-offers.sh +28 -0
- package/skills/opendevbrowser-shopping/scripts/render-deals.sh +13 -0
- package/skills/opendevbrowser-shopping/scripts/run-deal-hunt.sh +32 -0
- package/skills/opendevbrowser-shopping/scripts/run-shopping.sh +19 -0
- package/skills/opendevbrowser-shopping/scripts/validate-skill-assets.sh +53 -0
- package/dist/chunk-WTFSMBVH.js +0 -2815
- package/dist/chunk-WTFSMBVH.js.map +0 -1
- package/extension/dist/popup.jsx +0 -150
- package/skills/data-extraction/SKILL.md +0 -136
- package/skills/form-testing/SKILL.md +0 -113
- package/skills/login-automation/SKILL.md +0 -98
package/dist/opendevbrowser.js
CHANGED
|
@@ -1,11 +1,351 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DaemonClient,
|
|
3
|
+
ScriptRunner,
|
|
4
|
+
buildAnnotateResult,
|
|
5
|
+
buildBlockerArtifacts,
|
|
6
|
+
classifyBlockerSignal,
|
|
7
|
+
createBrowserFallbackPort,
|
|
8
|
+
createConfiguredProviderRuntime,
|
|
2
9
|
createOpenDevBrowserCore,
|
|
3
|
-
|
|
4
|
-
|
|
10
|
+
createRequestId,
|
|
11
|
+
executeMacroResolution,
|
|
12
|
+
extractExtension,
|
|
13
|
+
fetchDaemonStatusFromMetadata,
|
|
14
|
+
runProductVideoWorkflow,
|
|
15
|
+
runResearchWorkflow,
|
|
16
|
+
runShoppingWorkflow,
|
|
17
|
+
shapeExecutionPayload,
|
|
18
|
+
startDaemon
|
|
19
|
+
} from "./chunk-ST7CO5FA.js";
|
|
20
|
+
import "./chunk-7W3SPXIB.js";
|
|
21
|
+
|
|
22
|
+
// src/cli/remote-manager.ts
|
|
23
|
+
function isLegacyRelayEndpoint(wsEndpoint) {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(wsEndpoint);
|
|
26
|
+
const path = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
|
|
27
|
+
return path === "/cdp";
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
var RemoteManager = class {
|
|
33
|
+
client;
|
|
34
|
+
constructor(client) {
|
|
35
|
+
this.client = client;
|
|
36
|
+
}
|
|
37
|
+
launch(options) {
|
|
38
|
+
return this.client.call("session.launch", options);
|
|
39
|
+
}
|
|
40
|
+
connect(options) {
|
|
41
|
+
return this.client.call("session.connect", options);
|
|
42
|
+
}
|
|
43
|
+
connectRelay(wsEndpoint) {
|
|
44
|
+
return this.client.call(
|
|
45
|
+
"session.connect",
|
|
46
|
+
isLegacyRelayEndpoint(wsEndpoint) ? { wsEndpoint, extensionLegacy: true } : { wsEndpoint }
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
disconnect(sessionId, closeBrowser = false) {
|
|
50
|
+
return this.client.call("session.disconnect", { sessionId, closeBrowser });
|
|
51
|
+
}
|
|
52
|
+
status(sessionId) {
|
|
53
|
+
return this.client.call("session.status", { sessionId });
|
|
54
|
+
}
|
|
55
|
+
cookieImport(sessionId, cookies, strict = true, requestId) {
|
|
56
|
+
return this.client.call("session.cookieImport", {
|
|
57
|
+
sessionId,
|
|
58
|
+
cookies,
|
|
59
|
+
strict,
|
|
60
|
+
requestId
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
cookieList(sessionId, urls, requestId) {
|
|
64
|
+
return this.client.call("session.cookieList", {
|
|
65
|
+
sessionId,
|
|
66
|
+
...urls && urls.length > 0 ? { urls } : {},
|
|
67
|
+
requestId
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
goto(sessionId, url, waitUntil = "load", timeoutMs = 3e4, _sessionOverride, targetId) {
|
|
71
|
+
return this.client.call("nav.goto", {
|
|
72
|
+
sessionId,
|
|
73
|
+
url,
|
|
74
|
+
waitUntil,
|
|
75
|
+
timeoutMs,
|
|
76
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
waitForLoad(sessionId, until, timeoutMs = 3e4, targetId) {
|
|
80
|
+
return this.client.call("nav.wait", {
|
|
81
|
+
sessionId,
|
|
82
|
+
until,
|
|
83
|
+
timeoutMs,
|
|
84
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
waitForRef(sessionId, ref, state = "attached", timeoutMs = 3e4, targetId) {
|
|
88
|
+
return this.client.call("nav.wait", {
|
|
89
|
+
sessionId,
|
|
90
|
+
ref,
|
|
91
|
+
state,
|
|
92
|
+
timeoutMs,
|
|
93
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
snapshot(sessionId, mode, maxChars, cursor, targetId) {
|
|
97
|
+
return this.client.call("nav.snapshot", {
|
|
98
|
+
sessionId,
|
|
99
|
+
mode,
|
|
100
|
+
maxChars,
|
|
101
|
+
cursor,
|
|
102
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
click(sessionId, ref, targetId) {
|
|
106
|
+
return this.client.call("interact.click", {
|
|
107
|
+
sessionId,
|
|
108
|
+
ref,
|
|
109
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
hover(sessionId, ref, targetId) {
|
|
113
|
+
return this.client.call("interact.hover", {
|
|
114
|
+
sessionId,
|
|
115
|
+
ref,
|
|
116
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
press(sessionId, key, ref, targetId) {
|
|
120
|
+
return this.client.call("interact.press", {
|
|
121
|
+
sessionId,
|
|
122
|
+
key,
|
|
123
|
+
ref,
|
|
124
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
check(sessionId, ref, targetId) {
|
|
128
|
+
return this.client.call("interact.check", {
|
|
129
|
+
sessionId,
|
|
130
|
+
ref,
|
|
131
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
uncheck(sessionId, ref, targetId) {
|
|
135
|
+
return this.client.call("interact.uncheck", {
|
|
136
|
+
sessionId,
|
|
137
|
+
ref,
|
|
138
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
type(sessionId, ref, text, clear = false, submit = false, targetId) {
|
|
142
|
+
return this.client.call("interact.type", {
|
|
143
|
+
sessionId,
|
|
144
|
+
ref,
|
|
145
|
+
text,
|
|
146
|
+
clear,
|
|
147
|
+
submit,
|
|
148
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
select(sessionId, ref, values, targetId) {
|
|
152
|
+
return this.client.call("interact.select", {
|
|
153
|
+
sessionId,
|
|
154
|
+
ref,
|
|
155
|
+
values,
|
|
156
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
scroll(sessionId, dy, ref, targetId) {
|
|
160
|
+
return this.client.call("interact.scroll", {
|
|
161
|
+
sessionId,
|
|
162
|
+
dy,
|
|
163
|
+
ref,
|
|
164
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
scrollIntoView(sessionId, ref, targetId) {
|
|
168
|
+
return this.client.call("interact.scrollIntoView", {
|
|
169
|
+
sessionId,
|
|
170
|
+
ref,
|
|
171
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
domGetHtml(sessionId, ref, maxChars = 8e3, targetId) {
|
|
175
|
+
return this.client.call("dom.getHtml", {
|
|
176
|
+
sessionId,
|
|
177
|
+
ref,
|
|
178
|
+
maxChars,
|
|
179
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
domGetText(sessionId, ref, maxChars = 8e3, targetId) {
|
|
183
|
+
return this.client.call("dom.getText", {
|
|
184
|
+
sessionId,
|
|
185
|
+
ref,
|
|
186
|
+
maxChars,
|
|
187
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
domGetAttr(sessionId, ref, name, targetId) {
|
|
191
|
+
return this.client.call("dom.getAttr", {
|
|
192
|
+
sessionId,
|
|
193
|
+
ref,
|
|
194
|
+
name,
|
|
195
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
domGetValue(sessionId, ref, targetId) {
|
|
199
|
+
return this.client.call("dom.getValue", {
|
|
200
|
+
sessionId,
|
|
201
|
+
ref,
|
|
202
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
domIsVisible(sessionId, ref, targetId) {
|
|
206
|
+
return this.client.call("dom.isVisible", {
|
|
207
|
+
sessionId,
|
|
208
|
+
ref,
|
|
209
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
domIsEnabled(sessionId, ref, targetId) {
|
|
213
|
+
return this.client.call("dom.isEnabled", {
|
|
214
|
+
sessionId,
|
|
215
|
+
ref,
|
|
216
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
domIsChecked(sessionId, ref, targetId) {
|
|
220
|
+
return this.client.call("dom.isChecked", {
|
|
221
|
+
sessionId,
|
|
222
|
+
ref,
|
|
223
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
clonePage(sessionId, targetId) {
|
|
227
|
+
return this.client.call("export.clonePage", {
|
|
228
|
+
sessionId,
|
|
229
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
cloneComponent(sessionId, ref, targetId) {
|
|
233
|
+
return this.client.call("export.cloneComponent", {
|
|
234
|
+
sessionId,
|
|
235
|
+
ref,
|
|
236
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
perfMetrics(sessionId, targetId) {
|
|
240
|
+
return this.client.call("devtools.perf", {
|
|
241
|
+
sessionId,
|
|
242
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
screenshot(sessionId, path, targetId) {
|
|
246
|
+
return this.client.call("page.screenshot", {
|
|
247
|
+
sessionId,
|
|
248
|
+
path,
|
|
249
|
+
...typeof targetId === "string" ? { targetId } : {}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
consolePoll(sessionId, sinceSeq, max = 50) {
|
|
253
|
+
return this.client.call("devtools.consolePoll", { sessionId, sinceSeq, max });
|
|
254
|
+
}
|
|
255
|
+
networkPoll(sessionId, sinceSeq, max = 50) {
|
|
256
|
+
return this.client.call("devtools.networkPoll", { sessionId, sinceSeq, max });
|
|
257
|
+
}
|
|
258
|
+
listTargets(sessionId, includeUrls = false) {
|
|
259
|
+
return this.client.call("targets.list", { sessionId, includeUrls });
|
|
260
|
+
}
|
|
261
|
+
useTarget(sessionId, targetId) {
|
|
262
|
+
return this.client.call("targets.use", { sessionId, targetId });
|
|
263
|
+
}
|
|
264
|
+
newTarget(sessionId, url) {
|
|
265
|
+
return this.client.call("targets.new", { sessionId, url });
|
|
266
|
+
}
|
|
267
|
+
closeTarget(sessionId, targetId) {
|
|
268
|
+
return this.client.call("targets.close", { sessionId, targetId });
|
|
269
|
+
}
|
|
270
|
+
page(sessionId, name, url) {
|
|
271
|
+
return this.client.call("page.open", { sessionId, name, url });
|
|
272
|
+
}
|
|
273
|
+
listPages(sessionId) {
|
|
274
|
+
return this.client.call("page.list", { sessionId });
|
|
275
|
+
}
|
|
276
|
+
closePage(sessionId, name) {
|
|
277
|
+
return this.client.call("page.close", { sessionId, name });
|
|
278
|
+
}
|
|
279
|
+
async withPage(_sessionId, _targetId, _fn) {
|
|
280
|
+
throw new Error("Direct annotate is unavailable via daemon-managed sessions.");
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// src/cli/remote-relay.ts
|
|
285
|
+
var emptyStatus = {
|
|
286
|
+
running: false,
|
|
287
|
+
extensionConnected: false,
|
|
288
|
+
extensionHandshakeComplete: false,
|
|
289
|
+
cdpConnected: false,
|
|
290
|
+
annotationConnected: false,
|
|
291
|
+
opsConnected: false,
|
|
292
|
+
pairingRequired: false,
|
|
293
|
+
instanceId: "",
|
|
294
|
+
epoch: 0,
|
|
295
|
+
health: {
|
|
296
|
+
ok: false,
|
|
297
|
+
reason: "relay_down",
|
|
298
|
+
extensionConnected: false,
|
|
299
|
+
extensionHandshakeComplete: false,
|
|
300
|
+
cdpConnected: false,
|
|
301
|
+
annotationConnected: false,
|
|
302
|
+
opsConnected: false,
|
|
303
|
+
pairingRequired: false
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
var RemoteRelay = class {
|
|
307
|
+
client;
|
|
308
|
+
lastStatus = emptyStatus;
|
|
309
|
+
lastCdpUrl = null;
|
|
310
|
+
lastAnnotationUrl = null;
|
|
311
|
+
lastOpsUrl = null;
|
|
312
|
+
constructor(client) {
|
|
313
|
+
this.client = client;
|
|
314
|
+
}
|
|
315
|
+
async refresh() {
|
|
316
|
+
try {
|
|
317
|
+
const status = await this.client.call("relay.status");
|
|
318
|
+
this.lastStatus = status;
|
|
319
|
+
const cdpUrl = await this.client.call("relay.cdpUrl");
|
|
320
|
+
this.lastCdpUrl = typeof cdpUrl === "string" ? cdpUrl : null;
|
|
321
|
+
const annotationUrl = await this.client.call("relay.annotationUrl");
|
|
322
|
+
this.lastAnnotationUrl = typeof annotationUrl === "string" ? annotationUrl : null;
|
|
323
|
+
const opsUrl = await this.client.call("relay.opsUrl");
|
|
324
|
+
this.lastOpsUrl = typeof opsUrl === "string" ? opsUrl : null;
|
|
325
|
+
} catch {
|
|
326
|
+
this.lastStatus = emptyStatus;
|
|
327
|
+
this.lastCdpUrl = null;
|
|
328
|
+
this.lastAnnotationUrl = null;
|
|
329
|
+
this.lastOpsUrl = null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
status() {
|
|
333
|
+
return this.lastStatus;
|
|
334
|
+
}
|
|
335
|
+
getCdpUrl() {
|
|
336
|
+
return this.lastCdpUrl;
|
|
337
|
+
}
|
|
338
|
+
getAnnotationUrl() {
|
|
339
|
+
return this.lastAnnotationUrl;
|
|
340
|
+
}
|
|
341
|
+
getOpsUrl() {
|
|
342
|
+
return this.lastOpsUrl;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
5
345
|
|
|
6
346
|
// src/skills/skill-nudge.ts
|
|
7
347
|
var SKILL_NUDGE_MARKER = "[opendevbrowser:skill-nudge]";
|
|
8
|
-
var SKILL_NUDGE_MESSAGE = `${SKILL_NUDGE_MARKER} If this task likely matches a skill,
|
|
348
|
+
var SKILL_NUDGE_MESSAGE = `${SKILL_NUDGE_MARKER} If this task likely matches a skill, start with skill("opendevbrowser-best-practices", "quick start"). Use another skill only when it is more relevant.`;
|
|
9
349
|
function createSkillNudgeState() {
|
|
10
350
|
return { pending: false, requestedAtMs: null };
|
|
11
351
|
}
|
|
@@ -68,7 +408,7 @@ function consumeContinuityNudge(state, nowMs, maxAgeMs) {
|
|
|
68
408
|
}
|
|
69
409
|
function buildContinuityNudgeMessage(filePath) {
|
|
70
410
|
const target = filePath?.trim() || DEFAULT_FILE_PATH;
|
|
71
|
-
return `${CONTINUITY_NUDGE_MARKER} For long-running tasks, create or update ${target} at the repo root with Goal, Constraints/Assumptions, Key decisions, State (Done/Now/Next), Open questions, and Working set. Keep it brief.`;
|
|
411
|
+
return `${CONTINUITY_NUDGE_MARKER} For long-running tasks, load skill("opendevbrowser-continuity-ledger") and create or update ${target} at the repo root with Goal, Constraints/Assumptions, Key decisions, State (Done/Now/Next), Open questions, and Working set. Keep it brief.`;
|
|
72
412
|
}
|
|
73
413
|
|
|
74
414
|
// src/tools/launch.ts
|
|
@@ -98,7 +438,7 @@ function serializeError(error) {
|
|
|
98
438
|
var z = tool.schema;
|
|
99
439
|
function createLaunchTool(deps) {
|
|
100
440
|
return tool({
|
|
101
|
-
description: "Launch a
|
|
441
|
+
description: "Launch a browser session (extension relay first) and return a sessionId.",
|
|
102
442
|
args: {
|
|
103
443
|
profile: z.string().optional().describe("Profile name for persistent browsing"),
|
|
104
444
|
headless: z.boolean().optional().describe("Run Chrome in headless mode"),
|
|
@@ -108,102 +448,356 @@ function createLaunchTool(deps) {
|
|
|
108
448
|
persistProfile: z.boolean().optional().describe("Persist profile data between sessions"),
|
|
109
449
|
noExtension: z.boolean().optional().describe("Skip extension relay and launch a new browser"),
|
|
110
450
|
extensionOnly: z.boolean().optional().describe("Require extension relay or fail"),
|
|
451
|
+
extensionLegacy: z.boolean().optional().describe("Use legacy extension relay (/cdp) instead of ops"),
|
|
111
452
|
waitForExtension: z.boolean().optional().describe("Wait for extension to connect before launching"),
|
|
112
453
|
waitTimeoutMs: z.number().int().optional().describe("Timeout for waiting on extension (ms)")
|
|
113
454
|
},
|
|
114
455
|
async execute(args) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
456
|
+
let attemptedRebind = false;
|
|
457
|
+
while (true) {
|
|
458
|
+
try {
|
|
459
|
+
await deps.relay?.refresh?.();
|
|
460
|
+
const config = deps.config.get();
|
|
461
|
+
const extensionLegacy = args.extensionLegacy === true;
|
|
462
|
+
let relayStatus = deps.relay?.status();
|
|
463
|
+
let relayUrl = extensionLegacy ? deps.relay?.getCdpUrl() ?? null : deps.relay?.getOpsUrl?.() ?? null;
|
|
464
|
+
const relayPort = relayStatus?.port;
|
|
465
|
+
if (!relayUrl && isValidPort(relayPort)) {
|
|
466
|
+
relayUrl = `ws://127.0.0.1:${relayPort}/${extensionLegacy ? "cdp" : "ops"}`;
|
|
123
467
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
|
|
468
|
+
const waitTimeoutMs = clampWaitTimeout(args.waitTimeoutMs ?? 3e4);
|
|
469
|
+
const headlessExplicit = args.headless === true;
|
|
470
|
+
const managedExplicit = Boolean(args.noExtension || headlessExplicit);
|
|
471
|
+
const managedHeadless = headlessExplicit ? true : false;
|
|
472
|
+
if (headlessExplicit && !args.noExtension) {
|
|
473
|
+
return failure(
|
|
474
|
+
"Extension mode does not support headless launches. Use noExtension=true with headless=true for managed mode.",
|
|
475
|
+
"unsupported_mode"
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
if (args.waitForExtension && !managedExplicit) {
|
|
479
|
+
const observedPort2 = resolveObservedPort(relayStatus, config.relayPort);
|
|
480
|
+
const connected = await waitForExtensionHandshake(deps.relay, observedPort2, waitTimeoutMs);
|
|
481
|
+
if (connected) {
|
|
482
|
+
relayStatus = deps.relay?.status() ?? relayStatus;
|
|
483
|
+
relayUrl = extensionLegacy ? deps.relay?.getCdpUrl() ?? relayUrl : deps.relay?.getOpsUrl?.() ?? relayUrl;
|
|
139
484
|
}
|
|
140
|
-
relayWarning = "Relay connection failed; falling back to managed Chrome.";
|
|
141
485
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
486
|
+
const observedPort = resolveObservedPort(relayStatus, config.relayPort);
|
|
487
|
+
const shouldFetchObserved = !managedExplicit && (!relayUrl || !(relayStatus?.extensionHandshakeComplete || relayStatus?.extensionConnected));
|
|
488
|
+
const observedStatus = shouldFetchObserved ? await fetchRelayObservedStatus(observedPort) : null;
|
|
489
|
+
if (!relayUrl) {
|
|
490
|
+
const fallbackPort = isValidPort(observedStatus?.port) ? observedStatus?.port : observedPort;
|
|
491
|
+
relayUrl = fallbackPort ? `ws://127.0.0.1:${fallbackPort}/${extensionLegacy ? "cdp" : "ops"}` : null;
|
|
492
|
+
}
|
|
493
|
+
const extensionReady = Boolean(
|
|
494
|
+
relayUrl && (relayStatus?.extensionHandshakeComplete || relayStatus?.extensionConnected || observedStatus?.extensionHandshakeComplete || observedStatus?.extensionConnected)
|
|
495
|
+
);
|
|
496
|
+
let usedRelay = false;
|
|
497
|
+
let result = null;
|
|
498
|
+
if (args.extensionOnly && !extensionReady) {
|
|
499
|
+
const diagnostics = buildRelayNotReadyDiagnostics("Extension not connected.", {
|
|
500
|
+
relayUrl,
|
|
501
|
+
relayStatus,
|
|
502
|
+
observedStatus,
|
|
503
|
+
observedPort
|
|
504
|
+
});
|
|
505
|
+
if (await maybeRetryHubMismatch(diagnostics.hint, attemptedRebind, deps)) {
|
|
506
|
+
attemptedRebind = true;
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
return failure(buildExtensionMissingMessage(diagnostics.message), "extension_not_connected");
|
|
510
|
+
}
|
|
511
|
+
if (!managedExplicit) {
|
|
512
|
+
if (!extensionReady || !relayUrl) {
|
|
513
|
+
const diagnostics = buildRelayNotReadyDiagnostics("Extension not connected.", {
|
|
514
|
+
relayUrl,
|
|
515
|
+
relayStatus,
|
|
516
|
+
observedStatus,
|
|
517
|
+
observedPort
|
|
518
|
+
});
|
|
519
|
+
if (await maybeRetryHubMismatch(diagnostics.hint, attemptedRebind, deps)) {
|
|
520
|
+
attemptedRebind = true;
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
return failure(buildExtensionMissingMessage(diagnostics.message), "extension_not_connected");
|
|
524
|
+
}
|
|
525
|
+
try {
|
|
526
|
+
result = await deps.manager.connectRelay(relayUrl);
|
|
527
|
+
usedRelay = true;
|
|
528
|
+
} catch (error) {
|
|
529
|
+
const errorMessage = serializeError(error).message;
|
|
530
|
+
const unauthorized = errorMessage.toLowerCase().includes("unauthorized") || errorMessage.includes("401");
|
|
531
|
+
const relayLabel = extensionLegacy ? "/cdp" : "/ops";
|
|
532
|
+
const errorObservedStatus = observedStatus ?? await fetchRelayObservedStatus(observedPort);
|
|
533
|
+
const diagnostics = buildRelayNotReadyDiagnostics(
|
|
534
|
+
unauthorized ? `Extension relay connection failed: relay ${relayLabel} unauthorized (token mismatch).` : `Extension relay connection failed: ${errorMessage}`,
|
|
535
|
+
{
|
|
536
|
+
relayUrl,
|
|
537
|
+
relayStatus,
|
|
538
|
+
observedStatus: errorObservedStatus,
|
|
539
|
+
observedPort
|
|
540
|
+
}
|
|
541
|
+
);
|
|
542
|
+
if (await maybeRetryHubMismatch(diagnostics.hint, attemptedRebind, deps)) {
|
|
543
|
+
attemptedRebind = true;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
return failure(buildExtensionMissingMessage(diagnostics.message), "extension_connect_failed");
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (!result) {
|
|
550
|
+
try {
|
|
551
|
+
result = await deps.manager.launch({
|
|
552
|
+
profile: args.profile,
|
|
553
|
+
headless: managedHeadless,
|
|
554
|
+
startUrl: args.startUrl,
|
|
555
|
+
chromePath: args.chromePath,
|
|
556
|
+
flags: args.flags,
|
|
557
|
+
persistProfile: args.persistProfile,
|
|
558
|
+
noExtension: args.noExtension
|
|
559
|
+
});
|
|
560
|
+
} catch (error) {
|
|
561
|
+
return failure(buildManagedFailureMessage(error), "launch_failed");
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (usedRelay && args.startUrl && result.activeTargetId) {
|
|
565
|
+
await deps.manager.goto(result.sessionId, args.startUrl, "load", 3e4);
|
|
146
566
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
567
|
+
const warnings = result.warnings ?? [];
|
|
568
|
+
return ok({
|
|
569
|
+
sessionId: result.sessionId,
|
|
570
|
+
mode: result.mode,
|
|
571
|
+
browserWsEndpoint: result.wsEndpoint,
|
|
572
|
+
activeTargetId: result.activeTargetId,
|
|
573
|
+
warnings: warnings.length ? warnings : void 0
|
|
154
574
|
});
|
|
575
|
+
} catch (error) {
|
|
576
|
+
return failure(serializeError(error).message, "launch_failed");
|
|
155
577
|
}
|
|
156
|
-
if (usedRelay && args.startUrl && result.activeTargetId) {
|
|
157
|
-
await deps.manager.goto(result.sessionId, args.startUrl, "load", 3e4);
|
|
158
|
-
}
|
|
159
|
-
const warnings = [
|
|
160
|
-
...result.warnings ?? [],
|
|
161
|
-
...relayWarning ? [relayWarning] : []
|
|
162
|
-
];
|
|
163
|
-
return ok({
|
|
164
|
-
sessionId: result.sessionId,
|
|
165
|
-
mode: result.mode,
|
|
166
|
-
browserWsEndpoint: result.wsEndpoint,
|
|
167
|
-
activeTargetId: result.activeTargetId,
|
|
168
|
-
warnings: warnings.length ? warnings : void 0
|
|
169
|
-
});
|
|
170
|
-
} catch (error) {
|
|
171
|
-
return failure(serializeError(error).message, "launch_failed");
|
|
172
578
|
}
|
|
173
579
|
}
|
|
174
580
|
});
|
|
175
581
|
}
|
|
176
|
-
|
|
582
|
+
var buildExtensionMissingMessage = (reason) => {
|
|
583
|
+
return [
|
|
584
|
+
reason,
|
|
585
|
+
"Connect the extension: open the Chrome extension popup and click Connect, then retry.",
|
|
586
|
+
"Tip: If the popup says Connected, it may be connected to a different relay instance/port than this tool expects.",
|
|
587
|
+
"Legend: ext=extension websocket, handshake=extension handshake, ops=active /ops client, cdp=active /cdp client, pairing=token required.",
|
|
588
|
+
"",
|
|
589
|
+
"Other options (explicit):",
|
|
590
|
+
"- Managed (headed): npx opendevbrowser launch --no-extension",
|
|
591
|
+
"- Managed (headless): npx opendevbrowser launch --no-extension --headless",
|
|
592
|
+
"- Legacy extension relay: npx opendevbrowser launch --extension-legacy",
|
|
593
|
+
"- CDPConnect (default port): npx opendevbrowser connect --cdp-port 9222",
|
|
594
|
+
"- CDPConnect (explicit WS): npx opendevbrowser connect --ws-endpoint ws://127.0.0.1:9222/devtools/browser/<id>",
|
|
595
|
+
"Note: CDPConnect requires Chrome started with --remote-debugging-port=9222."
|
|
596
|
+
].join("\n");
|
|
597
|
+
};
|
|
598
|
+
var buildManagedFailureMessage = (error) => {
|
|
599
|
+
const detail = serializeError(error).message;
|
|
600
|
+
return [
|
|
601
|
+
`Managed session failed: ${detail}`,
|
|
602
|
+
"",
|
|
603
|
+
"Final option (explicit):",
|
|
604
|
+
"- CDPConnect (default port): npx opendevbrowser connect --cdp-port 9222",
|
|
605
|
+
"- CDPConnect (explicit WS): npx opendevbrowser connect --ws-endpoint ws://127.0.0.1:9222/devtools/browser/<id>"
|
|
606
|
+
].join("\n");
|
|
607
|
+
};
|
|
608
|
+
var MIN_WAIT_TIMEOUT_MS = 3e3;
|
|
609
|
+
var WAIT_MIN_DELAY_MS = 250;
|
|
610
|
+
var WAIT_MAX_DELAY_MS = 2e3;
|
|
611
|
+
function clampWaitTimeout(timeoutMs) {
|
|
612
|
+
if (!Number.isFinite(timeoutMs)) {
|
|
613
|
+
return MIN_WAIT_TIMEOUT_MS;
|
|
614
|
+
}
|
|
615
|
+
return Math.max(timeoutMs, MIN_WAIT_TIMEOUT_MS);
|
|
616
|
+
}
|
|
617
|
+
async function waitForExtensionHandshake(relay, observedPort, timeoutMs) {
|
|
177
618
|
const start = Date.now();
|
|
619
|
+
let delay = WAIT_MIN_DELAY_MS;
|
|
178
620
|
while (Date.now() - start < timeoutMs) {
|
|
179
|
-
if (relay
|
|
621
|
+
if (relay?.status().extensionHandshakeComplete) {
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
const observedStatus = await fetchRelayObservedStatus(observedPort);
|
|
625
|
+
if (observedStatus?.extensionHandshakeComplete) {
|
|
180
626
|
return true;
|
|
181
627
|
}
|
|
182
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
628
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
629
|
+
delay = Math.min(delay * 2, WAIT_MAX_DELAY_MS);
|
|
183
630
|
}
|
|
184
631
|
return false;
|
|
185
632
|
}
|
|
633
|
+
function resolveObservedPort(relayStatus, configPort) {
|
|
634
|
+
const relayPort = relayStatus?.port;
|
|
635
|
+
if (isValidPort(relayPort)) return relayPort;
|
|
636
|
+
if (isValidPort(configPort)) return configPort;
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
function isValidPort(port) {
|
|
640
|
+
return typeof port === "number" && Number.isInteger(port) && port > 0 && port <= 65535;
|
|
641
|
+
}
|
|
642
|
+
function shortInstanceId(value) {
|
|
643
|
+
if (!value) return "?";
|
|
644
|
+
return value.slice(0, 8);
|
|
645
|
+
}
|
|
646
|
+
function formatRelayUrl(relayUrl) {
|
|
647
|
+
return relayUrl ?? "null";
|
|
648
|
+
}
|
|
649
|
+
function formatLocalStatus(status) {
|
|
650
|
+
return [
|
|
651
|
+
"local(instance=",
|
|
652
|
+
shortInstanceId(status?.instanceId),
|
|
653
|
+
" port=",
|
|
654
|
+
typeof status?.port === "number" ? String(status.port) : "?",
|
|
655
|
+
" ext=",
|
|
656
|
+
String(Boolean(status?.extensionConnected)),
|
|
657
|
+
" handshake=",
|
|
658
|
+
String(Boolean(status?.extensionHandshakeComplete)),
|
|
659
|
+
" ops=",
|
|
660
|
+
String(Boolean(status?.opsConnected)),
|
|
661
|
+
" cdp=",
|
|
662
|
+
String(Boolean(status?.cdpConnected)),
|
|
663
|
+
" pairing=",
|
|
664
|
+
String(Boolean(status?.pairingRequired)),
|
|
665
|
+
")"
|
|
666
|
+
].join("");
|
|
667
|
+
}
|
|
668
|
+
function formatObservedStatus(status, port) {
|
|
669
|
+
const label = port ?? "?";
|
|
670
|
+
if (!status) {
|
|
671
|
+
return `observed@${label}=none`;
|
|
672
|
+
}
|
|
673
|
+
return [
|
|
674
|
+
"observed@",
|
|
675
|
+
label,
|
|
676
|
+
"=instance=",
|
|
677
|
+
shortInstanceId(status.instanceId),
|
|
678
|
+
" ext=",
|
|
679
|
+
String(Boolean(status.extensionConnected)),
|
|
680
|
+
" handshake=",
|
|
681
|
+
String(Boolean(status.extensionHandshakeComplete)),
|
|
682
|
+
" ops=",
|
|
683
|
+
String(Boolean(status.opsConnected)),
|
|
684
|
+
" cdp=",
|
|
685
|
+
String(Boolean(status.cdpConnected)),
|
|
686
|
+
" pairing=",
|
|
687
|
+
String(Boolean(status.pairingRequired))
|
|
688
|
+
].join("");
|
|
689
|
+
}
|
|
690
|
+
function buildRelayNotReadyDiagnostics(baseReason, detail) {
|
|
691
|
+
const localExt = Boolean(detail.relayStatus?.extensionConnected);
|
|
692
|
+
const observedExt = Boolean(detail.observedStatus?.extensionConnected);
|
|
693
|
+
let hint = "none";
|
|
694
|
+
if (detail.relayUrl === null) {
|
|
695
|
+
hint = "relayUrl_null";
|
|
696
|
+
} else if (detail.observedStatus && !localExt && observedExt) {
|
|
697
|
+
hint = "possible_mismatch";
|
|
698
|
+
} else if (detail.relayStatus?.instanceId && detail.observedStatus?.instanceId && detail.relayStatus.instanceId !== detail.observedStatus.instanceId) {
|
|
699
|
+
hint = "possible_mismatch";
|
|
700
|
+
}
|
|
701
|
+
const diagnostics = [
|
|
702
|
+
"Diagnostics: relayUrl=",
|
|
703
|
+
formatRelayUrl(detail.relayUrl),
|
|
704
|
+
" ",
|
|
705
|
+
formatLocalStatus(detail.relayStatus),
|
|
706
|
+
" ",
|
|
707
|
+
formatObservedStatus(detail.observedStatus, detail.observedPort),
|
|
708
|
+
" hint=",
|
|
709
|
+
hint
|
|
710
|
+
].join("");
|
|
711
|
+
return { message: [baseReason, diagnostics].join("\n"), hint };
|
|
712
|
+
}
|
|
713
|
+
async function maybeRetryHubMismatch(hint, attempted, deps) {
|
|
714
|
+
if (attempted) return false;
|
|
715
|
+
if (hint !== "possible_mismatch") return false;
|
|
716
|
+
if (!deps.ensureHub) return false;
|
|
717
|
+
await deps.ensureHub();
|
|
718
|
+
await deps.relay?.refresh?.();
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
721
|
+
async function fetchRelayObservedStatus(port) {
|
|
722
|
+
if (!isValidPort(port)) return null;
|
|
723
|
+
if (typeof fetch !== "function") return null;
|
|
724
|
+
const controller = new AbortController();
|
|
725
|
+
const timeoutId = setTimeout(() => controller.abort(), 500);
|
|
726
|
+
try {
|
|
727
|
+
const response = await fetch(`http://127.0.0.1:${port}/status`, { signal: controller.signal });
|
|
728
|
+
if (!response.ok) return null;
|
|
729
|
+
const payload = await response.json();
|
|
730
|
+
if (!payload || typeof payload.instanceId !== "string") return null;
|
|
731
|
+
return {
|
|
732
|
+
instanceId: payload.instanceId,
|
|
733
|
+
running: Boolean(payload.running),
|
|
734
|
+
port: typeof payload.port === "number" ? payload.port : void 0,
|
|
735
|
+
extensionConnected: Boolean(payload.extensionConnected),
|
|
736
|
+
extensionHandshakeComplete: Boolean(payload.extensionHandshakeComplete),
|
|
737
|
+
cdpConnected: Boolean(payload.cdpConnected),
|
|
738
|
+
opsConnected: Boolean(payload.opsConnected),
|
|
739
|
+
pairingRequired: Boolean(payload.pairingRequired)
|
|
740
|
+
};
|
|
741
|
+
} catch {
|
|
742
|
+
return null;
|
|
743
|
+
} finally {
|
|
744
|
+
clearTimeout(timeoutId);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
186
747
|
|
|
187
748
|
// src/tools/connect.ts
|
|
188
749
|
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
189
750
|
var z2 = tool2.schema;
|
|
751
|
+
function normalizeRelayEndpoint(wsEndpoint, path, allowBase) {
|
|
752
|
+
if (!wsEndpoint) return null;
|
|
753
|
+
try {
|
|
754
|
+
const url = new URL(wsEndpoint);
|
|
755
|
+
if (url.protocol !== "ws:" && url.protocol !== "wss:") return null;
|
|
756
|
+
if (url.hostname !== "127.0.0.1" && url.hostname !== "localhost") return null;
|
|
757
|
+
if (!url.port || !/^\d+$/.test(url.port)) return null;
|
|
758
|
+
const normalizedPath = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
|
|
759
|
+
if (!allowBase && normalizedPath === "") return null;
|
|
760
|
+
if (normalizedPath && normalizedPath !== `/${path}`) return null;
|
|
761
|
+
return `${url.protocol}//${url.hostname}:${url.port}/${path}`;
|
|
762
|
+
} catch {
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
190
766
|
function createConnectTool(deps) {
|
|
191
767
|
return tool2({
|
|
192
|
-
description: "Connect to an existing Chrome CDP endpoint.",
|
|
768
|
+
description: "Connect to an existing Chrome CDP endpoint or extension relay.",
|
|
193
769
|
args: {
|
|
194
770
|
wsEndpoint: z2.string().optional().describe("Full WebSocket endpoint to connect to"),
|
|
195
771
|
host: z2.string().optional().describe("Host for /json/version lookup"),
|
|
196
|
-
port: z2.number().int().optional().describe("Port for /json/version lookup")
|
|
772
|
+
port: z2.number().int().optional().describe("Port for /json/version lookup"),
|
|
773
|
+
extensionLegacy: z2.boolean().optional().describe("Use legacy extension relay (/cdp) instead of ops")
|
|
197
774
|
},
|
|
198
775
|
async execute(args) {
|
|
199
776
|
try {
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
777
|
+
await deps.relay?.refresh?.();
|
|
778
|
+
const wsEndpoint = args.wsEndpoint;
|
|
779
|
+
const extensionLegacy = args.extensionLegacy === true;
|
|
780
|
+
const hasExplicitCdp = Boolean(wsEndpoint || args.host || args.port);
|
|
781
|
+
const relayUrl = extensionLegacy ? deps.relay?.getCdpUrl() ?? null : deps.relay?.getOpsUrl?.() ?? null;
|
|
782
|
+
const normalizedOpsEndpoint = normalizeRelayEndpoint(wsEndpoint, "ops", true);
|
|
783
|
+
const normalizedLegacyEndpoint = normalizeRelayEndpoint(wsEndpoint, "cdp", extensionLegacy);
|
|
784
|
+
if (normalizedLegacyEndpoint && !extensionLegacy) {
|
|
785
|
+
return failure("Legacy extension relay (/cdp) requires extensionLegacy=true.", "extension_legacy_required");
|
|
786
|
+
}
|
|
787
|
+
const relayEndpoint = relayUrl && wsEndpoint === relayUrl ? relayUrl : extensionLegacy ? normalizedLegacyEndpoint ?? normalizedOpsEndpoint : normalizedOpsEndpoint;
|
|
788
|
+
let result;
|
|
789
|
+
if (relayEndpoint || !hasExplicitCdp && relayUrl) {
|
|
790
|
+
result = await deps.manager.connectRelay(relayEndpoint ?? relayUrl ?? "");
|
|
791
|
+
} else {
|
|
792
|
+
if (!hasExplicitCdp) {
|
|
793
|
+
return failure("Extension relay not available. Connect the extension or pass wsEndpoint/host/port.", "extension_not_connected");
|
|
794
|
+
}
|
|
795
|
+
result = await deps.manager.connect({
|
|
796
|
+
wsEndpoint,
|
|
797
|
+
host: args.host,
|
|
798
|
+
port: args.port
|
|
799
|
+
});
|
|
800
|
+
}
|
|
207
801
|
return ok({
|
|
208
802
|
sessionId: result.sessionId,
|
|
209
803
|
mode: result.mode,
|
|
@@ -244,6 +838,13 @@ import { readFileSync } from "fs";
|
|
|
244
838
|
import { dirname, join } from "path";
|
|
245
839
|
import { fileURLToPath } from "url";
|
|
246
840
|
import { tool as tool4 } from "@opencode-ai/plugin";
|
|
841
|
+
|
|
842
|
+
// src/utils/hub-enabled.ts
|
|
843
|
+
var isHubEnabled = (config) => {
|
|
844
|
+
return config.relayToken !== false && config.relayPort > 0;
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
// src/tools/status.ts
|
|
247
848
|
var z4 = tool4.schema;
|
|
248
849
|
function getPackageVersion() {
|
|
249
850
|
try {
|
|
@@ -285,17 +886,45 @@ async function fetchLatestVersion(packageName) {
|
|
|
285
886
|
}
|
|
286
887
|
function createStatusTool(deps) {
|
|
287
888
|
return tool4({
|
|
288
|
-
description: "Get
|
|
889
|
+
description: "Get daemon or session status.",
|
|
289
890
|
args: {
|
|
290
|
-
sessionId: z4.string().describe("Session id")
|
|
891
|
+
sessionId: z4.string().optional().describe("Session id (required when hub is disabled)")
|
|
291
892
|
},
|
|
292
893
|
async execute(args) {
|
|
293
894
|
try {
|
|
294
|
-
const status = await deps.manager.status(args.sessionId);
|
|
295
|
-
const extensionPath = deps.getExtensionPath?.() ?? null;
|
|
296
895
|
const config = deps.config.get();
|
|
896
|
+
const hubEnabled = isHubEnabled(config);
|
|
897
|
+
const extensionPath = deps.getExtensionPath?.() ?? null;
|
|
297
898
|
const version = getPackageVersion();
|
|
298
899
|
let updateHint;
|
|
900
|
+
let sessionStatus = null;
|
|
901
|
+
if (hubEnabled) {
|
|
902
|
+
const daemonStatus = await fetchDaemonStatusFromMetadata();
|
|
903
|
+
if (!daemonStatus) {
|
|
904
|
+
return failure("Daemon not running. Start with `npx opendevbrowser serve`.", "status_failed");
|
|
905
|
+
}
|
|
906
|
+
if (args.sessionId) {
|
|
907
|
+
sessionStatus = await deps.manager.status(args.sessionId);
|
|
908
|
+
}
|
|
909
|
+
if (config.checkForUpdates && version) {
|
|
910
|
+
const latest = await fetchLatestVersion("opendevbrowser");
|
|
911
|
+
if (latest && latest !== version) {
|
|
912
|
+
updateHint = `Update available: ${version} -> ${latest}`;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return ok({
|
|
916
|
+
...sessionStatus ?? {},
|
|
917
|
+
daemon: daemonStatus,
|
|
918
|
+
hubEnabled: true,
|
|
919
|
+
extensionPath: extensionPath ?? void 0,
|
|
920
|
+
version,
|
|
921
|
+
updateHint
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
if (!args.sessionId) {
|
|
925
|
+
return failure("Missing sessionId for status.", "status_failed");
|
|
926
|
+
}
|
|
927
|
+
sessionStatus = await deps.manager.status(args.sessionId);
|
|
299
928
|
if (config.checkForUpdates && version) {
|
|
300
929
|
const latest = await fetchLatestVersion("opendevbrowser");
|
|
301
930
|
if (latest && latest !== version) {
|
|
@@ -303,10 +932,10 @@ function createStatusTool(deps) {
|
|
|
303
932
|
}
|
|
304
933
|
}
|
|
305
934
|
return ok({
|
|
306
|
-
mode:
|
|
307
|
-
activeTargetId:
|
|
308
|
-
url:
|
|
309
|
-
title:
|
|
935
|
+
mode: sessionStatus.mode,
|
|
936
|
+
activeTargetId: sessionStatus.activeTargetId,
|
|
937
|
+
url: sessionStatus.url,
|
|
938
|
+
title: sessionStatus.title,
|
|
310
939
|
extensionPath: extensionPath ?? void 0,
|
|
311
940
|
version,
|
|
312
941
|
updateHint
|
|
@@ -599,266 +1228,921 @@ function createClickTool(deps) {
|
|
|
599
1228
|
});
|
|
600
1229
|
}
|
|
601
1230
|
|
|
602
|
-
// src/tools/
|
|
1231
|
+
// src/tools/hover.ts
|
|
603
1232
|
import { tool as tool16 } from "@opencode-ai/plugin";
|
|
604
1233
|
var z16 = tool16.schema;
|
|
605
|
-
function
|
|
1234
|
+
function createHoverTool(deps) {
|
|
606
1235
|
return tool16({
|
|
607
|
-
description: "
|
|
1236
|
+
description: "Hover over an element by ref.",
|
|
608
1237
|
args: {
|
|
609
|
-
sessionId: z16.string().describe("
|
|
610
|
-
ref: z16.string().describe("Element ref")
|
|
611
|
-
text: z16.string().describe("Text to type"),
|
|
612
|
-
clear: z16.boolean().optional().describe("Clear before typing"),
|
|
613
|
-
submit: z16.boolean().optional().describe("Press Enter after typing")
|
|
1238
|
+
sessionId: z16.string().describe("Active browser session id"),
|
|
1239
|
+
ref: z16.string().describe("Element ref from snapshot")
|
|
614
1240
|
},
|
|
615
1241
|
async execute(args) {
|
|
616
1242
|
try {
|
|
617
|
-
const result = await deps.manager.
|
|
618
|
-
args.sessionId,
|
|
619
|
-
args.ref,
|
|
620
|
-
args.text,
|
|
621
|
-
Boolean(args.clear),
|
|
622
|
-
Boolean(args.submit)
|
|
623
|
-
);
|
|
1243
|
+
const result = await deps.manager.hover(args.sessionId, args.ref);
|
|
624
1244
|
return ok(result);
|
|
625
1245
|
} catch (error) {
|
|
626
|
-
return failure(serializeError(error).message, "
|
|
1246
|
+
return failure(serializeError(error).message, "hover_failed");
|
|
627
1247
|
}
|
|
628
1248
|
}
|
|
629
1249
|
});
|
|
630
1250
|
}
|
|
631
1251
|
|
|
632
|
-
// src/tools/
|
|
1252
|
+
// src/tools/press.ts
|
|
633
1253
|
import { tool as tool17 } from "@opencode-ai/plugin";
|
|
634
1254
|
var z17 = tool17.schema;
|
|
635
|
-
function
|
|
1255
|
+
function createPressTool(deps) {
|
|
636
1256
|
return tool17({
|
|
637
|
-
description: "
|
|
1257
|
+
description: "Press a keyboard key, optionally focusing a ref first.",
|
|
638
1258
|
args: {
|
|
639
|
-
sessionId: z17.string().describe("
|
|
640
|
-
|
|
641
|
-
|
|
1259
|
+
sessionId: z17.string().describe("Active browser session id"),
|
|
1260
|
+
key: z17.string().describe("Keyboard key to press, e.g. Enter or ArrowDown"),
|
|
1261
|
+
ref: z17.string().optional().describe("Optional element ref to focus first")
|
|
642
1262
|
},
|
|
643
1263
|
async execute(args) {
|
|
644
1264
|
try {
|
|
645
|
-
await deps.manager.
|
|
646
|
-
return ok(
|
|
1265
|
+
const result = await deps.manager.press(args.sessionId, args.key, args.ref);
|
|
1266
|
+
return ok(result);
|
|
647
1267
|
} catch (error) {
|
|
648
|
-
return failure(serializeError(error).message, "
|
|
1268
|
+
return failure(serializeError(error).message, "press_failed");
|
|
649
1269
|
}
|
|
650
1270
|
}
|
|
651
1271
|
});
|
|
652
1272
|
}
|
|
653
1273
|
|
|
654
|
-
// src/tools/
|
|
1274
|
+
// src/tools/check.ts
|
|
655
1275
|
import { tool as tool18 } from "@opencode-ai/plugin";
|
|
656
1276
|
var z18 = tool18.schema;
|
|
657
|
-
function
|
|
1277
|
+
function createCheckTool(deps) {
|
|
658
1278
|
return tool18({
|
|
659
|
-
description: "
|
|
1279
|
+
description: "Check a checkbox or toggle by ref.",
|
|
660
1280
|
args: {
|
|
661
|
-
sessionId: z18.string().describe("
|
|
662
|
-
|
|
663
|
-
ref: z18.string().optional().describe("Optional element ref to scroll")
|
|
1281
|
+
sessionId: z18.string().describe("Active browser session id"),
|
|
1282
|
+
ref: z18.string().describe("Element ref from snapshot")
|
|
664
1283
|
},
|
|
665
1284
|
async execute(args) {
|
|
666
1285
|
try {
|
|
667
|
-
await deps.manager.
|
|
668
|
-
return ok(
|
|
1286
|
+
const result = await deps.manager.check(args.sessionId, args.ref);
|
|
1287
|
+
return ok(result);
|
|
669
1288
|
} catch (error) {
|
|
670
|
-
return failure(serializeError(error).message, "
|
|
1289
|
+
return failure(serializeError(error).message, "check_failed");
|
|
671
1290
|
}
|
|
672
1291
|
}
|
|
673
1292
|
});
|
|
674
1293
|
}
|
|
675
1294
|
|
|
676
|
-
// src/tools/
|
|
1295
|
+
// src/tools/uncheck.ts
|
|
677
1296
|
import { tool as tool19 } from "@opencode-ai/plugin";
|
|
678
1297
|
var z19 = tool19.schema;
|
|
679
|
-
function
|
|
1298
|
+
function createUncheckTool(deps) {
|
|
680
1299
|
return tool19({
|
|
681
|
-
description: "
|
|
1300
|
+
description: "Uncheck a checkbox or toggle by ref.",
|
|
682
1301
|
args: {
|
|
683
|
-
sessionId: z19.string().describe("
|
|
684
|
-
ref: z19.string().describe("Element ref")
|
|
685
|
-
maxChars: z19.number().int().optional().describe("Max characters")
|
|
1302
|
+
sessionId: z19.string().describe("Active browser session id"),
|
|
1303
|
+
ref: z19.string().describe("Element ref from snapshot")
|
|
686
1304
|
},
|
|
687
1305
|
async execute(args) {
|
|
688
1306
|
try {
|
|
689
|
-
const result = await deps.manager.
|
|
690
|
-
|
|
691
|
-
args.ref,
|
|
692
|
-
args.maxChars ?? 8e3
|
|
693
|
-
);
|
|
694
|
-
return ok({
|
|
695
|
-
ref: args.ref,
|
|
696
|
-
outerHTML: result.outerHTML,
|
|
697
|
-
truncated: result.truncated
|
|
698
|
-
});
|
|
1307
|
+
const result = await deps.manager.uncheck(args.sessionId, args.ref);
|
|
1308
|
+
return ok(result);
|
|
699
1309
|
} catch (error) {
|
|
700
|
-
return failure(serializeError(error).message, "
|
|
1310
|
+
return failure(serializeError(error).message, "uncheck_failed");
|
|
701
1311
|
}
|
|
702
1312
|
}
|
|
703
1313
|
});
|
|
704
1314
|
}
|
|
705
1315
|
|
|
706
|
-
// src/tools/
|
|
1316
|
+
// src/tools/type.ts
|
|
707
1317
|
import { tool as tool20 } from "@opencode-ai/plugin";
|
|
708
1318
|
var z20 = tool20.schema;
|
|
709
|
-
function
|
|
1319
|
+
function createTypeTool(deps) {
|
|
710
1320
|
return tool20({
|
|
711
|
-
description: "
|
|
1321
|
+
description: "Type text into a referenced input.",
|
|
712
1322
|
args: {
|
|
713
1323
|
sessionId: z20.string().describe("Session id"),
|
|
714
1324
|
ref: z20.string().describe("Element ref"),
|
|
715
|
-
|
|
1325
|
+
text: z20.string().describe("Text to type"),
|
|
1326
|
+
clear: z20.boolean().optional().describe("Clear before typing"),
|
|
1327
|
+
submit: z20.boolean().optional().describe("Press Enter after typing")
|
|
716
1328
|
},
|
|
717
1329
|
async execute(args) {
|
|
718
1330
|
try {
|
|
719
|
-
const result = await deps.manager.
|
|
1331
|
+
const result = await deps.manager.type(
|
|
720
1332
|
args.sessionId,
|
|
721
1333
|
args.ref,
|
|
722
|
-
args.
|
|
1334
|
+
args.text,
|
|
1335
|
+
Boolean(args.clear),
|
|
1336
|
+
Boolean(args.submit)
|
|
723
1337
|
);
|
|
724
|
-
return ok(
|
|
725
|
-
ref: args.ref,
|
|
726
|
-
text: result.text,
|
|
727
|
-
truncated: result.truncated
|
|
728
|
-
});
|
|
1338
|
+
return ok(result);
|
|
729
1339
|
} catch (error) {
|
|
730
|
-
return failure(serializeError(error).message, "
|
|
1340
|
+
return failure(serializeError(error).message, "type_failed");
|
|
731
1341
|
}
|
|
732
1342
|
}
|
|
733
1343
|
});
|
|
734
1344
|
}
|
|
735
1345
|
|
|
736
|
-
// src/tools/
|
|
1346
|
+
// src/tools/select.ts
|
|
737
1347
|
import { tool as tool21 } from "@opencode-ai/plugin";
|
|
738
1348
|
var z21 = tool21.schema;
|
|
739
|
-
|
|
740
|
-
action: z21.string().describe("Action name"),
|
|
741
|
-
args: z21.record(z21.string(), z21.unknown()).optional().describe("Action arguments")
|
|
742
|
-
});
|
|
743
|
-
function createRunTool(deps) {
|
|
1349
|
+
function createSelectTool(deps) {
|
|
744
1350
|
return tool21({
|
|
745
|
-
description: "
|
|
1351
|
+
description: "Select options in a referenced select element.",
|
|
746
1352
|
args: {
|
|
747
1353
|
sessionId: z21.string().describe("Session id"),
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
maxSnapshotChars: z21.number().int().optional().describe("Default maxChars for snapshot steps")
|
|
1354
|
+
ref: z21.string().describe("Element ref"),
|
|
1355
|
+
values: z21.array(z21.string()).describe("Values to select")
|
|
751
1356
|
},
|
|
752
1357
|
async execute(args) {
|
|
753
1358
|
try {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
args.sessionId,
|
|
757
|
-
steps,
|
|
758
|
-
args.stopOnError ?? true
|
|
759
|
-
);
|
|
760
|
-
return ok(result);
|
|
1359
|
+
await deps.manager.select(args.sessionId, args.ref, args.values);
|
|
1360
|
+
return ok({});
|
|
761
1361
|
} catch (error) {
|
|
762
|
-
return failure(serializeError(error).message, "
|
|
1362
|
+
return failure(serializeError(error).message, "select_failed");
|
|
763
1363
|
}
|
|
764
1364
|
}
|
|
765
1365
|
});
|
|
766
1366
|
}
|
|
767
|
-
function normalizeSteps(steps, maxSnapshotChars) {
|
|
768
|
-
if (!maxSnapshotChars) return steps;
|
|
769
|
-
return steps.map((step) => {
|
|
770
|
-
if (step.action !== "snapshot") return step;
|
|
771
|
-
if (step.args && typeof step.args.maxChars === "number") return step;
|
|
772
|
-
return {
|
|
773
|
-
...step,
|
|
774
|
-
args: {
|
|
775
|
-
...step.args,
|
|
776
|
-
maxChars: maxSnapshotChars
|
|
777
|
-
}
|
|
778
|
-
};
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
1367
|
|
|
782
|
-
// src/tools/
|
|
1368
|
+
// src/tools/scroll.ts
|
|
783
1369
|
import { tool as tool22 } from "@opencode-ai/plugin";
|
|
784
1370
|
var z22 = tool22.schema;
|
|
785
|
-
function
|
|
1371
|
+
function createScrollTool(deps) {
|
|
786
1372
|
return tool22({
|
|
787
|
-
description: "
|
|
1373
|
+
description: "Scroll the page or a referenced element.",
|
|
788
1374
|
args: {
|
|
789
|
-
|
|
1375
|
+
sessionId: z22.string().describe("Session id"),
|
|
1376
|
+
dy: z22.number().describe("Scroll delta in pixels"),
|
|
1377
|
+
ref: z22.string().optional().describe("Optional element ref to scroll")
|
|
790
1378
|
},
|
|
791
1379
|
async execute(args) {
|
|
792
1380
|
try {
|
|
793
|
-
|
|
794
|
-
return ok({
|
|
1381
|
+
await deps.manager.scroll(args.sessionId, args.dy, args.ref);
|
|
1382
|
+
return ok({});
|
|
795
1383
|
} catch (error) {
|
|
796
|
-
return failure(serializeError(error).message, "
|
|
1384
|
+
return failure(serializeError(error).message, "scroll_failed");
|
|
797
1385
|
}
|
|
798
1386
|
}
|
|
799
1387
|
});
|
|
800
1388
|
}
|
|
801
1389
|
|
|
802
|
-
// src/tools/
|
|
1390
|
+
// src/tools/scroll_into_view.ts
|
|
803
1391
|
import { tool as tool23 } from "@opencode-ai/plugin";
|
|
804
1392
|
var z23 = tool23.schema;
|
|
805
|
-
function
|
|
1393
|
+
function createScrollIntoViewTool(deps) {
|
|
806
1394
|
return tool23({
|
|
807
|
-
description: "
|
|
1395
|
+
description: "Scroll an element into view by ref.",
|
|
808
1396
|
args: {
|
|
809
|
-
sessionId: z23.string().describe("
|
|
810
|
-
|
|
811
|
-
max: z23.number().int().optional().describe("Max events to return")
|
|
1397
|
+
sessionId: z23.string().describe("Active browser session id"),
|
|
1398
|
+
ref: z23.string().describe("Element ref from snapshot")
|
|
812
1399
|
},
|
|
813
1400
|
async execute(args) {
|
|
814
1401
|
try {
|
|
815
|
-
const result = deps.manager.
|
|
816
|
-
args.sessionId,
|
|
817
|
-
args.sinceSeq,
|
|
818
|
-
args.max ?? 50
|
|
819
|
-
);
|
|
1402
|
+
const result = await deps.manager.scrollIntoView(args.sessionId, args.ref);
|
|
820
1403
|
return ok(result);
|
|
821
1404
|
} catch (error) {
|
|
822
|
-
return failure(serializeError(error).message, "
|
|
1405
|
+
return failure(serializeError(error).message, "scroll_into_view_failed");
|
|
823
1406
|
}
|
|
824
1407
|
}
|
|
825
1408
|
});
|
|
826
1409
|
}
|
|
827
1410
|
|
|
828
|
-
// src/tools/
|
|
1411
|
+
// src/tools/dom_get_html.ts
|
|
829
1412
|
import { tool as tool24 } from "@opencode-ai/plugin";
|
|
830
1413
|
var z24 = tool24.schema;
|
|
831
|
-
function
|
|
1414
|
+
function createDomGetHtmlTool(deps) {
|
|
832
1415
|
return tool24({
|
|
833
|
-
description: "
|
|
1416
|
+
description: "Get outerHTML for a referenced element.",
|
|
834
1417
|
args: {
|
|
835
1418
|
sessionId: z24.string().describe("Session id"),
|
|
836
|
-
|
|
837
|
-
|
|
1419
|
+
ref: z24.string().describe("Element ref"),
|
|
1420
|
+
maxChars: z24.number().int().optional().describe("Max characters")
|
|
838
1421
|
},
|
|
839
1422
|
async execute(args) {
|
|
840
1423
|
try {
|
|
841
|
-
const result = deps.manager.
|
|
1424
|
+
const result = await deps.manager.domGetHtml(
|
|
842
1425
|
args.sessionId,
|
|
843
|
-
args.
|
|
844
|
-
args.
|
|
1426
|
+
args.ref,
|
|
1427
|
+
args.maxChars ?? 8e3
|
|
845
1428
|
);
|
|
846
|
-
return ok(
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1429
|
+
return ok({
|
|
1430
|
+
ref: args.ref,
|
|
1431
|
+
outerHTML: result.outerHTML,
|
|
1432
|
+
truncated: result.truncated
|
|
1433
|
+
});
|
|
1434
|
+
} catch (error) {
|
|
1435
|
+
return failure(serializeError(error).message, "dom_get_html_failed");
|
|
1436
|
+
}
|
|
850
1437
|
}
|
|
851
1438
|
});
|
|
852
1439
|
}
|
|
853
1440
|
|
|
854
|
-
// src/tools/
|
|
1441
|
+
// src/tools/dom_get_text.ts
|
|
855
1442
|
import { tool as tool25 } from "@opencode-ai/plugin";
|
|
856
1443
|
var z25 = tool25.schema;
|
|
857
|
-
function
|
|
1444
|
+
function createDomGetTextTool(deps) {
|
|
858
1445
|
return tool25({
|
|
1446
|
+
description: "Get inner text for a referenced element.",
|
|
1447
|
+
args: {
|
|
1448
|
+
sessionId: z25.string().describe("Session id"),
|
|
1449
|
+
ref: z25.string().describe("Element ref"),
|
|
1450
|
+
maxChars: z25.number().int().optional().describe("Max characters")
|
|
1451
|
+
},
|
|
1452
|
+
async execute(args) {
|
|
1453
|
+
try {
|
|
1454
|
+
const result = await deps.manager.domGetText(
|
|
1455
|
+
args.sessionId,
|
|
1456
|
+
args.ref,
|
|
1457
|
+
args.maxChars ?? 8e3
|
|
1458
|
+
);
|
|
1459
|
+
return ok({
|
|
1460
|
+
ref: args.ref,
|
|
1461
|
+
text: result.text,
|
|
1462
|
+
truncated: result.truncated
|
|
1463
|
+
});
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
return failure(serializeError(error).message, "dom_get_text_failed");
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// src/tools/get_attr.ts
|
|
1472
|
+
import { tool as tool26 } from "@opencode-ai/plugin";
|
|
1473
|
+
var z26 = tool26.schema;
|
|
1474
|
+
function createGetAttrTool(deps) {
|
|
1475
|
+
return tool26({
|
|
1476
|
+
description: "Get a DOM attribute value by ref.",
|
|
1477
|
+
args: {
|
|
1478
|
+
sessionId: z26.string().describe("Active browser session id"),
|
|
1479
|
+
ref: z26.string().describe("Element ref from snapshot"),
|
|
1480
|
+
name: z26.string().describe("Attribute name, e.g. href or aria-label")
|
|
1481
|
+
},
|
|
1482
|
+
async execute(args) {
|
|
1483
|
+
try {
|
|
1484
|
+
const result = await deps.manager.domGetAttr(args.sessionId, args.ref, args.name);
|
|
1485
|
+
return ok(result);
|
|
1486
|
+
} catch (error) {
|
|
1487
|
+
return failure(serializeError(error).message, "get_attr_failed");
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// src/tools/get_value.ts
|
|
1494
|
+
import { tool as tool27 } from "@opencode-ai/plugin";
|
|
1495
|
+
var z27 = tool27.schema;
|
|
1496
|
+
function createGetValueTool(deps) {
|
|
1497
|
+
return tool27({
|
|
1498
|
+
description: "Get the input value for an element by ref.",
|
|
1499
|
+
args: {
|
|
1500
|
+
sessionId: z27.string().describe("Active browser session id"),
|
|
1501
|
+
ref: z27.string().describe("Element ref from snapshot")
|
|
1502
|
+
},
|
|
1503
|
+
async execute(args) {
|
|
1504
|
+
try {
|
|
1505
|
+
const result = await deps.manager.domGetValue(args.sessionId, args.ref);
|
|
1506
|
+
return ok(result);
|
|
1507
|
+
} catch (error) {
|
|
1508
|
+
return failure(serializeError(error).message, "get_value_failed");
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// src/tools/is_visible.ts
|
|
1515
|
+
import { tool as tool28 } from "@opencode-ai/plugin";
|
|
1516
|
+
var z28 = tool28.schema;
|
|
1517
|
+
function createIsVisibleTool(deps) {
|
|
1518
|
+
return tool28({
|
|
1519
|
+
description: "Check if an element is visible by ref.",
|
|
1520
|
+
args: {
|
|
1521
|
+
sessionId: z28.string().describe("Active browser session id"),
|
|
1522
|
+
ref: z28.string().describe("Element ref from snapshot")
|
|
1523
|
+
},
|
|
1524
|
+
async execute(args) {
|
|
1525
|
+
try {
|
|
1526
|
+
const result = await deps.manager.domIsVisible(args.sessionId, args.ref);
|
|
1527
|
+
return ok(result);
|
|
1528
|
+
} catch (error) {
|
|
1529
|
+
return failure(serializeError(error).message, "is_visible_failed");
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
// src/tools/is_enabled.ts
|
|
1536
|
+
import { tool as tool29 } from "@opencode-ai/plugin";
|
|
1537
|
+
var z29 = tool29.schema;
|
|
1538
|
+
function createIsEnabledTool(deps) {
|
|
1539
|
+
return tool29({
|
|
1540
|
+
description: "Check if an element is enabled by ref.",
|
|
1541
|
+
args: {
|
|
1542
|
+
sessionId: z29.string().describe("Active browser session id"),
|
|
1543
|
+
ref: z29.string().describe("Element ref from snapshot")
|
|
1544
|
+
},
|
|
1545
|
+
async execute(args) {
|
|
1546
|
+
try {
|
|
1547
|
+
const result = await deps.manager.domIsEnabled(args.sessionId, args.ref);
|
|
1548
|
+
return ok(result);
|
|
1549
|
+
} catch (error) {
|
|
1550
|
+
return failure(serializeError(error).message, "is_enabled_failed");
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// src/tools/is_checked.ts
|
|
1557
|
+
import { tool as tool30 } from "@opencode-ai/plugin";
|
|
1558
|
+
var z30 = tool30.schema;
|
|
1559
|
+
function createIsCheckedTool(deps) {
|
|
1560
|
+
return tool30({
|
|
1561
|
+
description: "Check if an element is checked by ref.",
|
|
1562
|
+
args: {
|
|
1563
|
+
sessionId: z30.string().describe("Active browser session id"),
|
|
1564
|
+
ref: z30.string().describe("Element ref from snapshot")
|
|
1565
|
+
},
|
|
1566
|
+
async execute(args) {
|
|
1567
|
+
try {
|
|
1568
|
+
const result = await deps.manager.domIsChecked(args.sessionId, args.ref);
|
|
1569
|
+
return ok(result);
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
return failure(serializeError(error).message, "is_checked_failed");
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// src/tools/run.ts
|
|
1578
|
+
import { tool as tool31 } from "@opencode-ai/plugin";
|
|
1579
|
+
var z31 = tool31.schema;
|
|
1580
|
+
var stepSchema = z31.object({
|
|
1581
|
+
action: z31.string().describe("Action name"),
|
|
1582
|
+
args: z31.record(z31.string(), z31.unknown()).optional().describe("Action arguments")
|
|
1583
|
+
});
|
|
1584
|
+
function createRunTool(deps) {
|
|
1585
|
+
return tool31({
|
|
1586
|
+
description: "Run multiple actions in a single tool call.",
|
|
1587
|
+
args: {
|
|
1588
|
+
sessionId: z31.string().describe("Session id"),
|
|
1589
|
+
steps: z31.array(stepSchema).describe("Steps to execute"),
|
|
1590
|
+
stopOnError: z31.boolean().optional().describe("Stop when a step fails"),
|
|
1591
|
+
maxSnapshotChars: z31.number().int().optional().describe("Default maxChars for snapshot steps")
|
|
1592
|
+
},
|
|
1593
|
+
async execute(args) {
|
|
1594
|
+
try {
|
|
1595
|
+
const steps = normalizeSteps(args.steps, args.maxSnapshotChars);
|
|
1596
|
+
const result = await deps.runner.run(
|
|
1597
|
+
args.sessionId,
|
|
1598
|
+
steps,
|
|
1599
|
+
args.stopOnError ?? true
|
|
1600
|
+
);
|
|
1601
|
+
return ok(result);
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
return failure(serializeError(error).message, "run_failed");
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
function normalizeSteps(steps, maxSnapshotChars) {
|
|
1609
|
+
if (!maxSnapshotChars) return steps;
|
|
1610
|
+
return steps.map((step) => {
|
|
1611
|
+
if (step.action !== "snapshot") return step;
|
|
1612
|
+
if (step.args && typeof step.args.maxChars === "number") return step;
|
|
1613
|
+
return {
|
|
1614
|
+
...step,
|
|
1615
|
+
args: {
|
|
1616
|
+
...step.args,
|
|
1617
|
+
maxChars: maxSnapshotChars
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// src/tools/prompting_guide.ts
|
|
1624
|
+
import { tool as tool32 } from "@opencode-ai/plugin";
|
|
1625
|
+
var z32 = tool32.schema;
|
|
1626
|
+
function createPromptingGuideTool(deps) {
|
|
1627
|
+
return tool32({
|
|
1628
|
+
description: "Return best-practice prompting guidance for OpenDevBrowser.",
|
|
1629
|
+
args: {
|
|
1630
|
+
topic: z32.string().optional().describe("Optional topic for guidance")
|
|
1631
|
+
},
|
|
1632
|
+
async execute(args) {
|
|
1633
|
+
try {
|
|
1634
|
+
const guide = await deps.skills.loadBestPractices(args.topic);
|
|
1635
|
+
return ok({ guide });
|
|
1636
|
+
} catch (error) {
|
|
1637
|
+
return failure(serializeError(error).message, "prompting_guide_failed");
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
// src/tools/console_poll.ts
|
|
1644
|
+
import { tool as tool33 } from "@opencode-ai/plugin";
|
|
1645
|
+
var z33 = tool33.schema;
|
|
1646
|
+
function createConsolePollTool(deps) {
|
|
1647
|
+
return tool33({
|
|
1648
|
+
description: "Poll console events for the active target.",
|
|
1649
|
+
args: {
|
|
1650
|
+
sessionId: z33.string().describe("Session id"),
|
|
1651
|
+
sinceSeq: z33.number().int().optional().describe("Sequence to resume from"),
|
|
1652
|
+
max: z33.number().int().optional().describe("Max events to return")
|
|
1653
|
+
},
|
|
1654
|
+
async execute(args) {
|
|
1655
|
+
try {
|
|
1656
|
+
const result = await deps.manager.consolePoll(
|
|
1657
|
+
args.sessionId,
|
|
1658
|
+
args.sinceSeq,
|
|
1659
|
+
args.max ?? 50
|
|
1660
|
+
);
|
|
1661
|
+
return ok(result);
|
|
1662
|
+
} catch (error) {
|
|
1663
|
+
return failure(serializeError(error).message, "console_poll_failed");
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// src/tools/network_poll.ts
|
|
1670
|
+
import { tool as tool34 } from "@opencode-ai/plugin";
|
|
1671
|
+
var z34 = tool34.schema;
|
|
1672
|
+
function createNetworkPollTool(deps) {
|
|
1673
|
+
return tool34({
|
|
1674
|
+
description: "Poll network events for the active target.",
|
|
1675
|
+
args: {
|
|
1676
|
+
sessionId: z34.string().describe("Session id"),
|
|
1677
|
+
sinceSeq: z34.number().int().optional().describe("Sequence to resume from"),
|
|
1678
|
+
max: z34.number().int().optional().describe("Max events to return")
|
|
1679
|
+
},
|
|
1680
|
+
async execute(args) {
|
|
1681
|
+
try {
|
|
1682
|
+
const result = await deps.manager.networkPoll(
|
|
1683
|
+
args.sessionId,
|
|
1684
|
+
args.sinceSeq,
|
|
1685
|
+
args.max ?? 50
|
|
1686
|
+
);
|
|
1687
|
+
return ok(result);
|
|
1688
|
+
} catch (error) {
|
|
1689
|
+
return failure(serializeError(error).message, "network_poll_failed");
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// src/tools/debug_trace_snapshot.ts
|
|
1696
|
+
import { tool as tool35 } from "@opencode-ai/plugin";
|
|
1697
|
+
var z35 = tool35.schema;
|
|
1698
|
+
function createDebugTraceSnapshotTool(deps) {
|
|
1699
|
+
return tool35({
|
|
1700
|
+
description: "Capture a combined debug trace snapshot (page + console + network + exceptions).",
|
|
1701
|
+
args: {
|
|
1702
|
+
sessionId: z35.string().describe("Session id"),
|
|
1703
|
+
sinceConsoleSeq: z35.number().int().optional().describe("Resume cursor for console events"),
|
|
1704
|
+
sinceNetworkSeq: z35.number().int().optional().describe("Resume cursor for network events"),
|
|
1705
|
+
sinceExceptionSeq: z35.number().int().optional().describe("Resume cursor for exception events"),
|
|
1706
|
+
max: z35.number().int().optional().describe("Max events per channel"),
|
|
1707
|
+
requestId: z35.string().optional().describe("Optional trace request id")
|
|
1708
|
+
},
|
|
1709
|
+
async execute(args) {
|
|
1710
|
+
try {
|
|
1711
|
+
const manager = deps.manager;
|
|
1712
|
+
const options = {
|
|
1713
|
+
sinceConsoleSeq: args.sinceConsoleSeq,
|
|
1714
|
+
sinceNetworkSeq: args.sinceNetworkSeq,
|
|
1715
|
+
sinceExceptionSeq: args.sinceExceptionSeq,
|
|
1716
|
+
max: args.max,
|
|
1717
|
+
requestId: args.requestId
|
|
1718
|
+
};
|
|
1719
|
+
if (typeof manager.debugTraceSnapshot === "function") {
|
|
1720
|
+
const result = await manager.debugTraceSnapshot(args.sessionId, options);
|
|
1721
|
+
return ok(result);
|
|
1722
|
+
}
|
|
1723
|
+
const max = args.max ?? 500;
|
|
1724
|
+
const requestId = args.requestId ?? createRequestId();
|
|
1725
|
+
const [page, consoleChannel, networkChannel] = await Promise.all([
|
|
1726
|
+
deps.manager.status(args.sessionId),
|
|
1727
|
+
deps.manager.consolePoll(args.sessionId, args.sinceConsoleSeq, max),
|
|
1728
|
+
deps.manager.networkPoll(args.sessionId, args.sinceNetworkSeq, max)
|
|
1729
|
+
]);
|
|
1730
|
+
const exceptionChannel = typeof manager.exceptionPoll === "function" ? await manager.exceptionPoll(args.sessionId, args.sinceExceptionSeq, max) : {
|
|
1731
|
+
events: [],
|
|
1732
|
+
nextSeq: args.sinceExceptionSeq ?? 0,
|
|
1733
|
+
truncated: false
|
|
1734
|
+
};
|
|
1735
|
+
const annotateTraceContext = (events) => events.map((event) => ({
|
|
1736
|
+
...event,
|
|
1737
|
+
requestId,
|
|
1738
|
+
sessionId: args.sessionId
|
|
1739
|
+
}));
|
|
1740
|
+
const blocker = classifyBlockerSignal({
|
|
1741
|
+
source: "network",
|
|
1742
|
+
url: typeof page.url === "string" ? page.url : void 0,
|
|
1743
|
+
finalUrl: typeof page.url === "string" ? page.url : void 0,
|
|
1744
|
+
title: typeof page.title === "string" ? page.title : void 0,
|
|
1745
|
+
status: findLatestStatus(networkChannel.events),
|
|
1746
|
+
traceRequestId: requestId,
|
|
1747
|
+
networkHosts: extractHosts(networkChannel.events),
|
|
1748
|
+
threshold: deps.config.get().blockerDetectionThreshold,
|
|
1749
|
+
promptGuardEnabled: deps.config.get().security.promptInjectionGuard?.enabled ?? true
|
|
1750
|
+
});
|
|
1751
|
+
const blockerArtifacts = blocker ? buildBlockerArtifacts({
|
|
1752
|
+
networkEvents: networkChannel.events,
|
|
1753
|
+
consoleEvents: consoleChannel.events,
|
|
1754
|
+
exceptionEvents: exceptionChannel.events,
|
|
1755
|
+
promptGuardEnabled: deps.config.get().security.promptInjectionGuard?.enabled ?? true,
|
|
1756
|
+
caps: deps.config.get().blockerArtifactCaps
|
|
1757
|
+
}) : void 0;
|
|
1758
|
+
return ok({
|
|
1759
|
+
requestId,
|
|
1760
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1761
|
+
page,
|
|
1762
|
+
channels: {
|
|
1763
|
+
console: {
|
|
1764
|
+
nextSeq: consoleChannel.nextSeq,
|
|
1765
|
+
truncated: consoleChannel.truncated,
|
|
1766
|
+
events: annotateTraceContext(consoleChannel.events)
|
|
1767
|
+
},
|
|
1768
|
+
network: {
|
|
1769
|
+
nextSeq: networkChannel.nextSeq,
|
|
1770
|
+
truncated: networkChannel.truncated,
|
|
1771
|
+
events: annotateTraceContext(networkChannel.events)
|
|
1772
|
+
},
|
|
1773
|
+
exception: {
|
|
1774
|
+
nextSeq: exceptionChannel.nextSeq,
|
|
1775
|
+
truncated: exceptionChannel.truncated,
|
|
1776
|
+
events: annotateTraceContext(exceptionChannel.events)
|
|
1777
|
+
}
|
|
1778
|
+
},
|
|
1779
|
+
meta: {
|
|
1780
|
+
blockerState: blocker ? "active" : "clear",
|
|
1781
|
+
...blocker ? { blocker } : {},
|
|
1782
|
+
...blockerArtifacts ? { blockerArtifacts } : {}
|
|
1783
|
+
}
|
|
1784
|
+
});
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
return failure(serializeError(error).message, "debug_trace_snapshot_failed");
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
function findLatestStatus(events) {
|
|
1792
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
1793
|
+
const status = events[index]?.status;
|
|
1794
|
+
if (typeof status === "number") {
|
|
1795
|
+
return status;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
return void 0;
|
|
1799
|
+
}
|
|
1800
|
+
function extractHosts(events) {
|
|
1801
|
+
const hosts = [];
|
|
1802
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1803
|
+
for (const event of events) {
|
|
1804
|
+
if (typeof event.url !== "string") continue;
|
|
1805
|
+
try {
|
|
1806
|
+
const host = new URL(event.url).hostname.toLowerCase();
|
|
1807
|
+
if (!host || seen.has(host)) continue;
|
|
1808
|
+
seen.add(host);
|
|
1809
|
+
hosts.push(host);
|
|
1810
|
+
} catch {
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
return hosts;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// src/tools/cookie_import.ts
|
|
1817
|
+
import { tool as tool36 } from "@opencode-ai/plugin";
|
|
1818
|
+
var z36 = tool36.schema;
|
|
1819
|
+
function validateCookieRecord(cookie) {
|
|
1820
|
+
const name = cookie.name?.trim();
|
|
1821
|
+
if (!name) {
|
|
1822
|
+
return { valid: false, reason: "Cookie name is required.", cookie };
|
|
1823
|
+
}
|
|
1824
|
+
if (!/^[^\s;=]+$/.test(name)) {
|
|
1825
|
+
return { valid: false, reason: `Invalid cookie name: ${cookie.name}.`, cookie };
|
|
1826
|
+
}
|
|
1827
|
+
if (typeof cookie.value !== "string") {
|
|
1828
|
+
return { valid: false, reason: `Invalid cookie value for ${name}.`, cookie };
|
|
1829
|
+
}
|
|
1830
|
+
const value = cookie.value;
|
|
1831
|
+
if (/\r|\n|;/.test(value)) {
|
|
1832
|
+
return { valid: false, reason: `Invalid cookie value for ${name}.`, cookie };
|
|
1833
|
+
}
|
|
1834
|
+
const hasUrl = typeof cookie.url === "string" && cookie.url.trim().length > 0;
|
|
1835
|
+
const hasDomain = typeof cookie.domain === "string" && cookie.domain.trim().length > 0;
|
|
1836
|
+
if (!hasUrl && !hasDomain) {
|
|
1837
|
+
return { valid: false, reason: `Cookie ${name} requires url or domain.`, cookie };
|
|
1838
|
+
}
|
|
1839
|
+
let normalizedUrl;
|
|
1840
|
+
if (hasUrl) {
|
|
1841
|
+
try {
|
|
1842
|
+
const parsedUrl = new URL(cookie.url);
|
|
1843
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
1844
|
+
return { valid: false, reason: `Cookie ${name} url must be http(s).`, cookie };
|
|
1845
|
+
}
|
|
1846
|
+
normalizedUrl = parsedUrl.toString();
|
|
1847
|
+
} catch {
|
|
1848
|
+
return { valid: false, reason: `Cookie ${name} has invalid url.`, cookie };
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
let normalizedDomain;
|
|
1852
|
+
if (hasDomain) {
|
|
1853
|
+
normalizedDomain = String(cookie.domain).trim().toLowerCase();
|
|
1854
|
+
if (!/^\.?[a-z0-9.-]+$/.test(normalizedDomain)) {
|
|
1855
|
+
return { valid: false, reason: `Cookie ${name} has invalid domain.`, cookie };
|
|
1856
|
+
}
|
|
1857
|
+
if (normalizedDomain.includes("..")) {
|
|
1858
|
+
return { valid: false, reason: `Cookie ${name} has invalid domain.`, cookie };
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
const normalizedPath = typeof cookie.path === "string" ? cookie.path.trim() : void 0;
|
|
1862
|
+
if (typeof normalizedPath === "string" && !normalizedPath.startsWith("/")) {
|
|
1863
|
+
return { valid: false, reason: `Cookie ${name} path must start with '/'.`, cookie };
|
|
1864
|
+
}
|
|
1865
|
+
if (typeof cookie.expires !== "undefined") {
|
|
1866
|
+
if (!Number.isFinite(cookie.expires)) {
|
|
1867
|
+
return { valid: false, reason: `Cookie ${name} has invalid expires.`, cookie };
|
|
1868
|
+
}
|
|
1869
|
+
if (cookie.expires < -1) {
|
|
1870
|
+
return { valid: false, reason: `Cookie ${name} has invalid expires.`, cookie };
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
if (cookie.sameSite === "None" && cookie.secure !== true) {
|
|
1874
|
+
return { valid: false, reason: `Cookie ${name} with SameSite=None must set secure=true.`, cookie };
|
|
1875
|
+
}
|
|
1876
|
+
const normalizedCookie = {
|
|
1877
|
+
name,
|
|
1878
|
+
value,
|
|
1879
|
+
...typeof cookie.expires === "number" ? { expires: cookie.expires } : {},
|
|
1880
|
+
...typeof cookie.httpOnly === "boolean" ? { httpOnly: cookie.httpOnly } : {},
|
|
1881
|
+
...typeof cookie.secure === "boolean" ? { secure: cookie.secure } : {},
|
|
1882
|
+
...cookie.sameSite ? { sameSite: cookie.sameSite } : {}
|
|
1883
|
+
};
|
|
1884
|
+
if (normalizedDomain) {
|
|
1885
|
+
normalizedCookie.domain = normalizedDomain;
|
|
1886
|
+
normalizedCookie.path = normalizedPath ?? "/";
|
|
1887
|
+
} else if (normalizedUrl) {
|
|
1888
|
+
normalizedCookie.url = normalizedUrl;
|
|
1889
|
+
}
|
|
1890
|
+
return {
|
|
1891
|
+
valid: true,
|
|
1892
|
+
reason: "",
|
|
1893
|
+
cookie: normalizedCookie
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
function createCookieImportTool(deps) {
|
|
1897
|
+
return tool36({
|
|
1898
|
+
description: "Import validated cookies into the current session context.",
|
|
1899
|
+
args: {
|
|
1900
|
+
sessionId: z36.string().describe("Session id"),
|
|
1901
|
+
cookies: z36.array(z36.object({
|
|
1902
|
+
name: z36.string().min(1),
|
|
1903
|
+
value: z36.string(),
|
|
1904
|
+
url: z36.string().optional(),
|
|
1905
|
+
domain: z36.string().optional(),
|
|
1906
|
+
path: z36.string().optional(),
|
|
1907
|
+
expires: z36.number().optional(),
|
|
1908
|
+
httpOnly: z36.boolean().optional(),
|
|
1909
|
+
secure: z36.boolean().optional(),
|
|
1910
|
+
sameSite: z36.enum(["Strict", "Lax", "None"]).optional()
|
|
1911
|
+
})).min(1).describe("Cookies to import"),
|
|
1912
|
+
strict: z36.boolean().optional().describe("Reject on first invalid cookie (default true)"),
|
|
1913
|
+
requestId: z36.string().optional().describe("Optional trace request id")
|
|
1914
|
+
},
|
|
1915
|
+
async execute(args) {
|
|
1916
|
+
try {
|
|
1917
|
+
const strict = args.strict ?? true;
|
|
1918
|
+
const requestId = args.requestId ?? createRequestId();
|
|
1919
|
+
const manager = deps.manager;
|
|
1920
|
+
const normalized = [];
|
|
1921
|
+
const rejected = [];
|
|
1922
|
+
args.cookies.forEach((cookie, index) => {
|
|
1923
|
+
const validation = validateCookieRecord(cookie);
|
|
1924
|
+
if (!validation.valid) {
|
|
1925
|
+
rejected.push({ index, reason: validation.reason });
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
normalized.push(validation.cookie);
|
|
1929
|
+
});
|
|
1930
|
+
if (typeof manager.cookieImport === "function") {
|
|
1931
|
+
return ok(await manager.cookieImport(args.sessionId, normalized, strict, requestId));
|
|
1932
|
+
}
|
|
1933
|
+
if (strict && rejected.length > 0) {
|
|
1934
|
+
return failure(`Cookie import rejected ${rejected.length} entries.`, "cookie_import_failed");
|
|
1935
|
+
}
|
|
1936
|
+
if (normalized.length > 0) {
|
|
1937
|
+
await deps.manager.withPage(args.sessionId, null, async (page) => {
|
|
1938
|
+
await page.context().addCookies(normalized);
|
|
1939
|
+
return void 0;
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
return ok({
|
|
1943
|
+
requestId,
|
|
1944
|
+
imported: normalized.length,
|
|
1945
|
+
rejected
|
|
1946
|
+
});
|
|
1947
|
+
} catch (error) {
|
|
1948
|
+
return failure(serializeError(error).message, "cookie_import_failed");
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
// src/tools/cookie_list.ts
|
|
1955
|
+
import { tool as tool37 } from "@opencode-ai/plugin";
|
|
1956
|
+
var z37 = tool37.schema;
|
|
1957
|
+
function normalizeCookieUrls(urls) {
|
|
1958
|
+
if (!urls || urls.length === 0) {
|
|
1959
|
+
return void 0;
|
|
1960
|
+
}
|
|
1961
|
+
const normalized = [];
|
|
1962
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1963
|
+
for (const value of urls) {
|
|
1964
|
+
const trimmed = value.trim();
|
|
1965
|
+
if (!trimmed) {
|
|
1966
|
+
throw new Error("Cookie list urls must be non-empty strings.");
|
|
1967
|
+
}
|
|
1968
|
+
let parsedUrl;
|
|
1969
|
+
try {
|
|
1970
|
+
parsedUrl = new URL(trimmed);
|
|
1971
|
+
} catch {
|
|
1972
|
+
throw new Error(`Cookie list url is invalid: ${trimmed}`);
|
|
1973
|
+
}
|
|
1974
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
1975
|
+
throw new Error(`Cookie list url must be http(s): ${trimmed}`);
|
|
1976
|
+
}
|
|
1977
|
+
const normalizedUrl = parsedUrl.toString();
|
|
1978
|
+
if (seen.has(normalizedUrl)) {
|
|
1979
|
+
continue;
|
|
1980
|
+
}
|
|
1981
|
+
seen.add(normalizedUrl);
|
|
1982
|
+
normalized.push(normalizedUrl);
|
|
1983
|
+
}
|
|
1984
|
+
return normalized;
|
|
1985
|
+
}
|
|
1986
|
+
function createCookieListTool(deps) {
|
|
1987
|
+
return tool37({
|
|
1988
|
+
description: "List cookies in the current session context with optional URL filtering.",
|
|
1989
|
+
args: {
|
|
1990
|
+
sessionId: z37.string().describe("Session id"),
|
|
1991
|
+
urls: z37.array(z37.string().min(1)).optional().describe("Optional URL filters for cookie scoping"),
|
|
1992
|
+
requestId: z37.string().optional().describe("Optional trace request id")
|
|
1993
|
+
},
|
|
1994
|
+
async execute(args) {
|
|
1995
|
+
try {
|
|
1996
|
+
const manager = deps.manager;
|
|
1997
|
+
const normalizedUrls = normalizeCookieUrls(args.urls);
|
|
1998
|
+
const requestId = args.requestId ?? createRequestId();
|
|
1999
|
+
if (typeof manager.cookieList === "function") {
|
|
2000
|
+
return ok(await manager.cookieList(args.sessionId, normalizedUrls, requestId));
|
|
2001
|
+
}
|
|
2002
|
+
const cookies = await deps.manager.withPage(
|
|
2003
|
+
args.sessionId,
|
|
2004
|
+
null,
|
|
2005
|
+
async (page) => {
|
|
2006
|
+
const listed = normalizedUrls ? await page.context().cookies(normalizedUrls) : await page.context().cookies();
|
|
2007
|
+
return listed.map((cookie) => ({
|
|
2008
|
+
name: cookie.name,
|
|
2009
|
+
value: cookie.value,
|
|
2010
|
+
domain: cookie.domain,
|
|
2011
|
+
path: cookie.path,
|
|
2012
|
+
expires: cookie.expires,
|
|
2013
|
+
httpOnly: cookie.httpOnly,
|
|
2014
|
+
secure: cookie.secure,
|
|
2015
|
+
...cookie.sameSite ? { sameSite: cookie.sameSite } : {}
|
|
2016
|
+
}));
|
|
2017
|
+
}
|
|
2018
|
+
);
|
|
2019
|
+
return ok({
|
|
2020
|
+
requestId,
|
|
2021
|
+
cookies,
|
|
2022
|
+
count: cookies.length
|
|
2023
|
+
});
|
|
2024
|
+
} catch (error) {
|
|
2025
|
+
return failure(serializeError(error).message, "cookie_list_failed");
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// src/tools/macro_resolve.ts
|
|
2032
|
+
import { tool as tool38 } from "@opencode-ai/plugin";
|
|
2033
|
+
var z38 = tool38.schema;
|
|
2034
|
+
async function loadMacroRuntime() {
|
|
2035
|
+
try {
|
|
2036
|
+
const module = await import("./macros-NUBRM44Y.js");
|
|
2037
|
+
return module;
|
|
2038
|
+
} catch {
|
|
2039
|
+
return null;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
function parseFallbackMacro(expression, defaultProvider) {
|
|
2043
|
+
const raw = expression.trim();
|
|
2044
|
+
if (!raw.startsWith("@")) {
|
|
2045
|
+
throw new Error("Macro expressions must start with '@'");
|
|
2046
|
+
}
|
|
2047
|
+
const body = raw.slice(1).trim();
|
|
2048
|
+
if (!body) {
|
|
2049
|
+
throw new Error("Macro name is required");
|
|
2050
|
+
}
|
|
2051
|
+
const openParen = body.indexOf("(");
|
|
2052
|
+
const closeParen = body.endsWith(")") ? body.length - 1 : -1;
|
|
2053
|
+
const macroName = openParen >= 0 ? body.slice(0, openParen).trim() : body;
|
|
2054
|
+
const argsBody = openParen >= 0 && closeParen > openParen ? body.slice(openParen + 1, closeParen).trim() : "";
|
|
2055
|
+
const positional = argsBody ? argsBody.split(",").map((part) => part.trim().replace(/^['\"]|['\"]$/g, "")).filter(Boolean) : [];
|
|
2056
|
+
const query = positional[0] ?? macroName;
|
|
2057
|
+
const provider = defaultProvider ?? "web/default";
|
|
2058
|
+
return {
|
|
2059
|
+
action: {
|
|
2060
|
+
source: "web",
|
|
2061
|
+
operation: "search",
|
|
2062
|
+
input: {
|
|
2063
|
+
query,
|
|
2064
|
+
limit: 10,
|
|
2065
|
+
providerId: provider
|
|
2066
|
+
}
|
|
2067
|
+
},
|
|
2068
|
+
provenance: {
|
|
2069
|
+
macro: macroName,
|
|
2070
|
+
provider,
|
|
2071
|
+
resolvedQuery: query,
|
|
2072
|
+
pack: "fallback",
|
|
2073
|
+
args: {
|
|
2074
|
+
positional,
|
|
2075
|
+
named: {}
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
function createMacroResolveTool(deps) {
|
|
2081
|
+
return tool38({
|
|
2082
|
+
description: "Resolve a macro expression to a provider action with provenance metadata.",
|
|
2083
|
+
args: {
|
|
2084
|
+
expression: z38.string().min(2).describe('Macro expression, e.g. @web.search("query")'),
|
|
2085
|
+
defaultProvider: z38.string().optional().describe("Default provider fallback"),
|
|
2086
|
+
includeCatalog: z38.boolean().optional().describe("Include available runtime macro names"),
|
|
2087
|
+
execute: z38.boolean().optional().describe("Execute the resolved provider action and include execution payload")
|
|
2088
|
+
},
|
|
2089
|
+
async execute(args) {
|
|
2090
|
+
try {
|
|
2091
|
+
const runtime = await loadMacroRuntime();
|
|
2092
|
+
const registry = runtime?.createDefaultMacroRegistry?.();
|
|
2093
|
+
let resolvedRuntime = "fallback";
|
|
2094
|
+
let resolution;
|
|
2095
|
+
let catalog;
|
|
2096
|
+
if (registry) {
|
|
2097
|
+
resolvedRuntime = "macros";
|
|
2098
|
+
resolution = await registry.resolve(args.expression, {
|
|
2099
|
+
defaultProvider: args.defaultProvider
|
|
2100
|
+
});
|
|
2101
|
+
catalog = args.includeCatalog ? registry.list().map((entry) => ({
|
|
2102
|
+
name: entry.name,
|
|
2103
|
+
pack: entry.pack,
|
|
2104
|
+
description: entry.description
|
|
2105
|
+
})) : void 0;
|
|
2106
|
+
} else {
|
|
2107
|
+
resolution = parseFallbackMacro(args.expression, args.defaultProvider);
|
|
2108
|
+
}
|
|
2109
|
+
if (!args.execute) {
|
|
2110
|
+
return ok({
|
|
2111
|
+
runtime: resolvedRuntime,
|
|
2112
|
+
resolution,
|
|
2113
|
+
...catalog ? { catalog } : {}
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
const runtimeConfig = deps.config?.get?.();
|
|
2117
|
+
const providerRuntime = deps.providerRuntime ?? createConfiguredProviderRuntime({
|
|
2118
|
+
config: runtimeConfig,
|
|
2119
|
+
manager: deps.manager,
|
|
2120
|
+
browserFallbackPort: deps.browserFallbackPort
|
|
2121
|
+
});
|
|
2122
|
+
const execution = shapeExecutionPayload(
|
|
2123
|
+
await executeMacroResolution(resolution, providerRuntime)
|
|
2124
|
+
);
|
|
2125
|
+
return ok({
|
|
2126
|
+
runtime: resolvedRuntime,
|
|
2127
|
+
resolution,
|
|
2128
|
+
...catalog ? { catalog } : {},
|
|
2129
|
+
execution
|
|
2130
|
+
});
|
|
2131
|
+
} catch (error) {
|
|
2132
|
+
return failure(serializeError(error).message, "macro_resolve_failed");
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// src/tools/clone_page.ts
|
|
2139
|
+
import { tool as tool39 } from "@opencode-ai/plugin";
|
|
2140
|
+
var z39 = tool39.schema;
|
|
2141
|
+
function createClonePageTool(deps) {
|
|
2142
|
+
return tool39({
|
|
859
2143
|
description: "Export the active page as a React component and CSS bundle.",
|
|
860
2144
|
args: {
|
|
861
|
-
sessionId:
|
|
2145
|
+
sessionId: z39.string().describe("Active browser session id")
|
|
862
2146
|
},
|
|
863
2147
|
async execute(args) {
|
|
864
2148
|
try {
|
|
@@ -872,14 +2156,14 @@ function createClonePageTool(deps) {
|
|
|
872
2156
|
}
|
|
873
2157
|
|
|
874
2158
|
// src/tools/clone_component.ts
|
|
875
|
-
import { tool as
|
|
876
|
-
var
|
|
2159
|
+
import { tool as tool40 } from "@opencode-ai/plugin";
|
|
2160
|
+
var z40 = tool40.schema;
|
|
877
2161
|
function createCloneComponentTool(deps) {
|
|
878
|
-
return
|
|
2162
|
+
return tool40({
|
|
879
2163
|
description: "Export a selected element subtree as a React component and CSS bundle.",
|
|
880
2164
|
args: {
|
|
881
|
-
sessionId:
|
|
882
|
-
ref:
|
|
2165
|
+
sessionId: z40.string().describe("Active browser session id"),
|
|
2166
|
+
ref: z40.string().describe("Element ref from snapshot")
|
|
883
2167
|
},
|
|
884
2168
|
async execute(args) {
|
|
885
2169
|
try {
|
|
@@ -893,13 +2177,13 @@ function createCloneComponentTool(deps) {
|
|
|
893
2177
|
}
|
|
894
2178
|
|
|
895
2179
|
// src/tools/perf.ts
|
|
896
|
-
import { tool as
|
|
897
|
-
var
|
|
2180
|
+
import { tool as tool41 } from "@opencode-ai/plugin";
|
|
2181
|
+
var z41 = tool41.schema;
|
|
898
2182
|
function createPerfTool(deps) {
|
|
899
|
-
return
|
|
2183
|
+
return tool41({
|
|
900
2184
|
description: "Fetch lightweight performance metrics from the active page.",
|
|
901
2185
|
args: {
|
|
902
|
-
sessionId:
|
|
2186
|
+
sessionId: z41.string().describe("Active browser session id")
|
|
903
2187
|
},
|
|
904
2188
|
async execute(args) {
|
|
905
2189
|
try {
|
|
@@ -913,14 +2197,14 @@ function createPerfTool(deps) {
|
|
|
913
2197
|
}
|
|
914
2198
|
|
|
915
2199
|
// src/tools/screenshot.ts
|
|
916
|
-
import { tool as
|
|
917
|
-
var
|
|
2200
|
+
import { tool as tool42 } from "@opencode-ai/plugin";
|
|
2201
|
+
var z42 = tool42.schema;
|
|
918
2202
|
function createScreenshotTool(deps) {
|
|
919
|
-
return
|
|
2203
|
+
return tool42({
|
|
920
2204
|
description: "Capture a screenshot of the active page.",
|
|
921
2205
|
args: {
|
|
922
|
-
sessionId:
|
|
923
|
-
path:
|
|
2206
|
+
sessionId: z42.string().describe("Active browser session id"),
|
|
2207
|
+
path: z42.string().optional().describe("Optional output file path")
|
|
924
2208
|
},
|
|
925
2209
|
async execute(args) {
|
|
926
2210
|
try {
|
|
@@ -933,10 +2217,243 @@ function createScreenshotTool(deps) {
|
|
|
933
2217
|
});
|
|
934
2218
|
}
|
|
935
2219
|
|
|
2220
|
+
// src/tools/annotate.ts
|
|
2221
|
+
import { tool as tool43 } from "@opencode-ai/plugin";
|
|
2222
|
+
var z43 = tool43.schema;
|
|
2223
|
+
var screenshotModeSchema = z43.enum(["visible", "full", "none"]);
|
|
2224
|
+
var transportSchema = z43.enum(["auto", "direct", "relay"]);
|
|
2225
|
+
function createAnnotateTool(deps) {
|
|
2226
|
+
return tool43({
|
|
2227
|
+
description: "Request interactive annotations via direct (CDP) or relay transport.",
|
|
2228
|
+
args: {
|
|
2229
|
+
sessionId: z43.string().describe("Session id"),
|
|
2230
|
+
transport: transportSchema.optional().describe("auto | direct | relay (default: auto)"),
|
|
2231
|
+
targetId: z43.string().optional().describe("Optional target id for direct mode"),
|
|
2232
|
+
tabId: z43.number().int().optional().describe("Optional Chrome tab id for relay mode"),
|
|
2233
|
+
url: z43.string().optional().describe("Optional URL to open before annotating"),
|
|
2234
|
+
screenshotMode: screenshotModeSchema.optional().describe("visible | full | none (default: visible)"),
|
|
2235
|
+
debug: z43.boolean().optional().describe("Include debug metadata"),
|
|
2236
|
+
context: z43.string().optional().describe("Optional context for the annotator"),
|
|
2237
|
+
timeoutMs: z43.number().int().optional().describe("Timeout in milliseconds")
|
|
2238
|
+
},
|
|
2239
|
+
async execute(args) {
|
|
2240
|
+
try {
|
|
2241
|
+
const transport = args.transport ?? "auto";
|
|
2242
|
+
if (transport === "relay") {
|
|
2243
|
+
const status = await deps.manager.status(args.sessionId);
|
|
2244
|
+
if (status.mode !== "extension") {
|
|
2245
|
+
return failure("Annotations require extension mode (relay).", "annotate_requires_extension");
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
const response = await deps.annotationManager.requestAnnotation({
|
|
2249
|
+
sessionId: args.sessionId,
|
|
2250
|
+
transport,
|
|
2251
|
+
targetId: args.targetId,
|
|
2252
|
+
tabId: args.tabId,
|
|
2253
|
+
url: args.url,
|
|
2254
|
+
screenshotMode: args.screenshotMode ?? "visible",
|
|
2255
|
+
debug: args.debug ?? false,
|
|
2256
|
+
context: args.context,
|
|
2257
|
+
timeoutMs: args.timeoutMs
|
|
2258
|
+
});
|
|
2259
|
+
if (response.status !== "ok" || !response.payload) {
|
|
2260
|
+
const message2 = response.error?.message ?? "Annotation failed.";
|
|
2261
|
+
const code = response.error?.code ?? "annotate_failed";
|
|
2262
|
+
return failure(message2, code);
|
|
2263
|
+
}
|
|
2264
|
+
const { message, details, screenshots } = await buildAnnotateResult(response.payload);
|
|
2265
|
+
return ok({
|
|
2266
|
+
message,
|
|
2267
|
+
details,
|
|
2268
|
+
screenshots
|
|
2269
|
+
});
|
|
2270
|
+
} catch (error) {
|
|
2271
|
+
return failure(serializeError(error).message, "annotate_failed");
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
// src/tools/research_run.ts
|
|
2278
|
+
import { tool as tool44 } from "@opencode-ai/plugin";
|
|
2279
|
+
|
|
2280
|
+
// src/tools/workflow-runtime.ts
|
|
2281
|
+
var resolveProviderRuntime = (deps) => {
|
|
2282
|
+
if (deps.providerRuntime) {
|
|
2283
|
+
return deps.providerRuntime;
|
|
2284
|
+
}
|
|
2285
|
+
return createConfiguredProviderRuntime({
|
|
2286
|
+
config: deps.config?.get?.(),
|
|
2287
|
+
manager: deps.manager,
|
|
2288
|
+
browserFallbackPort: deps.browserFallbackPort
|
|
2289
|
+
});
|
|
2290
|
+
};
|
|
2291
|
+
|
|
2292
|
+
// src/tools/research_run.ts
|
|
2293
|
+
var z44 = tool44.schema;
|
|
2294
|
+
var sourceSelectionSchema = z44.enum(["auto", "web", "community", "social", "shopping", "all"]);
|
|
2295
|
+
var sourceSchema = z44.enum(["web", "community", "social", "shopping"]);
|
|
2296
|
+
var modeSchema = z44.enum(["compact", "json", "md", "context", "path"]);
|
|
2297
|
+
var cookiePolicySchema = z44.enum(["off", "auto", "required"]);
|
|
2298
|
+
function createResearchRunTool(deps) {
|
|
2299
|
+
return tool44({
|
|
2300
|
+
description: "Run cross-source research with strict timebox and artifact outputs.",
|
|
2301
|
+
args: {
|
|
2302
|
+
topic: z44.string().min(1).describe("Research topic"),
|
|
2303
|
+
days: z44.number().int().positive().optional().describe("Timebox in days"),
|
|
2304
|
+
from: z44.string().optional().describe("ISO start date"),
|
|
2305
|
+
to: z44.string().optional().describe("ISO end date"),
|
|
2306
|
+
sourceSelection: sourceSelectionSchema.optional().describe("auto|web|community|social|shopping|all"),
|
|
2307
|
+
sources: z44.array(sourceSchema).optional().describe("Explicit source list"),
|
|
2308
|
+
mode: modeSchema.optional().describe("compact|json|md|context|path"),
|
|
2309
|
+
includeEngagement: z44.boolean().optional().describe("Include engagement enrichment"),
|
|
2310
|
+
limitPerSource: z44.number().int().positive().optional().describe("Result limit per source"),
|
|
2311
|
+
outputDir: z44.string().optional().describe("Optional artifact output directory"),
|
|
2312
|
+
ttlHours: z44.number().int().positive().optional().describe("Artifact retention TTL in hours"),
|
|
2313
|
+
useCookies: z44.boolean().optional().describe("Enable/disable provider cookie injection for this run"),
|
|
2314
|
+
cookiePolicyOverride: cookiePolicySchema.optional().describe("Override cookie policy: off|auto|required")
|
|
2315
|
+
},
|
|
2316
|
+
async execute(args) {
|
|
2317
|
+
try {
|
|
2318
|
+
const runtime = resolveProviderRuntime(deps);
|
|
2319
|
+
const result = await runResearchWorkflow(runtime, {
|
|
2320
|
+
topic: args.topic,
|
|
2321
|
+
days: args.days,
|
|
2322
|
+
from: args.from,
|
|
2323
|
+
to: args.to,
|
|
2324
|
+
sourceSelection: args.sourceSelection,
|
|
2325
|
+
sources: args.sources,
|
|
2326
|
+
mode: args.mode ?? "compact",
|
|
2327
|
+
includeEngagement: args.includeEngagement,
|
|
2328
|
+
limitPerSource: args.limitPerSource,
|
|
2329
|
+
outputDir: args.outputDir,
|
|
2330
|
+
ttlHours: args.ttlHours,
|
|
2331
|
+
useCookies: args.useCookies,
|
|
2332
|
+
cookiePolicyOverride: args.cookiePolicyOverride
|
|
2333
|
+
});
|
|
2334
|
+
return ok(result);
|
|
2335
|
+
} catch (error) {
|
|
2336
|
+
return failure(serializeError(error).message, "research_run_failed");
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
// src/tools/shopping_run.ts
|
|
2343
|
+
import { tool as tool45 } from "@opencode-ai/plugin";
|
|
2344
|
+
var z45 = tool45.schema;
|
|
2345
|
+
var sortSchema = z45.enum(["best_deal", "lowest_price", "highest_rating", "fastest_shipping"]);
|
|
2346
|
+
var modeSchema2 = z45.enum(["compact", "json", "md", "context", "path"]);
|
|
2347
|
+
var cookiePolicySchema2 = z45.enum(["off", "auto", "required"]);
|
|
2348
|
+
function createShoppingRunTool(deps) {
|
|
2349
|
+
return tool45({
|
|
2350
|
+
description: "Run shopping/deal intelligence across shopping providers.",
|
|
2351
|
+
args: {
|
|
2352
|
+
query: z45.string().min(1).describe("Shopping query"),
|
|
2353
|
+
providers: z45.array(z45.string()).optional().describe("Optional provider allow-list"),
|
|
2354
|
+
budget: z45.number().positive().optional().describe("Optional budget amount"),
|
|
2355
|
+
region: z45.string().optional().describe("Region hint"),
|
|
2356
|
+
sort: sortSchema.optional().describe("best_deal|lowest_price|highest_rating|fastest_shipping"),
|
|
2357
|
+
mode: modeSchema2.optional().describe("compact|json|md|context|path"),
|
|
2358
|
+
outputDir: z45.string().optional().describe("Optional artifact output directory"),
|
|
2359
|
+
ttlHours: z45.number().int().positive().optional().describe("Artifact retention TTL in hours"),
|
|
2360
|
+
useCookies: z45.boolean().optional().describe("Enable/disable provider cookie injection for this run"),
|
|
2361
|
+
cookiePolicyOverride: cookiePolicySchema2.optional().describe("Override cookie policy: off|auto|required")
|
|
2362
|
+
},
|
|
2363
|
+
async execute(args) {
|
|
2364
|
+
try {
|
|
2365
|
+
const runtime = resolveProviderRuntime(deps);
|
|
2366
|
+
const result = await runShoppingWorkflow(runtime, {
|
|
2367
|
+
query: args.query,
|
|
2368
|
+
providers: args.providers,
|
|
2369
|
+
budget: args.budget,
|
|
2370
|
+
region: args.region,
|
|
2371
|
+
sort: args.sort,
|
|
2372
|
+
mode: args.mode ?? "compact",
|
|
2373
|
+
outputDir: args.outputDir,
|
|
2374
|
+
ttlHours: args.ttlHours,
|
|
2375
|
+
useCookies: args.useCookies,
|
|
2376
|
+
cookiePolicyOverride: args.cookiePolicyOverride
|
|
2377
|
+
});
|
|
2378
|
+
return ok(result);
|
|
2379
|
+
} catch (error) {
|
|
2380
|
+
return failure(serializeError(error).message, "shopping_run_failed");
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
// src/tools/product_video_run.ts
|
|
2387
|
+
import { tool as tool46 } from "@opencode-ai/plugin";
|
|
2388
|
+
var z46 = tool46.schema;
|
|
2389
|
+
var cookiePolicySchema3 = z46.enum(["off", "auto", "required"]);
|
|
2390
|
+
async function captureScreenshotBuffer(deps, url) {
|
|
2391
|
+
let sessionId = null;
|
|
2392
|
+
try {
|
|
2393
|
+
const launched = await deps.manager.launch({
|
|
2394
|
+
headless: true,
|
|
2395
|
+
startUrl: url
|
|
2396
|
+
});
|
|
2397
|
+
sessionId = launched.sessionId;
|
|
2398
|
+
const screenshot = await deps.manager.screenshot(sessionId);
|
|
2399
|
+
if (typeof screenshot.base64 === "string" && screenshot.base64.length > 0) {
|
|
2400
|
+
return Buffer.from(screenshot.base64, "base64");
|
|
2401
|
+
}
|
|
2402
|
+
return null;
|
|
2403
|
+
} catch {
|
|
2404
|
+
return null;
|
|
2405
|
+
} finally {
|
|
2406
|
+
if (sessionId) {
|
|
2407
|
+
await deps.manager.disconnect(sessionId, true).catch(() => {
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
function createProductVideoRunTool(deps) {
|
|
2413
|
+
return tool46({
|
|
2414
|
+
description: "Collect a product presentation asset pack for video/UGC workflows.",
|
|
2415
|
+
args: {
|
|
2416
|
+
product_url: z46.string().optional().describe("Product URL"),
|
|
2417
|
+
product_name: z46.string().optional().describe("Product name"),
|
|
2418
|
+
provider_hint: z46.string().optional().describe("Optional provider hint"),
|
|
2419
|
+
include_screenshots: z46.boolean().optional().describe("Include screenshots (default true)"),
|
|
2420
|
+
include_all_images: z46.boolean().optional().describe("Include all discovered images (default true)"),
|
|
2421
|
+
include_copy: z46.boolean().optional().describe("Include product copy extraction (default true)"),
|
|
2422
|
+
output_dir: z46.string().optional().describe("Optional output directory"),
|
|
2423
|
+
ttl_hours: z46.number().int().positive().optional().describe("Artifact retention TTL in hours"),
|
|
2424
|
+
useCookies: z46.boolean().optional().describe("Enable/disable provider cookie injection for this run"),
|
|
2425
|
+
cookiePolicyOverride: cookiePolicySchema3.optional().describe("Override cookie policy: off|auto|required")
|
|
2426
|
+
},
|
|
2427
|
+
async execute(args) {
|
|
2428
|
+
try {
|
|
2429
|
+
const runtime = resolveProviderRuntime(deps);
|
|
2430
|
+
const includeScreenshots = args.include_screenshots ?? true;
|
|
2431
|
+
const result = await runProductVideoWorkflow(runtime, {
|
|
2432
|
+
product_url: args.product_url,
|
|
2433
|
+
product_name: args.product_name,
|
|
2434
|
+
provider_hint: args.provider_hint,
|
|
2435
|
+
include_screenshots: includeScreenshots,
|
|
2436
|
+
include_all_images: args.include_all_images,
|
|
2437
|
+
include_copy: args.include_copy,
|
|
2438
|
+
output_dir: args.output_dir,
|
|
2439
|
+
ttl_hours: args.ttl_hours,
|
|
2440
|
+
useCookies: args.useCookies,
|
|
2441
|
+
cookiePolicyOverride: args.cookiePolicyOverride
|
|
2442
|
+
}, {
|
|
2443
|
+
captureScreenshot: includeScreenshots ? async (url) => captureScreenshotBuffer(deps, url) : void 0
|
|
2444
|
+
});
|
|
2445
|
+
return ok(result);
|
|
2446
|
+
} catch (error) {
|
|
2447
|
+
return failure(serializeError(error).message, "product_video_run_failed");
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
|
|
936
2453
|
// src/tools/skill_list.ts
|
|
937
|
-
import { tool as
|
|
2454
|
+
import { tool as tool47 } from "@opencode-ai/plugin";
|
|
938
2455
|
function createSkillListTool(deps) {
|
|
939
|
-
return
|
|
2456
|
+
return tool47({
|
|
940
2457
|
description: "List available skills from OpenCode skill directories (compatibility wrapper)",
|
|
941
2458
|
args: {},
|
|
942
2459
|
async execute() {
|
|
@@ -952,14 +2469,14 @@ function createSkillListTool(deps) {
|
|
|
952
2469
|
}
|
|
953
2470
|
|
|
954
2471
|
// src/tools/skill_load.ts
|
|
955
|
-
import { tool as
|
|
956
|
-
var
|
|
2472
|
+
import { tool as tool48 } from "@opencode-ai/plugin";
|
|
2473
|
+
var z47 = tool48.schema;
|
|
957
2474
|
function createSkillLoadTool(deps) {
|
|
958
|
-
return
|
|
2475
|
+
return tool48({
|
|
959
2476
|
description: "Load a specific skill by name from OpenCode skill directories (compatibility wrapper)",
|
|
960
2477
|
args: {
|
|
961
|
-
name:
|
|
962
|
-
topic:
|
|
2478
|
+
name: z47.string().describe("Name of the skill to load (e.g., 'opendevbrowser-login-automation', 'opendevbrowser-form-testing')"),
|
|
2479
|
+
topic: z47.string().optional().describe("Optional topic to filter the skill content")
|
|
963
2480
|
},
|
|
964
2481
|
async execute(args) {
|
|
965
2482
|
try {
|
|
@@ -975,44 +2492,83 @@ function createSkillLoadTool(deps) {
|
|
|
975
2492
|
|
|
976
2493
|
// src/tools/index.ts
|
|
977
2494
|
function createTools(deps) {
|
|
2495
|
+
const wrap = (definition) => {
|
|
2496
|
+
if (!deps.ensureHub) return definition;
|
|
2497
|
+
return {
|
|
2498
|
+
...definition,
|
|
2499
|
+
execute: async (args, context) => {
|
|
2500
|
+
try {
|
|
2501
|
+
await deps.ensureHub?.();
|
|
2502
|
+
} catch {
|
|
2503
|
+
}
|
|
2504
|
+
return definition.execute(args, context);
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
};
|
|
978
2508
|
return {
|
|
979
|
-
opendevbrowser_launch: createLaunchTool(deps),
|
|
980
|
-
opendevbrowser_connect: createConnectTool(deps),
|
|
981
|
-
opendevbrowser_disconnect: createDisconnectTool(deps),
|
|
982
|
-
opendevbrowser_status: createStatusTool(deps),
|
|
983
|
-
opendevbrowser_targets_list: createTargetsListTool(deps),
|
|
984
|
-
opendevbrowser_target_use: createTargetUseTool(deps),
|
|
985
|
-
opendevbrowser_target_new: createTargetNewTool(deps),
|
|
986
|
-
opendevbrowser_target_close: createTargetCloseTool(deps),
|
|
987
|
-
opendevbrowser_page: createPageTool(deps),
|
|
988
|
-
opendevbrowser_list: createListTool(deps),
|
|
989
|
-
opendevbrowser_close: createCloseTool(deps),
|
|
990
|
-
opendevbrowser_goto: createGotoTool(deps),
|
|
991
|
-
opendevbrowser_wait: createWaitTool(deps),
|
|
992
|
-
opendevbrowser_snapshot: createSnapshotTool(deps),
|
|
993
|
-
opendevbrowser_click: createClickTool(deps),
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
2509
|
+
opendevbrowser_launch: wrap(createLaunchTool(deps)),
|
|
2510
|
+
opendevbrowser_connect: wrap(createConnectTool(deps)),
|
|
2511
|
+
opendevbrowser_disconnect: wrap(createDisconnectTool(deps)),
|
|
2512
|
+
opendevbrowser_status: wrap(createStatusTool(deps)),
|
|
2513
|
+
opendevbrowser_targets_list: wrap(createTargetsListTool(deps)),
|
|
2514
|
+
opendevbrowser_target_use: wrap(createTargetUseTool(deps)),
|
|
2515
|
+
opendevbrowser_target_new: wrap(createTargetNewTool(deps)),
|
|
2516
|
+
opendevbrowser_target_close: wrap(createTargetCloseTool(deps)),
|
|
2517
|
+
opendevbrowser_page: wrap(createPageTool(deps)),
|
|
2518
|
+
opendevbrowser_list: wrap(createListTool(deps)),
|
|
2519
|
+
opendevbrowser_close: wrap(createCloseTool(deps)),
|
|
2520
|
+
opendevbrowser_goto: wrap(createGotoTool(deps)),
|
|
2521
|
+
opendevbrowser_wait: wrap(createWaitTool(deps)),
|
|
2522
|
+
opendevbrowser_snapshot: wrap(createSnapshotTool(deps)),
|
|
2523
|
+
opendevbrowser_click: wrap(createClickTool(deps)),
|
|
2524
|
+
opendevbrowser_hover: wrap(createHoverTool(deps)),
|
|
2525
|
+
opendevbrowser_press: wrap(createPressTool(deps)),
|
|
2526
|
+
opendevbrowser_check: wrap(createCheckTool(deps)),
|
|
2527
|
+
opendevbrowser_uncheck: wrap(createUncheckTool(deps)),
|
|
2528
|
+
opendevbrowser_type: wrap(createTypeTool(deps)),
|
|
2529
|
+
opendevbrowser_select: wrap(createSelectTool(deps)),
|
|
2530
|
+
opendevbrowser_scroll: wrap(createScrollTool(deps)),
|
|
2531
|
+
opendevbrowser_scroll_into_view: wrap(createScrollIntoViewTool(deps)),
|
|
2532
|
+
opendevbrowser_dom_get_html: wrap(createDomGetHtmlTool(deps)),
|
|
2533
|
+
opendevbrowser_dom_get_text: wrap(createDomGetTextTool(deps)),
|
|
2534
|
+
opendevbrowser_get_attr: wrap(createGetAttrTool(deps)),
|
|
2535
|
+
opendevbrowser_get_value: wrap(createGetValueTool(deps)),
|
|
2536
|
+
opendevbrowser_is_visible: wrap(createIsVisibleTool(deps)),
|
|
2537
|
+
opendevbrowser_is_enabled: wrap(createIsEnabledTool(deps)),
|
|
2538
|
+
opendevbrowser_is_checked: wrap(createIsCheckedTool(deps)),
|
|
2539
|
+
opendevbrowser_run: wrap(createRunTool(deps)),
|
|
2540
|
+
opendevbrowser_prompting_guide: wrap(createPromptingGuideTool(deps)),
|
|
2541
|
+
opendevbrowser_console_poll: wrap(createConsolePollTool(deps)),
|
|
2542
|
+
opendevbrowser_network_poll: wrap(createNetworkPollTool(deps)),
|
|
2543
|
+
opendevbrowser_debug_trace_snapshot: wrap(createDebugTraceSnapshotTool(deps)),
|
|
2544
|
+
opendevbrowser_cookie_import: wrap(createCookieImportTool(deps)),
|
|
2545
|
+
opendevbrowser_cookie_list: wrap(createCookieListTool(deps)),
|
|
2546
|
+
opendevbrowser_macro_resolve: wrap(createMacroResolveTool(deps)),
|
|
2547
|
+
opendevbrowser_research_run: wrap(createResearchRunTool(deps)),
|
|
2548
|
+
opendevbrowser_shopping_run: wrap(createShoppingRunTool(deps)),
|
|
2549
|
+
opendevbrowser_product_video_run: wrap(createProductVideoRunTool(deps)),
|
|
2550
|
+
opendevbrowser_clone_page: wrap(createClonePageTool(deps)),
|
|
2551
|
+
opendevbrowser_clone_component: wrap(createCloneComponentTool(deps)),
|
|
2552
|
+
opendevbrowser_perf: wrap(createPerfTool(deps)),
|
|
2553
|
+
opendevbrowser_screenshot: wrap(createScreenshotTool(deps)),
|
|
2554
|
+
opendevbrowser_annotate: wrap(createAnnotateTool(deps)),
|
|
2555
|
+
opendevbrowser_skill_list: wrap(createSkillListTool(deps)),
|
|
2556
|
+
opendevbrowser_skill_load: wrap(createSkillLoadTool(deps))
|
|
1009
2557
|
};
|
|
1010
2558
|
}
|
|
1011
2559
|
|
|
1012
2560
|
// src/index.ts
|
|
1013
2561
|
var OpenDevBrowserPlugin = async ({ directory, worktree }) => {
|
|
1014
2562
|
const core = createOpenDevBrowserCore({ directory, worktree });
|
|
1015
|
-
const { config, configStore,
|
|
2563
|
+
const { config, configStore, skills, ensureRelay, cleanup, getExtensionPath } = core;
|
|
2564
|
+
let relay = core.relay;
|
|
2565
|
+
let manager = core.manager;
|
|
2566
|
+
let runner = core.runner;
|
|
2567
|
+
let annotationManager = core.annotationManager;
|
|
2568
|
+
let providerRuntime = core.providerRuntime;
|
|
2569
|
+
let browserFallbackPort = core.browserFallbackPort;
|
|
2570
|
+
let hubStop = null;
|
|
2571
|
+
let daemonClient = null;
|
|
1016
2572
|
const skillNudgeState = createSkillNudgeState();
|
|
1017
2573
|
const continuityNudgeState = createContinuityNudgeState();
|
|
1018
2574
|
console.info(
|
|
@@ -1023,12 +2579,99 @@ var OpenDevBrowserPlugin = async ({ directory, worktree }) => {
|
|
|
1023
2579
|
} catch (error) {
|
|
1024
2580
|
console.warn("Extension extraction failed:", error instanceof Error ? error.message : error);
|
|
1025
2581
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
2582
|
+
const toolDeps = {
|
|
2583
|
+
manager,
|
|
2584
|
+
annotationManager,
|
|
2585
|
+
runner,
|
|
2586
|
+
config: configStore,
|
|
2587
|
+
skills,
|
|
2588
|
+
providerRuntime,
|
|
2589
|
+
browserFallbackPort,
|
|
2590
|
+
relay,
|
|
2591
|
+
getExtensionPath
|
|
2592
|
+
};
|
|
2593
|
+
const bindRemote = () => {
|
|
2594
|
+
if (!daemonClient) {
|
|
2595
|
+
daemonClient = new DaemonClient({ autoRenew: true });
|
|
2596
|
+
}
|
|
2597
|
+
manager = new RemoteManager(daemonClient);
|
|
2598
|
+
relay = new RemoteRelay(daemonClient);
|
|
2599
|
+
annotationManager.setRelay(relay);
|
|
2600
|
+
annotationManager.setBrowserManager(manager);
|
|
2601
|
+
runner = new ScriptRunner(manager);
|
|
2602
|
+
browserFallbackPort = createBrowserFallbackPort(manager);
|
|
2603
|
+
providerRuntime = createConfiguredProviderRuntime({
|
|
2604
|
+
config: configStore.get(),
|
|
2605
|
+
manager,
|
|
2606
|
+
browserFallbackPort
|
|
2607
|
+
});
|
|
2608
|
+
toolDeps.manager = manager;
|
|
2609
|
+
toolDeps.relay = relay;
|
|
2610
|
+
toolDeps.runner = runner;
|
|
2611
|
+
toolDeps.providerRuntime = providerRuntime;
|
|
2612
|
+
toolDeps.browserFallbackPort = browserFallbackPort;
|
|
2613
|
+
};
|
|
2614
|
+
const ensureHub = async () => {
|
|
2615
|
+
const currentConfig = configStore.get();
|
|
2616
|
+
if (!isHubEnabled(currentConfig)) {
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
if (!daemonClient) {
|
|
2620
|
+
daemonClient = new DaemonClient({ autoRenew: true });
|
|
2621
|
+
}
|
|
2622
|
+
const deadline = Date.now() + 2e3;
|
|
2623
|
+
let attempt = 0;
|
|
2624
|
+
let lastError = null;
|
|
2625
|
+
while (attempt < 2 && Date.now() < deadline) {
|
|
2626
|
+
attempt += 1;
|
|
2627
|
+
const status = await fetchDaemonStatusFromMetadata(currentConfig);
|
|
2628
|
+
if (status?.ok) {
|
|
2629
|
+
bindRemote();
|
|
2630
|
+
await relay?.refresh?.();
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2633
|
+
try {
|
|
2634
|
+
const { stop } = await startDaemon({ config: currentConfig, directory, worktree });
|
|
2635
|
+
hubStop = stop;
|
|
2636
|
+
} catch (error) {
|
|
2637
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2638
|
+
}
|
|
2639
|
+
if (Date.now() < deadline) {
|
|
2640
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
if (lastError) {
|
|
2644
|
+
throw lastError;
|
|
2645
|
+
}
|
|
2646
|
+
throw new Error("Hub daemon unavailable.");
|
|
2647
|
+
};
|
|
2648
|
+
toolDeps.ensureHub = ensureHub;
|
|
2649
|
+
const hubEnabled = isHubEnabled(config);
|
|
2650
|
+
if (hubEnabled) {
|
|
2651
|
+
bindRemote();
|
|
2652
|
+
try {
|
|
2653
|
+
await ensureHub();
|
|
2654
|
+
} catch (error) {
|
|
2655
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2656
|
+
console.warn(`[opendevbrowser] Hub daemon unavailable: ${message}`);
|
|
2657
|
+
}
|
|
2658
|
+
} else {
|
|
2659
|
+
await ensureRelay(config.relayPort);
|
|
2660
|
+
}
|
|
2661
|
+
const cleanupAll = () => {
|
|
2662
|
+
if (hubStop) {
|
|
2663
|
+
hubStop().catch(() => {
|
|
2664
|
+
});
|
|
2665
|
+
}
|
|
2666
|
+
daemonClient?.releaseBinding().catch(() => {
|
|
2667
|
+
});
|
|
2668
|
+
cleanup();
|
|
2669
|
+
};
|
|
2670
|
+
process.on("SIGINT", cleanupAll);
|
|
2671
|
+
process.on("SIGTERM", cleanupAll);
|
|
2672
|
+
process.on("beforeExit", cleanupAll);
|
|
1030
2673
|
return {
|
|
1031
|
-
tool: createTools(
|
|
2674
|
+
tool: createTools(toolDeps),
|
|
1032
2675
|
"chat.message": async (_input, output) => {
|
|
1033
2676
|
const config2 = configStore.get();
|
|
1034
2677
|
if (output.message.role !== "user") return;
|