@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.
Files changed (354) hide show
  1. package/apps/desktop-console/default-settings.json +1 -0
  2. package/apps/desktop-console/dist/main/index.mjs +1618 -0
  3. package/apps/desktop-console/{src → dist}/main/preload.mjs +10 -0
  4. package/apps/desktop-console/dist/renderer/index.js +3063 -0
  5. package/apps/desktop-console/entry/ui-console.mjs +299 -0
  6. package/apps/webauto/entry/account.mjs +356 -0
  7. package/apps/webauto/entry/lib/account-detect.mjs +160 -0
  8. package/apps/webauto/entry/lib/account-store.mjs +587 -0
  9. package/apps/webauto/entry/lib/profilepool.mjs +1 -1
  10. package/apps/webauto/entry/xhs-install.mjs +27 -3
  11. package/apps/webauto/entry/xhs-status.mjs +152 -0
  12. package/apps/webauto/entry/xhs-unified.mjs +595 -17
  13. package/bin/webauto.mjs +247 -12
  14. package/dist/apps/webauto/server.js +66 -0
  15. package/dist/modules/camo-backend/src/index.js +575 -0
  16. package/dist/modules/camo-backend/src/internal/BrowserSession.js +817 -0
  17. package/dist/modules/camo-backend/src/internal/ElementRegistry.js +61 -0
  18. package/dist/modules/camo-backend/src/internal/ProfileLock.js +85 -0
  19. package/dist/modules/camo-backend/src/internal/SessionManager.js +172 -0
  20. package/dist/modules/camo-backend/src/internal/container-matcher.js +852 -0
  21. package/dist/modules/camo-backend/src/internal/engine-manager.js +258 -0
  22. package/dist/modules/camo-backend/src/internal/fingerprint.js +203 -0
  23. package/dist/modules/camo-backend/src/internal/pageRuntime.js +29 -0
  24. package/dist/modules/camo-backend/src/internal/runtimeInjector.js +30 -0
  25. package/dist/modules/camo-backend/src/internal/state-bus.js +46 -0
  26. package/dist/modules/camo-backend/src/internal/storage-paths.js +36 -0
  27. package/dist/modules/camo-backend/src/internal/ws-server.js +1202 -0
  28. package/dist/modules/camo-runtime/src/utils/browser-service.mjs +423 -0
  29. package/dist/modules/camo-runtime/src/utils/config.mjs +77 -0
  30. package/dist/modules/container-registry/src/index.js +184 -0
  31. package/dist/modules/logging/src/index.js +92 -0
  32. package/dist/modules/operations/src/builtin.js +27 -0
  33. package/dist/modules/operations/src/container-binding.js +75 -0
  34. package/dist/modules/operations/src/executor.js +146 -0
  35. package/dist/modules/operations/src/operations/click.js +167 -0
  36. package/dist/modules/operations/src/operations/extract.js +204 -0
  37. package/dist/modules/operations/src/operations/find-child.js +17 -0
  38. package/dist/modules/operations/src/operations/highlight.js +138 -0
  39. package/dist/modules/operations/src/operations/key.js +61 -0
  40. package/dist/modules/operations/src/operations/navigate.js +148 -0
  41. package/dist/modules/operations/src/operations/scroll.js +126 -0
  42. package/dist/modules/operations/src/operations/type.js +190 -0
  43. package/dist/modules/operations/src/queue.js +100 -0
  44. package/dist/modules/operations/src/registry.js +11 -0
  45. package/dist/modules/operations/src/system/mouse.js +33 -0
  46. package/dist/modules/state/src/atomic-json.js +33 -0
  47. package/dist/modules/workflow/blocks/AnchorVerificationBlock.js +71 -0
  48. package/dist/modules/workflow/blocks/BehaviorRandomizer.js +26 -0
  49. package/dist/modules/workflow/blocks/CallWorkflowBlock.js +38 -0
  50. package/dist/modules/workflow/blocks/CloseDetailBlock.js +209 -0
  51. package/dist/modules/workflow/blocks/CollectBatch.js +137 -0
  52. package/dist/modules/workflow/blocks/CollectCommentsBlock.js +415 -0
  53. package/dist/modules/workflow/blocks/CollectSearchListBlock.js +599 -0
  54. package/dist/modules/workflow/blocks/CollectWeiboPosts.js +229 -0
  55. package/dist/modules/workflow/blocks/DetectPageStateBlock.js +259 -0
  56. package/dist/modules/workflow/blocks/EnsureLoginBlock.js +162 -0
  57. package/dist/modules/workflow/blocks/EnsureSession.js +426 -0
  58. package/dist/modules/workflow/blocks/ErrorClassifier.js +164 -0
  59. package/dist/modules/workflow/blocks/ErrorRecoveryBlock.js +319 -0
  60. package/dist/modules/workflow/blocks/ExpandCommentsBlock.js +1032 -0
  61. package/dist/modules/workflow/blocks/ExtractDetailBlock.js +310 -0
  62. package/dist/modules/workflow/blocks/ExtractPostFields.js +88 -0
  63. package/dist/modules/workflow/blocks/GenerateSmartReplyBlock.js +68 -0
  64. package/dist/modules/workflow/blocks/GoToSearchBlock.js +497 -0
  65. package/dist/modules/workflow/blocks/GracefulFallbackBlock.js +104 -0
  66. package/dist/modules/workflow/blocks/HighlightBlock.js +66 -0
  67. package/dist/modules/workflow/blocks/InitAutoScroll.js +65 -0
  68. package/dist/modules/workflow/blocks/LoadContainerDefinition.js +50 -0
  69. package/dist/modules/workflow/blocks/LoadContainerIndex.js +43 -0
  70. package/dist/modules/workflow/blocks/LocateAndGuardBlock.js +176 -0
  71. package/dist/modules/workflow/blocks/LoginRecoveryBlock.js +242 -0
  72. package/dist/modules/workflow/blocks/MatchContainers.js +64 -0
  73. package/dist/modules/workflow/blocks/MonitoringBlock.js +190 -0
  74. package/dist/modules/workflow/blocks/OpenDetailBlock.js +1240 -0
  75. package/dist/modules/workflow/blocks/OrganizeXhsNotesBlock.js +117 -0
  76. package/dist/modules/workflow/blocks/PersistXhsNoteBlock.js +270 -0
  77. package/dist/modules/workflow/blocks/PickSinglePost.js +69 -0
  78. package/dist/modules/workflow/blocks/ProgressTracker.js +125 -0
  79. package/dist/modules/workflow/blocks/RecordFixtureBlock.js +44 -0
  80. package/dist/modules/workflow/blocks/RenderMarkdown.js +48 -0
  81. package/dist/modules/workflow/blocks/SaveFile.js +54 -0
  82. package/dist/modules/workflow/blocks/ScrollNextBatch.js +72 -0
  83. package/dist/modules/workflow/blocks/SessionHealthBlock.js +73 -0
  84. package/dist/modules/workflow/blocks/StartBrowserService.js +45 -0
  85. package/dist/modules/workflow/blocks/ValidateContainerDefinition.js +67 -0
  86. package/dist/modules/workflow/blocks/ValidateExtract.js +35 -0
  87. package/dist/modules/workflow/blocks/WaitSearchPermitBlock.js +162 -0
  88. package/dist/modules/workflow/blocks/WaitStable.js +74 -0
  89. package/dist/modules/workflow/blocks/WarmupCommentsBlock.js +120 -0
  90. package/dist/modules/workflow/blocks/WorkflowExecutor.js +156 -0
  91. package/dist/modules/workflow/blocks/XiaohongshuCollectFromLinksBlock.js +1004 -0
  92. package/dist/modules/workflow/blocks/XiaohongshuCollectLinksBlock.js +1049 -0
  93. package/dist/modules/workflow/blocks/XiaohongshuFullCollectBlock.js +782 -0
  94. package/dist/modules/workflow/blocks/helpers/anchorVerify.js +198 -0
  95. package/dist/modules/workflow/blocks/helpers/asyncWorkQueue.js +53 -0
  96. package/dist/modules/workflow/blocks/helpers/commentScroller.js +334 -0
  97. package/dist/modules/workflow/blocks/helpers/commentSectionLocator.js +126 -0
  98. package/dist/modules/workflow/blocks/helpers/containerAnchors.js +301 -0
  99. package/dist/modules/workflow/blocks/helpers/debugArtifacts.js +6 -0
  100. package/dist/modules/workflow/blocks/helpers/downloadPaths.js +29 -0
  101. package/dist/modules/workflow/blocks/helpers/expandCommentsController.js +53 -0
  102. package/dist/modules/workflow/blocks/helpers/expandCommentsExtractor.js +129 -0
  103. package/dist/modules/workflow/blocks/helpers/macosVisionOcrPlugin.js +116 -0
  104. package/dist/modules/workflow/blocks/helpers/mergeXhsMarkdown.js +109 -0
  105. package/dist/modules/workflow/blocks/helpers/openDetailController.js +56 -0
  106. package/dist/modules/workflow/blocks/helpers/openDetailTypes.js +7 -0
  107. package/dist/modules/workflow/blocks/helpers/openDetailViewport.js +474 -0
  108. package/dist/modules/workflow/blocks/helpers/openDetailWaiter.js +104 -0
  109. package/dist/modules/workflow/blocks/helpers/operationLogger.js +195 -0
  110. package/dist/modules/workflow/blocks/helpers/persistedNotes.js +107 -0
  111. package/dist/modules/workflow/blocks/helpers/replyExpander.js +260 -0
  112. package/dist/modules/workflow/blocks/helpers/scrollIntoView.js +138 -0
  113. package/dist/modules/workflow/blocks/helpers/searchExecutor.js +328 -0
  114. package/dist/modules/workflow/blocks/helpers/searchGate.js +46 -0
  115. package/dist/modules/workflow/blocks/helpers/searchPageState.js +164 -0
  116. package/dist/modules/workflow/blocks/helpers/searchResultWaiter.js +64 -0
  117. package/dist/modules/workflow/blocks/helpers/simpleAnchor.js +134 -0
  118. package/dist/modules/workflow/blocks/helpers/smartReply.js +40 -0
  119. package/dist/modules/workflow/blocks/helpers/systemInput.js +635 -0
  120. package/dist/modules/workflow/blocks/helpers/targetCountMode.js +9 -0
  121. package/dist/modules/workflow/blocks/helpers/xhsCliArgs.js +80 -0
  122. package/dist/modules/workflow/blocks/helpers/xhsCommentDom.js +805 -0
  123. package/dist/modules/workflow/blocks/helpers/xhsNoteOrganizer.js +140 -0
  124. package/dist/modules/workflow/blocks/restore/RestorePhaseBlock.js +204 -0
  125. package/dist/modules/workflow/config/workflowRegistry.js +32 -0
  126. package/dist/modules/workflow/definitions/batch-collect-workflow.js +63 -0
  127. package/dist/modules/workflow/definitions/scroll-extract-workflow.js +74 -0
  128. package/dist/modules/workflow/definitions/xiaohongshu-collect-workflow-v2.js +81 -0
  129. package/dist/modules/workflow/definitions/xiaohongshu-collect-workflow.js +57 -0
  130. package/dist/modules/workflow/definitions/xiaohongshu-full-collect-workflow-v3.js +68 -0
  131. package/dist/modules/workflow/definitions/xiaohongshu-note-collect.js +49 -0
  132. package/dist/modules/workflow/definitions/xiaohongshu-phase1-workflow-v3.js +30 -0
  133. package/dist/modules/workflow/definitions/xiaohongshu-phase2-links-workflow-v3.js +40 -0
  134. package/dist/modules/workflow/definitions/xiaohongshu-phase3-collect-workflow-v1.js +54 -0
  135. package/dist/modules/workflow/definitions/xiaohongshu-phase34-from-links-workflow-v3.js +25 -0
  136. package/dist/modules/workflow/src/WeiboEventDrivenWorkflowRunner.js +308 -0
  137. package/dist/modules/workflow/src/context.js +70 -0
  138. package/dist/modules/workflow/src/index.js +5 -0
  139. package/dist/modules/workflow/src/orchestrator.js +230 -0
  140. package/dist/modules/workflow/src/runner.js +55 -0
  141. package/dist/modules/workflow/src/runtime.js +70 -0
  142. package/dist/modules/workflow/workflows/WeiboFeedExtractionWorkflow.js +359 -0
  143. package/dist/modules/workflow/workflows/XiaohongshuLoginWorkflow.js +110 -0
  144. package/dist/modules/xiaohongshu/app/src/blocks/MatchCommentsBlock.js +139 -0
  145. package/dist/modules/xiaohongshu/app/src/blocks/Phase1EnsureServicesBlock.js +36 -0
  146. package/dist/modules/xiaohongshu/app/src/blocks/Phase1MonitorCookieBlock.js +213 -0
  147. package/dist/modules/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.js +121 -0
  148. package/dist/modules/xiaohongshu/app/src/blocks/Phase2CollectLinksBlock.js +1249 -0
  149. package/dist/modules/xiaohongshu/app/src/blocks/Phase2SearchBlock.js +703 -0
  150. package/dist/modules/xiaohongshu/app/src/blocks/Phase34CloseDetailBlock.js +41 -0
  151. package/dist/modules/xiaohongshu/app/src/blocks/Phase34CloseTabsBlock.js +44 -0
  152. package/dist/modules/xiaohongshu/app/src/blocks/Phase34CollectCommentsBlock.js +150 -0
  153. package/dist/modules/xiaohongshu/app/src/blocks/Phase34ExtractDetailBlock.js +117 -0
  154. package/dist/modules/xiaohongshu/app/src/blocks/Phase34OpenDetailBlock.js +102 -0
  155. package/dist/modules/xiaohongshu/app/src/blocks/Phase34OpenTabsBlock.js +109 -0
  156. package/dist/modules/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.js +117 -0
  157. package/dist/modules/xiaohongshu/app/src/blocks/Phase34ProcessSingleNoteBlock.js +114 -0
  158. package/dist/modules/xiaohongshu/app/src/blocks/Phase34ValidateLinksBlock.js +90 -0
  159. package/dist/modules/xiaohongshu/app/src/blocks/Phase3InteractBlock.js +1009 -0
  160. package/dist/modules/xiaohongshu/app/src/blocks/Phase4MultiTabHarvestBlock.js +233 -0
  161. package/dist/modules/xiaohongshu/app/src/blocks/ReplyInteractBlock.js +291 -0
  162. package/dist/modules/xiaohongshu/app/src/blocks/XhsDiscoverFallbackBlock.js +240 -0
  163. package/dist/modules/xiaohongshu/app/src/blocks/helpers/commentMatchDsl.js +126 -0
  164. package/dist/modules/xiaohongshu/app/src/blocks/helpers/commentMatcher.js +99 -0
  165. package/dist/modules/xiaohongshu/app/src/blocks/helpers/evidence.js +27 -0
  166. package/dist/modules/xiaohongshu/app/src/blocks/helpers/sharding.js +42 -0
  167. package/dist/modules/xiaohongshu/app/src/blocks/helpers/xhsComments.js +270 -0
  168. package/dist/modules/xiaohongshu/app/src/index.js +9 -0
  169. package/dist/modules/xiaohongshu/app/src/utils/checkpoints.js +222 -0
  170. package/dist/modules/xiaohongshu/app/src/utils/controllerAction.js +43 -0
  171. package/dist/services/controller/src/controller.js +1476 -0
  172. package/dist/services/controller/src/index.js +2 -0
  173. package/dist/services/controller/src/payload-normalizer.js +129 -0
  174. package/dist/services/shared/heartbeat.js +120 -0
  175. package/dist/services/shared/lib/errorHandler.js +2 -0
  176. package/dist/services/shared/serviceProcessLogger.js +139 -0
  177. package/dist/services/unified-api/RemoteBrowserSession.js +176 -0
  178. package/dist/services/unified-api/RemoteSessionManager.js +148 -0
  179. package/dist/services/unified-api/container-operations-handler.js +115 -0
  180. package/dist/services/unified-api/server.js +652 -0
  181. package/dist/services/unified-api/state-registry.js +274 -0
  182. package/dist/services/unified-api/task-persistence.js +66 -0
  183. package/dist/services/unified-api/task-state.js +130 -0
  184. package/modules/camo-runtime/src/autoscript/action-providers/xhs/search.mjs +12 -5
  185. package/modules/xiaohongshu/app/pnpm-lock.yaml +24 -0
  186. package/package.json +37 -9
  187. package/.beads/README.md +0 -81
  188. package/.beads/config.yaml +0 -67
  189. package/.beads/interactions.jsonl +0 -0
  190. package/.beads/issues.jsonl +0 -180
  191. package/.beads/metadata.json +0 -4
  192. package/.claude/settings.local.json +0 -10
  193. package/.github/workflows/ci.yml +0 -55
  194. package/AGENTS.md +0 -253
  195. package/apps/desktop-console/README.md +0 -27
  196. package/apps/desktop-console/package-lock.json +0 -897
  197. package/apps/desktop-console/package.json +0 -20
  198. package/apps/desktop-console/scripts/build-and-install.mjs +0 -19
  199. package/apps/desktop-console/scripts/build.mjs +0 -45
  200. package/apps/desktop-console/scripts/test-preload.mjs +0 -13
  201. package/apps/desktop-console/src/main/config.mts +0 -26
  202. package/apps/desktop-console/src/main/core-daemon-manager.mts +0 -131
  203. package/apps/desktop-console/src/main/desktop-settings.mts +0 -267
  204. package/apps/desktop-console/src/main/heartbeat-watchdog.mts +0 -50
  205. package/apps/desktop-console/src/main/heartbeat-watchdog.test.mts +0 -68
  206. package/apps/desktop-console/src/main/index-streaming.test.mts +0 -20
  207. package/apps/desktop-console/src/main/index.mts +0 -980
  208. package/apps/desktop-console/src/main/profile-store.mts +0 -239
  209. package/apps/desktop-console/src/main/profile-store.test.mts +0 -54
  210. package/apps/desktop-console/src/main/state-bridge.mts +0 -114
  211. package/apps/desktop-console/src/main/task-state-types.ts +0 -32
  212. package/apps/desktop-console/src/renderer/hooks/use-task-state.mts +0 -120
  213. package/apps/desktop-console/src/renderer/index.mts +0 -133
  214. package/apps/desktop-console/src/renderer/index.test.mts +0 -34
  215. package/apps/desktop-console/src/renderer/path-helpers.mts +0 -46
  216. package/apps/desktop-console/src/renderer/path-helpers.test.mts +0 -14
  217. package/apps/desktop-console/src/renderer/tabs/debug.mts +0 -48
  218. package/apps/desktop-console/src/renderer/tabs/debug.test.mts +0 -22
  219. package/apps/desktop-console/src/renderer/tabs/logs.mts +0 -421
  220. package/apps/desktop-console/src/renderer/tabs/logs.test.mts +0 -27
  221. package/apps/desktop-console/src/renderer/tabs/preflight.mts +0 -486
  222. package/apps/desktop-console/src/renderer/tabs/preflight.test.mts +0 -33
  223. package/apps/desktop-console/src/renderer/tabs/profile-pool.mts +0 -213
  224. package/apps/desktop-console/src/renderer/tabs/results.mts +0 -171
  225. package/apps/desktop-console/src/renderer/tabs/run.test.mts +0 -63
  226. package/apps/desktop-console/src/renderer/tabs/runtime.mts +0 -151
  227. package/apps/desktop-console/src/renderer/tabs/settings.mts +0 -146
  228. package/apps/desktop-console/src/renderer/tabs/xiaohongshu/account-flow.mts +0 -486
  229. package/apps/desktop-console/src/renderer/tabs/xiaohongshu/guide-browser-check.mts +0 -56
  230. package/apps/desktop-console/src/renderer/tabs/xiaohongshu/helpers.mts +0 -262
  231. package/apps/desktop-console/src/renderer/tabs/xiaohongshu/layout-block.mts +0 -430
  232. package/apps/desktop-console/src/renderer/tabs/xiaohongshu/live-stats.mts +0 -847
  233. package/apps/desktop-console/src/renderer/tabs/xiaohongshu/run-flow.mts +0 -443
  234. package/apps/desktop-console/src/renderer/tabs/xiaohongshu-state.mts +0 -425
  235. package/apps/desktop-console/src/renderer/tabs/xiaohongshu.mts +0 -497
  236. package/apps/desktop-console/src/renderer/tabs/xiaohongshu.test.mts +0 -291
  237. package/apps/desktop-console/src/renderer/ui-components.mts +0 -31
  238. package/docs/README_camoufox_chinese.md +0 -141
  239. package/docs/USAGE_V3.md +0 -163
  240. package/docs/arch/OCR_MACOS_PLUGIN.md +0 -39
  241. package/docs/arch/PORTS.md +0 -40
  242. package/docs/arch/REGRESSION_CHECKLIST.md +0 -121
  243. package/docs/arch/SEARCH_GATE.md +0 -224
  244. package/docs/arch/VIEWPORT_SAFETY.md +0 -182
  245. package/docs/arch/XIAOHONGSHU_OFFLINE_MOCK_DESIGN.md +0 -267
  246. package/docs/xiaohongshu-container-driven-summary.md +0 -221
  247. package/docs/xiaohongshu-full-collect-runbook.md +0 -134
  248. package/docs/xiaohongshu-next-steps.md +0 -228
  249. package/docs/xiaohongshu-quickstart.md +0 -73
  250. package/docs/xiaohongshu-workflow-summary.md +0 -227
  251. package/modules/container-registry/tests/container-registry.test.ts +0 -16
  252. package/modules/logging/tests/logging.test.ts +0 -38
  253. package/modules/operations/tests/operations.test.ts +0 -22
  254. package/modules/operations/tests/viewport-filter.test.ts +0 -161
  255. package/modules/operations/tests/visible-only.test.ts +0 -250
  256. package/modules/session-manager/tests/session-manager.test.ts +0 -23
  257. package/modules/state/src/atomic-json.test.ts +0 -30
  258. package/modules/state/src/paths.test.ts +0 -59
  259. package/modules/state/src/xiaohongshu-collect-state.test.ts +0 -259
  260. package/modules/workflow/blocks/AnchorVerificationBlock.d.ts.map +0 -1
  261. package/modules/workflow/blocks/AnchorVerificationBlock.js.map +0 -1
  262. package/modules/workflow/blocks/DetectPageStateBlock.d.ts.map +0 -1
  263. package/modules/workflow/blocks/DetectPageStateBlock.js.map +0 -1
  264. package/modules/workflow/blocks/ErrorRecoveryBlock.d.ts.map +0 -1
  265. package/modules/workflow/blocks/ErrorRecoveryBlock.js.map +0 -1
  266. package/modules/workflow/blocks/WaitSearchPermitBlock.d.ts.map +0 -1
  267. package/modules/workflow/blocks/WaitSearchPermitBlock.js.map +0 -1
  268. package/modules/workflow/blocks/helpers/containerAnchors.d.ts.map +0 -1
  269. package/modules/workflow/blocks/helpers/containerAnchors.js.map +0 -1
  270. package/modules/workflow/blocks/helpers/downloadPaths.test.ts +0 -62
  271. package/modules/workflow/blocks/helpers/mergeXhsMarkdown.test.ts +0 -121
  272. package/modules/workflow/blocks/helpers/operationLogger.d.ts.map +0 -1
  273. package/modules/workflow/blocks/helpers/operationLogger.js.map +0 -1
  274. package/modules/workflow/blocks/helpers/persistedNotes.test.ts +0 -268
  275. package/modules/workflow/blocks/helpers/searchPageState.d.ts.map +0 -1
  276. package/modules/workflow/blocks/helpers/searchPageState.js.map +0 -1
  277. package/modules/workflow/blocks/helpers/targetCountMode.test.ts +0 -29
  278. package/modules/workflow/blocks/helpers/xhsCliArgs.test.ts +0 -75
  279. package/modules/workflow/tests/smartReply.test.ts +0 -32
  280. package/modules/xiaohongshu/app/src/blocks/Phase3Interact.matcher.test.ts +0 -33
  281. package/modules/xiaohongshu/app/src/utils/__tests__/checkpoints.test.ts +0 -141
  282. package/modules/xiaohongshu/app/tests/commentMatchDsl.test.ts +0 -50
  283. package/modules/xiaohongshu/app/tests/commentMatcher.test.ts +0 -46
  284. package/modules/xiaohongshu/app/tests/sharding.test.ts +0 -31
  285. package/package-scripts.json +0 -8
  286. package/runtime/infra/utils/README.md +0 -13
  287. package/runtime/infra/utils/scripts/README.md +0 -0
  288. package/runtime/infra/utils/scripts/development/eval-in-session.mjs +0 -40
  289. package/runtime/infra/utils/scripts/development/highlight-search-containers.mjs +0 -35
  290. package/runtime/infra/utils/scripts/service/kill-port.mjs +0 -24
  291. package/runtime/infra/utils/scripts/service/start-api.mjs +0 -39
  292. package/runtime/infra/utils/scripts/service/start-browser-service.mjs +0 -106
  293. package/runtime/infra/utils/scripts/service/stop-api.mjs +0 -18
  294. package/runtime/infra/utils/scripts/service/stop-browser-service.mjs +0 -104
  295. package/runtime/infra/utils/scripts/test-services.mjs +0 -94
  296. package/services/shared/heartbeat.test.ts +0 -102
  297. package/services/unified-api/__tests__/task-state.test.ts +0 -95
  298. package/sitecustomize.py +0 -19
  299. package/tests/README.md +0 -194
  300. package/tests/e2e/workflows/weibo-feed-extraction.test.ts +0 -171
  301. package/tests/fixtures/data/container-definitions.json +0 -67
  302. package/tests/fixtures/pages/simple-page.html +0 -69
  303. package/tests/integration/01-test-container-match.mjs +0 -188
  304. package/tests/integration/02-test-dom-branch.mjs +0 -161
  305. package/tests/integration/03-test-container-operation-system.mjs +0 -91
  306. package/tests/integration/05-test-container-lifecycle-events.mjs +0 -224
  307. package/tests/integration/05-test-container-lifecycle-with-events.mjs +0 -250
  308. package/tests/integration/06-test-container-dom-tree-drawing.mjs +0 -256
  309. package/tests/integration/07-test-weibo-container-lifecycle.mjs +0 -355
  310. package/tests/integration/08-test-weibo-feed-workflow.test.mjs +0 -164
  311. package/tests/integration/10-test-visual-analyzer.mjs +0 -312
  312. package/tests/integration/11-test-visual-loop.mjs +0 -284
  313. package/tests/integration/12-test-simple-visual-loop.mjs +0 -242
  314. package/tests/integration/13-test-visual-robust.mjs +0 -185
  315. package/tests/integration/14-test-visual-highlight-loop.mjs +0 -271
  316. package/tests/integration/inspect-page.mjs +0 -50
  317. package/tests/integration/run-all-tests.mjs +0 -95
  318. package/tests/patch_verification/CODEX_PATCH_TEST.md +0 -103
  319. package/tests/patch_verification/PHASE2_ANALYSIS.md +0 -179
  320. package/tests/patch_verification/PHASE2_OPTIMIZATION_REPORT.md +0 -55
  321. package/tests/patch_verification/PHASE2_TO_PHASE4_SUMMARY.md +0 -126
  322. package/tests/patch_verification/QUICK_TEST_SEQUENCE.md +0 -262
  323. package/tests/patch_verification/README.md +0 -143
  324. package/tests/patch_verification/RUN_TESTS.md +0 -60
  325. package/tests/patch_verification/TEST_EXECUTION.md +0 -99
  326. package/tests/patch_verification/TEST_PLAN.md +0 -328
  327. package/tests/patch_verification/TEST_RESULTS.md +0 -34
  328. package/tests/patch_verification/TOOL_TEST_PLAN.md +0 -48
  329. package/tests/patch_verification/run-tool-test.mjs +0 -121
  330. package/tests/patch_verification/temp_test_files/test01.txt +0 -1
  331. package/tests/patch_verification/temp_test_files/test02.txt +0 -3
  332. package/tests/patch_verification/temp_test_files/test02_gnu.txt +0 -3
  333. package/tests/patch_verification/temp_test_files/test03.txt +0 -1
  334. package/tests/patch_verification/temp_test_files/test03_multiline.txt +0 -5
  335. package/tests/patch_verification/temp_test_files/test04_function.ts +0 -5
  336. package/tests/patch_verification/temp_test_files/test05_import.ts +0 -4
  337. package/tests/patch_verification/temp_test_files/test06_special_chars.txt +0 -4
  338. package/tests/patch_verification/temp_test_files/test07_indentation.ts +0 -5
  339. package/tests/patch_verification/temp_test_files/test08_mismatch.txt +0 -1
  340. package/tests/patch_verification/temp_test_files/test_add_02.txt +0 -3
  341. package/tests/patch_verification/temp_test_files/test_simple.txt +0 -1
  342. package/tests/runner/TestReporter.mjs +0 -57
  343. package/tests/runner/TestRunner.mjs +0 -244
  344. package/tests/unit/commands/profile.test.mjs +0 -10
  345. package/tests/unit/container/change-notifier.test.mjs +0 -181
  346. package/tests/unit/lifecycle/session-registry.test.mjs +0 -135
  347. package/tests/unit/operations/registry.test.ts +0 -73
  348. package/tests/unit/utils/browser-service.test.mjs +0 -153
  349. package/tests/unit/utils/config.test.mjs +0 -166
  350. package/tests/unit/utils/fingerprint.test.mjs +0 -166
  351. package/tsconfig.json +0 -31
  352. package/tsconfig.services.json +0 -26
  353. /package/apps/desktop-console/{src → dist}/renderer/index.html +0 -0
  354. /package/apps/desktop-console/{src/renderer/tabs → dist/renderer}/run.mts +0 -0
