@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,270 @@
1
+ import { controllerAction, delay } from '../../utils/controllerAction.js';
2
+ export async function ensureCommentsOpened(sessionId, apiUrl) {
3
+ await controllerAction('container:operation', {
4
+ containerId: 'xiaohongshu_detail.comment_button',
5
+ operationId: 'highlight',
6
+ sessionId,
7
+ timeoutMs: 15000,
8
+ config: { duration: 1800, channel: 'xhs-comments' },
9
+ }, apiUrl).catch(() => null);
10
+ await controllerAction('container:operation', { containerId: 'xiaohongshu_detail.comment_button', operationId: 'click', sessionId, timeoutMs: 15000 }, apiUrl).catch(() => { });
11
+ await delay(1200);
12
+ }
13
+ export async function isCommentEnd(sessionId, apiUrl) {
14
+ const end = await controllerAction('container:operation', { containerId: 'xiaohongshu_detail.comment_section.end_marker', operationId: 'extract', sessionId, timeoutMs: 12000 }, apiUrl).catch(() => null);
15
+ if (end && end?.success === true) {
16
+ const extracted = Array.isArray(end?.extracted) ? end.extracted : [];
17
+ if (extracted.length > 0)
18
+ return true;
19
+ }
20
+ // 空评论标记:"这是一片荒地"
21
+ try {
22
+ const res = await controllerAction('browser:execute', {
23
+ profile: sessionId,
24
+ timeoutMs: 12000,
25
+ script: `(() => {
26
+ const candidates = [
27
+ 'p.no-comments-text',
28
+ '.no-comments-text',
29
+ '[class*="no-comments"]',
30
+ '[class*="no-comment"]',
31
+ '[class*="empty-comment"]',
32
+ ];
33
+ for (const sel of candidates) {
34
+ const el = document.querySelector(sel);
35
+ const t = (el?.textContent || '').replace(/\s+/g, ' ').trim();
36
+ if (t && (t.includes('这是一片荒地') || t.includes('荒地'))) return true;
37
+ }
38
+ return false;
39
+ })()`,
40
+ }, apiUrl);
41
+ if (res?.result === true)
42
+ return true;
43
+ }
44
+ catch {
45
+ // ignore
46
+ }
47
+ return false;
48
+ }
49
+ async function getVisibleCommentSignature(sessionId, apiUrl) {
50
+ try {
51
+ const res = await controllerAction('browser:execute', {
52
+ profile: sessionId,
53
+ timeoutMs: 12000,
54
+ script: `(() => {
55
+ const isVisible = (el) => {
56
+ const r = el.getBoundingClientRect();
57
+ return r.width > 0 && r.height > 0 && r.bottom > 0 && r.top < window.innerHeight;
58
+ };
59
+ const items = Array.from(document.querySelectorAll('.comment-item')).filter(isVisible);
60
+ const texts = items
61
+ .map((el) => {
62
+ const t = el.querySelector('.content')?.textContent || el.querySelector('p')?.textContent || '';
63
+ return t.replace(/\s+/g, ' ').trim();
64
+ })
65
+ .filter(Boolean)
66
+ .slice(0, 6);
67
+ return { count: items.length, head: texts.join('|').slice(0, 200) };
68
+ })()`,
69
+ }, apiUrl);
70
+ return {
71
+ count: Number(res?.result?.count ?? -1),
72
+ head: String(res?.result?.head ?? ''),
73
+ };
74
+ }
75
+ catch {
76
+ return { count: -1, head: '' };
77
+ }
78
+ }
79
+ /**
80
+ * 往返滚动检测(风控友好):
81
+ * - 下滚无变化 -> 上滚一点 -> 再下滚
82
+ * - 往返 maxRounds 次仍无变化:判定到底(或评论区无法继续加载)
83
+ */
84
+ export async function checkBottomWithBackAndForth(sessionId, apiUrl, maxRounds = 3) {
85
+ let prev = await getVisibleCommentSignature(sessionId, apiUrl);
86
+ for (let i = 0; i < maxRounds; i += 1) {
87
+ await scrollComments(sessionId, apiUrl, 420);
88
+ await delay(800);
89
+ const afterDown = await getVisibleCommentSignature(sessionId, apiUrl);
90
+ if (afterDown.count !== prev.count || afterDown.head !== prev.head) {
91
+ return { reachedBottom: false, reason: 'down_changed' };
92
+ }
93
+ // 往回滚动几次再尝试向下,防止卡住
94
+ await scrollComments(sessionId, apiUrl, -240);
95
+ await delay(500);
96
+ await scrollComments(sessionId, apiUrl, 240);
97
+ await delay(700);
98
+ const afterBounce = await getVisibleCommentSignature(sessionId, apiUrl);
99
+ if (afterBounce.count !== prev.count || afterBounce.head !== prev.head) {
100
+ return { reachedBottom: false, reason: 'bounce_changed' };
101
+ }
102
+ prev = afterBounce;
103
+ }
104
+ return { reachedBottom: true, reason: 'no_change_after_back_and_forth' };
105
+ }
106
+ export async function extractVisibleComments(sessionId, apiUrl, maxItems) {
107
+ const res = await controllerAction('container:operation', {
108
+ containerId: 'xiaohongshu_detail.comment_section.comment_item',
109
+ operationId: 'extract',
110
+ sessionId,
111
+ timeoutMs: 15000,
112
+ config: { max_items: Math.max(1, Math.min(80, Math.floor(maxItems))), visibleOnly: true },
113
+ }, apiUrl);
114
+ if (!res?.success)
115
+ return [];
116
+ const extracted = Array.isArray(res?.extracted) ? res.extracted : [];
117
+ // Map extracted items with their DOM index (position in selectorAll result)
118
+ const containerRes = await controllerAction('browser:execute', {
119
+ profile: sessionId,
120
+ timeoutMs: 12000,
121
+ script: `(() => {
122
+ const items = Array.from(document.querySelectorAll('.comment-item'));
123
+ return items.map((el, idx) => {
124
+ const rect = el.getBoundingClientRect();
125
+ return {
126
+ domIndex: idx,
127
+ visible: rect.top >= 0 && rect.bottom <= window.innerHeight && rect.height > 0
128
+ };
129
+ }).filter(x => x.visible);
130
+ })()`,
131
+ }, apiUrl);
132
+ const visibleIndices = (containerRes?.result || []).map((x) => x.domIndex);
133
+ return extracted.map((item, idx) => ({ ...item, domIndex: visibleIndices[idx] ?? idx }));
134
+ }
135
+ export async function highlightCommentRow(sessionId, index, apiUrl, channel = 'xhs-comment-row') {
136
+ return controllerAction('container:operation', {
137
+ containerId: 'xiaohongshu_detail.comment_section.comment_item',
138
+ operationId: 'highlight',
139
+ sessionId,
140
+ config: {
141
+ index,
142
+ target: 'self',
143
+ style: '6px solid #ff00ff',
144
+ duration: 8000,
145
+ channel,
146
+ visibleOnly: true,
147
+ },
148
+ }, apiUrl);
149
+ }
150
+ export async function scrollComments(sessionId, apiUrl, distance = 650) {
151
+ return controllerAction('container:operation', {
152
+ containerId: 'xiaohongshu_detail.comment_section',
153
+ operationId: 'scroll',
154
+ sessionId,
155
+ timeoutMs: 15000,
156
+ config: { direction: 'down', distance: Math.max(60, Math.min(800, Math.floor(distance))) },
157
+ }, apiUrl);
158
+ }
159
+ export async function expandAllVisibleReplyButtons(sessionId, apiUrl, options = {}) {
160
+ const maxPasses = Math.max(1, Math.min(12, Number(options.maxPasses || 6)));
161
+ const maxClicksPerPass = Math.max(1, Math.min(30, Number(options.maxClicksPerPass || 12)));
162
+ const probeTargets = async () => {
163
+ const probe = await controllerAction('browser:execute', {
164
+ profile: sessionId,
165
+ timeoutMs: 12000,
166
+ script: `(() => {
167
+ const root =
168
+ document.querySelector('.comments-el') ||
169
+ document.querySelector('.comment-list') ||
170
+ document.querySelector('.comments-container') ||
171
+ document.querySelector('[class*="comment-section"]') ||
172
+ document.body;
173
+
174
+ const selector = '.show-more, .reply-expand, [class*="show-more"], [class*="expand"]';
175
+ const nodes = Array.from(root.querySelectorAll(selector));
176
+ const viewportH = window.innerHeight || 0;
177
+ const viewportW = window.innerWidth || 0;
178
+
179
+ const targets = [];
180
+
181
+ const isVisible = (el) => {
182
+ const r = el.getBoundingClientRect();
183
+ return r.width > 0 && r.height > 0 && r.bottom > 0 && r.top < viewportH && r.right > 0 && r.left < viewportW;
184
+ };
185
+
186
+ for (const el of nodes) {
187
+ if (!(el instanceof HTMLElement)) continue;
188
+ if (!isVisible(el)) continue;
189
+
190
+ const text = (el.textContent || '').replace(/\s+/g, ' ').trim();
191
+ if (!text || !text.includes('展开')) continue;
192
+ if (text.includes('收起') || text.includes('折叠')) continue;
193
+ if (!(text.includes('回复') || text.includes('评论') || text.includes('更多'))) continue;
194
+
195
+ const r = el.getBoundingClientRect();
196
+ const points = [
197
+ { x: Math.round(r.left + r.width * 0.72), y: Math.round(r.top + r.height * 0.55) },
198
+ { x: Math.round(r.left + r.width * 0.55), y: Math.round(r.top + r.height * 0.55) },
199
+ { x: Math.round(r.left + r.width * 0.85), y: Math.round(r.top + r.height * 0.5) },
200
+ ];
201
+
202
+ let picked = null;
203
+ for (const p of points) {
204
+ if (!Number.isFinite(p.x) || !Number.isFinite(p.y)) continue;
205
+ if (p.x < 8 || p.y < 8 || p.x > viewportW - 8 || p.y > viewportH - 8) continue;
206
+ const hit = document.elementFromPoint(p.x, p.y);
207
+ if (!hit || !(hit instanceof Element)) continue;
208
+ if (!(hit === el || el.contains(hit) || hit.contains(el))) continue;
209
+ if (hit.closest && hit.closest('a[href]')) continue;
210
+ picked = p;
211
+ break;
212
+ }
213
+
214
+ if (!picked) continue;
215
+ targets.push({ x: picked.x, y: picked.y, text });
216
+ }
217
+
218
+ targets.sort((a, b) => (a.y - b.y) || (a.x - b.x));
219
+ return { targets };
220
+ })()`,
221
+ }, apiUrl).catch(() => null);
222
+ const raw = Array.isArray(probe?.result?.targets)
223
+ ? probe.result.targets
224
+ : Array.isArray(probe?.targets)
225
+ ? probe.targets
226
+ : [];
227
+ return raw
228
+ .map((t) => ({
229
+ x: Math.round(Number(t?.x)),
230
+ y: Math.round(Number(t?.y)),
231
+ text: String(t?.text || ''),
232
+ }))
233
+ .filter((t) => Number.isFinite(t.x) && Number.isFinite(t.y));
234
+ };
235
+ let totalClicked = 0;
236
+ let totalDetected = 0;
237
+ let passes = 0;
238
+ for (let pass = 0; pass < maxPasses; pass += 1) {
239
+ const targets = await probeTargets();
240
+ if (!targets.length)
241
+ break;
242
+ totalDetected += targets.length;
243
+ let clickedThisPass = 0;
244
+ const toClick = targets.slice(0, maxClicksPerPass);
245
+ for (const t of toClick) {
246
+ const clicked = await controllerAction('container:operation', {
247
+ containerId: 'xiaohongshu_detail.comment_section.show_more_button',
248
+ operationId: 'click',
249
+ sessionId,
250
+ timeoutMs: 15000,
251
+ config: {
252
+ x: t.x,
253
+ y: t.y,
254
+ },
255
+ }, apiUrl).catch(() => null);
256
+ if (clicked?.success !== false)
257
+ clickedThisPass += 1;
258
+ await delay(220);
259
+ }
260
+ passes += 1;
261
+ totalClicked += clickedThisPass;
262
+ // No successful click in this pass means target is currently not actionable.
263
+ if (clickedThisPass <= 0)
264
+ break;
265
+ await delay(380);
266
+ }
267
+ const remaining = (await probeTargets()).length;
268
+ return { clicked: totalClicked, passes, remaining, detected: totalDetected };
269
+ }
270
+ //# sourceMappingURL=xhsComments.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 小红书 App 模块入口
3
+ */
4
+ export * as Phase1EnsureServicesBlock from './blocks/Phase1EnsureServicesBlock.js';
5
+ export * as Phase1StartProfileBlock from './blocks/Phase1StartProfileBlock.js';
6
+ export * as Phase1MonitorCookieBlock from './blocks/Phase1MonitorCookieBlock.js';
7
+ export * as Phase2SearchBlock from './blocks/Phase2SearchBlock.js';
8
+ export * as Phase2CollectLinksBlock from './blocks/Phase2CollectLinksBlock.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,222 @@
1
+ import { execute as detectPageState } from '../../../../workflow/blocks/DetectPageStateBlock.js';
2
+ import { execute as anchorVerify } from '../../../../workflow/blocks/AnchorVerificationBlock.js';
3
+ import { execute as errorRecovery } from '../../../../workflow/blocks/ErrorRecoveryBlock.js';
4
+ function hasAny(matchIds, ids) {
5
+ const set = new Set(matchIds || []);
6
+ return ids.some((x) => set.has(x));
7
+ }
8
+ function hasAll(matchIds, ids) {
9
+ const set = new Set(matchIds || []);
10
+ return ids.every((x) => set.has(x));
11
+ }
12
+ export async function detectXhsCheckpoint(input) {
13
+ const { sessionId, serviceUrl = 'http://127.0.0.1:7701' } = input;
14
+ const state = await detectPageState({ sessionId, platform: 'xiaohongshu', serviceUrl });
15
+ const url = String(state?.url || '').trim();
16
+ const stage = String(state?.stage || 'unknown');
17
+ const rootId = state?.rootId ?? null;
18
+ const matchIds = Array.isArray(state?.matchIds) ? state.matchIds : [];
19
+ const dom = {
20
+ hasDetailMask: typeof state?.dom?.hasDetailMask === 'boolean' ? state.dom.hasDetailMask : undefined,
21
+ hasSearchInput: typeof state?.dom?.hasSearchInput === 'boolean' ? state.dom.hasSearchInput : undefined,
22
+ readyState: typeof state?.dom?.readyState === 'string' ? state.dom.readyState : undefined,
23
+ title: typeof state?.dom?.title === 'string' ? state.dom.title : undefined,
24
+ };
25
+ const signals = [];
26
+ const allIds = [String(rootId || ''), ...matchIds].filter(Boolean);
27
+ // Priority 0 (DOM-first): XHS may keep /explore/<id> in the URL even after closing the detail modal.
28
+ // In that case URL-based assumptions are wrong; prefer DOM signals.
29
+ if (dom.hasDetailMask === false && dom.hasSearchInput === true) {
30
+ const isInSearch = url.includes('/search_result') || url.includes('keyword=');
31
+ const checkpoint = isInSearch ? 'search_ready' : 'home_ready';
32
+ return {
33
+ success: true,
34
+ checkpoint,
35
+ stage,
36
+ url,
37
+ rootId,
38
+ matchIds,
39
+ signals: ['no_detail_mask', 'has_search_input', checkpoint],
40
+ dom,
41
+ error: state?.error,
42
+ };
43
+ }
44
+ // Risk-control / captcha URL patterns are hard stops (even if container match fails).
45
+ // We must avoid any automated retries here to reduce further risk-control triggers.
46
+ const lowerUrl = url.toLowerCase();
47
+ if (lowerUrl.includes('/website-login/captcha') ||
48
+ lowerUrl.includes('verifyuuid=') ||
49
+ lowerUrl.includes('verifytype=') ||
50
+ lowerUrl.includes('verifybiz=') ||
51
+ lowerUrl.includes('/website-login/verify') ||
52
+ lowerUrl.includes('/website-login/security')) {
53
+ return {
54
+ success: true,
55
+ checkpoint: 'risk_control',
56
+ stage,
57
+ url,
58
+ rootId,
59
+ matchIds,
60
+ signals: ['risk_control_url'],
61
+ dom,
62
+ error: state?.error,
63
+ };
64
+ }
65
+ // offsite is a hard stop
66
+ if (!url || !url.includes('xiaohongshu.com')) {
67
+ return { success: false, checkpoint: 'offsite', stage, url, rootId, matchIds, signals: ['offsite'], dom, error: state?.error };
68
+ }
69
+ // login guard
70
+ if (hasAny(allIds, ['xiaohongshu_login.login_guard'])) {
71
+ return { success: true, checkpoint: 'login_guard', stage, url, rootId, matchIds, signals: ['login_guard'], dom };
72
+ }
73
+ // risk control (placeholder ids; extend as container library grows)
74
+ if (hasAny(allIds, ['xiaohongshu_login.qrcode_guard', 'xiaohongshu_login.captcha_guard'])) {
75
+ return { success: true, checkpoint: 'risk_control', stage, url, rootId, matchIds, signals: ['risk_control'], dom };
76
+ }
77
+ // comments_ready
78
+ if (hasAny(allIds, [
79
+ 'xiaohongshu_detail.comment_section',
80
+ 'xiaohongshu_detail.comment_section.comment_item',
81
+ 'xiaohongshu_detail.end_marker',
82
+ ])) {
83
+ return { success: true, checkpoint: 'comments_ready', stage, url, rootId, matchIds, signals: ['comments_anchor'], dom };
84
+ }
85
+ // detail_ready
86
+ if (hasAll(allIds, ['xiaohongshu_detail.modal_shell', 'xiaohongshu_detail.content_anchor'])) {
87
+ return { success: true, checkpoint: 'detail_ready', stage, url, rootId, matchIds, signals: ['detail_shell', 'content_anchor'], dom };
88
+ }
89
+ // search_ready
90
+ if (hasAll(allIds, ['xiaohongshu_search.search_bar', 'xiaohongshu_search.search_result_list'])) {
91
+ return { success: true, checkpoint: 'search_ready', stage, url, rootId, matchIds, signals: ['search_bar', 'search_result_list'], dom };
92
+ }
93
+ // home_ready
94
+ if (hasAny(allIds, ['xiaohongshu_home.search_input', 'xiaohongshu_home'])) {
95
+ // IMPORTANT:
96
+ // On XHS, the URL may still contain /explore/<noteId>?xsec_token=... even after the detail modal is closed.
97
+ // We must prefer DOM/container evidence over URL.
98
+ // If detail mask is absent and home/search anchors are present, treat as home_ready.
99
+ return { success: true, checkpoint: 'home_ready', stage, url, rootId, matchIds, signals: ['home'], dom };
100
+ }
101
+ return {
102
+ success: stage !== 'unknown',
103
+ checkpoint: 'unknown',
104
+ stage,
105
+ url,
106
+ rootId,
107
+ matchIds,
108
+ signals,
109
+ dom,
110
+ error: state?.error,
111
+ };
112
+ }
113
+ async function highlightAnchors(sessionId, serviceUrl, ids, ms) {
114
+ for (const id of ids) {
115
+ try {
116
+ await anchorVerify({ sessionId, containerId: id, operation: 'enter', serviceUrl });
117
+ return;
118
+ }
119
+ catch {
120
+ // try next
121
+ }
122
+ }
123
+ }
124
+ function fallbackTarget(target) {
125
+ // one-level-up policy
126
+ if (target === 'search_ready')
127
+ return 'home_ready';
128
+ if (target === 'comments_ready')
129
+ return 'detail_ready';
130
+ if (target === 'detail_ready')
131
+ return 'search_ready';
132
+ return null;
133
+ }
134
+ export async function ensureXhsCheckpoint(input) {
135
+ const { sessionId, target, serviceUrl = 'http://127.0.0.1:7701', timeoutMs = 15000, allowOneLevelUpFallback = true, evidence = { highlightMs: 1200 }, } = input;
136
+ const start = Date.now();
137
+ const attempts = [];
138
+ const det0 = await detectXhsCheckpoint({ sessionId, serviceUrl });
139
+ const from = det0.checkpoint;
140
+ let last = det0;
141
+ // quick success
142
+ if (from === target) {
143
+ return { success: true, from, to: target, reached: target, url: det0.url, stage: det0.stage, attempts, signals: det0.signals };
144
+ }
145
+ while (Date.now() - start < timeoutMs) {
146
+ last = await detectXhsCheckpoint({ sessionId, serviceUrl });
147
+ if (last.checkpoint === target) {
148
+ return { success: true, from, to: target, reached: target, url: last.url, stage: last.stage, attempts, signals: last.signals };
149
+ }
150
+ // Always highlight best-effort so user sees where we are.
151
+ const hm = Math.max(200, Math.min(2000, Number(evidence.highlightMs || 1200)));
152
+ if (last.checkpoint === 'detail_ready' || last.checkpoint === 'comments_ready') {
153
+ await highlightAnchors(sessionId, serviceUrl, ['xiaohongshu_detail.modal_shell', 'xiaohongshu_detail.content_anchor'], hm);
154
+ }
155
+ else if (last.checkpoint === 'search_ready') {
156
+ await highlightAnchors(sessionId, serviceUrl, ['xiaohongshu_search.search_bar', 'xiaohongshu_search.search_result_list'], hm);
157
+ }
158
+ else if (last.checkpoint === 'home_ready') {
159
+ await highlightAnchors(sessionId, serviceUrl, ['xiaohongshu_home.search_input'], hm);
160
+ }
161
+ // Recovery actions: conservative (ESC-based).
162
+ if ((target === 'search_ready' || target === 'home_ready') &&
163
+ (last.checkpoint === 'detail_ready' || last.checkpoint === 'comments_ready')) {
164
+ try {
165
+ await errorRecovery({
166
+ sessionId,
167
+ fromStage: 'detail',
168
+ targetStage: target === 'home_ready' ? 'home' : 'search',
169
+ recoveryMode: 'esc',
170
+ serviceUrl,
171
+ maxRetries: 3,
172
+ });
173
+ attempts.push({ action: 'esc', ok: true });
174
+ }
175
+ catch (e) {
176
+ attempts.push({ action: 'esc', ok: false, reason: e?.message || String(e) });
177
+ }
178
+ continue;
179
+ }
180
+ // No safe automated path; break to fallback or fail.
181
+ break;
182
+ }
183
+ // one-level-up fallback
184
+ if (allowOneLevelUpFallback) {
185
+ const up = fallbackTarget(target);
186
+ if (up && up !== target) {
187
+ const res = await ensureXhsCheckpoint({
188
+ sessionId,
189
+ target: up,
190
+ serviceUrl,
191
+ timeoutMs,
192
+ allowOneLevelUpFallback: false,
193
+ evidence,
194
+ });
195
+ if (res.success) {
196
+ return {
197
+ success: false,
198
+ from,
199
+ to: target,
200
+ reached: up,
201
+ url: res.url,
202
+ stage: res.stage,
203
+ attempts: [...attempts, ...res.attempts, { action: 'need_user_action', ok: false, reason: `need to reach ${target}` }],
204
+ signals: res.signals,
205
+ error: `fallback_reached_${up}_need_${target}`,
206
+ };
207
+ }
208
+ }
209
+ }
210
+ return {
211
+ success: false,
212
+ from,
213
+ to: target,
214
+ reached: last.checkpoint,
215
+ url: last.url,
216
+ stage: last.stage,
217
+ attempts,
218
+ signals: last.signals,
219
+ error: `ensure_checkpoint_failed target=${target} reached=${last.checkpoint}`,
220
+ };
221
+ }
222
+ //# sourceMappingURL=checkpoints.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Controller Action 工具函数
3
+ */
4
+ export async function controllerAction(action, payload, apiUrl) {
5
+ const timeoutMs = typeof payload?.timeoutMs === 'number' && Number.isFinite(payload.timeoutMs) && payload.timeoutMs > 0
6
+ ? Math.floor(payload.timeoutMs)
7
+ : 60000;
8
+ const res = await fetch(`${apiUrl}/v1/controller/action`, {
9
+ method: 'POST',
10
+ headers: { 'Content-Type': 'application/json' },
11
+ // Unified API expects payload nested under `payload`.
12
+ body: JSON.stringify({ action, payload: payload || {} }),
13
+ // 截图/容器操作在某些页面会更慢,允许调用方覆盖超时。
14
+ signal: AbortSignal.timeout(timeoutMs),
15
+ });
16
+ const data = await res.json().catch(() => ({}));
17
+ return data.data || data;
18
+ }
19
+ export function delay(ms) {
20
+ return new Promise(resolve => setTimeout(resolve, ms));
21
+ }
22
+ export function expandHome(p) {
23
+ if (!p)
24
+ return p;
25
+ if (p.startsWith('~/'))
26
+ return `${process.env.HOME}/${p.slice(2)}`;
27
+ return p;
28
+ }
29
+ export async function ensureDir(dir) {
30
+ const { mkdir } = await import('node:fs/promises');
31
+ await mkdir(dir, { recursive: true });
32
+ }
33
+ export async function writeFile(filePath, content) {
34
+ const { writeFile: wf } = await import('node:fs/promises');
35
+ await wf(filePath, content, 'utf8');
36
+ }
37
+ export async function downloadImage(url, destPath) {
38
+ const response = await fetch(url);
39
+ const buffer = await response.arrayBuffer();
40
+ const { writeFile: wf } = await import('node:fs/promises');
41
+ await wf(destPath, Buffer.from(buffer));
42
+ }
43
+ //# sourceMappingURL=controllerAction.js.map