@web-auto/webauto 0.1.1 → 0.1.3
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/apps/desktop-console/default-settings.json +1 -0
- package/apps/desktop-console/dist/main/index.mjs +1618 -0
- package/apps/desktop-console/{src → dist}/main/preload.mjs +10 -0
- package/apps/desktop-console/dist/renderer/index.js +3063 -0
- package/apps/desktop-console/entry/ui-console.mjs +299 -0
- package/apps/webauto/entry/account.mjs +356 -0
- package/apps/webauto/entry/lib/account-detect.mjs +160 -0
- package/apps/webauto/entry/lib/account-store.mjs +587 -0
- package/apps/webauto/entry/lib/profilepool.mjs +1 -1
- package/apps/webauto/entry/xhs-install.mjs +27 -3
- package/apps/webauto/entry/xhs-status.mjs +152 -0
- package/apps/webauto/entry/xhs-unified.mjs +595 -17
- package/bin/webauto.mjs +263 -15
- package/dist/apps/webauto/server.js +66 -0
- package/dist/modules/camo-backend/src/index.js +575 -0
- package/dist/modules/camo-backend/src/internal/BrowserSession.js +817 -0
- package/dist/modules/camo-backend/src/internal/ElementRegistry.js +61 -0
- package/dist/modules/camo-backend/src/internal/ProfileLock.js +85 -0
- package/dist/modules/camo-backend/src/internal/SessionManager.js +172 -0
- package/dist/modules/camo-backend/src/internal/container-matcher.js +852 -0
- package/dist/modules/camo-backend/src/internal/engine-manager.js +258 -0
- package/dist/modules/camo-backend/src/internal/fingerprint.js +203 -0
- package/dist/modules/camo-backend/src/internal/pageRuntime.js +29 -0
- package/dist/modules/camo-backend/src/internal/runtimeInjector.js +30 -0
- package/dist/modules/camo-backend/src/internal/state-bus.js +46 -0
- package/dist/modules/camo-backend/src/internal/storage-paths.js +36 -0
- package/dist/modules/camo-backend/src/internal/ws-server.js +1202 -0
- package/dist/modules/camo-runtime/src/utils/browser-service.mjs +423 -0
- package/dist/modules/camo-runtime/src/utils/config.mjs +77 -0
- package/dist/modules/container-registry/src/index.js +184 -0
- package/dist/modules/logging/src/index.js +92 -0
- package/dist/modules/operations/src/builtin.js +27 -0
- package/dist/modules/operations/src/container-binding.js +75 -0
- package/dist/modules/operations/src/executor.js +146 -0
- package/dist/modules/operations/src/operations/click.js +167 -0
- package/dist/modules/operations/src/operations/extract.js +204 -0
- package/dist/modules/operations/src/operations/find-child.js +17 -0
- package/dist/modules/operations/src/operations/highlight.js +138 -0
- package/dist/modules/operations/src/operations/key.js +61 -0
- package/dist/modules/operations/src/operations/navigate.js +148 -0
- package/dist/modules/operations/src/operations/scroll.js +126 -0
- package/dist/modules/operations/src/operations/type.js +190 -0
- package/dist/modules/operations/src/queue.js +100 -0
- package/dist/modules/operations/src/registry.js +11 -0
- package/dist/modules/operations/src/system/mouse.js +33 -0
- package/dist/modules/state/src/atomic-json.js +33 -0
- package/dist/modules/workflow/blocks/AnchorVerificationBlock.js +71 -0
- package/dist/modules/workflow/blocks/BehaviorRandomizer.js +26 -0
- package/dist/modules/workflow/blocks/CallWorkflowBlock.js +38 -0
- package/dist/modules/workflow/blocks/CloseDetailBlock.js +209 -0
- package/dist/modules/workflow/blocks/CollectBatch.js +137 -0
- package/dist/modules/workflow/blocks/CollectCommentsBlock.js +415 -0
- package/dist/modules/workflow/blocks/CollectSearchListBlock.js +599 -0
- package/dist/modules/workflow/blocks/CollectWeiboPosts.js +229 -0
- package/dist/modules/workflow/blocks/DetectPageStateBlock.js +259 -0
- package/dist/modules/workflow/blocks/EnsureLoginBlock.js +162 -0
- package/dist/modules/workflow/blocks/EnsureSession.js +426 -0
- package/dist/modules/workflow/blocks/ErrorClassifier.js +164 -0
- package/dist/modules/workflow/blocks/ErrorRecoveryBlock.js +319 -0
- package/dist/modules/workflow/blocks/ExpandCommentsBlock.js +1032 -0
- package/dist/modules/workflow/blocks/ExtractDetailBlock.js +310 -0
- package/dist/modules/workflow/blocks/ExtractPostFields.js +88 -0
- package/dist/modules/workflow/blocks/GenerateSmartReplyBlock.js +68 -0
- package/dist/modules/workflow/blocks/GoToSearchBlock.js +497 -0
- package/dist/modules/workflow/blocks/GracefulFallbackBlock.js +104 -0
- package/dist/modules/workflow/blocks/HighlightBlock.js +66 -0
- package/dist/modules/workflow/blocks/InitAutoScroll.js +65 -0
- package/dist/modules/workflow/blocks/LoadContainerDefinition.js +50 -0
- package/dist/modules/workflow/blocks/LoadContainerIndex.js +43 -0
- package/dist/modules/workflow/blocks/LocateAndGuardBlock.js +176 -0
- package/dist/modules/workflow/blocks/LoginRecoveryBlock.js +242 -0
- package/dist/modules/workflow/blocks/MatchContainers.js +64 -0
- package/dist/modules/workflow/blocks/MonitoringBlock.js +190 -0
- package/dist/modules/workflow/blocks/OpenDetailBlock.js +1240 -0
- package/dist/modules/workflow/blocks/OrganizeXhsNotesBlock.js +117 -0
- package/dist/modules/workflow/blocks/PersistXhsNoteBlock.js +270 -0
- package/dist/modules/workflow/blocks/PickSinglePost.js +69 -0
- package/dist/modules/workflow/blocks/ProgressTracker.js +125 -0
- package/dist/modules/workflow/blocks/RecordFixtureBlock.js +44 -0
- package/dist/modules/workflow/blocks/RenderMarkdown.js +48 -0
- package/dist/modules/workflow/blocks/SaveFile.js +54 -0
- package/dist/modules/workflow/blocks/ScrollNextBatch.js +72 -0
- package/dist/modules/workflow/blocks/SessionHealthBlock.js +73 -0
- package/dist/modules/workflow/blocks/StartBrowserService.js +45 -0
- package/dist/modules/workflow/blocks/ValidateContainerDefinition.js +67 -0
- package/dist/modules/workflow/blocks/ValidateExtract.js +35 -0
- package/dist/modules/workflow/blocks/WaitSearchPermitBlock.js +162 -0
- package/dist/modules/workflow/blocks/WaitStable.js +74 -0
- package/dist/modules/workflow/blocks/WarmupCommentsBlock.js +120 -0
- package/dist/modules/workflow/blocks/WorkflowExecutor.js +156 -0
- package/dist/modules/workflow/blocks/XiaohongshuCollectFromLinksBlock.js +1004 -0
- package/dist/modules/workflow/blocks/XiaohongshuCollectLinksBlock.js +1049 -0
- package/dist/modules/workflow/blocks/XiaohongshuFullCollectBlock.js +782 -0
- package/dist/modules/workflow/blocks/helpers/anchorVerify.js +198 -0
- package/dist/modules/workflow/blocks/helpers/asyncWorkQueue.js +53 -0
- package/dist/modules/workflow/blocks/helpers/commentScroller.js +334 -0
- package/dist/modules/workflow/blocks/helpers/commentSectionLocator.js +126 -0
- package/dist/modules/workflow/blocks/helpers/containerAnchors.js +301 -0
- package/dist/modules/workflow/blocks/helpers/debugArtifacts.js +6 -0
- package/dist/modules/workflow/blocks/helpers/downloadPaths.js +29 -0
- package/dist/modules/workflow/blocks/helpers/expandCommentsController.js +53 -0
- package/dist/modules/workflow/blocks/helpers/expandCommentsExtractor.js +129 -0
- package/dist/modules/workflow/blocks/helpers/macosVisionOcrPlugin.js +116 -0
- package/dist/modules/workflow/blocks/helpers/mergeXhsMarkdown.js +109 -0
- package/dist/modules/workflow/blocks/helpers/openDetailController.js +56 -0
- package/dist/modules/workflow/blocks/helpers/openDetailTypes.js +7 -0
- package/dist/modules/workflow/blocks/helpers/openDetailViewport.js +474 -0
- package/dist/modules/workflow/blocks/helpers/openDetailWaiter.js +104 -0
- package/dist/modules/workflow/blocks/helpers/operationLogger.js +195 -0
- package/dist/modules/workflow/blocks/helpers/persistedNotes.js +107 -0
- package/dist/modules/workflow/blocks/helpers/replyExpander.js +260 -0
- package/dist/modules/workflow/blocks/helpers/scrollIntoView.js +138 -0
- package/dist/modules/workflow/blocks/helpers/searchExecutor.js +328 -0
- package/dist/modules/workflow/blocks/helpers/searchGate.js +46 -0
- package/dist/modules/workflow/blocks/helpers/searchPageState.js +164 -0
- package/dist/modules/workflow/blocks/helpers/searchResultWaiter.js +64 -0
- package/dist/modules/workflow/blocks/helpers/simpleAnchor.js +134 -0
- package/dist/modules/workflow/blocks/helpers/smartReply.js +40 -0
- package/dist/modules/workflow/blocks/helpers/systemInput.js +635 -0
- package/dist/modules/workflow/blocks/helpers/targetCountMode.js +9 -0
- package/dist/modules/workflow/blocks/helpers/xhsCliArgs.js +80 -0
- package/dist/modules/workflow/blocks/helpers/xhsCommentDom.js +805 -0
- package/dist/modules/workflow/blocks/helpers/xhsNoteOrganizer.js +140 -0
- package/dist/modules/workflow/blocks/restore/RestorePhaseBlock.js +204 -0
- package/dist/modules/workflow/config/workflowRegistry.js +32 -0
- package/dist/modules/workflow/definitions/batch-collect-workflow.js +63 -0
- package/dist/modules/workflow/definitions/scroll-extract-workflow.js +74 -0
- package/dist/modules/workflow/definitions/xiaohongshu-collect-workflow-v2.js +81 -0
- package/dist/modules/workflow/definitions/xiaohongshu-collect-workflow.js +57 -0
- package/dist/modules/workflow/definitions/xiaohongshu-full-collect-workflow-v3.js +68 -0
- package/dist/modules/workflow/definitions/xiaohongshu-note-collect.js +49 -0
- package/dist/modules/workflow/definitions/xiaohongshu-phase1-workflow-v3.js +30 -0
- package/dist/modules/workflow/definitions/xiaohongshu-phase2-links-workflow-v3.js +40 -0
- package/dist/modules/workflow/definitions/xiaohongshu-phase3-collect-workflow-v1.js +54 -0
- package/dist/modules/workflow/definitions/xiaohongshu-phase34-from-links-workflow-v3.js +25 -0
- package/dist/modules/workflow/src/WeiboEventDrivenWorkflowRunner.js +308 -0
- package/dist/modules/workflow/src/context.js +70 -0
- package/dist/modules/workflow/src/index.js +5 -0
- package/dist/modules/workflow/src/orchestrator.js +230 -0
- package/dist/modules/workflow/src/runner.js +55 -0
- package/dist/modules/workflow/src/runtime.js +70 -0
- package/dist/modules/workflow/workflows/WeiboFeedExtractionWorkflow.js +359 -0
- package/dist/modules/workflow/workflows/XiaohongshuLoginWorkflow.js +110 -0
- package/dist/modules/xiaohongshu/app/src/blocks/MatchCommentsBlock.js +139 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase1EnsureServicesBlock.js +36 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase1MonitorCookieBlock.js +213 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.js +121 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase2CollectLinksBlock.js +1249 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase2SearchBlock.js +703 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34CloseDetailBlock.js +41 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34CloseTabsBlock.js +44 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34CollectCommentsBlock.js +150 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34ExtractDetailBlock.js +117 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34OpenDetailBlock.js +102 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34OpenTabsBlock.js +109 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.js +117 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34ProcessSingleNoteBlock.js +114 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34ValidateLinksBlock.js +90 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase3InteractBlock.js +1009 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase4MultiTabHarvestBlock.js +233 -0
- package/dist/modules/xiaohongshu/app/src/blocks/ReplyInteractBlock.js +291 -0
- package/dist/modules/xiaohongshu/app/src/blocks/XhsDiscoverFallbackBlock.js +240 -0
- package/dist/modules/xiaohongshu/app/src/blocks/helpers/commentMatchDsl.js +126 -0
- package/dist/modules/xiaohongshu/app/src/blocks/helpers/commentMatcher.js +99 -0
- package/dist/modules/xiaohongshu/app/src/blocks/helpers/evidence.js +27 -0
- package/dist/modules/xiaohongshu/app/src/blocks/helpers/sharding.js +42 -0
- package/dist/modules/xiaohongshu/app/src/blocks/helpers/xhsComments.js +270 -0
- package/dist/modules/xiaohongshu/app/src/index.js +9 -0
- package/dist/modules/xiaohongshu/app/src/utils/checkpoints.js +222 -0
- package/dist/modules/xiaohongshu/app/src/utils/controllerAction.js +43 -0
- package/dist/services/controller/src/controller.js +1476 -0
- package/dist/services/controller/src/index.js +2 -0
- package/dist/services/controller/src/payload-normalizer.js +129 -0
- package/dist/services/shared/heartbeat.js +120 -0
- package/dist/services/shared/lib/errorHandler.js +2 -0
- package/dist/services/shared/serviceProcessLogger.js +139 -0
- package/dist/services/unified-api/RemoteBrowserSession.js +176 -0
- package/dist/services/unified-api/RemoteSessionManager.js +148 -0
- package/dist/services/unified-api/container-operations-handler.js +115 -0
- package/dist/services/unified-api/server.js +652 -0
- package/dist/services/unified-api/state-registry.js +274 -0
- package/dist/services/unified-api/task-persistence.js +66 -0
- package/dist/services/unified-api/task-state.js +130 -0
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/search.mjs +12 -5
- package/modules/xiaohongshu/app/pnpm-lock.yaml +24 -0
- package/package.json +38 -10
- package/.beads/README.md +0 -81
- package/.beads/config.yaml +0 -67
- package/.beads/interactions.jsonl +0 -0
- package/.beads/issues.jsonl +0 -180
- package/.beads/metadata.json +0 -4
- package/.claude/settings.local.json +0 -10
- package/.github/workflows/ci.yml +0 -55
- package/AGENTS.md +0 -253
- package/apps/desktop-console/README.md +0 -27
- package/apps/desktop-console/package-lock.json +0 -897
- package/apps/desktop-console/package.json +0 -20
- package/apps/desktop-console/scripts/build-and-install.mjs +0 -19
- package/apps/desktop-console/scripts/build.mjs +0 -45
- package/apps/desktop-console/scripts/test-preload.mjs +0 -13
- package/apps/desktop-console/src/main/config.mts +0 -26
- package/apps/desktop-console/src/main/core-daemon-manager.mts +0 -131
- package/apps/desktop-console/src/main/desktop-settings.mts +0 -267
- package/apps/desktop-console/src/main/heartbeat-watchdog.mts +0 -50
- package/apps/desktop-console/src/main/heartbeat-watchdog.test.mts +0 -68
- package/apps/desktop-console/src/main/index-streaming.test.mts +0 -20
- package/apps/desktop-console/src/main/index.mts +0 -980
- package/apps/desktop-console/src/main/profile-store.mts +0 -239
- package/apps/desktop-console/src/main/profile-store.test.mts +0 -54
- package/apps/desktop-console/src/main/state-bridge.mts +0 -114
- package/apps/desktop-console/src/main/task-state-types.ts +0 -32
- package/apps/desktop-console/src/renderer/hooks/use-task-state.mts +0 -120
- package/apps/desktop-console/src/renderer/index.mts +0 -133
- package/apps/desktop-console/src/renderer/index.test.mts +0 -34
- package/apps/desktop-console/src/renderer/path-helpers.mts +0 -46
- package/apps/desktop-console/src/renderer/path-helpers.test.mts +0 -14
- package/apps/desktop-console/src/renderer/tabs/debug.mts +0 -48
- package/apps/desktop-console/src/renderer/tabs/debug.test.mts +0 -22
- package/apps/desktop-console/src/renderer/tabs/logs.mts +0 -421
- package/apps/desktop-console/src/renderer/tabs/logs.test.mts +0 -27
- package/apps/desktop-console/src/renderer/tabs/preflight.mts +0 -486
- package/apps/desktop-console/src/renderer/tabs/preflight.test.mts +0 -33
- package/apps/desktop-console/src/renderer/tabs/profile-pool.mts +0 -213
- package/apps/desktop-console/src/renderer/tabs/results.mts +0 -171
- package/apps/desktop-console/src/renderer/tabs/run.test.mts +0 -63
- package/apps/desktop-console/src/renderer/tabs/runtime.mts +0 -151
- package/apps/desktop-console/src/renderer/tabs/settings.mts +0 -146
- package/apps/desktop-console/src/renderer/tabs/xiaohongshu/account-flow.mts +0 -486
- package/apps/desktop-console/src/renderer/tabs/xiaohongshu/guide-browser-check.mts +0 -56
- package/apps/desktop-console/src/renderer/tabs/xiaohongshu/helpers.mts +0 -262
- package/apps/desktop-console/src/renderer/tabs/xiaohongshu/layout-block.mts +0 -430
- package/apps/desktop-console/src/renderer/tabs/xiaohongshu/live-stats.mts +0 -847
- package/apps/desktop-console/src/renderer/tabs/xiaohongshu/run-flow.mts +0 -443
- package/apps/desktop-console/src/renderer/tabs/xiaohongshu-state.mts +0 -425
- package/apps/desktop-console/src/renderer/tabs/xiaohongshu.mts +0 -497
- package/apps/desktop-console/src/renderer/tabs/xiaohongshu.test.mts +0 -291
- package/apps/desktop-console/src/renderer/ui-components.mts +0 -31
- package/docs/README_camoufox_chinese.md +0 -141
- package/docs/USAGE_V3.md +0 -163
- package/docs/arch/OCR_MACOS_PLUGIN.md +0 -39
- package/docs/arch/PORTS.md +0 -40
- package/docs/arch/REGRESSION_CHECKLIST.md +0 -121
- package/docs/arch/SEARCH_GATE.md +0 -224
- package/docs/arch/VIEWPORT_SAFETY.md +0 -182
- package/docs/arch/XIAOHONGSHU_OFFLINE_MOCK_DESIGN.md +0 -267
- package/docs/xiaohongshu-container-driven-summary.md +0 -221
- package/docs/xiaohongshu-full-collect-runbook.md +0 -134
- package/docs/xiaohongshu-next-steps.md +0 -228
- package/docs/xiaohongshu-quickstart.md +0 -73
- package/docs/xiaohongshu-workflow-summary.md +0 -227
- package/modules/container-registry/tests/container-registry.test.ts +0 -16
- package/modules/logging/tests/logging.test.ts +0 -38
- package/modules/operations/tests/operations.test.ts +0 -22
- package/modules/operations/tests/viewport-filter.test.ts +0 -161
- package/modules/operations/tests/visible-only.test.ts +0 -250
- package/modules/session-manager/tests/session-manager.test.ts +0 -23
- package/modules/state/src/atomic-json.test.ts +0 -30
- package/modules/state/src/paths.test.ts +0 -59
- package/modules/state/src/xiaohongshu-collect-state.test.ts +0 -259
- package/modules/workflow/blocks/AnchorVerificationBlock.d.ts.map +0 -1
- package/modules/workflow/blocks/AnchorVerificationBlock.js.map +0 -1
- package/modules/workflow/blocks/DetectPageStateBlock.d.ts.map +0 -1
- package/modules/workflow/blocks/DetectPageStateBlock.js.map +0 -1
- package/modules/workflow/blocks/ErrorRecoveryBlock.d.ts.map +0 -1
- package/modules/workflow/blocks/ErrorRecoveryBlock.js.map +0 -1
- package/modules/workflow/blocks/WaitSearchPermitBlock.d.ts.map +0 -1
- package/modules/workflow/blocks/WaitSearchPermitBlock.js.map +0 -1
- package/modules/workflow/blocks/helpers/containerAnchors.d.ts.map +0 -1
- package/modules/workflow/blocks/helpers/containerAnchors.js.map +0 -1
- package/modules/workflow/blocks/helpers/downloadPaths.test.ts +0 -62
- package/modules/workflow/blocks/helpers/mergeXhsMarkdown.test.ts +0 -121
- package/modules/workflow/blocks/helpers/operationLogger.d.ts.map +0 -1
- package/modules/workflow/blocks/helpers/operationLogger.js.map +0 -1
- package/modules/workflow/blocks/helpers/persistedNotes.test.ts +0 -268
- package/modules/workflow/blocks/helpers/searchPageState.d.ts.map +0 -1
- package/modules/workflow/blocks/helpers/searchPageState.js.map +0 -1
- package/modules/workflow/blocks/helpers/targetCountMode.test.ts +0 -29
- package/modules/workflow/blocks/helpers/xhsCliArgs.test.ts +0 -75
- package/modules/workflow/tests/smartReply.test.ts +0 -32
- package/modules/xiaohongshu/app/src/blocks/Phase3Interact.matcher.test.ts +0 -33
- package/modules/xiaohongshu/app/src/utils/__tests__/checkpoints.test.ts +0 -141
- package/modules/xiaohongshu/app/tests/commentMatchDsl.test.ts +0 -50
- package/modules/xiaohongshu/app/tests/commentMatcher.test.ts +0 -46
- package/modules/xiaohongshu/app/tests/sharding.test.ts +0 -31
- package/package-scripts.json +0 -8
- package/runtime/infra/utils/README.md +0 -13
- package/runtime/infra/utils/scripts/README.md +0 -0
- package/runtime/infra/utils/scripts/development/eval-in-session.mjs +0 -40
- package/runtime/infra/utils/scripts/development/highlight-search-containers.mjs +0 -35
- package/runtime/infra/utils/scripts/service/kill-port.mjs +0 -24
- package/runtime/infra/utils/scripts/service/start-api.mjs +0 -39
- package/runtime/infra/utils/scripts/service/start-browser-service.mjs +0 -106
- package/runtime/infra/utils/scripts/service/stop-api.mjs +0 -18
- package/runtime/infra/utils/scripts/service/stop-browser-service.mjs +0 -104
- package/runtime/infra/utils/scripts/test-services.mjs +0 -94
- package/services/shared/heartbeat.test.ts +0 -102
- package/services/unified-api/__tests__/task-state.test.ts +0 -95
- package/sitecustomize.py +0 -19
- package/tests/README.md +0 -194
- package/tests/e2e/workflows/weibo-feed-extraction.test.ts +0 -171
- package/tests/fixtures/data/container-definitions.json +0 -67
- package/tests/fixtures/pages/simple-page.html +0 -69
- package/tests/integration/01-test-container-match.mjs +0 -188
- package/tests/integration/02-test-dom-branch.mjs +0 -161
- package/tests/integration/03-test-container-operation-system.mjs +0 -91
- package/tests/integration/05-test-container-lifecycle-events.mjs +0 -224
- package/tests/integration/05-test-container-lifecycle-with-events.mjs +0 -250
- package/tests/integration/06-test-container-dom-tree-drawing.mjs +0 -256
- package/tests/integration/07-test-weibo-container-lifecycle.mjs +0 -355
- package/tests/integration/08-test-weibo-feed-workflow.test.mjs +0 -164
- package/tests/integration/10-test-visual-analyzer.mjs +0 -312
- package/tests/integration/11-test-visual-loop.mjs +0 -284
- package/tests/integration/12-test-simple-visual-loop.mjs +0 -242
- package/tests/integration/13-test-visual-robust.mjs +0 -185
- package/tests/integration/14-test-visual-highlight-loop.mjs +0 -271
- package/tests/integration/inspect-page.mjs +0 -50
- package/tests/integration/run-all-tests.mjs +0 -95
- package/tests/patch_verification/CODEX_PATCH_TEST.md +0 -103
- package/tests/patch_verification/PHASE2_ANALYSIS.md +0 -179
- package/tests/patch_verification/PHASE2_OPTIMIZATION_REPORT.md +0 -55
- package/tests/patch_verification/PHASE2_TO_PHASE4_SUMMARY.md +0 -126
- package/tests/patch_verification/QUICK_TEST_SEQUENCE.md +0 -262
- package/tests/patch_verification/README.md +0 -143
- package/tests/patch_verification/RUN_TESTS.md +0 -60
- package/tests/patch_verification/TEST_EXECUTION.md +0 -99
- package/tests/patch_verification/TEST_PLAN.md +0 -328
- package/tests/patch_verification/TEST_RESULTS.md +0 -34
- package/tests/patch_verification/TOOL_TEST_PLAN.md +0 -48
- package/tests/patch_verification/run-tool-test.mjs +0 -121
- package/tests/patch_verification/temp_test_files/test01.txt +0 -1
- package/tests/patch_verification/temp_test_files/test02.txt +0 -3
- package/tests/patch_verification/temp_test_files/test02_gnu.txt +0 -3
- package/tests/patch_verification/temp_test_files/test03.txt +0 -1
- package/tests/patch_verification/temp_test_files/test03_multiline.txt +0 -5
- package/tests/patch_verification/temp_test_files/test04_function.ts +0 -5
- package/tests/patch_verification/temp_test_files/test05_import.ts +0 -4
- package/tests/patch_verification/temp_test_files/test06_special_chars.txt +0 -4
- package/tests/patch_verification/temp_test_files/test07_indentation.ts +0 -5
- package/tests/patch_verification/temp_test_files/test08_mismatch.txt +0 -1
- package/tests/patch_verification/temp_test_files/test_add_02.txt +0 -3
- package/tests/patch_verification/temp_test_files/test_simple.txt +0 -1
- package/tests/runner/TestReporter.mjs +0 -57
- package/tests/runner/TestRunner.mjs +0 -244
- package/tests/unit/commands/profile.test.mjs +0 -10
- package/tests/unit/container/change-notifier.test.mjs +0 -181
- package/tests/unit/lifecycle/session-registry.test.mjs +0 -135
- package/tests/unit/operations/registry.test.ts +0 -73
- package/tests/unit/utils/browser-service.test.mjs +0 -153
- package/tests/unit/utils/config.test.mjs +0 -166
- package/tests/unit/utils/fingerprint.test.mjs +0 -166
- package/tsconfig.json +0 -31
- package/tsconfig.services.json +0 -26
- /package/apps/desktop-console/{src → dist}/renderer/index.html +0 -0
- /package/apps/desktop-console/{src/renderer/tabs → dist/renderer}/run.mts +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
async function runExtract(ctx, config) {
|
|
2
|
+
if (!config.selector) {
|
|
3
|
+
throw new Error('extract operation requires selector');
|
|
4
|
+
}
|
|
5
|
+
const maxItems = config.max_items ?? 32;
|
|
6
|
+
const includeText = config.include_text ?? false;
|
|
7
|
+
const index = Number.isFinite(config.index) ? Math.max(0, Math.floor(config.index)) : null;
|
|
8
|
+
const visibleOnly = config.visibleOnly === true;
|
|
9
|
+
return ctx.page.evaluate((data) => {
|
|
10
|
+
const isVisible = (el) => {
|
|
11
|
+
const r = el.getBoundingClientRect();
|
|
12
|
+
return (r.width > 0 &&
|
|
13
|
+
r.height > 0 &&
|
|
14
|
+
r.bottom > 0 &&
|
|
15
|
+
r.top < window.innerHeight &&
|
|
16
|
+
r.right > 0 &&
|
|
17
|
+
r.left < window.innerWidth);
|
|
18
|
+
};
|
|
19
|
+
const allNodes = Array.from(document.querySelectorAll(data.selector || ''));
|
|
20
|
+
if (!allNodes.length) {
|
|
21
|
+
return { success: false, error: 'no elements found', count: 0, extracted: [] };
|
|
22
|
+
}
|
|
23
|
+
const nodes = data.visibleOnly ? allNodes.filter((n) => isVisible(n)) : allNodes;
|
|
24
|
+
if (!nodes.length) {
|
|
25
|
+
return { success: false, error: data.visibleOnly ? 'no visible elements found' : 'no elements found', count: 0, extracted: [] };
|
|
26
|
+
}
|
|
27
|
+
const roots = typeof data.index === 'number'
|
|
28
|
+
? (() => {
|
|
29
|
+
const el = nodes[data.index];
|
|
30
|
+
return el ? [el] : [];
|
|
31
|
+
})()
|
|
32
|
+
: nodes;
|
|
33
|
+
if (!roots.length) {
|
|
34
|
+
return { success: false, error: 'no elements found', count: 0, extracted: [] };
|
|
35
|
+
}
|
|
36
|
+
const extracted = [];
|
|
37
|
+
const limit = Math.min(roots.length, data.maxItems);
|
|
38
|
+
const getAttrValue = (node, attr) => {
|
|
39
|
+
if (!node)
|
|
40
|
+
return '';
|
|
41
|
+
if (!attr || attr === 'textContent')
|
|
42
|
+
return (node.textContent || '').trim();
|
|
43
|
+
if (attr === 'href') {
|
|
44
|
+
const href = node.href || (typeof node.getAttribute === 'function' ? node.getAttribute('href') : '') || '';
|
|
45
|
+
return String(href || '').trim();
|
|
46
|
+
}
|
|
47
|
+
if (attr === 'src') {
|
|
48
|
+
const src = node.currentSrc || node.src || (typeof node.getAttribute === 'function' ? node.getAttribute('src') : '') || '';
|
|
49
|
+
return String(src || '').trim();
|
|
50
|
+
}
|
|
51
|
+
if (typeof node.getAttribute === 'function') {
|
|
52
|
+
const v = node.getAttribute(attr);
|
|
53
|
+
return v ? String(v).trim() : '';
|
|
54
|
+
}
|
|
55
|
+
return '';
|
|
56
|
+
};
|
|
57
|
+
const containerExtractorMode = Array.isArray(data.fields) && data.fields.length > 0 && data.extractors && typeof data.extractors === 'object';
|
|
58
|
+
for (let i = 0; i < limit; i++) {
|
|
59
|
+
const el = roots[i];
|
|
60
|
+
const item = {};
|
|
61
|
+
if (containerExtractorMode) {
|
|
62
|
+
const extractorKeys = data.fields;
|
|
63
|
+
const extractors = data.extractors || {};
|
|
64
|
+
for (const key of extractorKeys) {
|
|
65
|
+
const def = extractors[key] || {};
|
|
66
|
+
const selectors = Array.isArray(def.selectors) ? def.selectors : [];
|
|
67
|
+
const attr = def.attr || 'textContent';
|
|
68
|
+
const multiple = !!def.multiple;
|
|
69
|
+
if (!selectors.length) {
|
|
70
|
+
item[key] = multiple ? [] : '';
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (multiple) {
|
|
74
|
+
const values = [];
|
|
75
|
+
for (const sel of selectors) {
|
|
76
|
+
try {
|
|
77
|
+
const nodes = Array.from(el.querySelectorAll(sel));
|
|
78
|
+
for (const n of nodes) {
|
|
79
|
+
const v = getAttrValue(n, attr);
|
|
80
|
+
if (v)
|
|
81
|
+
values.push(v);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// ignore selector errors
|
|
86
|
+
}
|
|
87
|
+
if (values.length)
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
item[key] = values;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
let value = '';
|
|
94
|
+
for (const sel of selectors) {
|
|
95
|
+
try {
|
|
96
|
+
const n = el.querySelector(sel);
|
|
97
|
+
if (!n)
|
|
98
|
+
continue;
|
|
99
|
+
value = getAttrValue(n, attr);
|
|
100
|
+
if (value)
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// ignore selector errors
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
item[key] = value;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Back-compat: common link field names
|
|
111
|
+
if (!item.href) {
|
|
112
|
+
const linkCandidate = item.link ||
|
|
113
|
+
item.detail_url ||
|
|
114
|
+
item.detailUrl ||
|
|
115
|
+
item.author_link ||
|
|
116
|
+
item.user_link ||
|
|
117
|
+
'';
|
|
118
|
+
if (typeof linkCandidate === 'string' && linkCandidate) {
|
|
119
|
+
item.href = linkCandidate;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
// Generic selector-based extraction (legacy)
|
|
125
|
+
if (el.tagName === 'A' && el instanceof HTMLAnchorElement) {
|
|
126
|
+
item.href = el.href;
|
|
127
|
+
}
|
|
128
|
+
else if (data.fields && typeof data.fields === 'object' && data.fields.href) {
|
|
129
|
+
const hrefSel = data.fields.href;
|
|
130
|
+
const linkEl = hrefSel ? el.querySelector(hrefSel) : null;
|
|
131
|
+
if (linkEl instanceof HTMLAnchorElement) {
|
|
132
|
+
item.href = linkEl.href;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (data.includeText) {
|
|
136
|
+
item.text = (el.textContent || '').trim().substring(0, 200);
|
|
137
|
+
}
|
|
138
|
+
if (data.fields && typeof data.fields === 'object' && !Array.isArray(data.fields)) {
|
|
139
|
+
for (const [field, selector] of Object.entries(data.fields)) {
|
|
140
|
+
if (field === 'href')
|
|
141
|
+
continue;
|
|
142
|
+
try {
|
|
143
|
+
const fieldEl = selector ? el.querySelector(selector) : null;
|
|
144
|
+
if (!fieldEl)
|
|
145
|
+
continue;
|
|
146
|
+
if (fieldEl instanceof HTMLImageElement) {
|
|
147
|
+
item[field] = fieldEl.currentSrc || fieldEl.src;
|
|
148
|
+
}
|
|
149
|
+
else if (fieldEl instanceof HTMLAnchorElement) {
|
|
150
|
+
item[field] = fieldEl.href;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
item[field] = (fieldEl.textContent || '').trim().substring(0, 200);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// ignore selector errors
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Apply whitelist/blacklist filters
|
|
163
|
+
if (item.href) {
|
|
164
|
+
let skip = false;
|
|
165
|
+
// Check blacklist
|
|
166
|
+
if (data.blacklist?.contains) {
|
|
167
|
+
for (const term of data.blacklist.contains) {
|
|
168
|
+
if (item.href.includes(term)) {
|
|
169
|
+
skip = true;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (data.blacklist?.suffix) {
|
|
175
|
+
for (const suffix of data.blacklist.suffix) {
|
|
176
|
+
if (item.href.endsWith(suffix)) {
|
|
177
|
+
skip = true;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Check whitelist
|
|
183
|
+
if (!skip && data.whitelist?.prefix) {
|
|
184
|
+
skip = !data.whitelist.prefix.some((prefix) => item.href.startsWith(prefix));
|
|
185
|
+
}
|
|
186
|
+
if (!skip) {
|
|
187
|
+
extracted.push(item);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// If no href filtering, just add the item
|
|
192
|
+
extracted.push(item);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return { success: true, count: extracted.length, extracted };
|
|
196
|
+
}, { ...config, maxItems, includeText, index, visibleOnly });
|
|
197
|
+
}
|
|
198
|
+
export const extractOperation = {
|
|
199
|
+
id: 'extract',
|
|
200
|
+
description: 'Extract structured data from elements',
|
|
201
|
+
requiredCapabilities: ['extract'],
|
|
202
|
+
run: runExtract,
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const findChildOperation = {
|
|
2
|
+
id: 'find-child',
|
|
3
|
+
description: 'Find child containers',
|
|
4
|
+
run: async (ctx, config) => {
|
|
5
|
+
// This is a placeholder operation.
|
|
6
|
+
// The actual find-child logic is handled by RuntimeController directly.
|
|
7
|
+
// However, to satisfy OperationExecutor, we provide this definition.
|
|
8
|
+
// In a full implementation, this could call discovery logic.
|
|
9
|
+
// Returning success to indicate the operation request was valid.
|
|
10
|
+
// The side effects (discovery events) are handled by the caller or container system.
|
|
11
|
+
return {
|
|
12
|
+
success: true,
|
|
13
|
+
message: 'find-child operation triggered'
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=find-child.js.map
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
async function runHighlight(ctx, config) {
|
|
2
|
+
if (!config.selector) {
|
|
3
|
+
throw new Error('highlight operation requires selector');
|
|
4
|
+
}
|
|
5
|
+
const style = config.style || '2px solid #fbbc05';
|
|
6
|
+
const duration = config.duration ?? 1500;
|
|
7
|
+
const index = Number.isFinite(config.index) ? Math.max(0, Math.floor(config.index)) : null;
|
|
8
|
+
const target = typeof config.target === 'string' ? config.target.trim() : '';
|
|
9
|
+
const channel = typeof config.channel === 'string' && config.channel.trim() ? config.channel.trim() : 'container-op';
|
|
10
|
+
const sticky = config.sticky === true;
|
|
11
|
+
const visibleOnly = config.visibleOnly === true;
|
|
12
|
+
return ctx.page.evaluate((data) => {
|
|
13
|
+
const isVisible = (el) => {
|
|
14
|
+
const r = el.getBoundingClientRect();
|
|
15
|
+
return (r.width > 0 &&
|
|
16
|
+
r.height > 0 &&
|
|
17
|
+
r.bottom > 0 &&
|
|
18
|
+
r.top < window.innerHeight &&
|
|
19
|
+
r.right > 0 &&
|
|
20
|
+
r.left < window.innerWidth);
|
|
21
|
+
};
|
|
22
|
+
const allNodes = Array.from(document.querySelectorAll(data.selector));
|
|
23
|
+
if (!allNodes.length) {
|
|
24
|
+
return { success: false, error: 'element not found', count: 0 };
|
|
25
|
+
}
|
|
26
|
+
const nodes = data.visibleOnly ? allNodes.filter((n) => isVisible(n)) : allNodes;
|
|
27
|
+
if (!nodes.length) {
|
|
28
|
+
return { success: false, error: data.visibleOnly ? 'no visible elements found' : 'element not found', count: 0 };
|
|
29
|
+
}
|
|
30
|
+
const selected = typeof data.index === 'number'
|
|
31
|
+
? (() => {
|
|
32
|
+
const root = nodes[data.index];
|
|
33
|
+
if (!root)
|
|
34
|
+
return [];
|
|
35
|
+
let el = root;
|
|
36
|
+
if (data.target) {
|
|
37
|
+
if (data.target === 'self') {
|
|
38
|
+
el = root;
|
|
39
|
+
}
|
|
40
|
+
else if (data.target === 'img') {
|
|
41
|
+
el = root.querySelector('img') || root;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
el = root.querySelector(data.target) || root;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return [el];
|
|
48
|
+
})()
|
|
49
|
+
: nodes;
|
|
50
|
+
if (!selected.length) {
|
|
51
|
+
return { success: false, error: 'element not found', count: 0 };
|
|
52
|
+
}
|
|
53
|
+
const first = selected[0];
|
|
54
|
+
const r = first.getBoundingClientRect();
|
|
55
|
+
const viewport = { w: window.innerWidth, h: window.innerHeight };
|
|
56
|
+
const inViewport = r.width > 0 &&
|
|
57
|
+
r.height > 0 &&
|
|
58
|
+
r.bottom > 0 &&
|
|
59
|
+
r.top < viewport.h &&
|
|
60
|
+
r.right > 0 &&
|
|
61
|
+
r.left < viewport.w;
|
|
62
|
+
// 优先使用 runtime overlay(截图可见,且不污染元素 style)
|
|
63
|
+
try {
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
const rt = window.__webautoRuntime;
|
|
66
|
+
const api = rt?.highlight;
|
|
67
|
+
if (api && typeof api.highlightElements === 'function') {
|
|
68
|
+
api.highlightElements(selected, {
|
|
69
|
+
channel: data.channel,
|
|
70
|
+
style: data.style,
|
|
71
|
+
duration: data.duration,
|
|
72
|
+
sticky: Boolean(data.sticky),
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
success: true,
|
|
76
|
+
count: selected.length,
|
|
77
|
+
rect: { x1: r.left, y1: r.top, x2: r.right, y2: r.bottom },
|
|
78
|
+
viewport,
|
|
79
|
+
inViewport,
|
|
80
|
+
mode: 'overlay',
|
|
81
|
+
channel: data.channel,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// fallback below
|
|
87
|
+
}
|
|
88
|
+
// fallback: element outline (not guaranteed in screenshots on some pages)
|
|
89
|
+
const cleanups = selected.map((el) => {
|
|
90
|
+
const originalOutline = el.style?.outline;
|
|
91
|
+
const originalOutlineOffset = el.style?.outlineOffset;
|
|
92
|
+
try {
|
|
93
|
+
el.style.outline = data.style;
|
|
94
|
+
el.style.outlineOffset = '2px';
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// ignore
|
|
98
|
+
}
|
|
99
|
+
return () => {
|
|
100
|
+
try {
|
|
101
|
+
el.style.outline = originalOutline;
|
|
102
|
+
el.style.outlineOffset = originalOutlineOffset;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// ignore cleanup errors
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
const cleanup = () => {
|
|
110
|
+
cleanups.forEach((fn) => {
|
|
111
|
+
try {
|
|
112
|
+
fn();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// ignore cleanup errors
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
if (data.duration > 0 && !data.sticky) {
|
|
120
|
+
setTimeout(cleanup, data.duration);
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
count: selected.length,
|
|
125
|
+
rect: { x1: r.left, y1: r.top, x2: r.right, y2: r.bottom },
|
|
126
|
+
viewport,
|
|
127
|
+
inViewport,
|
|
128
|
+
mode: 'outline',
|
|
129
|
+
};
|
|
130
|
+
}, { selector: config.selector, style, duration, index, target, channel, sticky, visibleOnly });
|
|
131
|
+
}
|
|
132
|
+
export const highlightOperation = {
|
|
133
|
+
id: 'highlight',
|
|
134
|
+
description: 'Highlight element using outline',
|
|
135
|
+
requiredCapabilities: ['highlight'],
|
|
136
|
+
run: runHighlight,
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=highlight.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
function delay(ms) {
|
|
2
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
3
|
+
}
|
|
4
|
+
async function runKey(ctx, config) {
|
|
5
|
+
const key = String(config.key || '').trim();
|
|
6
|
+
if (!key) {
|
|
7
|
+
throw new Error('key operation requires key');
|
|
8
|
+
}
|
|
9
|
+
const selector = typeof config.selector === 'string' ? config.selector.trim() : '';
|
|
10
|
+
const waitAfter = typeof config.wait_after === 'number'
|
|
11
|
+
? config.wait_after
|
|
12
|
+
: typeof config.waitAfter === 'number'
|
|
13
|
+
? config.waitAfter
|
|
14
|
+
: 0;
|
|
15
|
+
const useSystemMouse = config.useSystemMouse !== false;
|
|
16
|
+
const protocolMouse = ctx.page?.mouse;
|
|
17
|
+
if (selector) {
|
|
18
|
+
const rect = await ctx.page.evaluate((sel) => {
|
|
19
|
+
const el = document.querySelector(sel);
|
|
20
|
+
if (!el)
|
|
21
|
+
return null;
|
|
22
|
+
const r = el.getBoundingClientRect();
|
|
23
|
+
const visible = r.width > 0 && r.height > 0 && r.y < window.innerHeight && r.y + r.height > 0;
|
|
24
|
+
return { x1: r.left, y1: r.top, x2: r.right, y2: r.bottom, visible };
|
|
25
|
+
}, selector);
|
|
26
|
+
if (!rect || !rect.visible) {
|
|
27
|
+
return { success: false, error: 'element not visible' };
|
|
28
|
+
}
|
|
29
|
+
const cx = Math.round((rect.x1 + rect.x2) / 2);
|
|
30
|
+
const cy = Math.round((rect.y1 + rect.y2) / 2);
|
|
31
|
+
if (useSystemMouse) {
|
|
32
|
+
if (!ctx.systemInput?.mouseClick) {
|
|
33
|
+
return { success: false, error: 'system mouse not available' };
|
|
34
|
+
}
|
|
35
|
+
await ctx.systemInput.mouseClick(cx, cy);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
if (!protocolMouse || typeof protocolMouse.click !== 'function') {
|
|
39
|
+
return { success: false, error: 'protocol mouse not available' };
|
|
40
|
+
}
|
|
41
|
+
await protocolMouse.click(cx, cy);
|
|
42
|
+
}
|
|
43
|
+
await delay(150);
|
|
44
|
+
}
|
|
45
|
+
const keyboard = ctx.page.keyboard;
|
|
46
|
+
if (!keyboard || typeof keyboard.press !== 'function') {
|
|
47
|
+
return { success: false, error: 'keyboard press not available' };
|
|
48
|
+
}
|
|
49
|
+
await keyboard.press(key);
|
|
50
|
+
if (waitAfter > 0) {
|
|
51
|
+
await delay(waitAfter);
|
|
52
|
+
}
|
|
53
|
+
return { success: true, inputMode: useSystemMouse ? 'system' : 'protocol' };
|
|
54
|
+
}
|
|
55
|
+
export const keyOperation = {
|
|
56
|
+
id: 'key',
|
|
57
|
+
description: 'Press a key using system-level keyboard',
|
|
58
|
+
requiredCapabilities: [],
|
|
59
|
+
run: runKey,
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=key.js.map
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
function resolveWaitAfter(config) {
|
|
2
|
+
const wait = typeof config.waitAfterMs === 'number' ? config.waitAfterMs : config.wait_after_ms;
|
|
3
|
+
return Number.isFinite(wait) ? Math.max(0, wait) : 0;
|
|
4
|
+
}
|
|
5
|
+
function ensureAbsoluteUrl(raw, base) {
|
|
6
|
+
if (!raw)
|
|
7
|
+
return '';
|
|
8
|
+
if (/^https?:\/\//i.test(raw))
|
|
9
|
+
return raw;
|
|
10
|
+
if (base) {
|
|
11
|
+
try {
|
|
12
|
+
return new URL(raw, base).toString();
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return raw;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return raw;
|
|
19
|
+
}
|
|
20
|
+
function normalizedIndex(config, ctxIndex) {
|
|
21
|
+
if (typeof config.match_index === 'number')
|
|
22
|
+
return Math.max(0, Math.floor(config.match_index));
|
|
23
|
+
if (typeof config.matchIndex === 'number')
|
|
24
|
+
return Math.max(0, Math.floor(config.matchIndex));
|
|
25
|
+
if (typeof ctxIndex === 'number')
|
|
26
|
+
return Math.max(0, Math.floor(ctxIndex));
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
function parseNoteInfo(url) {
|
|
30
|
+
const noteMatch = url?.match(/\/explore\/([0-9a-z]+)/i);
|
|
31
|
+
const xsecMatch = url?.match(/[?&]xsec_token=([^&]+)/i);
|
|
32
|
+
return {
|
|
33
|
+
noteId: noteMatch ? noteMatch[1] : '',
|
|
34
|
+
xsecToken: xsecMatch ? decodeURIComponent(xsecMatch[1]) : '',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async function resolveTargetUrl(ctx, config) {
|
|
38
|
+
if (config.url) {
|
|
39
|
+
const absolute = ensureAbsoluteUrl(config.url, config.baseUrl || config.base_url);
|
|
40
|
+
return { url: absolute, ...parseNoteInfo(absolute) };
|
|
41
|
+
}
|
|
42
|
+
const scopeSelector = config.scopeSelector || config.scope_selector || config.selector || '';
|
|
43
|
+
const anchorSelector = config.anchorSelector || config.anchor_selector || '';
|
|
44
|
+
if (!scopeSelector && !anchorSelector && !config.selector) {
|
|
45
|
+
throw new Error('navigate operation requires url or selector');
|
|
46
|
+
}
|
|
47
|
+
const attr = config.attribute || 'href';
|
|
48
|
+
const matchIndex = normalizedIndex(config, ctx.node?.index);
|
|
49
|
+
const evaluation = await ctx.page.evaluate(({ scopeSelector, anchorSelector, attribute, matchIndex }) => {
|
|
50
|
+
const resolveFromElement = (el) => {
|
|
51
|
+
if (!el) {
|
|
52
|
+
return { url: '', noteId: '', xsecToken: '' };
|
|
53
|
+
}
|
|
54
|
+
const target = el;
|
|
55
|
+
let raw = '';
|
|
56
|
+
if (attribute === 'textContent') {
|
|
57
|
+
raw = (target.textContent || '').trim();
|
|
58
|
+
}
|
|
59
|
+
else if (attribute) {
|
|
60
|
+
raw = target.getAttribute(attribute) || '';
|
|
61
|
+
if (!raw && attribute === 'href' && target instanceof HTMLAnchorElement) {
|
|
62
|
+
raw = target.href;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const href = target instanceof HTMLAnchorElement && attribute === 'href' ? target.href : raw;
|
|
66
|
+
const url = href || raw || '';
|
|
67
|
+
const noteMatch = url.match(/\/explore\/([0-9a-z]+)/i);
|
|
68
|
+
const tokenMatch = url.match(/[?&]xsec_token=([^&]+)/i);
|
|
69
|
+
return {
|
|
70
|
+
url,
|
|
71
|
+
noteId: noteMatch ? noteMatch[1] : '',
|
|
72
|
+
xsecToken: tokenMatch ? decodeURIComponent(tokenMatch[1]) : '',
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
const index = Number.isFinite(matchIndex) ? Math.max(0, Number(matchIndex)) : 0;
|
|
76
|
+
const scopes = scopeSelector ? Array.from(document.querySelectorAll(scopeSelector)) : [];
|
|
77
|
+
let scope = scopes[index] || scopes[0] || null;
|
|
78
|
+
if (!scope && anchorSelector) {
|
|
79
|
+
scope = document.body;
|
|
80
|
+
}
|
|
81
|
+
if (!scope) {
|
|
82
|
+
return { url: '', noteId: '', xsecToken: '' };
|
|
83
|
+
}
|
|
84
|
+
if (anchorSelector) {
|
|
85
|
+
const anchor = scope.matches(anchorSelector)
|
|
86
|
+
? scope
|
|
87
|
+
: scope.querySelector(anchorSelector);
|
|
88
|
+
return resolveFromElement(anchor);
|
|
89
|
+
}
|
|
90
|
+
return resolveFromElement(scope);
|
|
91
|
+
}, { scopeSelector, anchorSelector, attribute: attr, matchIndex });
|
|
92
|
+
const absoluteUrl = ensureAbsoluteUrl(evaluation.url, config.baseUrl || config.base_url);
|
|
93
|
+
if (!absoluteUrl) {
|
|
94
|
+
throw new Error('navigate: selector did not resolve to a URL');
|
|
95
|
+
}
|
|
96
|
+
const info = parseNoteInfo(absoluteUrl);
|
|
97
|
+
return { url: absoluteUrl, ...info };
|
|
98
|
+
}
|
|
99
|
+
function isXiaohongshuUrl(url) {
|
|
100
|
+
return /(^https?:\/\/)?(www\.)?xiaohongshu\.com/i.test(url || '');
|
|
101
|
+
}
|
|
102
|
+
function isAllowedXiaohongshuNavigate(url) {
|
|
103
|
+
// 小红书强规则:禁止通过代码构造/抽取链接进行导航(尤其是 /explore、/search_result)
|
|
104
|
+
// 仅允许回到站点主页,其他页面必须通过页面内点击/搜索流转生成带 token 的 URL。
|
|
105
|
+
try {
|
|
106
|
+
const u = new URL(url);
|
|
107
|
+
const p = u.pathname || '/';
|
|
108
|
+
if (p === '/' || p === '' || p === '/explore')
|
|
109
|
+
return true; // /explore 作为站点入口页允许
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export async function runNavigateOperation(ctx, config) {
|
|
117
|
+
const target = await resolveTargetUrl(ctx, config);
|
|
118
|
+
if (!target.url) {
|
|
119
|
+
return { success: false, error: 'navigate: empty url' };
|
|
120
|
+
}
|
|
121
|
+
if (isXiaohongshuUrl(target.url) && !isAllowedXiaohongshuNavigate(target.url)) {
|
|
122
|
+
return { success: false, error: `navigate: disabled for xiaohongshu url=${target.url}` };
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
await ctx.page.goto(target.url, { waitUntil: 'domcontentloaded' });
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
return { success: false, error: `navigate: page.goto failed: ${e?.message || String(e)}` };
|
|
129
|
+
}
|
|
130
|
+
const waitAfter = resolveWaitAfter(config);
|
|
131
|
+
if (waitAfter > 0) {
|
|
132
|
+
await new Promise((resolve) => setTimeout(resolve, waitAfter));
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
url: target.url,
|
|
137
|
+
noteId: target.noteId,
|
|
138
|
+
xsecToken: target.xsecToken,
|
|
139
|
+
navigation: { success: true, href: target.url, via: 'page.goto' },
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export const navigateOperation = {
|
|
143
|
+
id: 'navigate',
|
|
144
|
+
description: 'Navigate current page using selector-derived URL or explicit target',
|
|
145
|
+
requiredCapabilities: ['navigate'],
|
|
146
|
+
run: runNavigateOperation,
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=navigate.js.map
|