@@ -0,0 +1,195 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ const LOG_ROOT = path.join(os.homedir(), '.webauto', 'logs');
5
+ const OPS_LOG_FILE = path.join(LOG_ROOT, 'ops.jsonl');
6
+ const ERR_LOG_FILE = path.join(LOG_ROOT, 'ops-errors.jsonl');
7
+ const MAX_RECENT = 50;
8
+ let seq = 0;
9
+ const recentOps = [];
10
+ let logReady = false;
11
+ function ensureLogDir() {
12
+ if (logReady)
13
+ return;
14
+ try {
15
+ fs.mkdirSync(LOG_ROOT, { recursive: true });
16
+ }
17
+ catch {
18
+ // ignore
19
+ }
20
+ logReady = true;
21
+ }
22
+ function nextId() {
23
+ seq += 1;
24
+ return seq;
25
+ }
26
+ function truncateString(value, maxLen = 400) {
27
+ if (value.length <= maxLen)
28
+ return value;
29
+ return `${value.slice(0, maxLen)}...[len=${value.length}]`;
30
+ }
31
+ function shouldOmitKey(key) {
32
+ const k = key.toLowerCase();
33
+ return (k.includes('script') ||
34
+ k.includes('html') ||
35
+ k.includes('cookie') ||
36
+ k.includes('buffer') ||
37
+ k.includes('base64') ||
38
+ k.includes('screenshot') ||
39
+ k.includes('image'));
40
+ }
41
+ function summarizeValue(value, depth = 0) {
42
+ if (depth > 3)
43
+ return '[max-depth]';
44
+ if (value === null || value === undefined)
45
+ return value;
46
+ if (typeof value === 'string')
47
+ return truncateString(value);
48
+ if (typeof value === 'number' || typeof value === 'boolean')
49
+ return value;
50
+ if (Array.isArray(value)) {
51
+ const len = value.length;
52
+ if (len === 0)
53
+ return [];
54
+ if (len <= 5)
55
+ return value.map((v) => summarizeValue(v, depth + 1));
56
+ return {
57
+ _type: 'array',
58
+ length: len,
59
+ sample: value.slice(0, 2).map((v) => summarizeValue(v, depth + 1)),
60
+ };
61
+ }
62
+ if (typeof value === 'object') {
63
+ const out = {};
64
+ const keys = Object.keys(value);
65
+ let count = 0;
66
+ for (const key of keys) {
67
+ if (count >= 20)
68
+ break;
69
+ count += 1;
70
+ if (shouldOmitKey(key)) {
71
+ out[key] = '[omitted]';
72
+ continue;
73
+ }
74
+ out[key] = summarizeValue(value[key], depth + 1);
75
+ }
76
+ if (keys.length > count)
77
+ out._more = keys.length - count;
78
+ return out;
79
+ }
80
+ return String(value);
81
+ }
82
+ function summarizeRecent(entry) {
83
+ return {
84
+ opId: entry.opId,
85
+ kind: entry.kind,
86
+ action: entry.action,
87
+ sessionId: entry.sessionId,
88
+ context: entry.context,
89
+ reason: entry.reason,
90
+ };
91
+ }
92
+ function pushRecent(entry) {
93
+ recentOps.push(entry);
94
+ if (recentOps.length > MAX_RECENT) {
95
+ recentOps.splice(0, recentOps.length - MAX_RECENT);
96
+ }
97
+ }
98
+ export function logOperation(entry) {
99
+ ensureLogDir();
100
+ const opId = entry.opId ?? nextId();
101
+ const record = {
102
+ ts: new Date().toISOString(),
103
+ opId,
104
+ kind: entry.kind,
105
+ action: entry.action || null,
106
+ sessionId: entry.sessionId || null,
107
+ context: entry.context || null,
108
+ reason: entry.reason || null,
109
+ payload: entry.payload ? summarizeValue(entry.payload) : null,
110
+ result: entry.result ? summarizeValue(entry.result) : null,
111
+ target: entry.target ? summarizeValue(entry.target) : null,
112
+ meta: entry.meta ? summarizeValue(entry.meta) : null,
113
+ };
114
+ try {
115
+ fs.appendFileSync(OPS_LOG_FILE, `${JSON.stringify(record)}\n`);
116
+ }
117
+ catch {
118
+ // ignore
119
+ }
120
+ pushRecent(record);
121
+ return opId;
122
+ }
123
+ export function logError(entry) {
124
+ ensureLogDir();
125
+ const opId = entry.opId ?? nextId();
126
+ const record = {
127
+ ts: new Date().toISOString(),
128
+ opId,
129
+ kind: entry.kind,
130
+ action: entry.action || null,
131
+ sessionId: entry.sessionId || null,
132
+ context: entry.context || null,
133
+ reason: entry.reason || null,
134
+ error: entry.error || null,
135
+ payload: entry.payload ? summarizeValue(entry.payload) : null,
136
+ meta: entry.meta ? summarizeValue(entry.meta) : null,
137
+ recent: recentOps.map(summarizeRecent),
138
+ };
139
+ try {
140
+ fs.appendFileSync(ERR_LOG_FILE, `${JSON.stringify(record)}\n`);
141
+ }
142
+ catch {
143
+ // ignore
144
+ }
145
+ return opId;
146
+ }
147
+ export function logControllerActionStart(action, payload, meta = {}) {
148
+ const sessionId = payload?.profileId || payload?.profile || payload?.sessionId || null;
149
+ const reason = payload?.reason ||
150
+ payload?.context ||
151
+ payload?.operationId ||
152
+ payload?.actionId ||
153
+ null;
154
+ const target = payload?.containerId
155
+ ? {
156
+ containerId: payload.containerId,
157
+ operationId: payload?.operationId || null,
158
+ index: payload?.index ?? null,
159
+ }
160
+ : payload?.x !== undefined && payload?.y !== undefined
161
+ ? { x: payload.x, y: payload.y }
162
+ : payload?.coordinates
163
+ ? { coordinates: payload.coordinates }
164
+ : null;
165
+ return logOperation({
166
+ kind: 'controller_action',
167
+ action,
168
+ sessionId,
169
+ reason,
170
+ target,
171
+ payload,
172
+ meta,
173
+ });
174
+ }
175
+ export function logControllerActionResult(opId, action, result, meta = {}) {
176
+ logOperation({
177
+ kind: 'controller_result',
178
+ action,
179
+ opId,
180
+ result,
181
+ meta,
182
+ });
183
+ }
184
+ export function logControllerActionError(opId, action, error, payload, meta = {}) {
185
+ const err = error instanceof Error ? error.message : String(error);
186
+ logError({
187
+ kind: 'controller_error',
188
+ action,
189
+ opId,
190
+ error: err,
191
+ payload,
192
+ meta,
193
+ });
194
+ }
195
+ //# sourceMappingURL=operationLogger.js.map
@@ -0,0 +1,107 @@
1
+ import path from 'node:path';
2
+ import { promises as fs } from 'node:fs';
3
+ import { resolveKeywordDir } from './downloadPaths.js';
4
+ async function pathExists(filepath) {
5
+ try {
6
+ await fs.access(filepath);
7
+ return true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ export async function countPersistedNotes(input) {
14
+ const { platform, env, keyword, homeDir, downloadRoot, requiredFiles = ['content.md'], requireCommentsDone = false, minCommentsCoverageRatio, } = input;
15
+ const keywordDir = resolveKeywordDir({ platform, env, keyword, homeDir, downloadRoot });
16
+ const exists = await pathExists(keywordDir);
17
+ if (!exists) {
18
+ return { keywordDir, noteIds: [], count: 0 };
19
+ }
20
+ const entries = await fs.readdir(keywordDir, { withFileTypes: true }).catch(() => []);
21
+ const noteIds = [];
22
+ function parseCommentsStats(text) {
23
+ // PersistXhsNoteBlock 写入格式:抓取=10, header=123(reachedEnd=是, empty=否, coverage=...)
24
+ const m = text.match(/评论统计:\s*抓取=(\d+),\s*header=([^(\n]+)/);
25
+ if (!m)
26
+ return { fetched: null, header: null };
27
+ const fetched = Number(m[1]);
28
+ const headerRaw = String(m[2] || '').trim();
29
+ const header = /^\d+$/.test(headerRaw) ? Number(headerRaw) : null;
30
+ return {
31
+ fetched: Number.isFinite(fetched) ? fetched : null,
32
+ header: Number.isFinite(header) ? header : null,
33
+ };
34
+ }
35
+ async function commentsDone(commentsPath) {
36
+ try {
37
+ const donePath = path.join(path.dirname(commentsPath), 'comments.done.json');
38
+ if (await pathExists(donePath))
39
+ return true;
40
+ const text = await fs.readFile(commentsPath, 'utf-8');
41
+ // PersistXhsNoteBlock 写入格式:reachedEnd=是/否, empty=是/否
42
+ if (text.includes('empty=是'))
43
+ return true;
44
+ if (text.includes('reachedEnd=是'))
45
+ return true;
46
+ if (text.includes('stoppedByMaxComments=yes'))
47
+ return true;
48
+ return false;
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }
54
+ async function commentsCoverageOk(commentsPath) {
55
+ const ratio = typeof minCommentsCoverageRatio === 'number' &&
56
+ Number.isFinite(minCommentsCoverageRatio) &&
57
+ minCommentsCoverageRatio > 0
58
+ ? minCommentsCoverageRatio
59
+ : null;
60
+ if (!ratio)
61
+ return true;
62
+ try {
63
+ const text = await fs.readFile(commentsPath, 'utf-8');
64
+ if (text.includes('stoppedByMaxComments=yes'))
65
+ return true;
66
+ const stats = parseCommentsStats(text);
67
+ if (stats.header === null || stats.header <= 0)
68
+ return true; // 标称未知/0:不做覆盖率要求
69
+ if (stats.fetched === null)
70
+ return false;
71
+ return stats.fetched >= Math.ceil(stats.header * ratio);
72
+ }
73
+ catch {
74
+ return false;
75
+ }
76
+ }
77
+ for (const ent of entries) {
78
+ if (!ent?.isDirectory?.())
79
+ continue;
80
+ const noteId = ent.name;
81
+ let ok = true;
82
+ for (const filename of requiredFiles) {
83
+ const p = path.join(keywordDir, noteId, filename);
84
+ if (!(await pathExists(p))) {
85
+ ok = false;
86
+ break;
87
+ }
88
+ if (ok && requireCommentsDone && filename === 'comments.md') {
89
+ const done = await commentsDone(p);
90
+ if (!done) {
91
+ ok = false;
92
+ break;
93
+ }
94
+ const covOk = await commentsCoverageOk(p);
95
+ if (!covOk) {
96
+ ok = false;
97
+ break;
98
+ }
99
+ }
100
+ }
101
+ if (!ok)
102
+ continue;
103
+ noteIds.push(noteId);
104
+ }
105
+ return { keywordDir, noteIds, count: noteIds.length };
106
+ }
107
+ //# sourceMappingURL=persistedNotes.js.map
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Reply Expander Helper
3
+ *
4
+ * 共享展开逻辑:WarmupCommentsBlock / ExpandCommentsBlock
5
+ * - 仅负责查找“视口内可见”的展开按钮,并返回系统点击坐标
6
+ * - 不做 DOM click(仅做只读 query + 标记 + 坐标计算)
7
+ */
8
+ export async function expandRepliesInView(options) {
9
+ const { controllerUrl, profile, browserServiceUrl = 'http://127.0.0.1:7704', maxTargets, recomputeEachClick = true, focusPoint, showMoreContainerId, showMoreSelector, logPrefix = '[WarmupComments]', round, } = options;
10
+ const log = (msg) => console.log(`${logPrefix} ${msg}`);
11
+ // 注意:展开回复必须基于“视口内可见”的按钮坐标做系统点击。
12
+ // 这里不再额外做容器 click(容易引发 off-screen 误点/噪音日志)。
13
+ const maxClickTargets = typeof maxTargets === 'number' && maxTargets > 0 ? Math.floor(maxTargets) : 2;
14
+ let clicked = 0;
15
+ let visible = 0;
16
+ let candidates = 0;
17
+ let all = 0;
18
+ let error;
19
+ const { systemClickAt, systemHoverAt, isDevMode } = await import('./systemInput.js');
20
+ const failFast = isDevMode();
21
+ // 注意:展开后会导致布局变化,预先计算一堆坐标容易“点偏/点不到”。
22
+ // 因此默认每次点击都重新计算一次(maxTargets 次),保证每次点的都是“当前仍可见”的按钮。
23
+ for (let i = 0; i < maxClickTargets; i += 1) {
24
+ const targets = await findExpandTargets(controllerUrl, profile, {
25
+ maxTargets: recomputeEachClick ? 1 : maxClickTargets,
26
+ selector: showMoreSelector || undefined,
27
+ });
28
+ visible = targets.visible;
29
+ candidates = targets.candidates;
30
+ all = targets.all;
31
+ error = targets.error;
32
+ if (i === 0) {
33
+ log(`round=${typeof round === 'number' ? round : 'n/a'} expand buttons: clickTargets=${targets.targets.length}, visible=${targets.visible}, candidates=${targets.candidates}, all=${targets.all}`);
34
+ }
35
+ const t = targets.targets[0];
36
+ if (!t || !Number.isFinite(t.x) || !Number.isFinite(t.y))
37
+ break;
38
+ try {
39
+ await systemClickAt(profile, Math.floor(t.x), Math.floor(t.y), browserServiceUrl, 'reply_expand');
40
+ clicked += 1;
41
+ // 等待 DOM/布局更新(展开回复通常伴随动画与异步加载)
42
+ await new Promise((r) => setTimeout(r, 650 + Math.random() * 450));
43
+ }
44
+ catch (e) {
45
+ const msg = String(e?.message || e || '');
46
+ if (msg.includes('captcha_modal_detected')) {
47
+ throw e;
48
+ }
49
+ if (msg.includes('unsafe_click_image_in_detail')) {
50
+ if (failFast)
51
+ throw e;
52
+ log(`reply_expand skipped unsafe click (continue): ${msg}`);
53
+ break;
54
+ }
55
+ // 其他点击失败(例如元素瞬移)跳过本次,继续尝试下一个
56
+ await new Promise((r) => setTimeout(r, 500));
57
+ continue;
58
+ }
59
+ if (!recomputeEachClick) {
60
+ // 非重算模式:一次拿到多个坐标并依次点击
61
+ const rest = targets.targets.slice(1);
62
+ for (const p of rest) {
63
+ if (!p || !Number.isFinite(p.x) || !Number.isFinite(p.y))
64
+ continue;
65
+ try {
66
+ await systemClickAt(profile, Math.floor(p.x), Math.floor(p.y), browserServiceUrl, 'reply_expand');
67
+ clicked += 1;
68
+ await new Promise((r) => setTimeout(r, 650 + Math.random() * 450));
69
+ }
70
+ catch (e) {
71
+ const msg = String(e?.message || e || '');
72
+ if (msg.includes('captcha_modal_detected'))
73
+ throw e;
74
+ if (msg.includes('unsafe_click_image_in_detail')) {
75
+ if (failFast)
76
+ throw e;
77
+ log(`reply_expand skipped unsafe click (continue): ${msg}`);
78
+ break;
79
+ }
80
+ }
81
+ }
82
+ break;
83
+ }
84
+ }
85
+ if (focusPoint && clicked > 0) {
86
+ await systemHoverAt(profile, Math.floor(focusPoint.x), Math.floor(focusPoint.y), browserServiceUrl);
87
+ await new Promise((r) => setTimeout(r, 180));
88
+ }
89
+ return {
90
+ clicked,
91
+ visible,
92
+ candidates,
93
+ all,
94
+ containerClick: undefined,
95
+ error,
96
+ };
97
+ }
98
+ export async function findExpandTargets(controllerUrl, profile, opts = {}) {
99
+ const maxTargets = typeof opts.maxTargets === 'number' && opts.maxTargets > 0 ? opts.maxTargets : 2;
100
+ const selector = typeof opts.selector === 'string' && opts.selector.trim() ? opts.selector.trim() : '';
101
+ try {
102
+ const res = await fetch(controllerUrl, {
103
+ method: 'POST',
104
+ headers: { 'Content-Type': 'application/json' },
105
+ body: JSON.stringify({
106
+ action: 'browser:execute',
107
+ payload: {
108
+ profile,
109
+ script: `(() => {
110
+ const root =
111
+ document.querySelector('.comments-el') ||
112
+ document.querySelector('.comment-list') ||
113
+ document.querySelector('.comments-container') ||
114
+ document.querySelector('[class*="comment-section"]');
115
+ if (!root) {
116
+ return { targets: [], total: 0, all: 0, candidates: 0, error: 'no root' };
117
+ }
118
+
119
+ const selector = ${JSON.stringify(selector)} || '.show-more, [class*="show-more"], [class*="expand"], [class*="more"]';
120
+ let expandElements = [];
121
+ try {
122
+ expandElements = Array.from(root.querySelectorAll(selector));
123
+ } catch (_) {
124
+ expandElements = Array.from(root.querySelectorAll('.show-more, [class*="show-more"], [class*="expand"], [class*="more"]'));
125
+ }
126
+ const viewportH = window.innerHeight || 0;
127
+ const viewportW = window.innerWidth || 0;
128
+
129
+ const targets = [];
130
+ let visibleCount = 0;
131
+ let candidateCount = 0;
132
+
133
+ const clamp = (n, lo, hi) => Math.min(Math.max(n, lo), hi);
134
+ const isImageLike = (el) => {
135
+ if (!el || !(el instanceof Element)) return false;
136
+ const tag = String(el.tagName || '').toUpperCase();
137
+ if (tag === 'IMG' || tag === 'VIDEO' || tag === 'PICTURE') return true;
138
+ try {
139
+ const cls = typeof el.className === 'string' ? el.className : '';
140
+ if (cls && /(image|img|photo|picture|cover|gallery)/i.test(cls)) return true;
141
+ } catch {}
142
+ // 祖先是图片/视频(避免点到媒体查看器)
143
+ try {
144
+ const m = el.closest('img,video,picture');
145
+ if (m) return true;
146
+ } catch {}
147
+ return false;
148
+ };
149
+
150
+ for (const el of expandElements) {
151
+ if (targets.length >= ${maxTargets}) break;
152
+ if (!(el instanceof HTMLElement)) continue;
153
+
154
+ const rect = el.getBoundingClientRect();
155
+ if (!rect || rect.width < 6 || rect.height < 6) continue;
156
+ // 防止 selector 过宽命中到 comment_item/大块区域(中心点可能落在评论图片上)
157
+ // 展开回复按钮一般是单行小高度元素
158
+ if (rect.height > 160) continue;
159
+ // 开发阶段严格要求:必须“完全在视口内”才允许点击,避免点到截断按钮造成误触
160
+ if (rect.bottom <= 0 || rect.top >= viewportH) continue;
161
+ if (rect.right <= 0 || rect.left >= viewportW) continue;
162
+ visibleCount += 1;
163
+
164
+ const style = window.getComputedStyle(el);
165
+ if (style.display === 'none' || style.visibility === 'hidden' || style.pointerEvents === 'none') {
166
+ continue;
167
+ }
168
+
169
+ // 过滤掉明显的“收起/折叠”,避免反复点导致状态抖动
170
+ const text = (el.textContent || '').trim();
171
+ if (text.includes('收起') || text.includes('折叠')) continue;
172
+ // 过滤:仅允许“展开xx回复/展开更多评论”等按钮
173
+ if (!text.includes('展开')) continue;
174
+ if (!(text.includes('回复') || text.includes('评论') || text.includes('更多'))) continue;
175
+ // 过滤:禁止点击任何链接内的元素(避免误点头像/话题/外链)
176
+ if (el.closest && el.closest('a[href]')) continue;
177
+
178
+ // ⚠️ 关键:评论区可能含图片,若选点落在 IMG 上会触发媒体查看器/风控。
179
+ // 这里在页面内做 elementFromPoint 探测,找到“落点不在图片且命中在该按钮内”的安全点击坐标。
180
+ const points = [
181
+ { fx: 0.5, fy: 0.55 },
182
+ { fx: 0.75, fy: 0.55 },
183
+ { fx: 0.6, fy: 0.55 },
184
+ { fx: 0.85, fy: 0.55 },
185
+ { fx: 0.5, fy: 0.35 },
186
+ { fx: 0.5, fy: 0.75 },
187
+ ];
188
+
189
+ let picked = null;
190
+ for (const p of points) {
191
+ const x = clamp(rect.left + rect.width * p.fx, 30, viewportW - 30);
192
+ const y = clamp(rect.top + rect.height * p.fy, 140, viewportH - 140);
193
+ const hit = document.elementFromPoint(x, y);
194
+ if (!hit || !(hit instanceof HTMLElement)) continue;
195
+ // 必须命中在本元素内(避免被遮挡导致点到别的元素)
196
+ if (!(el === hit || el.contains(hit))) continue;
197
+ // 保护:不允许落在图片/视频上
198
+ if (isImageLike(hit)) continue;
199
+ // 保护:不允许落在链接上(头像/话题等)
200
+ try {
201
+ if (hit.closest && hit.closest('a[href]')) continue;
202
+ } catch {}
203
+ picked = { x, y };
204
+ break;
205
+ }
206
+
207
+ if (!picked) continue;
208
+ candidateCount += 1;
209
+ targets.push(picked);
210
+ }
211
+
212
+ return {
213
+ targets,
214
+ total: visibleCount,
215
+ all: expandElements.length,
216
+ candidates: candidateCount,
217
+ };
218
+ })()`,
219
+ },
220
+ }),
221
+ signal: AbortSignal.timeout
222
+ ? AbortSignal.timeout(10000)
223
+ : undefined,
224
+ });
225
+ if (!res.ok) {
226
+ return {
227
+ targets: [],
228
+ visible: 0,
229
+ candidates: 0,
230
+ all: 0,
231
+ error: `HTTP ${res.status}: ${await res.text()}`,
232
+ };
233
+ }
234
+ const data = await res.json();
235
+ const payload = data?.data?.result || data?.result || data;
236
+ const targets = Array.isArray(payload?.targets) ? payload.targets : [];
237
+ const totalButtons = typeof payload?.total === 'number' ? payload.total : 0;
238
+ const allButtons = typeof payload?.all === 'number' ? payload.all : 0;
239
+ const candidates = typeof payload?.candidates === 'number' ? payload.candidates : 0;
240
+ return {
241
+ targets: targets
242
+ .map((t) => ({ x: Number(t?.x), y: Number(t?.y) }))
243
+ .filter((t) => Number.isFinite(t.x) && Number.isFinite(t.y)),
244
+ visible: totalButtons,
245
+ candidates,
246
+ all: allButtons,
247
+ error: typeof payload?.error === 'string' ? payload.error : undefined,
248
+ };
249
+ }
250
+ catch (err) {
251
+ return {
252
+ targets: [],
253
+ visible: 0,
254
+ candidates: 0,
255
+ all: 0,
256
+ error: err?.message || String(err),
257
+ };
258
+ }
259
+ }
260
+ //# sourceMappingURL=replyExpander.js.map
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Scroll Into View Helper
3
+ *
4
+ * 在点击元素前将其滚动到视口可见区域,避免点击视口外元素失败
5
+ */
6
+ import { logControllerActionError, logControllerActionResult, logControllerActionStart, } from './operationLogger.js';
7
+ /**
8
+ * 将元素滚动到视口可见区域
9
+ *
10
+ * @param options - 滚动选项
11
+ * @returns Promise<ScrollIntoViewResult>
12
+ */
13
+ export async function scrollIntoView(options) {
14
+ const { sessionId, serviceUrl = 'http://127.0.0.1:7701', selector, coordinates, block = 'center', behavior = 'instant', } = options;
15
+ const controllerUrl = `${serviceUrl}/v1/controller/action`;
16
+ async function controllerAction(action, payload) {
17
+ const opId = logControllerActionStart(action, payload, { source: 'scrollIntoView' });
18
+ try {
19
+ const response = await fetch(controllerUrl, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({ action, payload }),
23
+ signal: AbortSignal.timeout ? AbortSignal.timeout(8000) : undefined,
24
+ });
25
+ if (!response.ok) {
26
+ throw new Error(`HTTP ${response.status}: ${await response.text()}`);
27
+ }
28
+ const data = await response.json();
29
+ const result = data.data || data;
30
+ logControllerActionResult(opId, action, result, { source: 'scrollIntoView' });
31
+ return result;
32
+ }
33
+ catch (error) {
34
+ logControllerActionError(opId, action, error, payload, { source: 'scrollIntoView' });
35
+ throw error;
36
+ }
37
+ }
38
+ try {
39
+ // 构建脚本:根据 selector 或 coordinates 查找元素并滚动
40
+ const script = selector
41
+ ? `(() => {
42
+ const selector = ${JSON.stringify(selector)};
43
+ const el = document.querySelector(selector);
44
+ if (!el || !(el instanceof HTMLElement)) {
45
+ return { success: false, visible: false, error: 'Element not found' };
46
+ }
47
+
48
+ // 滚动到视口中心
49
+ try {
50
+ el.scrollIntoView({ behavior: '${behavior}', block: '${block}' });
51
+ } catch (e) {
52
+ return { success: false, visible: false, error: 'scrollIntoView failed: ' + e.message };
53
+ }
54
+
55
+ // 等待一小段时间让滚动完成
56
+ const rect = el.getBoundingClientRect();
57
+ const viewportH = window.innerHeight || 0;
58
+ const visible = rect.top >= 0 && rect.top < viewportH;
59
+
60
+ return {
61
+ success: true,
62
+ visible,
63
+ rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height }
64
+ };
65
+ })()`
66
+ : coordinates
67
+ ? `(() => {
68
+ const x = ${JSON.stringify(coordinates.x)};
69
+ const y = ${JSON.stringify(coordinates.y)};
70
+ const el = document.elementFromPoint(x, y);
71
+ if (!el || !(el instanceof HTMLElement)) {
72
+ return { success: false, visible: false, error: 'No element at coordinates' };
73
+ }
74
+
75
+ // 滚动到视口中心
76
+ try {
77
+ el.scrollIntoView({ behavior: '${behavior}', block: '${block}' });
78
+ } catch (e) {
79
+ return { success: false, visible: false, error: 'scrollIntoView failed: ' + e.message };
80
+ }
81
+
82
+ // 等待一小段时间让滚动完成
83
+ const rect = el.getBoundingClientRect();
84
+ const viewportH = window.innerHeight || 0;
85
+ const visible = rect.top >= 0 && rect.top < viewportH;
86
+
87
+ return {
88
+ success: true,
89
+ visible,
90
+ rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height }
91
+ };
92
+ })()`
93
+ : `(() => { return { success: false, visible: false, error: 'No selector or coordinates provided' }; })()`;
94
+ const result = await controllerAction('browser:execute', {
95
+ profile: sessionId,
96
+ script,
97
+ });
98
+ const payload = result?.result || result;
99
+ if (!payload?.success) {
100
+ return {
101
+ success: false,
102
+ visible: false,
103
+ error: payload?.error || 'Scroll into view failed',
104
+ };
105
+ }
106
+ return {
107
+ success: true,
108
+ visible: payload.visible ?? false,
109
+ rect: payload.rect,
110
+ };
111
+ }
112
+ catch (error) {
113
+ return {
114
+ success: false,
115
+ visible: false,
116
+ error: `scrollIntoView failed: ${error.message}`,
117
+ };
118
+ }
119
+ }
120
+ /**
121
+ * 通过坐标点滚动元素到视口
122
+ *
123
+ * @param profileId - 会话ID
124
+ * @param x - X坐标
125
+ * @param y - Y坐标
126
+ * @param serviceUrl - 服务URL
127
+ * @returns Promise<ScrollIntoViewResult>
128
+ */
129
+ export async function scrollElementAtPointIntoView(profileId, x, y, serviceUrl = 'http://127.0.0.1:7701') {
130
+ return scrollIntoView({
131
+ sessionId: profileId,
132
+ serviceUrl,
133
+ coordinates: { x, y },
134
+ block: 'center',
135
+ behavior: 'instant',
136
+ });
137
+ }
138
+ //# sourceMappingURL=scrollIntoView.js.map