@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
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { createEl, labeledInput, section } from '../ui-components.mjs';
|
|
2
|
-
import { resolveWebautoRoot } from '../path-helpers.mjs';
|
|
3
|
-
|
|
4
|
-
const PHASES = [
|
|
5
|
-
{ id: 'phase1', label: 'Phase1: 启动浏览器' },
|
|
6
|
-
{ id: 'phase2', label: 'Phase2: 搜索采集' },
|
|
7
|
-
{ id: 'phase3', label: 'Phase3: 评论互动' },
|
|
8
|
-
{ id: 'phase4', label: 'Phase4: 内容采集' },
|
|
9
|
-
{ id: 'unified', label: 'Unified: 统一采集' },
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
export function renderProfilePool(root: HTMLElement, ctx: any) {
|
|
13
|
-
const webautoRoot = resolveWebautoRoot(ctx.settings?.downloadRoot || '', window.api);
|
|
14
|
-
|
|
15
|
-
const containerStyle = 'background:#1a1a2e; color:#eee; padding:16px; border-radius:8px;';
|
|
16
|
-
const listStyle = 'max-height:150px; overflow:auto; background:#16213e; border:1px solid #0f3460; border-radius:4px; padding:8px;';
|
|
17
|
-
const itemStyle = 'cursor:pointer; padding:6px 10px; margin:3px 0; background:#0f3460; border-radius:4px; color:#fff; font-size:13px;';
|
|
18
|
-
const selectedItemStyle = 'cursor:pointer; padding:6px 10px; margin:3px 0; background:#e94560; border-radius:4px; color:#fff; font-weight:bold; font-size:13px;';
|
|
19
|
-
const labelStyle = 'color:#eaeaea; font-size:12px; margin-bottom:6px; font-weight:500;';
|
|
20
|
-
const sectionTitleStyle = 'color:#fff; font-size:14px; font-weight:bold; margin:16px 0 8px 0; border-bottom:1px solid #0f3460; padding-bottom:4px;';
|
|
21
|
-
|
|
22
|
-
// Top section: Available and Selected profiles
|
|
23
|
-
const availableProfiles = createEl('div', { style: listStyle });
|
|
24
|
-
const selectedProfiles = createEl('div', { style: listStyle });
|
|
25
|
-
const statusText = createEl('div', { style: 'color:#e94560; font-size:13px; margin:8px 0; font-weight:bold;' });
|
|
26
|
-
|
|
27
|
-
// Bottom section: Per-phase profile assignment
|
|
28
|
-
const phaseSections = createEl('div', { style: 'margin-top:16px;' });
|
|
29
|
-
|
|
30
|
-
let profiles: string[] = [];
|
|
31
|
-
let selected: Set<string> = new Set();
|
|
32
|
-
let phaseProfiles: Record<string, Set<string>> = {};
|
|
33
|
-
|
|
34
|
-
function loadSavedConfig() {
|
|
35
|
-
if (ctx.settings?.allowedProfiles) {
|
|
36
|
-
selected = new Set(ctx.settings.allowedProfiles);
|
|
37
|
-
}
|
|
38
|
-
// Load phase-specific assignments
|
|
39
|
-
if (ctx.settings?.phaseProfiles) {
|
|
40
|
-
phaseProfiles = {};
|
|
41
|
-
for (const [phaseId, profs] of Object.entries(ctx.settings.phaseProfiles)) {
|
|
42
|
-
phaseProfiles[phaseId] = new Set(profs as string[]);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
// Default: all selected profiles enabled for all phases
|
|
46
|
-
PHASES.forEach(phase => {
|
|
47
|
-
if (!phaseProfiles[phase.id]) {
|
|
48
|
-
phaseProfiles[phase.id] = new Set(selected);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function loadProfiles() {
|
|
54
|
-
try {
|
|
55
|
-
const res = await fetch((ctx.settings?.coreDaemonUrl || 'http://127.0.0.1:7700') + '/profile/list');
|
|
56
|
-
const data = await res.json();
|
|
57
|
-
profiles = data.allowed || [];
|
|
58
|
-
loadSavedConfig();
|
|
59
|
-
renderTopSection();
|
|
60
|
-
renderPhaseSections();
|
|
61
|
-
} catch (e) {
|
|
62
|
-
statusText.textContent = '❌ 加载失败: ' + (e as Error).message;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function renderTopSection() {
|
|
67
|
-
availableProfiles.textContent = '';
|
|
68
|
-
selectedProfiles.textContent = '';
|
|
69
|
-
|
|
70
|
-
profiles.forEach(p => {
|
|
71
|
-
if (!selected.has(p)) {
|
|
72
|
-
const row = createEl('div', { style: itemStyle }, ['+ ' + p]);
|
|
73
|
-
row.onclick = () => {
|
|
74
|
-
selected.add(p);
|
|
75
|
-
// Auto-enable for all phases
|
|
76
|
-
PHASES.forEach(ph => {
|
|
77
|
-
if (!phaseProfiles[ph.id]) phaseProfiles[ph.id] = new Set();
|
|
78
|
-
phaseProfiles[ph.id].add(p);
|
|
79
|
-
});
|
|
80
|
-
renderTopSection();
|
|
81
|
-
renderPhaseSections();
|
|
82
|
-
};
|
|
83
|
-
availableProfiles.appendChild(row);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
if (selected.size === 0) {
|
|
88
|
-
selectedProfiles.appendChild(createEl('div', { style: 'color:#888; padding:20px; text-align:center; font-size:12px;' }, ['点击左侧 + 添加 Profile']));
|
|
89
|
-
} else {
|
|
90
|
-
selected.forEach(p => {
|
|
91
|
-
const row = createEl('div', { style: selectedItemStyle }, ['✓ ' + p]);
|
|
92
|
-
row.onclick = () => {
|
|
93
|
-
selected.delete(p);
|
|
94
|
-
// Remove from all phases
|
|
95
|
-
PHASES.forEach(ph => phaseProfiles[ph.id]?.delete(p));
|
|
96
|
-
renderTopSection();
|
|
97
|
-
renderPhaseSections();
|
|
98
|
-
};
|
|
99
|
-
selectedProfiles.appendChild(row);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
statusText.textContent = `📊 可用: ${profiles.length - selected.size} | 已加入: ${selected.size}`;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function renderPhaseSections() {
|
|
107
|
-
phaseSections.textContent = '';
|
|
108
|
-
phaseSections.appendChild(createEl('div', { style: sectionTitleStyle }, ['阶段 Profile 分配(可取消勾选)']));
|
|
109
|
-
|
|
110
|
-
PHASES.forEach(phase => {
|
|
111
|
-
const phaseBox = createEl('div', { style: 'background:#0f3460; padding:10px; margin:8px 0; border-radius:4px;' });
|
|
112
|
-
const phaseHeader = createEl('div', { style: 'color:#e94560; font-size:13px; font-weight:bold; margin-bottom:8px;' }, [phase.label]);
|
|
113
|
-
phaseBox.appendChild(phaseHeader);
|
|
114
|
-
|
|
115
|
-
const checkboxesRow = createEl('div', { style: 'display:flex; flex-wrap:wrap; gap:8px;' });
|
|
116
|
-
|
|
117
|
-
if (selected.size === 0) {
|
|
118
|
-
checkboxesRow.appendChild(createEl('span', { style: 'color:#666; font-size:12px;' }, ['先添加 Profile 到上方池']));
|
|
119
|
-
} else {
|
|
120
|
-
selected.forEach(profile => {
|
|
121
|
-
const isEnabled = phaseProfiles[phase.id]?.has(profile) ?? true;
|
|
122
|
-
const label = createEl('label', { style: 'display:flex; align-items:center; gap:4px; cursor:pointer; color:#fff; font-size:12px; padding:4px 8px; background:#16213e; border-radius:3px;' });
|
|
123
|
-
const checkbox = createEl('input', { type: 'checkbox', checked: isEnabled }) as HTMLInputElement;
|
|
124
|
-
checkbox.onchange = () => {
|
|
125
|
-
if (!phaseProfiles[phase.id]) phaseProfiles[phase.id] = new Set();
|
|
126
|
-
if (checkbox.checked) {
|
|
127
|
-
phaseProfiles[phase.id].add(profile);
|
|
128
|
-
} else {
|
|
129
|
-
phaseProfiles[phase.id].delete(profile);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
label.appendChild(checkbox);
|
|
133
|
-
label.appendChild(createEl('span', {}, [profile]));
|
|
134
|
-
checkboxesRow.appendChild(label);
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
phaseBox.appendChild(checkboxesRow);
|
|
139
|
-
phaseSections.appendChild(phaseBox);
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const btnStyle = 'padding:6px 12px; margin-right:6px; background:#0f3460; color:#fff; border:none; border-radius:4px; cursor:pointer; font-size:12px;';
|
|
144
|
-
const btnPrimaryStyle = 'padding:6px 12px; margin-right:6px; background:#e94560; color:#fff; border:none; border-radius:4px; cursor:pointer; font-weight:bold; font-size:12px;';
|
|
145
|
-
|
|
146
|
-
const btnSave = createEl('button', { style: btnPrimaryStyle }, ['💾 保存']);
|
|
147
|
-
btnSave.onclick = async () => {
|
|
148
|
-
const phaseConfig: Record<string, string[]> = {};
|
|
149
|
-
PHASES.forEach(ph => {
|
|
150
|
-
phaseConfig[ph.id] = Array.from(phaseProfiles[ph.id] || []);
|
|
151
|
-
});
|
|
152
|
-
await window.api.settingsSet({
|
|
153
|
-
allowedProfiles: Array.from(selected),
|
|
154
|
-
phaseProfiles: phaseConfig
|
|
155
|
-
});
|
|
156
|
-
ctx.settings.allowedProfiles = Array.from(selected);
|
|
157
|
-
ctx.settings.phaseProfiles = phaseConfig;
|
|
158
|
-
ctx.appendLog('[ProfilePool] ✅ 已保存');
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const btnRefresh = createEl('button', { style: btnStyle }, ['🔄 刷新']);
|
|
162
|
-
btnRefresh.onclick = () => loadProfiles();
|
|
163
|
-
|
|
164
|
-
const btnSelectAll = createEl('button', { style: btnStyle }, ['☑️ 全选']);
|
|
165
|
-
btnSelectAll.onclick = () => {
|
|
166
|
-
profiles.forEach(p => {
|
|
167
|
-
selected.add(p);
|
|
168
|
-
PHASES.forEach(ph => {
|
|
169
|
-
if (!phaseProfiles[ph.id]) phaseProfiles[ph.id] = new Set();
|
|
170
|
-
phaseProfiles[ph.id].add(p);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
renderTopSection();
|
|
174
|
-
renderPhaseSections();
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const btnClear = createEl('button', { style: btnStyle }, ['清空']);
|
|
178
|
-
btnClear.onclick = () => {
|
|
179
|
-
selected.clear();
|
|
180
|
-
phaseProfiles = {};
|
|
181
|
-
renderTopSection();
|
|
182
|
-
renderPhaseSections();
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const wrapper = createEl('div', { style: containerStyle });
|
|
186
|
-
wrapper.appendChild(
|
|
187
|
-
section('Profile Pool', [
|
|
188
|
-
// Top: Pool management
|
|
189
|
-
createEl('div', { style: 'display:grid; grid-template-columns:1fr 1fr; gap:12px;' }, [
|
|
190
|
-
createEl('div', {}, [
|
|
191
|
-
createEl('div', { style: labelStyle }, ['可用 Profiles(点击加入)']),
|
|
192
|
-
availableProfiles,
|
|
193
|
-
]),
|
|
194
|
-
createEl('div', {}, [
|
|
195
|
-
createEl('div', { style: labelStyle }, ['Profile 池(点击移除)']),
|
|
196
|
-
selectedProfiles,
|
|
197
|
-
]),
|
|
198
|
-
]),
|
|
199
|
-
statusText,
|
|
200
|
-
createEl('div', { style: 'margin:8px 0;' }, [
|
|
201
|
-
btnRefresh,
|
|
202
|
-
btnSelectAll,
|
|
203
|
-
btnClear,
|
|
204
|
-
btnSave,
|
|
205
|
-
]),
|
|
206
|
-
// Bottom: Phase assignments
|
|
207
|
-
phaseSections,
|
|
208
|
-
]),
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
root.appendChild(wrapper);
|
|
212
|
-
loadProfiles();
|
|
213
|
-
}
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { createEl, section } from '../ui-components.mts';
|
|
2
|
-
|
|
3
|
-
export function renderResults(root: HTMLElement, ctx: any) {
|
|
4
|
-
const entryList = createEl('div', { className: 'list' });
|
|
5
|
-
const fileList = createEl('div', { className: 'list' });
|
|
6
|
-
const rightTop = createEl('div', {});
|
|
7
|
-
const preview = createEl('div', {});
|
|
8
|
-
|
|
9
|
-
const filterInput = createEl('input', { placeholder: 'filter filename (contains)', value: '' }) as HTMLInputElement;
|
|
10
|
-
const onlyImages = createEl('input', { type: 'checkbox' }) as HTMLInputElement;
|
|
11
|
-
const onlyText = createEl('input', { type: 'checkbox' }) as HTMLInputElement;
|
|
12
|
-
const statusLine = createEl('div', { className: 'muted' }, ['']);
|
|
13
|
-
|
|
14
|
-
let currentEntry: any = null;
|
|
15
|
-
let currentFiles: any[] = [];
|
|
16
|
-
|
|
17
|
-
async function refresh() {
|
|
18
|
-
entryList.textContent = '';
|
|
19
|
-
fileList.textContent = '';
|
|
20
|
-
rightTop.textContent = '';
|
|
21
|
-
preview.textContent = '';
|
|
22
|
-
statusLine.textContent = '';
|
|
23
|
-
currentEntry = null;
|
|
24
|
-
currentFiles = [];
|
|
25
|
-
|
|
26
|
-
const res = await window.api.resultsScan({ downloadRoot: ctx.settings?.downloadRoot });
|
|
27
|
-
if (!res?.ok) {
|
|
28
|
-
entryList.appendChild(createEl('div', { className: 'item' }, [String(res?.error || 'scan failed')]));
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
const entries = res.entries || [];
|
|
32
|
-
entries.forEach((e: any) => {
|
|
33
|
-
const s = e.state || null;
|
|
34
|
-
const badge =
|
|
35
|
-
s && s.status
|
|
36
|
-
? ` [${s.status}] links=${s.links || 0}/${s.target || 0} ok=${s.completed || 0} fail=${s.failed || 0}`
|
|
37
|
-
: ' [no-state]';
|
|
38
|
-
const item = createEl('div', { className: 'item' }, [`${e.env}/${e.keyword}${badge}`]);
|
|
39
|
-
item.onclick = () => void openEntry(e);
|
|
40
|
-
entryList.appendChild(item);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function openEntry(e: any) {
|
|
45
|
-
currentEntry = e;
|
|
46
|
-
currentFiles = [];
|
|
47
|
-
rightTop.textContent = '';
|
|
48
|
-
preview.textContent = '';
|
|
49
|
-
fileList.textContent = '';
|
|
50
|
-
|
|
51
|
-
const openDirBtn = createEl('button', { className: 'secondary' }, ['打开目录']) as HTMLButtonElement;
|
|
52
|
-
openDirBtn.onclick = () => void window.api.osOpenPath(e.path);
|
|
53
|
-
|
|
54
|
-
rightTop.appendChild(createEl('div', { className: 'row' }, [
|
|
55
|
-
openDirBtn,
|
|
56
|
-
createEl('div', { className: 'muted' }, [String(e.path || '')]),
|
|
57
|
-
]));
|
|
58
|
-
|
|
59
|
-
const res = await window.api.fsListDir({ root: e.path, recursive: true, maxEntries: 2000 });
|
|
60
|
-
if (!res?.ok) {
|
|
61
|
-
statusLine.textContent = `list failed: ${String(res?.error || '')}`;
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const files = (res.entries || []).filter((x: any) => x && x.isDir === false);
|
|
65
|
-
currentFiles = files;
|
|
66
|
-
statusLine.textContent = `files=${files.length}${res.truncated ? ' (truncated)' : ''}`;
|
|
67
|
-
renderFileList();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function isImagePath(p: string) {
|
|
71
|
-
const ext = p.toLowerCase().split('.').pop() || '';
|
|
72
|
-
return ext === 'png' || ext === 'jpg' || ext === 'jpeg' || ext === 'webp';
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function isTextPath(p: string) {
|
|
76
|
-
const ext = p.toLowerCase().split('.').pop() || '';
|
|
77
|
-
return ext === 'jsonl' || ext === 'json' || ext === 'txt' || ext === 'log' || ext === 'md';
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function renderFileList() {
|
|
81
|
-
fileList.textContent = '';
|
|
82
|
-
preview.textContent = '';
|
|
83
|
-
|
|
84
|
-
const needle = filterInput.value.trim().toLowerCase();
|
|
85
|
-
const list = currentFiles
|
|
86
|
-
.filter((x: any) => {
|
|
87
|
-
const rel = String(x.rel || x.name || '').toLowerCase();
|
|
88
|
-
if (needle && !rel.includes(needle)) return false;
|
|
89
|
-
if (onlyImages.checked && !isImagePath(rel)) return false;
|
|
90
|
-
if (onlyText.checked && !isTextPath(rel)) return false;
|
|
91
|
-
return true;
|
|
92
|
-
})
|
|
93
|
-
.slice(0, 600);
|
|
94
|
-
|
|
95
|
-
list.forEach((f: any) => {
|
|
96
|
-
const label = String(f.rel || f.name || '');
|
|
97
|
-
const item = createEl('div', { className: 'item' }, [label]);
|
|
98
|
-
item.onclick = () => void previewFile(f);
|
|
99
|
-
fileList.appendChild(item);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async function previewFile(f: any) {
|
|
104
|
-
preview.textContent = '';
|
|
105
|
-
const p = String(f.path || '');
|
|
106
|
-
if (!p) return;
|
|
107
|
-
|
|
108
|
-
const actions = createEl('div', { className: 'row' }, [
|
|
109
|
-
createEl('button', { className: 'secondary' }, ['打开文件']),
|
|
110
|
-
]);
|
|
111
|
-
(actions.querySelector('button') as HTMLButtonElement).onclick = () => void window.api.osOpenPath(p);
|
|
112
|
-
preview.appendChild(actions);
|
|
113
|
-
|
|
114
|
-
if (isImagePath(p)) {
|
|
115
|
-
const res = await window.api.fsReadFileBase64({ path: p, maxBytes: 8_000_000 });
|
|
116
|
-
if (!res?.ok) {
|
|
117
|
-
preview.appendChild(createEl('div', { className: 'muted' }, [String(res?.error || 'read image failed')]));
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
const ext = p.toLowerCase().endsWith('.png') ? 'png' : p.toLowerCase().endsWith('.webp') ? 'webp' : 'jpeg';
|
|
121
|
-
const img = createEl('img', { style: 'max-width:100%; border:1px solid #ddd; border-radius:6px;' }) as HTMLImageElement;
|
|
122
|
-
img.src = `data:image/${ext};base64,${res.data}`;
|
|
123
|
-
preview.appendChild(img);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
if (isTextPath(p)) {
|
|
127
|
-
await previewText(p);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
preview.appendChild(createEl('div', { className: 'muted' }, ['(no preview)']));
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async function previewText(filePath: string) {
|
|
134
|
-
const res = await window.api.fsReadTextPreview({ path: filePath, maxBytes: 50_000, maxLines: 200 });
|
|
135
|
-
if (!res?.ok) {
|
|
136
|
-
preview.appendChild(createEl('div', { className: 'muted' }, [String(res?.error || 'read failed')]));
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
preview.appendChild(createEl('pre', { style: 'white-space:pre-wrap; margin:0;' }, [res.text]));
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
root.appendChild(
|
|
143
|
-
section('结果目录', [
|
|
144
|
-
createEl('div', { className: 'row' }, [
|
|
145
|
-
createEl('button', { className: 'secondary' }, ['刷新']),
|
|
146
|
-
createEl('div', { className: 'muted' }, [`root=${ctx.settings?.downloadRoot || ''}`]),
|
|
147
|
-
]),
|
|
148
|
-
createEl('div', { className: 'row' }, [
|
|
149
|
-
createEl('div', { style: 'display:flex; flex-direction:column; gap:6px;' }, [
|
|
150
|
-
createEl('label', {}, ['only images']),
|
|
151
|
-
onlyImages,
|
|
152
|
-
]),
|
|
153
|
-
createEl('div', { style: 'display:flex; flex-direction:column; gap:6px;' }, [
|
|
154
|
-
createEl('label', {}, ['only text']),
|
|
155
|
-
onlyText,
|
|
156
|
-
]),
|
|
157
|
-
filterInput,
|
|
158
|
-
statusLine,
|
|
159
|
-
]),
|
|
160
|
-
createEl('div', { className: 'split' }, [
|
|
161
|
-
entryList,
|
|
162
|
-
createEl('div', {}, [rightTop, fileList, preview]),
|
|
163
|
-
]),
|
|
164
|
-
]),
|
|
165
|
-
);
|
|
166
|
-
(root.querySelector('button') as HTMLButtonElement).onclick = () => void refresh();
|
|
167
|
-
filterInput.oninput = () => renderFileList();
|
|
168
|
-
onlyImages.onchange = () => renderFileList();
|
|
169
|
-
onlyText.onchange = () => renderFileList();
|
|
170
|
-
void refresh();
|
|
171
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { test } from 'node:test';
|
|
4
|
-
import assert from 'node:assert/strict';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
|
|
7
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const runPath = path.join(__dirname, 'run.mts');
|
|
9
|
-
|
|
10
|
-
async function getSrc() {
|
|
11
|
-
return readFile(runPath, 'utf8');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
test('only fullCollect supports multi-profile modes in resolveProfileArgsForRun', async () => {
|
|
15
|
-
const src = await getSrc();
|
|
16
|
-
assert.match(
|
|
17
|
-
src,
|
|
18
|
-
/supportsMultiProfile\s*=\s*t\s*===\s*'fullCollect'/,
|
|
19
|
-
);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('fullCollect uses scripts scan result as script path when available', async () => {
|
|
23
|
-
const src = await getSrc();
|
|
24
|
-
assert.match(src, /scriptsXhsFullCollect/);
|
|
25
|
-
assert.match(src, /const scriptPath = chosen\?\.path \|\| window\.api\.pathJoin\('apps', 'webauto', 'entry', 'xhs-unified\.mjs'\);/);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('run tab persists default keyword/target/env/dry-run on execute', async () => {
|
|
29
|
-
const src = await getSrc();
|
|
30
|
-
assert.match(src, /persistRunInputs\(\{ keyword, target:.*env, dryRun: dryRun\.checked \}\)/);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('run template includes target argument and fullCollect validation', async () => {
|
|
34
|
-
const src = await getSrc();
|
|
35
|
-
assert.match(src, /\['--target', target\]/);
|
|
36
|
-
assert.match(src, /labeledInput\('target', targetInput\)/);
|
|
37
|
-
assert.match(src, /if \(t === 'fullCollect'\) \{/);
|
|
38
|
-
assert.match(src, /fullCollect: 必须填写 keyword/);
|
|
39
|
-
assert.match(src, /alert\('Full Collect: 必须填写 keyword'\)/);
|
|
40
|
-
assert.match(src, /fullCollect: 必须填写 target(正整数)/);
|
|
41
|
-
assert.match(src, /alert\('Full Collect: 必须填写 target(正整数)'\)/);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('non-fullCollect single selection uses runtime picker', async () => {
|
|
45
|
-
const src = await getSrc();
|
|
46
|
-
assert.match(src, /const useRuntimeForSingle = templateSel\.value !== 'fullCollect';/);
|
|
47
|
-
assert.match(src, /profilePickSel\.style\.display = mode === 'profile' && !useRuntimeForSingle \? '' : 'none';/);
|
|
48
|
-
assert.match(src, /runtimePickSel\.style\.display = mode === 'profile' && useRuntimeForSingle \? '' : 'none';/);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('profilepool mode shows profilesBox and auto-selects pool members', async () => {
|
|
52
|
-
const src = await getSrc();
|
|
53
|
-
assert.match(src, /profilesBox\.style\.display = \(mode === 'profiles' \|\| mode === 'profilepool'\) \? '' : 'none';/);
|
|
54
|
-
assert.match(src, /mode === 'profilepool'[\s\S]*pid\.startsWith\(v\)/);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('run tab is debug-only and points orchestration to xiaohongshu tab', async () => {
|
|
58
|
-
const src = await getSrc();
|
|
59
|
-
assert.match(src, /完整编排(Phase1\/2\/Unified \+ 分片)已迁移到“小红书”Tab;这里仅保留调试入口/);
|
|
60
|
-
assert.doesNotMatch(src, /orchestratePhase12Unified/);
|
|
61
|
-
assert.doesNotMatch(src, /phase-orchestrate\.mjs/);
|
|
62
|
-
assert.doesNotMatch(src, /Phase2 collect links/);
|
|
63
|
-
});
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { createEl, section } from '../ui-components.mts';
|
|
2
|
-
|
|
3
|
-
type SessionState = {
|
|
4
|
-
profileId: string;
|
|
5
|
-
sessionId?: string;
|
|
6
|
-
currentUrl?: string;
|
|
7
|
-
lastPhase?: string;
|
|
8
|
-
lastActiveAt?: string;
|
|
9
|
-
createdAt?: string;
|
|
10
|
-
status?: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
function toMap(obj: any): Record<string, string> {
|
|
14
|
-
return obj && typeof obj === 'object' ? (obj as any) : {};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function formatAge(iso?: string): string {
|
|
18
|
-
if (!iso) return '';
|
|
19
|
-
const t = Date.parse(iso);
|
|
20
|
-
if (!Number.isFinite(t)) return '';
|
|
21
|
-
const ms = Date.now() - t;
|
|
22
|
-
const sec = Math.max(0, Math.floor(ms / 1000));
|
|
23
|
-
if (sec < 60) return `${sec}s`;
|
|
24
|
-
const min = Math.floor(sec / 60);
|
|
25
|
-
if (min < 60) return `${min}m`;
|
|
26
|
-
const hr = Math.floor(min / 60);
|
|
27
|
-
return `${hr}h`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function titleFor(profileId: string, alias: string, color: string) {
|
|
31
|
-
// Window title is the only cross-browser surface we can touch without injecting UI overlays.
|
|
32
|
-
return `${color} ${alias || profileId} | ${profileId}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function headerLabelFor(profileId: string, alias: string) {
|
|
36
|
-
return alias ? `${alias} (${profileId})` : profileId;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function renderRuntime(root: HTMLElement, ctx: any) {
|
|
40
|
-
const status = createEl('div', { className: 'muted' }, ['']);
|
|
41
|
-
const list = createEl('div', { className: 'runtime-list' });
|
|
42
|
-
const refreshBtn = createEl('button', { className: 'secondary' }, ['刷新 runtime']) as HTMLButtonElement;
|
|
43
|
-
|
|
44
|
-
let sessions: SessionState[] = [];
|
|
45
|
-
|
|
46
|
-
async function refresh() {
|
|
47
|
-
status.textContent = 'loading...';
|
|
48
|
-
const res = await window.api.runtimeListSessions().catch(() => []);
|
|
49
|
-
sessions = Array.isArray(res) ? res : [];
|
|
50
|
-
status.textContent = `running=${sessions.length}`;
|
|
51
|
-
renderList();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function renderList() {
|
|
55
|
-
list.textContent = '';
|
|
56
|
-
const aliases = toMap(ctx.settings?.profileAliases);
|
|
57
|
-
const colors = toMap(ctx.settings?.profileColors);
|
|
58
|
-
|
|
59
|
-
if (sessions.length === 0) {
|
|
60
|
-
list.appendChild(createEl('div', { className: 'muted' }, ['暂无运行中的 session(或 Unified API 尚未同步 session 状态)']));
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
for (const s of sessions) {
|
|
65
|
-
const profileId = String(s?.profileId || '');
|
|
66
|
-
const alias = String(aliases[profileId] || '');
|
|
67
|
-
const color = String(colors[profileId] || '#cccccc');
|
|
68
|
-
const showName = alias ? `${alias}` : profileId;
|
|
69
|
-
|
|
70
|
-
const row = createEl('div', { className: 'runtime-item' });
|
|
71
|
-
const head = createEl('div', { className: 'runtime-head' });
|
|
72
|
-
const badge = createEl('div', { className: 'runtime-badge', style: `background:${color}` }, ['']);
|
|
73
|
-
const name = createEl('div', { className: 'runtime-name' }, [showName]);
|
|
74
|
-
const meta = createEl('div', { className: 'runtime-meta muted' }, [
|
|
75
|
-
profileId !== showName ? `(${profileId})` : '',
|
|
76
|
-
s?.currentUrl ? ` url=${String(s.currentUrl)}` : '',
|
|
77
|
-
]);
|
|
78
|
-
const phase = createEl('div', { className: 'runtime-phase muted' }, [s?.lastPhase ? `phase=${String(s.lastPhase)}` : '']);
|
|
79
|
-
const age = createEl('div', { className: 'runtime-age muted' }, [s?.lastActiveAt ? `active ${formatAge(s.lastActiveAt)}` : '']);
|
|
80
|
-
|
|
81
|
-
head.appendChild(badge);
|
|
82
|
-
head.appendChild(createEl('div', { style: 'display:flex; flex-direction:column; gap:2px; min-width:0;' }, [name, meta, phase, age]));
|
|
83
|
-
|
|
84
|
-
const controls = createEl('div', { className: 'runtime-controls' });
|
|
85
|
-
const colorInput = createEl('input', { type: 'color', value: color }) as HTMLInputElement;
|
|
86
|
-
colorInput.onchange = async () => {
|
|
87
|
-
const next = { ...toMap(ctx.settings?.profileColors) };
|
|
88
|
-
next[profileId] = String(colorInput.value || '#cccccc');
|
|
89
|
-
const updated = await window.api.settingsSet({ profileColors: next }).catch(() => null);
|
|
90
|
-
if (updated) ctx.settings = updated;
|
|
91
|
-
const alias2 = String(toMap(ctx.settings?.profileAliases)[profileId] || '');
|
|
92
|
-
const nextColor = String(colorInput.value || '#cccccc');
|
|
93
|
-
const title = titleFor(profileId, alias2, nextColor);
|
|
94
|
-
await window.api.runtimeSetBrowserTitle({ profileId, title }).catch(() => null);
|
|
95
|
-
await window.api.runtimeSetHeaderBar({ profileId, label: headerLabelFor(profileId, alias2), color: nextColor }).catch(() => null);
|
|
96
|
-
void refresh();
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const btnFocus = createEl('button', { className: 'secondary' }, ['focus']) as HTMLButtonElement;
|
|
100
|
-
btnFocus.onclick = async () => {
|
|
101
|
-
await window.api.runtimeFocus({ profileId }).catch(() => null);
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const btnMark = createEl('button', { className: 'secondary' }, ['mark']) as HTMLButtonElement;
|
|
105
|
-
btnMark.onclick = async () => {
|
|
106
|
-
const alias2 = String(toMap(ctx.settings?.profileAliases)[profileId] || '');
|
|
107
|
-
await window.api.runtimeSetHeaderBar({ profileId, label: headerLabelFor(profileId, alias2), color }).catch(() => null);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const btnRestart = createEl('button', { className: 'secondary' }, ['phase1']) as HTMLButtonElement;
|
|
111
|
-
btnRestart.onclick = async () => {
|
|
112
|
-
await window.api.runtimeRestartPhase1({ profileId }).catch(() => null);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const btnKill = createEl('button', { className: 'danger' }, ['kill']) as HTMLButtonElement;
|
|
116
|
-
btnKill.onclick = async () => {
|
|
117
|
-
if (!confirm(`关闭 session: ${profileId}?`)) return;
|
|
118
|
-
await window.api.runtimeKill({ profileId }).catch(() => null);
|
|
119
|
-
await refresh();
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
controls.appendChild(colorInput);
|
|
123
|
-
controls.appendChild(btnFocus);
|
|
124
|
-
controls.appendChild(btnMark);
|
|
125
|
-
controls.appendChild(btnRestart);
|
|
126
|
-
controls.appendChild(btnKill);
|
|
127
|
-
|
|
128
|
-
row.appendChild(head);
|
|
129
|
-
row.appendChild(controls);
|
|
130
|
-
list.appendChild(row);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Auto refresh on settings change (aliases/colors updated in Preflight)
|
|
135
|
-
window.api.onSettingsChanged((next: any) => {
|
|
136
|
-
ctx.settings = next;
|
|
137
|
-
renderList();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
refreshBtn.onclick = () => void refresh();
|
|
141
|
-
|
|
142
|
-
section('Runtime Dashboard', [
|
|
143
|
-
createEl('div', { style: 'display:flex; gap:8px; align-items:center;' }, [refreshBtn, status]),
|
|
144
|
-
list,
|
|
145
|
-
createEl('div', { className: 'muted' }, [
|
|
146
|
-
'提示:颜色与 alias 会持久化到 settings;颜色也会 best-effort 写入浏览器页面 title(用于在浏览器窗口标题/Tab 上区分)。',
|
|
147
|
-
]),
|
|
148
|
-
]).forEach((x) => root.appendChild(x));
|
|
149
|
-
|
|
150
|
-
void refresh();
|
|
151
|
-
}
|