@web-auto/webauto 0.1.1 → 0.1.2
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 +247 -12
- 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 +37 -9
- 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,299 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import minimist from 'minimist';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const APP_ROOT = path.resolve(__dirname, '../..');
|
|
10
|
+
const DIST_MAIN = path.join(APP_ROOT, 'dist', 'main', 'index.mjs');
|
|
11
|
+
|
|
12
|
+
const args = minimist(process.argv.slice(2), {
|
|
13
|
+
boolean: ['build', 'install', 'check', 'help', 'headless', 'no-daemon'],
|
|
14
|
+
string: ['profile', 'keyword', 'target', 'scenario', 'output'],
|
|
15
|
+
alias: { h: 'help', p: 'profile', k: 'keyword', t: 'target', o: 'output' }
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
function printHelp() {
|
|
19
|
+
console.log(`webauto ui console
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
webauto ui console [--build] [--install] [--check] [--no-daemon]
|
|
23
|
+
webauto ui test <scenario> [options]
|
|
24
|
+
|
|
25
|
+
Test Scenarios:
|
|
26
|
+
env-check - Environment check automation test
|
|
27
|
+
account-flow - Account creation/login flow test
|
|
28
|
+
config-save - Config save/load test
|
|
29
|
+
crawl-run - Full crawl flow test
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
--check Check build/dep status only
|
|
33
|
+
--build Auto-build if missing
|
|
34
|
+
--install Auto-install if missing deps
|
|
35
|
+
--no-daemon Run in foreground mode
|
|
36
|
+
--scenario Test scenario name
|
|
37
|
+
--profile Test profile ID
|
|
38
|
+
--keyword Test keyword
|
|
39
|
+
--target Target count
|
|
40
|
+
--headless Headless mode
|
|
41
|
+
--output Output report path
|
|
42
|
+
`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function checkBuildStatus() {
|
|
46
|
+
return existsSync(DIST_MAIN);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function build() {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
console.log('[ui-console] Building...');
|
|
52
|
+
const child = spawn('npm', ['run', 'build'], {
|
|
53
|
+
cwd: APP_ROOT,
|
|
54
|
+
stdio: 'inherit',
|
|
55
|
+
shell: true
|
|
56
|
+
});
|
|
57
|
+
child.on('close', (code) => {
|
|
58
|
+
if (code === 0) resolve();
|
|
59
|
+
else reject(new Error(`Build failed with code ${code}`));
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function install() {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
console.log('[ui-console] Installing dependencies...');
|
|
67
|
+
const child = spawn('npm', ['install'], {
|
|
68
|
+
cwd: APP_ROOT,
|
|
69
|
+
stdio: 'inherit',
|
|
70
|
+
shell: true
|
|
71
|
+
});
|
|
72
|
+
child.on('close', (code) => {
|
|
73
|
+
if (code === 0) resolve();
|
|
74
|
+
else reject(new Error(`Install failed with code ${code}`));
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function startConsole(noDaemon = false) {
|
|
80
|
+
if (!checkBuildStatus()) {
|
|
81
|
+
console.log('[ui-console] Build not found, building...');
|
|
82
|
+
await build();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log('[ui-console] Starting Desktop Console...');
|
|
86
|
+
const env = { ...process.env };
|
|
87
|
+
if (noDaemon) env.WEBAUTO_NO_DAEMON = '1';
|
|
88
|
+
|
|
89
|
+
const child = spawn('electron', [DIST_MAIN], {
|
|
90
|
+
cwd: APP_ROOT,
|
|
91
|
+
env,
|
|
92
|
+
stdio: 'inherit',
|
|
93
|
+
detached: !noDaemon
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (noDaemon) {
|
|
97
|
+
child.on('close', (code) => {
|
|
98
|
+
console.log(`[ui-console] Exited with code ${code}`);
|
|
99
|
+
process.exit(code);
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
child.unref();
|
|
103
|
+
console.log(`[ui-console] Started (PID: ${child.pid})`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
class UITestRunner {
|
|
108
|
+
constructor(options = {}) {
|
|
109
|
+
this.profile = options.profile || 'test-profile';
|
|
110
|
+
this.keyword = options.keyword || '自动化测试';
|
|
111
|
+
this.target = options.target || 5;
|
|
112
|
+
this.headless = options.headless || false;
|
|
113
|
+
this.output = options.output || null;
|
|
114
|
+
this.results = [];
|
|
115
|
+
this.startTime = Date.now();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
log(message, type = 'info') {
|
|
119
|
+
const ts = new Date().toISOString();
|
|
120
|
+
const line = `[${ts}] [${type}] ${message}`;
|
|
121
|
+
console.log(line);
|
|
122
|
+
this.results.push({ ts, type, message });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async runCommand(cmd, args, timeoutMs = 30000) {
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const child = spawn(cmd, args, { shell: true, stdio: 'pipe' });
|
|
128
|
+
let stdout = '';
|
|
129
|
+
let stderr = '';
|
|
130
|
+
const timer = setTimeout(() => {
|
|
131
|
+
child.kill('SIGTERM');
|
|
132
|
+
reject(new Error(`Command timeout: ${cmd}`));
|
|
133
|
+
}, timeoutMs);
|
|
134
|
+
child.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
135
|
+
child.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
136
|
+
child.on('close', (code) => {
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
if (code === 0) resolve({ ok: true, stdout, stderr });
|
|
139
|
+
else reject(new Error(stderr || `Exit code: ${code}`));
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async testEnvCheck() {
|
|
145
|
+
this.log('Starting environment check test', 'test');
|
|
146
|
+
try {
|
|
147
|
+
this.log('Testing: camo CLI');
|
|
148
|
+
await this.runCommand('which camo');
|
|
149
|
+
this.log('PASS: camo CLI found', 'pass');
|
|
150
|
+
|
|
151
|
+
this.log('Testing: Unified API');
|
|
152
|
+
const apiRes = await fetch('http://127.0.0.1:7701/health');
|
|
153
|
+
if (!apiRes.ok) throw new Error('Unified API not responding');
|
|
154
|
+
this.log('PASS: Unified API running', 'pass');
|
|
155
|
+
|
|
156
|
+
this.log('Testing: Browser Service');
|
|
157
|
+
const bsRes = await fetch('http://127.0.0.1:7704/health');
|
|
158
|
+
if (!bsRes.ok) throw new Error('Browser Service not responding');
|
|
159
|
+
this.log('PASS: Browser Service running', 'pass');
|
|
160
|
+
|
|
161
|
+
this.log('Environment check PASSED', 'success');
|
|
162
|
+
return { passed: true };
|
|
163
|
+
} catch (err) {
|
|
164
|
+
this.log(`FAILED: ${err.message}`, 'fail');
|
|
165
|
+
return { passed: false, error: err.message };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async testAccountFlow() {
|
|
170
|
+
this.log('Starting account flow test', 'test');
|
|
171
|
+
try {
|
|
172
|
+
this.log(`Testing: Create profile ${this.profile}`);
|
|
173
|
+
const result = await this.runCommand('node', [
|
|
174
|
+
path.join(process.cwd(), 'apps/webauto/entry/profilepool.mjs'),
|
|
175
|
+
'add', 'test', '--json'
|
|
176
|
+
], 60000);
|
|
177
|
+
const json = JSON.parse(result.stdout);
|
|
178
|
+
if (json.ok && json.profileId) {
|
|
179
|
+
this.log(`PASS: Profile created: ${json.profileId}`, 'pass');
|
|
180
|
+
} else {
|
|
181
|
+
throw new Error('Failed to create profile');
|
|
182
|
+
}
|
|
183
|
+
this.log('Account flow PASSED', 'success');
|
|
184
|
+
return { passed: true, profileId: json.profileId };
|
|
185
|
+
} catch (err) {
|
|
186
|
+
this.log(`FAILED: ${err.message}`, 'fail');
|
|
187
|
+
return { passed: false, error: err.message };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async testConfigSave() {
|
|
192
|
+
this.log('Starting config save test', 'test');
|
|
193
|
+
try {
|
|
194
|
+
const testConfig = {
|
|
195
|
+
keyword: this.keyword, target: this.target, env: 'debug',
|
|
196
|
+
fetchBody: true, fetchComments: true, maxComments: 50,
|
|
197
|
+
autoLike: false, likeKeywords: '', headless: this.headless, dryRun: true
|
|
198
|
+
};
|
|
199
|
+
const configPath = path.join(process.cwd(), 'test-config.json');
|
|
200
|
+
writeFileSync(configPath, JSON.stringify(testConfig, null, 2));
|
|
201
|
+
this.log(`PASS: Config exported`, 'pass');
|
|
202
|
+
const imported = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
203
|
+
if (imported.keyword === this.keyword) {
|
|
204
|
+
this.log('PASS: Config imported', 'pass');
|
|
205
|
+
}
|
|
206
|
+
import('node:fs').then(fs => fs.unlinkSync(configPath));
|
|
207
|
+
this.log('Config save PASSED', 'success');
|
|
208
|
+
return { passed: true };
|
|
209
|
+
} catch (err) {
|
|
210
|
+
this.log(`FAILED: ${err.message}`, 'fail');
|
|
211
|
+
return { passed: false, error: err.message };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async testCrawlRun() {
|
|
216
|
+
this.log('Starting crawl run test', 'test');
|
|
217
|
+
try {
|
|
218
|
+
this.log(`Testing: Dry-run crawl (keyword=${this.keyword}, target=${this.target})`);
|
|
219
|
+
const args = [
|
|
220
|
+
path.join(process.cwd(), 'apps/webauto/entry/xhs-unified.mjs'),
|
|
221
|
+
'--profile', this.profile,
|
|
222
|
+
'--keyword', this.keyword,
|
|
223
|
+
'--target', String(this.target),
|
|
224
|
+
'--env', 'debug',
|
|
225
|
+
'--dry-run', 'true'
|
|
226
|
+
];
|
|
227
|
+
if (this.headless) args.push('--headless', 'true');
|
|
228
|
+
await this.runCommand('node', args, 120000);
|
|
229
|
+
this.log('PASS: Crawl run completed', 'pass');
|
|
230
|
+
this.log('Crawl run PASSED', 'success');
|
|
231
|
+
return { passed: true };
|
|
232
|
+
} catch (err) {
|
|
233
|
+
this.log(`FAILED: ${err.message}`, 'fail');
|
|
234
|
+
return { passed: false, error: err.message };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async runScenario(scenario) {
|
|
239
|
+
this.log(`Running test scenario: ${scenario}`, 'info');
|
|
240
|
+
let result;
|
|
241
|
+
switch (scenario) {
|
|
242
|
+
case 'env-check': result = await this.testEnvCheck(); break;
|
|
243
|
+
case 'account-flow': result = await this.testAccountFlow(); break;
|
|
244
|
+
case 'config-save': result = await this.testConfigSave(); break;
|
|
245
|
+
case 'crawl-run': result = await this.testCrawlRun(); break;
|
|
246
|
+
default: throw new Error(`Unknown scenario: ${scenario}`);
|
|
247
|
+
}
|
|
248
|
+
result.duration = Date.now() - this.startTime;
|
|
249
|
+
if (this.output) {
|
|
250
|
+
const report = { scenario, ...result, results: this.results };
|
|
251
|
+
mkdirSync(path.dirname(this.output), { recursive: true });
|
|
252
|
+
writeFileSync(this.output, JSON.stringify(report, null, 2));
|
|
253
|
+
this.log(`Report saved to ${this.output}`, 'info');
|
|
254
|
+
}
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async function main() {
|
|
260
|
+
if (args.help || args.h) { printHelp(); process.exit(0); }
|
|
261
|
+
if (args.check) {
|
|
262
|
+
const buildOk = checkBuildStatus();
|
|
263
|
+
console.log(`Build: ${buildOk ? 'OK' : 'MISSING'}`);
|
|
264
|
+
process.exit(buildOk ? 0 : 1);
|
|
265
|
+
}
|
|
266
|
+
if (args.build) { await build(); process.exit(0); }
|
|
267
|
+
if (args.install) { await install(); process.exit(0); }
|
|
268
|
+
|
|
269
|
+
const testIndex = process.argv.indexOf('test');
|
|
270
|
+
if (testIndex !== -1 && process.argv[testIndex + 1]) {
|
|
271
|
+
const scenario = process.argv[testIndex + 1];
|
|
272
|
+
const runner = new UITestRunner({
|
|
273
|
+
profile: args.profile, keyword: args.keyword,
|
|
274
|
+
target: parseInt(args.target) || 5,
|
|
275
|
+
headless: args.headless, output: args.output
|
|
276
|
+
});
|
|
277
|
+
try {
|
|
278
|
+
const result = await runner.runScenario(scenario);
|
|
279
|
+
if (result.passed) {
|
|
280
|
+
console.log(`\n✅ Test PASSED (${result.duration}ms)`);
|
|
281
|
+
process.exit(0);
|
|
282
|
+
} else {
|
|
283
|
+
console.log(`\n❌ Test FAILED: ${result.error}`);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
} catch (err) {
|
|
287
|
+
console.log(`\n❌ Test ERROR: ${err.message}`);
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
await startConsole(args['no-daemon']);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
main().catch((err) => {
|
|
297
|
+
console.error('[ui-console] Error:', err.message);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
});
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import minimist from 'minimist';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import {
|
|
8
|
+
addAccount,
|
|
9
|
+
getAccount,
|
|
10
|
+
listAccountProfiles,
|
|
11
|
+
listAccounts,
|
|
12
|
+
removeAccount,
|
|
13
|
+
updateAccount,
|
|
14
|
+
} from './lib/account-store.mjs';
|
|
15
|
+
import { ensureProfile } from './lib/profilepool.mjs';
|
|
16
|
+
import { syncXhsAccountByProfile, syncXhsAccountsByProfiles } from './lib/account-detect.mjs';
|
|
17
|
+
|
|
18
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
|
|
19
|
+
const XHS_HOME_URL = 'https://www.xiaohongshu.com';
|
|
20
|
+
|
|
21
|
+
function output(payload, jsonMode) {
|
|
22
|
+
if (jsonMode) {
|
|
23
|
+
console.log(JSON.stringify(payload));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseBoolean(value, fallback = false) {
|
|
30
|
+
if (value === undefined || value === null) return fallback;
|
|
31
|
+
const normalized = String(value).trim().toLowerCase();
|
|
32
|
+
if (!normalized) return fallback;
|
|
33
|
+
if (normalized === '1' || normalized === 'true' || normalized === 'yes') return true;
|
|
34
|
+
if (normalized === '0' || normalized === 'false' || normalized === 'no') return false;
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getCamoRunner() {
|
|
39
|
+
const local = path.join(ROOT, 'node_modules', '.bin', process.platform === 'win32' ? 'camo.cmd' : 'camo');
|
|
40
|
+
if (existsSync(local)) return { cmd: local, prefix: [] };
|
|
41
|
+
return { cmd: process.platform === 'win32' ? 'npx.cmd' : 'npx', prefix: ['@web-auto/camo'] };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function runCamo(args) {
|
|
45
|
+
const runner = getCamoRunner();
|
|
46
|
+
const ret = spawnSync(runner.cmd, [...runner.prefix, ...args], {
|
|
47
|
+
cwd: ROOT,
|
|
48
|
+
env: process.env,
|
|
49
|
+
encoding: 'utf8',
|
|
50
|
+
});
|
|
51
|
+
const stdout = String(ret.stdout || '').trim();
|
|
52
|
+
const stderr = String(ret.stderr || '').trim();
|
|
53
|
+
let parsed = null;
|
|
54
|
+
if (stdout) {
|
|
55
|
+
const lines = stdout.split('\n').map((line) => line.trim()).filter(Boolean).reverse();
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
try {
|
|
58
|
+
parsed = JSON.parse(line);
|
|
59
|
+
break;
|
|
60
|
+
} catch {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
ok: ret.status === 0,
|
|
67
|
+
code: ret.status,
|
|
68
|
+
stdout,
|
|
69
|
+
stderr,
|
|
70
|
+
json: parsed,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function inferLoginUrl(platform) {
|
|
75
|
+
const value = String(platform || '').trim().toLowerCase();
|
|
76
|
+
if (!value || value === 'xiaohongshu' || value === 'xhs') return XHS_HOME_URL;
|
|
77
|
+
return 'https://example.com';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function normalizeAlias(input) {
|
|
81
|
+
const value = String(input ?? '').trim();
|
|
82
|
+
return value || null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function detectAliasFromActivePage(profileId, selector) {
|
|
86
|
+
const { callAPI } = await import('../../../modules/camo-runtime/src/utils/browser-service.mjs');
|
|
87
|
+
const script = `(() => {
|
|
88
|
+
const requested = ${JSON.stringify(String(selector || '').trim())};
|
|
89
|
+
const defaultSelectors = [
|
|
90
|
+
'[class*="user"] [class*="name"]',
|
|
91
|
+
'[class*="nickname"]',
|
|
92
|
+
'[class*="account"] [class*="name"]',
|
|
93
|
+
'a[href*="/user/profile"] span',
|
|
94
|
+
'header a[href*="/user"] span',
|
|
95
|
+
'nav a[href*="/user"] span'
|
|
96
|
+
];
|
|
97
|
+
const selectors = requested ? [requested] : defaultSelectors;
|
|
98
|
+
const clean = (value) => String(value || '').replace(/\\s+/g, ' ').trim();
|
|
99
|
+
const candidates = [];
|
|
100
|
+
for (const sel of selectors) {
|
|
101
|
+
const nodes = Array.from(document.querySelectorAll(sel)).slice(0, 6);
|
|
102
|
+
for (const node of nodes) {
|
|
103
|
+
const text = clean(node.textContent || '');
|
|
104
|
+
if (!text) continue;
|
|
105
|
+
candidates.push({ text, selector: sel });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const title = clean(document.title || '');
|
|
109
|
+
if (title) candidates.push({ text: title, selector: 'document.title' });
|
|
110
|
+
const bad = ['小红书', '登录', '注册', '搜索'];
|
|
111
|
+
const picked = candidates.find((item) => {
|
|
112
|
+
if (!item?.text) return false;
|
|
113
|
+
if (item.text.length < 2) return false;
|
|
114
|
+
return bad.every((word) => !item.text.includes(word));
|
|
115
|
+
}) || null;
|
|
116
|
+
return {
|
|
117
|
+
alias: picked?.text || null,
|
|
118
|
+
source: picked?.selector || null,
|
|
119
|
+
candidates
|
|
120
|
+
};
|
|
121
|
+
})()`;
|
|
122
|
+
|
|
123
|
+
const result = await callAPI('evaluate', { profileId, script });
|
|
124
|
+
const payload = result?.result || result || {};
|
|
125
|
+
const alias = normalizeAlias(payload.alias);
|
|
126
|
+
if (!alias) {
|
|
127
|
+
throw new Error('unable to detect alias from current page');
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
alias,
|
|
131
|
+
source: String(payload.source || 'page').trim() || 'page',
|
|
132
|
+
candidates: Array.isArray(payload.candidates) ? payload.candidates : [],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function printHelp() {
|
|
137
|
+
console.log(`webauto account
|
|
138
|
+
|
|
139
|
+
Usage:
|
|
140
|
+
webauto account --help
|
|
141
|
+
webauto account list [--json]
|
|
142
|
+
webauto account list --records [--json]
|
|
143
|
+
webauto account add [--platform <name>] [--alias <alias>] [--name <name>] [--username <username>] [--profile <id>] [--fingerprint <id>] [--json]
|
|
144
|
+
webauto account get <id|alias> [--json]
|
|
145
|
+
webauto account update <id|alias> [--alias <alias>|--clear-alias] [--name <name>] [--username <name>] [--profile <id>] [--fingerprint <id>] [--status active|disabled|archived] [--json]
|
|
146
|
+
webauto account delete <id|alias> [--delete-profile] [--delete-fingerprint] [--json]
|
|
147
|
+
webauto account login <id|alias> [--url <url>] [--sync-alias] [--json]
|
|
148
|
+
webauto account sync-alias <id|alias> [--selector <css>] [--alias <value>] [--json]
|
|
149
|
+
webauto account sync <profileId|all> [--json]
|
|
150
|
+
|
|
151
|
+
Notes:
|
|
152
|
+
- 账号数据默认保存到 ~/.webauto/accounts(可用 WEBAUTO_PATHS_ACCOUNTS 覆盖)
|
|
153
|
+
- list 默认按 profile 展示账号有效态(valid/invalid)
|
|
154
|
+
- add 会自动创建并关联 profile/fingerprint(未指定时自动编号)
|
|
155
|
+
- login 会通过 @web-auto/camo 拉起浏览器并绑定账号 profile
|
|
156
|
+
- 只有识别到账号 id 的 profile 才会进入 valid 状态
|
|
157
|
+
|
|
158
|
+
Examples:
|
|
159
|
+
webauto account add --platform xiaohongshu --alias 主号
|
|
160
|
+
webauto account list
|
|
161
|
+
webauto account sync all
|
|
162
|
+
webauto account login xhs-0001 --url https://www.xiaohongshu.com
|
|
163
|
+
webauto account sync-alias xhs-0001
|
|
164
|
+
webauto account update xhs-0001 --alias 运营1号
|
|
165
|
+
webauto account delete xhs-0001 --delete-profile --delete-fingerprint
|
|
166
|
+
`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function cmdList(jsonMode) {
|
|
170
|
+
const result = listAccountProfiles();
|
|
171
|
+
output({ ok: true, ...result }, jsonMode);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function cmdListRecords(jsonMode) {
|
|
175
|
+
const result = listAccounts();
|
|
176
|
+
output({ ok: true, ...result }, jsonMode);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function cmdAdd(argv, jsonMode) {
|
|
180
|
+
const result = await addAccount({
|
|
181
|
+
id: argv.id,
|
|
182
|
+
platform: argv.platform,
|
|
183
|
+
alias: argv.alias,
|
|
184
|
+
name: argv.name,
|
|
185
|
+
username: argv.username,
|
|
186
|
+
profileId: argv.profile || argv['profile-id'],
|
|
187
|
+
fingerprintId: argv.fingerprint || argv['fingerprint-id'],
|
|
188
|
+
status: argv.status,
|
|
189
|
+
});
|
|
190
|
+
output({ ok: true, ...result }, jsonMode);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function cmdGet(idOrAlias, jsonMode) {
|
|
194
|
+
const account = getAccount(idOrAlias);
|
|
195
|
+
output({ ok: true, account }, jsonMode);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function cmdUpdate(idOrAlias, argv, jsonMode) {
|
|
199
|
+
const patch = {};
|
|
200
|
+
if (argv.platform !== undefined) patch.platform = argv.platform;
|
|
201
|
+
if (argv.name !== undefined) patch.name = argv.name;
|
|
202
|
+
if (argv.username !== undefined) patch.username = argv.username;
|
|
203
|
+
if (argv.status !== undefined) patch.status = argv.status;
|
|
204
|
+
if (argv.profile !== undefined || argv['profile-id'] !== undefined) {
|
|
205
|
+
patch.profileId = argv.profile || argv['profile-id'];
|
|
206
|
+
}
|
|
207
|
+
if (argv.fingerprint !== undefined || argv['fingerprint-id'] !== undefined) {
|
|
208
|
+
patch.fingerprintId = argv.fingerprint || argv['fingerprint-id'];
|
|
209
|
+
}
|
|
210
|
+
if (argv['clear-alias']) {
|
|
211
|
+
patch.alias = null;
|
|
212
|
+
patch.aliasSource = 'manual';
|
|
213
|
+
} else if (argv.alias !== undefined) {
|
|
214
|
+
patch.alias = argv.alias;
|
|
215
|
+
patch.aliasSource = 'manual';
|
|
216
|
+
}
|
|
217
|
+
if (!Object.keys(patch).length) {
|
|
218
|
+
throw new Error('no update fields provided');
|
|
219
|
+
}
|
|
220
|
+
const account = await updateAccount(idOrAlias, patch);
|
|
221
|
+
output({ ok: true, account }, jsonMode);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function cmdDelete(idOrAlias, argv, jsonMode) {
|
|
225
|
+
const result = removeAccount(idOrAlias, {
|
|
226
|
+
deleteProfile: argv['delete-profile'] === true,
|
|
227
|
+
deleteFingerprint: argv['delete-fingerprint'] === true,
|
|
228
|
+
});
|
|
229
|
+
output({ ok: true, ...result }, jsonMode);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function cmdLogin(idOrAlias, argv, jsonMode) {
|
|
233
|
+
const account = getAccount(idOrAlias);
|
|
234
|
+
await ensureProfile(account.profileId);
|
|
235
|
+
const url = String(argv.url || inferLoginUrl(account.platform)).trim();
|
|
236
|
+
|
|
237
|
+
const initResult = runCamo(['init']);
|
|
238
|
+
if (!initResult.ok) {
|
|
239
|
+
output({
|
|
240
|
+
ok: false,
|
|
241
|
+
step: 'init',
|
|
242
|
+
code: initResult.code,
|
|
243
|
+
error: initResult.stderr || initResult.stdout || 'camo init failed',
|
|
244
|
+
}, jsonMode);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const startResult = runCamo(['start', account.profileId, '--url', url]);
|
|
249
|
+
if (!startResult.ok) {
|
|
250
|
+
output({
|
|
251
|
+
ok: false,
|
|
252
|
+
step: 'start',
|
|
253
|
+
code: startResult.code,
|
|
254
|
+
error: startResult.stderr || startResult.stdout || 'camo start failed',
|
|
255
|
+
account,
|
|
256
|
+
}, jsonMode);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let aliasSync = null;
|
|
261
|
+
if (parseBoolean(argv['sync-alias'], false)) {
|
|
262
|
+
try {
|
|
263
|
+
const detected = await detectAliasFromActivePage(account.profileId, argv.selector);
|
|
264
|
+
const updated = await updateAccount(account.id, {
|
|
265
|
+
alias: detected.alias,
|
|
266
|
+
aliasSource: `auto:${detected.source}`,
|
|
267
|
+
});
|
|
268
|
+
aliasSync = { ok: true, alias: updated.alias, source: detected.source };
|
|
269
|
+
} catch (error) {
|
|
270
|
+
aliasSync = { ok: false, error: error?.message || String(error) };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const accountSync = await syncXhsAccountByProfile(account.profileId).catch((error) => ({
|
|
275
|
+
profileId: account.profileId,
|
|
276
|
+
valid: false,
|
|
277
|
+
status: 'invalid',
|
|
278
|
+
reason: error?.message || String(error),
|
|
279
|
+
}));
|
|
280
|
+
|
|
281
|
+
output({
|
|
282
|
+
ok: true,
|
|
283
|
+
account,
|
|
284
|
+
profileId: account.profileId,
|
|
285
|
+
url,
|
|
286
|
+
camo: startResult.json || startResult.stdout || null,
|
|
287
|
+
aliasSync,
|
|
288
|
+
accountSync,
|
|
289
|
+
}, jsonMode);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function cmdSyncAlias(idOrAlias, argv, jsonMode) {
|
|
293
|
+
const account = getAccount(idOrAlias);
|
|
294
|
+
let alias = normalizeAlias(argv.alias);
|
|
295
|
+
let source = 'manual';
|
|
296
|
+
let candidates = [];
|
|
297
|
+
if (!alias) {
|
|
298
|
+
const detected = await detectAliasFromActivePage(account.profileId, argv.selector);
|
|
299
|
+
alias = detected.alias;
|
|
300
|
+
source = `auto:${detected.source}`;
|
|
301
|
+
candidates = detected.candidates;
|
|
302
|
+
}
|
|
303
|
+
const updated = await updateAccount(account.id, { alias, aliasSource: source });
|
|
304
|
+
output({
|
|
305
|
+
ok: true,
|
|
306
|
+
account: updated,
|
|
307
|
+
alias,
|
|
308
|
+
source,
|
|
309
|
+
candidates,
|
|
310
|
+
}, jsonMode);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async function cmdSync(target, jsonMode) {
|
|
314
|
+
const value = String(target || '').trim().toLowerCase();
|
|
315
|
+
if (!value || value === 'all') {
|
|
316
|
+
const rows = listAccountProfiles().profiles;
|
|
317
|
+
const profileIds = rows.map((item) => item.profileId);
|
|
318
|
+
const synced = await syncXhsAccountsByProfiles(profileIds);
|
|
319
|
+
output({ ok: true, count: synced.length, profiles: synced }, jsonMode);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const synced = await syncXhsAccountByProfile(target);
|
|
323
|
+
output({ ok: true, profile: synced }, jsonMode);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function main() {
|
|
327
|
+
const argv = minimist(process.argv.slice(2), {
|
|
328
|
+
boolean: ['help', 'json', 'clear-alias', 'delete-profile', 'delete-fingerprint', 'sync-alias', 'records'],
|
|
329
|
+
alias: { h: 'help' },
|
|
330
|
+
});
|
|
331
|
+
const cmd = String(argv._[0] || '').trim();
|
|
332
|
+
const arg1 = String(argv._[1] || '').trim();
|
|
333
|
+
const jsonMode = argv.json === true;
|
|
334
|
+
|
|
335
|
+
if (!cmd || cmd === 'help' || argv.help) {
|
|
336
|
+
printHelp();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (cmd === 'list') return argv.records ? cmdListRecords(jsonMode) : cmdList(jsonMode);
|
|
341
|
+
if (cmd === 'add') return cmdAdd(argv, jsonMode);
|
|
342
|
+
if (cmd === 'get') return cmdGet(arg1, jsonMode);
|
|
343
|
+
if (cmd === 'update') return cmdUpdate(arg1, argv, jsonMode);
|
|
344
|
+
if (cmd === 'delete' || cmd === 'remove' || cmd === 'rm') return cmdDelete(arg1, argv, jsonMode);
|
|
345
|
+
if (cmd === 'login') return cmdLogin(arg1, argv, jsonMode);
|
|
346
|
+
if (cmd === 'sync-alias') return cmdSyncAlias(arg1, argv, jsonMode);
|
|
347
|
+
if (cmd === 'sync') return cmdSync(arg1, jsonMode);
|
|
348
|
+
|
|
349
|
+
throw new Error(`unknown account command: ${cmd}`);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
main().catch((error) => {
|
|
353
|
+
const message = error?.message || String(error);
|
|
354
|
+
console.error(message);
|
|
355
|
+
process.exit(1);
|
|
356
|
+
});
|