@web-auto/webauto 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +263 -15
  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 +38 -10
  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,426 @@
1
+ /**
2
+ * Workflow Block: EnsureSession
3
+ *
4
+ * 确保浏览器 Session 存在并处于登录状态
5
+ */
6
+ import os from 'node:os';
7
+ /**
8
+ * 确保浏览器 Session 存在
9
+ *
10
+ * @param input - 输入参数
11
+ * @returns Promise<EnsureSessionOutput>
12
+ */
13
+ export async function execute(input) {
14
+ const { profileId, url, serviceUrl = 'http://127.0.0.1:7704', viewport } = input;
15
+ const headless = typeof input.headless === 'boolean' ? input.headless : false;
16
+ if (!profileId) {
17
+ return {
18
+ sessionId: '',
19
+ status: 'active',
20
+ currentPage: '',
21
+ error: 'Missing profileId'
22
+ };
23
+ }
24
+ try {
25
+ const statusUrl = `${serviceUrl}/command`;
26
+ const desiredViewport = viewport || {
27
+ width: Number(process.env.WEBAUTO_VIEWPORT_WIDTH || 1440),
28
+ height: Number(process.env.WEBAUTO_VIEWPORT_HEIGHT || 1100),
29
+ };
30
+ const zoomRaw = String(process.env.WEBAUTO_VIEWPORT_ZOOM ?? '').trim().toLowerCase();
31
+ const browserZoomRaw = String(process.env.WEBAUTO_BROWSER_ZOOM ?? '').trim().toLowerCase();
32
+ const allowCssZoom = String(process.env.WEBAUTO_ALLOW_CSS_ZOOM ?? '').trim() === '1';
33
+ const waitOnFail = String(process.env.WEBAUTO_SESSION_WAIT_ON_FAIL ?? '').trim() !== '0' &&
34
+ profileId.startsWith('xiaohongshu_');
35
+ const waitMsRaw = Number(process.env.WEBAUTO_SESSION_WAIT_MS ?? 0);
36
+ const waitLimitMs = waitMsRaw <= 0 ? Number.POSITIVE_INFINITY : waitMsRaw;
37
+ const waitIntervalMs = Math.max(1000, Number(process.env.WEBAUTO_SESSION_WAIT_INTERVAL_MS ?? 3000));
38
+ const waitStartTs = Date.now();
39
+ function clamp(n, min, max) {
40
+ if (!Number.isFinite(n))
41
+ return min;
42
+ return Math.min(Math.max(n, min), max);
43
+ }
44
+ function pickFirstPositive(...values) {
45
+ for (const value of values) {
46
+ const parsed = Number(value ?? 0);
47
+ if (Number.isFinite(parsed) && parsed > 0)
48
+ return parsed;
49
+ }
50
+ return 0;
51
+ }
52
+ function resolveEffectiveMax(displayValue, browserValue) {
53
+ if (displayValue > 0 && browserValue > 0) {
54
+ return Math.min(displayValue, browserValue);
55
+ }
56
+ return displayValue > 0 ? displayValue : browserValue;
57
+ }
58
+ async function getScreenMetrics() {
59
+ try {
60
+ const res = await fetch(statusUrl, {
61
+ method: 'POST',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify({
64
+ action: 'evaluate',
65
+ args: {
66
+ profileId,
67
+ script: `(() => ({
68
+ innerWidth: window.innerWidth,
69
+ innerHeight: window.innerHeight,
70
+ screenWidth: window.screen?.width,
71
+ screenHeight: window.screen?.height,
72
+ availWidth: window.screen?.availWidth,
73
+ availHeight: window.screen?.availHeight,
74
+ devicePixelRatio: window.devicePixelRatio
75
+ }))()`,
76
+ },
77
+ }),
78
+ }).then((r) => r.json().catch(() => ({})));
79
+ const payload = res?.body || res?.data || res;
80
+ const result = payload?.result || payload?.data?.result;
81
+ if (!result || typeof result !== 'object')
82
+ return null;
83
+ return result;
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ let cachedMetrics = null;
90
+ let cachedDisplay = null;
91
+ async function loadMetrics() {
92
+ if (cachedMetrics)
93
+ return cachedMetrics;
94
+ cachedMetrics = await getScreenMetrics();
95
+ return cachedMetrics;
96
+ }
97
+ async function getDisplayMetrics() {
98
+ try {
99
+ const res = await fetch(statusUrl, {
100
+ method: 'POST',
101
+ headers: { 'Content-Type': 'application/json' },
102
+ body: JSON.stringify({ action: 'system:display' }),
103
+ }).then((r) => r.json().catch(() => ({})));
104
+ const payload = res?.body || res?.data || res;
105
+ const metrics = payload?.metrics || payload?.data?.metrics || null;
106
+ if (!metrics || typeof metrics !== 'object')
107
+ return null;
108
+ return metrics;
109
+ }
110
+ catch {
111
+ return null;
112
+ }
113
+ }
114
+ async function loadDisplay() {
115
+ if (cachedDisplay)
116
+ return cachedDisplay;
117
+ cachedDisplay = await getDisplayMetrics();
118
+ return cachedDisplay;
119
+ }
120
+ async function resolveViewportSize() {
121
+ const metrics = await loadMetrics();
122
+ const maxW = Number(metrics?.availWidth || metrics?.screenWidth || 0);
123
+ const maxH = Number(metrics?.availHeight || metrics?.screenHeight || 0);
124
+ const innerW = Number(metrics?.innerWidth || 0);
125
+ const innerH = Number(metrics?.innerHeight || 0);
126
+ const display = await loadDisplay();
127
+ const displayW = pickFirstPositive(display?.workWidth, display?.width, display?.nativeWidth);
128
+ const displayH = pickFirstPositive(display?.workHeight, display?.height, display?.nativeHeight);
129
+ const effectiveMaxW = resolveEffectiveMax(displayW, maxW);
130
+ const effectiveMaxH = resolveEffectiveMax(displayH, maxH);
131
+ const widthCandidate = Number.isFinite(desiredViewport.width) ? desiredViewport.width : 1440;
132
+ const heightCandidate = Number.isFinite(desiredViewport.height) ? desiredViewport.height : 900;
133
+ const safeMaxW = effectiveMaxW > 0 ? Math.max(900, effectiveMaxW - 40) : 1920;
134
+ const safeMaxH = effectiveMaxH > 0 ? Math.max(700, effectiveMaxH - 40) : 1200;
135
+ const widthBase = effectiveMaxW > 0 ? effectiveMaxW - 40 : (innerW > 0 ? innerW : widthCandidate);
136
+ const heightBase = effectiveMaxH > 0 ? effectiveMaxH - 40 : (innerH > 0 ? innerH : heightCandidate);
137
+ const width = clamp(widthBase, 900, safeMaxW);
138
+ const height = clamp(heightBase, 700, safeMaxH);
139
+ console.log(`[EnsureSession] display metrics: browser=${maxW}x${maxH} display=${displayW}x${displayH} ` +
140
+ `effective=${effectiveMaxW}x${effectiveMaxH} inner=${innerW}x${innerH}`);
141
+ return { width: Math.floor(width), height: Math.floor(height) };
142
+ }
143
+ function resolveZoom(metrics) {
144
+ if (!allowCssZoom)
145
+ return null;
146
+ if (!zoomRaw)
147
+ return null;
148
+ if (zoomRaw === 'auto') {
149
+ const dpr = Number(metrics?.devicePixelRatio || 0);
150
+ if (Number.isFinite(dpr) && dpr > 1.05) {
151
+ return clamp(1 / dpr, 0.5, 1);
152
+ }
153
+ return 1;
154
+ }
155
+ if (zoomRaw) {
156
+ const parsed = Number(zoomRaw);
157
+ if (Number.isFinite(parsed))
158
+ return clamp(parsed, 0.25, 1);
159
+ }
160
+ return 1;
161
+ }
162
+ async function clearCssZoom() {
163
+ if (!profileId.startsWith('xiaohongshu_'))
164
+ return;
165
+ try {
166
+ await fetch(statusUrl, {
167
+ method: 'POST',
168
+ headers: { 'Content-Type': 'application/json' },
169
+ body: JSON.stringify({
170
+ action: 'evaluate',
171
+ args: {
172
+ profileId,
173
+ script: `(() => {
174
+ if (document.documentElement) document.documentElement.style.zoom = '';
175
+ if (document.body) document.body.style.zoom = '';
176
+ return true;
177
+ })()`,
178
+ },
179
+ }),
180
+ }).then((r) => r.json().catch(() => ({})));
181
+ }
182
+ catch (error) {
183
+ console.warn(`[EnsureSession] zoom clear failed: ${error?.message || String(error)}`);
184
+ }
185
+ }
186
+ async function applyZoom() {
187
+ if (!profileId.startsWith('xiaohongshu_'))
188
+ return;
189
+ const metrics = await loadMetrics();
190
+ const zoom = resolveZoom(metrics);
191
+ if (zoom === null) {
192
+ await clearCssZoom();
193
+ return;
194
+ }
195
+ try {
196
+ await fetch(statusUrl, {
197
+ method: 'POST',
198
+ headers: { 'Content-Type': 'application/json' },
199
+ body: JSON.stringify({
200
+ action: 'evaluate',
201
+ args: {
202
+ profileId,
203
+ script: `(() => {
204
+ const z = ${JSON.stringify(zoom)};
205
+ const normalized = Math.abs(z - 1) < 0.01 ? '' : String(z);
206
+ if (document.documentElement) document.documentElement.style.zoom = normalized;
207
+ if (document.body) document.body.style.zoom = normalized;
208
+ return { zoom: z };
209
+ })()`,
210
+ },
211
+ }),
212
+ }).then((r) => r.json().catch(() => ({})));
213
+ console.log(`[EnsureSession] zoom set: ${zoom} (profile=${profileId})`);
214
+ }
215
+ catch (error) {
216
+ console.warn(`[EnsureSession] zoom set failed: ${error?.message || String(error)}`);
217
+ }
218
+ }
219
+ function parseZoomValue(raw) {
220
+ if (!raw)
221
+ return null;
222
+ if (raw.endsWith('%')) {
223
+ const parsed = Number(raw.replace('%', '').trim());
224
+ if (Number.isFinite(parsed) && parsed > 0)
225
+ return parsed / 100;
226
+ return null;
227
+ }
228
+ const parsed = Number(raw);
229
+ if (Number.isFinite(parsed) && parsed > 0)
230
+ return parsed;
231
+ return null;
232
+ }
233
+ function resolveBrowserZoomTarget() {
234
+ if (browserZoomRaw === '0' || browserZoomRaw === '1' || browserZoomRaw === '100%')
235
+ return null;
236
+ const explicit = parseZoomValue(browserZoomRaw);
237
+ if (explicit)
238
+ return explicit;
239
+ return null;
240
+ }
241
+ async function applyBrowserZoom() {
242
+ if (!profileId.startsWith('xiaohongshu_'))
243
+ return;
244
+ const target = resolveBrowserZoomTarget();
245
+ if (!target || Math.abs(target - 1) < 0.01)
246
+ return;
247
+ const zoomOutSteps = [0.9, 0.8, 0.67, 0.5, 0.33, 0.25];
248
+ const zoomInSteps = [1.1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5];
249
+ const steps = target < 1
250
+ ? Math.max(1, zoomOutSteps.reduce((best, value, idx) => {
251
+ const diff = Math.abs(value - target);
252
+ return diff < best.diff ? { diff, idx } : best;
253
+ }, { diff: Number.POSITIVE_INFINITY, idx: 0 }).idx + 1)
254
+ : Math.max(1, zoomInSteps.reduce((best, value, idx) => {
255
+ const diff = Math.abs(value - target);
256
+ return diff < best.diff ? { diff, idx } : best;
257
+ }, { diff: Number.POSITIVE_INFINITY, idx: 0 }).idx + 1);
258
+ const key = os.platform() === 'darwin'
259
+ ? (target < 1 ? 'Meta+-' : 'Meta++')
260
+ : (target < 1 ? 'Control+-' : 'Control++');
261
+ for (let i = 0; i < steps; i += 1) {
262
+ try {
263
+ await fetch(statusUrl, {
264
+ method: 'POST',
265
+ headers: { 'Content-Type': 'application/json' },
266
+ body: JSON.stringify({
267
+ action: 'keyboard:press',
268
+ args: { profileId, key },
269
+ }),
270
+ }).then((r) => r.json().catch(() => ({})));
271
+ await new Promise((r) => setTimeout(r, 80));
272
+ }
273
+ catch (error) {
274
+ console.warn(`[EnsureSession] browser zoom step failed: ${error?.message || String(error)}`);
275
+ break;
276
+ }
277
+ }
278
+ console.log(`[EnsureSession] browser zoom applied target~${target} steps=${steps} key=${key} profile=${profileId}`);
279
+ }
280
+ async function resetBrowserZoom() {
281
+ if (!profileId.startsWith('xiaohongshu_'))
282
+ return;
283
+ if (process.env.WEBAUTO_RESET_BROWSER_ZOOM === '0')
284
+ return;
285
+ const key = os.platform() === 'darwin' ? 'Meta+0' : 'Control+0';
286
+ try {
287
+ await fetch(statusUrl, {
288
+ method: 'POST',
289
+ headers: { 'Content-Type': 'application/json' },
290
+ body: JSON.stringify({
291
+ action: 'keyboard:press',
292
+ args: { profileId, key },
293
+ }),
294
+ }).then((r) => r.json().catch(() => ({})));
295
+ console.log(`[EnsureSession] browser zoom reset (${key}) profile=${profileId}`);
296
+ }
297
+ catch (error) {
298
+ console.warn(`[EnsureSession] browser zoom reset failed: ${error?.message || String(error)}`);
299
+ }
300
+ }
301
+ async function setViewport() {
302
+ const resolvedViewport = await resolveViewportSize();
303
+ const res = await fetch(statusUrl, {
304
+ method: 'POST',
305
+ headers: { 'Content-Type': 'application/json' },
306
+ body: JSON.stringify({
307
+ action: 'page:setViewport',
308
+ args: {
309
+ profileId,
310
+ width: resolvedViewport.width,
311
+ height: resolvedViewport.height,
312
+ },
313
+ }),
314
+ }).then((r) => r.json().catch(() => ({})));
315
+ const ok = !(res?.ok === false || res?.success === false);
316
+ if (!ok)
317
+ return { ok: false, error: res?.error || 'viewport_set_failed' };
318
+ console.log(`[EnsureSession] viewport set: ${resolvedViewport.width}x${resolvedViewport.height} (profile=${profileId})`);
319
+ await resetBrowserZoom();
320
+ await applyBrowserZoom();
321
+ await applyZoom();
322
+ return { ok: true };
323
+ }
324
+ const statusRes = await fetch(statusUrl, {
325
+ method: 'POST',
326
+ headers: { 'Content-Type': 'application/json' },
327
+ body: JSON.stringify({ action: 'getStatus' })
328
+ });
329
+ const statusData = await statusRes.json();
330
+ const sessions = statusData.sessions ||
331
+ statusData.data?.sessions ||
332
+ statusData.body?.sessions ||
333
+ (Array.isArray(statusData.data) ? statusData.data : []) ||
334
+ [];
335
+ const existing = Array.isArray(sessions)
336
+ ? sessions.find((s) => s?.profileId === profileId)
337
+ : null;
338
+ if (existing) {
339
+ if (url && existing.url !== url) {
340
+ await fetch(statusUrl, {
341
+ method: 'POST',
342
+ headers: { 'Content-Type': 'application/json' },
343
+ body: JSON.stringify({
344
+ action: 'goto',
345
+ args: { profileId, url }
346
+ })
347
+ });
348
+ }
349
+ // Phase1 关键约束:保证视口高度足够(避免第二排卡片误点)
350
+ const vp = await setViewport();
351
+ if (!vp.ok) {
352
+ return {
353
+ sessionId: existing.sessionId || existing.id || profileId,
354
+ status: 'active',
355
+ currentPage: url || existing.url,
356
+ error: vp.error,
357
+ };
358
+ }
359
+ return {
360
+ sessionId: existing.sessionId || existing.id || profileId,
361
+ status: 'active',
362
+ currentPage: url || existing.url
363
+ };
364
+ }
365
+ let startData = null;
366
+ let startError = null;
367
+ while (true) {
368
+ startError = null;
369
+ try {
370
+ const startRes = await fetch(statusUrl, {
371
+ method: 'POST',
372
+ headers: { 'Content-Type': 'application/json' },
373
+ body: JSON.stringify({
374
+ action: 'start',
375
+ args: {
376
+ profileId,
377
+ url,
378
+ headless
379
+ }
380
+ })
381
+ });
382
+ startData = await startRes.json();
383
+ if (!startData.ok) {
384
+ startError = startData.error || 'Failed to start session';
385
+ }
386
+ }
387
+ catch (error) {
388
+ startError = error?.message || String(error);
389
+ }
390
+ if (!startError)
391
+ break;
392
+ if (!waitOnFail) {
393
+ throw new Error(startError);
394
+ }
395
+ if (Date.now() - waitStartTs > waitLimitMs) {
396
+ throw new Error(startError);
397
+ }
398
+ console.warn(`[EnsureSession] start failed: ${startError}`);
399
+ console.warn(`[EnsureSession] waiting ${waitIntervalMs}ms and retrying...`);
400
+ await new Promise((r) => setTimeout(r, waitIntervalMs));
401
+ }
402
+ const vp = await setViewport();
403
+ if (!vp.ok) {
404
+ return {
405
+ sessionId: startData.sessionId || profileId,
406
+ status: 'created',
407
+ currentPage: url || '',
408
+ error: vp.error,
409
+ };
410
+ }
411
+ return {
412
+ sessionId: startData.sessionId || profileId,
413
+ status: 'created',
414
+ currentPage: url || ''
415
+ };
416
+ }
417
+ catch (error) {
418
+ return {
419
+ sessionId: '',
420
+ status: 'active',
421
+ currentPage: '',
422
+ error: `Session error: ${error.message}`
423
+ };
424
+ }
425
+ }
426
+ //# sourceMappingURL=EnsureSession.js.map
@@ -0,0 +1,164 @@
1
+ /**
2
+ * ErrorClassifier - 错误分类与重试策略(P2.2 增强版)
3
+ *
4
+ * 用于区分:
5
+ * - 临时性错误(可重试):网络抖动、超时
6
+ * - 永久性错误(应跳过):404、无效选择器
7
+ * - 系统性错误(必须终止):session 失效、频繁风控
8
+ * - 可降级错误(可继续但降级处理):评论展开部分失败、图片下载失败
9
+ *
10
+ * P2.2 改进:
11
+ * - 细化错误分类,区分"跳过当前条目"和"终止整个任务"
12
+ * - 避免临时错误触发重型恢复(如回首页)
13
+ * - 对降级错误提供明确的 fallback 建议
14
+ */
15
+ /**
16
+ * 细化错误分类
17
+ *
18
+ * @param error - 错误对象或消息
19
+ * @param context - 错误发生的上下文(如 'search', 'detail', 'comment')
20
+ * @returns ErrorClassification
21
+ */
22
+ export function classifyError(error, context) {
23
+ const msg = String(error?.message || error || '').toLowerCase();
24
+ // 1. 系统性错误:必须终止整个任务
25
+ if (msg.includes('session') ||
26
+ msg.includes('unauthorized') ||
27
+ msg.includes('blocked') ||
28
+ msg.includes('login_status_uncertain') ||
29
+ msg.includes('频繁') ||
30
+ msg.includes('risk control')) {
31
+ return {
32
+ type: 'SYSTEMIC',
33
+ action: 'ABORT_TASK',
34
+ retryable: false,
35
+ fatal: true,
36
+ suggestion: '会话失效或触发风控,建议手动检查浏览器状态并重新登录'
37
+ };
38
+ }
39
+ // 2. 临时性错误:可重试,但不要过度重试
40
+ if (msg.includes('timeout') ||
41
+ msg.includes('etimedout') ||
42
+ msg.includes('network') ||
43
+ msg.includes('econnrefused') ||
44
+ msg.includes('fetch failed')) {
45
+ return {
46
+ type: 'TEMPORARY',
47
+ action: 'RETRY',
48
+ retryable: true,
49
+ backoffMs: 3000,
50
+ suggestion: '网络抖动或临时超时,已自动重试'
51
+ };
52
+ }
53
+ // 3. 永久性错误:跳过当前条目
54
+ if (msg.includes('not found') ||
55
+ msg.includes('404') ||
56
+ msg.includes('error_code=300031') || // 小红书 404 笔记
57
+ msg.includes('invalid selector') ||
58
+ msg.includes('容器未找到') ||
59
+ msg.includes('未获取到容器树')) {
60
+ return {
61
+ type: 'PERMANENT',
62
+ action: 'SKIP_ITEM',
63
+ retryable: false,
64
+ suggestion: '资源不存在或容器定义过时,跳过当前条目'
65
+ };
66
+ }
67
+ // 4. 可降级错误:部分功能失败但可继续
68
+ // 例如:评论展开失败、图片下载失败、锚点验证失败(但 DOM 存在)
69
+ if (msg.includes('comment') ||
70
+ msg.includes('image download') ||
71
+ msg.includes('anchor verification failed') ||
72
+ msg.includes('rect') ||
73
+ msg.includes('highlight failed')) {
74
+ // 根据上下文决定降级策略
75
+ if (context === 'comment') {
76
+ return {
77
+ type: 'DEGRADED',
78
+ action: 'GRACEFUL_DEGRADE',
79
+ retryable: false,
80
+ suggestion: '评论采集部分失败,保存已有数据并标记 commentsPartial=true'
81
+ };
82
+ }
83
+ if (context === 'detail') {
84
+ return {
85
+ type: 'DEGRADED',
86
+ action: 'GRACEFUL_DEGRADE',
87
+ retryable: false,
88
+ suggestion: '详情部分字段缺失,保存已提取内容并标记 detailPartial=true'
89
+ };
90
+ }
91
+ // 默认:锚点验证失败等非关键错误
92
+ return {
93
+ type: 'DEGRADED',
94
+ action: 'GRACEFUL_DEGRADE',
95
+ retryable: false,
96
+ suggestion: '非关键功能失败,继续执行后续步骤'
97
+ };
98
+ }
99
+ // 5. 默认未分类错误:跳过当前条目(保守策略)
100
+ return {
101
+ type: 'PERMANENT',
102
+ action: 'SKIP_ITEM',
103
+ retryable: false,
104
+ suggestion: '未知错误,跳过当前条目以避免阻塞后续采集'
105
+ };
106
+ }
107
+ /**
108
+ * 重试包装器(指数退避 + 随机抖动)
109
+ *
110
+ * P2.2 改进:
111
+ * - 支持上下文传递,用于细化错误分类
112
+ * - 对非 retryable 错误立即抛出(不浪费重试次数)
113
+ */
114
+ export async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000, context) {
115
+ let lastError;
116
+ for (let i = 0; i < maxRetries; i++) {
117
+ try {
118
+ return await fn();
119
+ }
120
+ catch (err) {
121
+ lastError = err;
122
+ const classified = classifyError(err, context);
123
+ // 系统性错误或永久性错误:立即抛出,不重试
124
+ if (classified.action === 'ABORT_TASK' || classified.action === 'SKIP_ITEM') {
125
+ console.warn(`[Retry] ${classified.type} error (${classified.action}): ${classified.suggestion}`);
126
+ throw err;
127
+ }
128
+ // 可降级错误:立即抛出,由上层决定是否降级
129
+ if (classified.action === 'GRACEFUL_DEGRADE') {
130
+ console.warn(`[Retry] ${classified.type} error: ${classified.suggestion}`);
131
+ throw err;
132
+ }
133
+ // 临时性错误:重试
134
+ if (classified.retryable && i < maxRetries - 1) {
135
+ const delay = (classified.backoffMs || baseDelay) * Math.pow(2, i) + Math.random() * 1000;
136
+ console.log(`[Retry] ${classified.type} error, retry ${i + 1}/${maxRetries} in ${Math.round(delay)}ms`);
137
+ await new Promise(resolve => setTimeout(resolve, delay));
138
+ }
139
+ else {
140
+ // 重试次数耗尽
141
+ console.warn(`[Retry] Max retries reached for ${classified.type} error`);
142
+ throw err;
143
+ }
144
+ }
145
+ }
146
+ throw lastError;
147
+ }
148
+ /**
149
+ * 根据错误分类决定下一步动作
150
+ *
151
+ * @param error - 错误对象
152
+ * @param context - 错误上下文
153
+ * @returns 建议的恢复动作
154
+ */
155
+ export function getRecoveryAction(error, context) {
156
+ const classified = classifyError(error, context);
157
+ return {
158
+ action: classified.action,
159
+ suggestion: classified.suggestion || '',
160
+ shouldLog: true,
161
+ shouldSavePartial: classified.action === 'GRACEFUL_DEGRADE'
162
+ };
163
+ }
164
+ //# sourceMappingURL=ErrorClassifier.js.map