@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,423 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { BROWSER_SERVICE_URL, loadConfig, setRepoRoot } from './config.mjs';
|
|
7
|
+
export async function callAPI(action, payload = {}) {
|
|
8
|
+
const r = await fetch(`${BROWSER_SERVICE_URL}/command`, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: { 'Content-Type': 'application/json' },
|
|
11
|
+
body: JSON.stringify({ action, args: payload }),
|
|
12
|
+
});
|
|
13
|
+
let body;
|
|
14
|
+
try {
|
|
15
|
+
body = await r.json();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
const text = await r.text();
|
|
19
|
+
throw new Error(`HTTP ${r.status}: ${text}`);
|
|
20
|
+
}
|
|
21
|
+
if (!r.ok)
|
|
22
|
+
throw new Error(body?.error || `HTTP ${r.status}`);
|
|
23
|
+
return body;
|
|
24
|
+
}
|
|
25
|
+
export async function getSessionByProfile(profileId) {
|
|
26
|
+
const status = await callAPI('getStatus', {});
|
|
27
|
+
const activeSession = status?.sessions?.find((s) => s.profileId === profileId) || null;
|
|
28
|
+
if (activeSession) {
|
|
29
|
+
return activeSession;
|
|
30
|
+
}
|
|
31
|
+
if (!profileId) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
// Some browser-service builds do not populate getStatus.sessions reliably.
|
|
35
|
+
// Fallback to page:list so runtime can still attach to an active profile tab set.
|
|
36
|
+
try {
|
|
37
|
+
const pagePayload = await callAPI('page:list', { profileId });
|
|
38
|
+
const pages = Array.isArray(pagePayload?.pages)
|
|
39
|
+
? pagePayload.pages
|
|
40
|
+
: Array.isArray(pagePayload?.data?.pages)
|
|
41
|
+
? pagePayload.data.pages
|
|
42
|
+
: [];
|
|
43
|
+
if (!pages.length)
|
|
44
|
+
return null;
|
|
45
|
+
const activeIndex = Number(pagePayload?.activeIndex ?? pagePayload?.data?.activeIndex);
|
|
46
|
+
const activePage = Number.isFinite(activeIndex)
|
|
47
|
+
? pages.find((page) => Number(page?.index) === activeIndex)
|
|
48
|
+
: (pages.find((page) => page?.active) || pages[0]);
|
|
49
|
+
return {
|
|
50
|
+
profileId,
|
|
51
|
+
session_id: profileId,
|
|
52
|
+
sessionId: profileId,
|
|
53
|
+
current_url: activePage?.url || null,
|
|
54
|
+
recoveredFromPages: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function buildDomSnapshotScript(maxDepth, maxChildren) {
|
|
62
|
+
return `(() => {
|
|
63
|
+
const MAX_DEPTH = ${maxDepth};
|
|
64
|
+
const MAX_CHILDREN = ${maxChildren};
|
|
65
|
+
const viewportWidth = Number(window.innerWidth || 0);
|
|
66
|
+
const viewportHeight = Number(window.innerHeight || 0);
|
|
67
|
+
|
|
68
|
+
const normalizeRect = (rect) => {
|
|
69
|
+
if (!rect) return null;
|
|
70
|
+
const left = Number(rect.left ?? rect.x ?? 0);
|
|
71
|
+
const top = Number(rect.top ?? rect.y ?? 0);
|
|
72
|
+
const width = Number(rect.width ?? 0);
|
|
73
|
+
const height = Number(rect.height ?? 0);
|
|
74
|
+
return {
|
|
75
|
+
left,
|
|
76
|
+
top,
|
|
77
|
+
right: left + width,
|
|
78
|
+
bottom: top + height,
|
|
79
|
+
x: left,
|
|
80
|
+
y: top,
|
|
81
|
+
width,
|
|
82
|
+
height,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const sanitizeClasses = (el) => {
|
|
87
|
+
const classAttr = typeof el.className === 'string'
|
|
88
|
+
? el.className
|
|
89
|
+
: (el.getAttribute && el.getAttribute('class')) || '';
|
|
90
|
+
return classAttr.split(/\\s+/).filter(Boolean).slice(0, 24);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const collectAttrs = (el) => {
|
|
94
|
+
if (!el || !el.getAttribute) return null;
|
|
95
|
+
const keys = [
|
|
96
|
+
'href',
|
|
97
|
+
'src',
|
|
98
|
+
'name',
|
|
99
|
+
'type',
|
|
100
|
+
'value',
|
|
101
|
+
'placeholder',
|
|
102
|
+
'role',
|
|
103
|
+
'aria-label',
|
|
104
|
+
'aria-hidden',
|
|
105
|
+
'title',
|
|
106
|
+
];
|
|
107
|
+
const attrs = {};
|
|
108
|
+
for (const key of keys) {
|
|
109
|
+
const value = el.getAttribute(key);
|
|
110
|
+
if (value === null || value === undefined || value === '') continue;
|
|
111
|
+
attrs[key] = String(value).slice(0, 400);
|
|
112
|
+
}
|
|
113
|
+
return Object.keys(attrs).length > 0 ? attrs : null;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const inViewport = (rect) => {
|
|
117
|
+
if (!rect) return false;
|
|
118
|
+
if (rect.width <= 0 || rect.height <= 0) return false;
|
|
119
|
+
return (
|
|
120
|
+
rect.right > 0
|
|
121
|
+
&& rect.bottom > 0
|
|
122
|
+
&& rect.left < viewportWidth
|
|
123
|
+
&& rect.top < viewportHeight
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const isRendered = (el) => {
|
|
128
|
+
try {
|
|
129
|
+
const style = window.getComputedStyle(el);
|
|
130
|
+
if (!style) return false;
|
|
131
|
+
if (style.display === 'none') return false;
|
|
132
|
+
if (style.visibility === 'hidden' || style.visibility === 'collapse') return false;
|
|
133
|
+
const opacity = Number.parseFloat(String(style.opacity || '1'));
|
|
134
|
+
if (Number.isFinite(opacity) && opacity <= 0.01) return false;
|
|
135
|
+
return true;
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const clampPoint = (value, max) => {
|
|
142
|
+
if (!Number.isFinite(value)) return 0;
|
|
143
|
+
if (max <= 1) return 0;
|
|
144
|
+
return Math.max(0, Math.min(max - 1, value));
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const hitTestVisible = (el, rect) => {
|
|
148
|
+
if (!rect || viewportWidth <= 0 || viewportHeight <= 0) return false;
|
|
149
|
+
const samplePoints = [
|
|
150
|
+
[rect.left + rect.width * 0.5, rect.top + rect.height * 0.5],
|
|
151
|
+
[rect.left + rect.width * 0.2, rect.top + rect.height * 0.2],
|
|
152
|
+
[rect.left + rect.width * 0.8, rect.top + rect.height * 0.8],
|
|
153
|
+
];
|
|
154
|
+
for (const [rawX, rawY] of samplePoints) {
|
|
155
|
+
const x = clampPoint(rawX, viewportWidth);
|
|
156
|
+
const y = clampPoint(rawY, viewportHeight);
|
|
157
|
+
const topEl = document.elementFromPoint(x, y);
|
|
158
|
+
if (!topEl) continue;
|
|
159
|
+
if (topEl === el) return true;
|
|
160
|
+
if (el.contains && el.contains(topEl)) return true;
|
|
161
|
+
if (topEl.contains && topEl.contains(el)) return true;
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const collect = (el, depth = 0, path = 'root') => {
|
|
167
|
+
if (!el || depth > MAX_DEPTH) return null;
|
|
168
|
+
const classes = sanitizeClasses(el);
|
|
169
|
+
const rect = normalizeRect(el.getBoundingClientRect ? el.getBoundingClientRect() : null);
|
|
170
|
+
const tag = String(el.tagName || el.nodeName || '').toLowerCase();
|
|
171
|
+
const id = el.id || null;
|
|
172
|
+
const text = typeof el.textContent === 'string'
|
|
173
|
+
? el.textContent.replace(/\\s+/g, ' ').trim()
|
|
174
|
+
: '';
|
|
175
|
+
const selector = tag
|
|
176
|
+
? \`\${tag}\${id ? '#' + id : ''}\${classes.length ? '.' + classes.slice(0, 3).join('.') : ''}\`
|
|
177
|
+
: null;
|
|
178
|
+
|
|
179
|
+
const node = {
|
|
180
|
+
tag,
|
|
181
|
+
id,
|
|
182
|
+
classes,
|
|
183
|
+
selector,
|
|
184
|
+
path,
|
|
185
|
+
};
|
|
186
|
+
const attrs = collectAttrs(el);
|
|
187
|
+
if (attrs) node.attrs = attrs;
|
|
188
|
+
if (attrs && attrs.href) node.href = attrs.href;
|
|
189
|
+
if (rect) node.rect = rect;
|
|
190
|
+
if (text) node.textSnippet = text.slice(0, 120);
|
|
191
|
+
if (rect) {
|
|
192
|
+
const rendered = isRendered(el);
|
|
193
|
+
const withinViewport = inViewport(rect);
|
|
194
|
+
const visible = rendered && withinViewport && hitTestVisible(el, rect);
|
|
195
|
+
node.visible = visible;
|
|
196
|
+
} else {
|
|
197
|
+
node.visible = false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const children = Array.from(el.children || []);
|
|
201
|
+
if (children.length > 0 && depth < MAX_DEPTH) {
|
|
202
|
+
node.children = [];
|
|
203
|
+
const limit = Math.min(children.length, MAX_CHILDREN);
|
|
204
|
+
for (let i = 0; i < limit; i += 1) {
|
|
205
|
+
const child = collect(children[i], depth + 1, \`\${path}/\${i}\`);
|
|
206
|
+
if (child) node.children.push(child);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return node;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const root = collect(document.body || document.documentElement, 0, 'root');
|
|
214
|
+
return {
|
|
215
|
+
dom_tree: root,
|
|
216
|
+
viewport: {
|
|
217
|
+
width: viewportWidth,
|
|
218
|
+
height: viewportHeight,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
})()`;
|
|
222
|
+
}
|
|
223
|
+
export async function getDomSnapshotByProfile(profileId, options = {}) {
|
|
224
|
+
const maxDepth = Math.max(1, Math.min(20, Number(options.maxDepth) || 10));
|
|
225
|
+
const maxChildren = Math.max(1, Math.min(500, Number(options.maxChildren) || 120));
|
|
226
|
+
const response = await callAPI('evaluate', {
|
|
227
|
+
profileId,
|
|
228
|
+
script: buildDomSnapshotScript(maxDepth, maxChildren),
|
|
229
|
+
});
|
|
230
|
+
const payload = response?.result || response || {};
|
|
231
|
+
const tree = payload.dom_tree || null;
|
|
232
|
+
if (tree && payload.viewport && typeof payload.viewport === 'object') {
|
|
233
|
+
tree.__viewport = {
|
|
234
|
+
width: Number(payload.viewport.width) || 0,
|
|
235
|
+
height: Number(payload.viewport.height) || 0,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
return tree;
|
|
239
|
+
}
|
|
240
|
+
export async function getViewportByProfile(profileId) {
|
|
241
|
+
const response = await callAPI('evaluate', {
|
|
242
|
+
profileId,
|
|
243
|
+
script: `(() => ({ width: Number(window.innerWidth || 0), height: Number(window.innerHeight || 0) }))()`,
|
|
244
|
+
});
|
|
245
|
+
const viewport = response?.result || response?.viewport || {};
|
|
246
|
+
const width = Number(viewport?.width) || 1280;
|
|
247
|
+
const height = Number(viewport?.height) || 720;
|
|
248
|
+
return { width, height };
|
|
249
|
+
}
|
|
250
|
+
export async function checkBrowserService() {
|
|
251
|
+
try {
|
|
252
|
+
const r = await fetch(`${BROWSER_SERVICE_URL}/health`, { signal: AbortSignal.timeout(2000) });
|
|
253
|
+
return r.ok;
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function hasContainerLibrary(repoRoot) {
|
|
260
|
+
if (!repoRoot)
|
|
261
|
+
return false;
|
|
262
|
+
const root = path.resolve(String(repoRoot));
|
|
263
|
+
const candidate = path.join(root, 'apps', 'webauto', 'resources', 'container-library');
|
|
264
|
+
return fs.existsSync(candidate);
|
|
265
|
+
}
|
|
266
|
+
function walkUpForRepoRoot(startDir) {
|
|
267
|
+
if (!startDir)
|
|
268
|
+
return null;
|
|
269
|
+
let cursor = path.resolve(startDir);
|
|
270
|
+
for (;;) {
|
|
271
|
+
if (hasContainerLibrary(cursor))
|
|
272
|
+
return cursor;
|
|
273
|
+
const parent = path.dirname(cursor);
|
|
274
|
+
if (parent === cursor)
|
|
275
|
+
return null;
|
|
276
|
+
cursor = parent;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function scanCommonRepoRoots() {
|
|
280
|
+
const home = os.homedir();
|
|
281
|
+
const roots = [
|
|
282
|
+
path.join(home, 'Documents', 'github'),
|
|
283
|
+
path.join(home, 'github'),
|
|
284
|
+
path.join(home, 'code'),
|
|
285
|
+
path.join(home, 'projects'),
|
|
286
|
+
path.join('/Volumes', 'extension', 'code'),
|
|
287
|
+
path.join('C:', 'code'),
|
|
288
|
+
path.join('D:', 'code'),
|
|
289
|
+
path.join('C:', 'projects'),
|
|
290
|
+
path.join('D:', 'projects'),
|
|
291
|
+
path.join('C:', 'Users', os.userInfo().username, 'code'),
|
|
292
|
+
path.join('C:', 'Users', os.userInfo().username, 'projects'),
|
|
293
|
+
path.join('C:', 'Users', os.userInfo().username, 'Documents', 'github'),
|
|
294
|
+
].filter(Boolean);
|
|
295
|
+
for (const root of roots) {
|
|
296
|
+
if (!fs.existsSync(root))
|
|
297
|
+
continue;
|
|
298
|
+
try {
|
|
299
|
+
const entries = fs.readdirSync(root, { withFileTypes: true });
|
|
300
|
+
for (const entry of entries) {
|
|
301
|
+
if (!entry.isDirectory())
|
|
302
|
+
continue;
|
|
303
|
+
if (!entry.name.toLowerCase().includes('webauto'))
|
|
304
|
+
continue;
|
|
305
|
+
const candidate = path.join(root, entry.name);
|
|
306
|
+
if (hasContainerLibrary(candidate))
|
|
307
|
+
return candidate;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
// ignore scanning errors and continue
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
export function findRepoRootCandidate() {
|
|
317
|
+
const cfg = loadConfig();
|
|
318
|
+
const candidates = [
|
|
319
|
+
process.env.WEBAUTO_REPO_ROOT,
|
|
320
|
+
process.cwd(),
|
|
321
|
+
cfg.repoRoot,
|
|
322
|
+
path.join('/Volumes', 'extension', 'code', 'webauto'),
|
|
323
|
+
path.join('/Volumes', 'extension', 'code', 'WebAuto'),
|
|
324
|
+
path.join(os.homedir(), 'Documents', 'github', 'webauto'),
|
|
325
|
+
path.join(os.homedir(), 'Documents', 'github', 'WebAuto'),
|
|
326
|
+
path.join(os.homedir(), 'github', 'webauto'),
|
|
327
|
+
path.join(os.homedir(), 'github', 'WebAuto'),
|
|
328
|
+
path.join('C:', 'code', 'webauto'),
|
|
329
|
+
path.join('C:', 'code', 'WebAuto'),
|
|
330
|
+
path.join('C:', 'Users', os.userInfo().username, 'code', 'webauto'),
|
|
331
|
+
path.join('C:', 'Users', os.userInfo().username, 'code', 'WebAuto'),
|
|
332
|
+
].filter(Boolean);
|
|
333
|
+
for (const root of candidates) {
|
|
334
|
+
if (!hasContainerLibrary(root))
|
|
335
|
+
continue;
|
|
336
|
+
const resolved = path.resolve(String(root));
|
|
337
|
+
if (cfg.repoRoot !== resolved) {
|
|
338
|
+
setRepoRoot(resolved);
|
|
339
|
+
}
|
|
340
|
+
return resolved;
|
|
341
|
+
}
|
|
342
|
+
const walked = walkUpForRepoRoot(process.cwd());
|
|
343
|
+
if (walked) {
|
|
344
|
+
if (cfg.repoRoot !== walked) {
|
|
345
|
+
setRepoRoot(walked);
|
|
346
|
+
}
|
|
347
|
+
return walked;
|
|
348
|
+
}
|
|
349
|
+
const scanned = scanCommonRepoRoots();
|
|
350
|
+
if (scanned) {
|
|
351
|
+
if (cfg.repoRoot !== scanned) {
|
|
352
|
+
setRepoRoot(scanned);
|
|
353
|
+
}
|
|
354
|
+
return scanned;
|
|
355
|
+
}
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
export function detectCamoufoxPath() {
|
|
359
|
+
try {
|
|
360
|
+
const cmd = process.platform === 'win32' ? 'python -m camoufox path' : 'python3 -m camoufox path';
|
|
361
|
+
const out = execSync(cmd, {
|
|
362
|
+
encoding: 'utf8',
|
|
363
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
364
|
+
});
|
|
365
|
+
const lines = out.trim().split(/\r?\n/);
|
|
366
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
367
|
+
const line = lines[i].trim();
|
|
368
|
+
if (line && (line.startsWith('/') || line.match(/^[A-Z]:\\/)))
|
|
369
|
+
return line;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
export function ensureCamoufox() {
|
|
378
|
+
if (detectCamoufoxPath())
|
|
379
|
+
return;
|
|
380
|
+
console.log('Camoufox is not found. Installing...');
|
|
381
|
+
execSync('npx --yes --package=camoufox camoufox fetch', { stdio: 'inherit' });
|
|
382
|
+
if (!detectCamoufoxPath()) {
|
|
383
|
+
throw new Error('Camoufox install finished but executable was not detected');
|
|
384
|
+
}
|
|
385
|
+
console.log('Camoufox installed.');
|
|
386
|
+
}
|
|
387
|
+
export async function ensureBrowserService() {
|
|
388
|
+
if (await checkBrowserService())
|
|
389
|
+
return;
|
|
390
|
+
const provider = String(process.env.WEBAUTO_BROWSER_PROVIDER || 'camo').trim().toLowerCase();
|
|
391
|
+
if (provider === 'none' || provider === 'external') {
|
|
392
|
+
throw new Error(`Browser backend is not healthy at ${BROWSER_SERVICE_URL} (provider=${provider}). ` +
|
|
393
|
+
'Start backend manually or set WEBAUTO_BROWSER_PROVIDER=camo.');
|
|
394
|
+
}
|
|
395
|
+
if (provider === 'camo') {
|
|
396
|
+
const repoRoot = findRepoRootCandidate();
|
|
397
|
+
if (repoRoot) {
|
|
398
|
+
try {
|
|
399
|
+
execSync(`npx --yes @web-auto/camo config repo-root ${JSON.stringify(repoRoot)}`, { stdio: 'ignore' });
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
// best-effort only; init will still try using current config
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
console.log('Starting browser backend via camo init...');
|
|
407
|
+
execSync('npx --yes @web-auto/camo init', { stdio: 'inherit' });
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
throw new Error(`camo init failed: ${error?.message || String(error)}`);
|
|
411
|
+
}
|
|
412
|
+
for (let i = 0; i < 20; i += 1) {
|
|
413
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
414
|
+
if (await checkBrowserService()) {
|
|
415
|
+
console.log('Browser backend is ready (provider=camo).');
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
throw new Error('Browser backend failed to become healthy after camo init');
|
|
420
|
+
}
|
|
421
|
+
throw new Error(`Unsupported WEBAUTO_BROWSER_PROVIDER=${provider}; only "camo" is supported.`);
|
|
422
|
+
}
|
|
423
|
+
//# sourceMappingURL=browser-service.mjs.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
export const CONFIG_DIR = path.join(os.homedir(), '.webauto');
|
|
6
|
+
export const PROFILES_DIR = path.join(CONFIG_DIR, 'profiles');
|
|
7
|
+
export const CONFIG_FILE = path.join(CONFIG_DIR, 'camo-cli.json');
|
|
8
|
+
export const BROWSER_SERVICE_URL = process.env.WEBAUTO_BROWSER_URL || 'http://127.0.0.1:7704';
|
|
9
|
+
export function ensureDir(p) {
|
|
10
|
+
if (!fs.existsSync(p))
|
|
11
|
+
fs.mkdirSync(p, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
export function readJson(p) {
|
|
14
|
+
try {
|
|
15
|
+
if (!fs.existsSync(p))
|
|
16
|
+
return null;
|
|
17
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function writeJson(p, data) {
|
|
24
|
+
ensureDir(path.dirname(p));
|
|
25
|
+
fs.writeFileSync(p, JSON.stringify(data, null, 2));
|
|
26
|
+
}
|
|
27
|
+
export function loadConfig() {
|
|
28
|
+
const raw = readJson(CONFIG_FILE) || {};
|
|
29
|
+
return {
|
|
30
|
+
defaultProfile: typeof raw.defaultProfile === 'string' ? raw.defaultProfile : null,
|
|
31
|
+
repoRoot: typeof raw.repoRoot === 'string' ? raw.repoRoot : null,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function saveConfig(config) {
|
|
35
|
+
writeJson(CONFIG_FILE, config);
|
|
36
|
+
}
|
|
37
|
+
export function listProfiles() {
|
|
38
|
+
if (!fs.existsSync(PROFILES_DIR))
|
|
39
|
+
return [];
|
|
40
|
+
return fs.readdirSync(PROFILES_DIR, { withFileTypes: true })
|
|
41
|
+
.filter((d) => d.isDirectory())
|
|
42
|
+
.map((d) => d.name)
|
|
43
|
+
.filter((name) => !name.includes(':') && !name.includes('/') && !name.startsWith('.'))
|
|
44
|
+
.sort();
|
|
45
|
+
}
|
|
46
|
+
export function isValidProfileId(profileId) {
|
|
47
|
+
return typeof profileId === 'string' && /^[a-zA-Z0-9._-]+$/.test(profileId);
|
|
48
|
+
}
|
|
49
|
+
export function createProfile(profileId) {
|
|
50
|
+
if (!isValidProfileId(profileId)) {
|
|
51
|
+
throw new Error('Invalid profileId. Use only letters, numbers, dot, underscore, dash.');
|
|
52
|
+
}
|
|
53
|
+
const profileDir = path.join(PROFILES_DIR, profileId);
|
|
54
|
+
if (fs.existsSync(profileDir))
|
|
55
|
+
throw new Error(`Profile already exists: ${profileId}`);
|
|
56
|
+
ensureDir(profileDir);
|
|
57
|
+
}
|
|
58
|
+
export function deleteProfile(profileId) {
|
|
59
|
+
const profileDir = path.join(PROFILES_DIR, profileId);
|
|
60
|
+
if (!fs.existsSync(profileDir))
|
|
61
|
+
throw new Error(`Profile not found: ${profileId}`);
|
|
62
|
+
fs.rmSync(profileDir, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
export function setDefaultProfile(profileId) {
|
|
65
|
+
const cfg = loadConfig();
|
|
66
|
+
cfg.defaultProfile = profileId;
|
|
67
|
+
saveConfig(cfg);
|
|
68
|
+
}
|
|
69
|
+
export function setRepoRoot(repoRoot) {
|
|
70
|
+
const cfg = loadConfig();
|
|
71
|
+
cfg.repoRoot = repoRoot;
|
|
72
|
+
saveConfig(cfg);
|
|
73
|
+
}
|
|
74
|
+
export function getDefaultProfile() {
|
|
75
|
+
return loadConfig().defaultProfile;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=config.mjs.map
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
const PROJECT_ROOT = resolveProjectRoot(__dirname);
|
|
8
|
+
// 新的外置容器树根目录(开发用):~/.webauto/container-lib
|
|
9
|
+
const PRIMARY_USER_CONTAINER_ROOT = process.env.WEBAUTO_CONTAINER_ROOT || path.join(os.homedir(), '.webauto', 'container-lib');
|
|
10
|
+
const INDEX_PATH = path.join(PROJECT_ROOT, 'apps/webauto/resources/container-library.index.json');
|
|
11
|
+
function isLegacyContainer(definition) {
|
|
12
|
+
try {
|
|
13
|
+
return Boolean(definition?.metadata?.legacy_data);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class ContainerRegistry {
|
|
20
|
+
indexCache = null;
|
|
21
|
+
listSites() {
|
|
22
|
+
const registry = this.ensureIndex();
|
|
23
|
+
return Object.entries(registry).map(([key, meta]) => ({
|
|
24
|
+
key,
|
|
25
|
+
website: meta.website || '',
|
|
26
|
+
path: meta.path || '',
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
getContainersForSite(siteKey) {
|
|
30
|
+
if (!siteKey)
|
|
31
|
+
return {};
|
|
32
|
+
const registry = this.ensureIndex();
|
|
33
|
+
const site = registry[siteKey] || { path: path.join('apps/webauto/resources/container-library', siteKey) };
|
|
34
|
+
return this.fetchContainersForSite(siteKey, site);
|
|
35
|
+
}
|
|
36
|
+
resolveSiteKey(url) {
|
|
37
|
+
const registry = this.ensureIndex();
|
|
38
|
+
return this.findSiteKey(url, registry);
|
|
39
|
+
}
|
|
40
|
+
async load() {
|
|
41
|
+
// 兼容异步加载,但当前是同步实现
|
|
42
|
+
this.ensureIndex();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
getContainersForUrl(url) {
|
|
46
|
+
const registry = this.ensureIndex();
|
|
47
|
+
const siteKey = this.findSiteKey(url, registry);
|
|
48
|
+
if (!siteKey) {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
const site = registry[siteKey] || { path: `apps/webauto/resources/container-library/${siteKey}` };
|
|
52
|
+
return this.fetchContainersForSite(siteKey, site);
|
|
53
|
+
}
|
|
54
|
+
fetchContainersForSite(siteKey, site) {
|
|
55
|
+
// 不做内存级别缓存,确保用户容器定义变更后,每次调用都能读取到最新文件。
|
|
56
|
+
// 内部会同时加载内置容器与用户容器目录并合并。
|
|
57
|
+
return this.loadSiteContainers(siteKey, site?.path);
|
|
58
|
+
}
|
|
59
|
+
ensureIndex() {
|
|
60
|
+
if (this.indexCache) {
|
|
61
|
+
return this.indexCache;
|
|
62
|
+
}
|
|
63
|
+
if (fs.existsSync(INDEX_PATH)) {
|
|
64
|
+
try {
|
|
65
|
+
this.indexCache = (JSON.parse(fs.readFileSync(INDEX_PATH, 'utf-8')) || {});
|
|
66
|
+
return this.indexCache;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// fall through
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
this.indexCache = {};
|
|
73
|
+
return this.indexCache;
|
|
74
|
+
}
|
|
75
|
+
loadSiteContainers(siteKey, relativePath) {
|
|
76
|
+
const containers = {};
|
|
77
|
+
const builtinPath = path.join(PROJECT_ROOT, relativePath || path.join('apps/webauto/resources/container-library', siteKey));
|
|
78
|
+
if (fs.existsSync(builtinPath)) {
|
|
79
|
+
this.walkSite(builtinPath, containers);
|
|
80
|
+
this.loadLegacyFile(builtinPath, containers);
|
|
81
|
+
}
|
|
82
|
+
const userPath = path.join(PRIMARY_USER_CONTAINER_ROOT, siteKey);
|
|
83
|
+
if (fs.existsSync(userPath)) {
|
|
84
|
+
this.walkSite(userPath, containers);
|
|
85
|
+
this.loadLegacyFile(userPath, containers);
|
|
86
|
+
}
|
|
87
|
+
return containers;
|
|
88
|
+
}
|
|
89
|
+
walkSite(sitePath, output) {
|
|
90
|
+
const stack = [{ dir: sitePath, parts: [] }];
|
|
91
|
+
while (stack.length) {
|
|
92
|
+
const { dir, parts } = stack.pop();
|
|
93
|
+
let hasContainerFile = false;
|
|
94
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
95
|
+
if (entry.isFile() && entry.name === 'container.json') {
|
|
96
|
+
const relParts = parts.length ? parts : [path.basename(dir)];
|
|
97
|
+
const containerId = relParts.join('.');
|
|
98
|
+
try {
|
|
99
|
+
const raw = JSON.parse(fs.readFileSync(path.join(dir, entry.name), 'utf-8'));
|
|
100
|
+
if (raw && typeof raw === 'object') {
|
|
101
|
+
if (isLegacyContainer(raw)) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const id = raw.id || containerId;
|
|
105
|
+
output[id] = { id, ...raw };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// ignore malformed container
|
|
110
|
+
}
|
|
111
|
+
hasContainerFile = true;
|
|
112
|
+
}
|
|
113
|
+
else if (entry.isDirectory()) {
|
|
114
|
+
stack.push({ dir: path.join(dir, entry.name), parts: [...parts, entry.name] });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!hasContainerFile && parts.length === 0) {
|
|
118
|
+
// root dir may not contain direct containers, continue
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
loadLegacyFile(sitePath, output) {
|
|
124
|
+
const legacyFile = path.join(sitePath, 'containers.json');
|
|
125
|
+
if (!fs.existsSync(legacyFile)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const raw = JSON.parse(fs.readFileSync(legacyFile, 'utf-8'));
|
|
130
|
+
const containers = raw?.containers;
|
|
131
|
+
if (containers && typeof containers === 'object') {
|
|
132
|
+
for (const [key, value] of Object.entries(containers)) {
|
|
133
|
+
if (!output[key] && value && typeof value === 'object') {
|
|
134
|
+
if (isLegacyContainer(value)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
output[key] = { id: key, ...value };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// ignore legacy parse error
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
findSiteKey(url, registry) {
|
|
147
|
+
let host = '';
|
|
148
|
+
try {
|
|
149
|
+
const parsed = new URL(url);
|
|
150
|
+
host = (parsed.hostname || '').toLowerCase();
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
let bestKey = null;
|
|
156
|
+
let bestLen = -1;
|
|
157
|
+
for (const [key, value] of Object.entries(registry)) {
|
|
158
|
+
const domain = (value.website || '').toLowerCase();
|
|
159
|
+
if (!domain)
|
|
160
|
+
continue;
|
|
161
|
+
if (host === domain || host.endsWith(`.${domain}`)) {
|
|
162
|
+
if (domain.length > bestLen) {
|
|
163
|
+
bestKey = key;
|
|
164
|
+
bestLen = domain.length;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return bestKey;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function resolveProjectRoot(startDir) {
|
|
172
|
+
let current = startDir;
|
|
173
|
+
const { root } = path.parse(startDir);
|
|
174
|
+
while (true) {
|
|
175
|
+
if (fs.existsSync(path.join(current, 'package.json'))) {
|
|
176
|
+
return current;
|
|
177
|
+
}
|
|
178
|
+
if (current === root) {
|
|
179
|
+
return startDir;
|
|
180
|
+
}
|
|
181
|
+
current = path.resolve(current, '..');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=index.js.map
|