@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,587 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import {
5
+ ensureProfile,
6
+ resolveFingerprintsRoot,
7
+ resolveNextProfileId,
8
+ resolveProfilesRoot,
9
+ } from './profilepool.mjs';
10
+
11
+ const INDEX_FILE = 'index.json';
12
+ const META_FILE = 'meta.json';
13
+ const DEFAULT_PLATFORM = 'xiaohongshu';
14
+ const STATUS_ACTIVE = 'active';
15
+ const STATUS_VALID = 'valid';
16
+ const STATUS_INVALID = 'invalid';
17
+
18
+ function nowIso() {
19
+ return new Date().toISOString();
20
+ }
21
+
22
+ function readJson(filePath, fallback) {
23
+ try {
24
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
25
+ } catch {
26
+ return fallback;
27
+ }
28
+ }
29
+
30
+ function writeJson(filePath, data) {
31
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
32
+ fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
33
+ }
34
+
35
+ function toSlug(input) {
36
+ return String(input || '')
37
+ .trim()
38
+ .toLowerCase()
39
+ .replace(/[^a-z0-9]+/g, '-')
40
+ .replace(/^-+|-+$/g, '');
41
+ }
42
+
43
+ function normalizePlatform(input) {
44
+ const raw = String(input || DEFAULT_PLATFORM).trim().toLowerCase();
45
+ if (!raw || raw === 'xhs') return DEFAULT_PLATFORM;
46
+ return raw;
47
+ }
48
+
49
+ function normalizeText(input) {
50
+ const value = String(input ?? '').trim();
51
+ return value || null;
52
+ }
53
+
54
+ function normalizeAlias(input) {
55
+ const alias = normalizeText(input);
56
+ return alias ? alias.slice(0, 80) : null;
57
+ }
58
+
59
+ function toTimeMs(value) {
60
+ if (!value) return 0;
61
+ const ms = Date.parse(String(value));
62
+ return Number.isFinite(ms) ? ms : 0;
63
+ }
64
+
65
+ function pickNewerRecord(a, b) {
66
+ if (!a) return b || null;
67
+ if (!b) return a;
68
+ const aTime = Math.max(toTimeMs(a.updatedAt), toTimeMs(a.detectedAt), toTimeMs(a.createdAt));
69
+ const bTime = Math.max(toTimeMs(b.updatedAt), toTimeMs(b.detectedAt), toTimeMs(b.createdAt));
70
+ if (aTime !== bTime) return bTime > aTime ? b : a;
71
+ const aSeq = Number(a.seq) || 0;
72
+ const bSeq = Number(b.seq) || 0;
73
+ return bSeq >= aSeq ? b : a;
74
+ }
75
+
76
+ function normalizeStatus(input) {
77
+ const status = String(input || STATUS_ACTIVE).trim().toLowerCase();
78
+ if (!status) return STATUS_ACTIVE;
79
+ if (
80
+ status === 'active'
81
+ || status === 'disabled'
82
+ || status === 'archived'
83
+ || status === 'pending'
84
+ || status === STATUS_VALID
85
+ || status === STATUS_INVALID
86
+ ) return status;
87
+ return STATUS_ACTIVE;
88
+ }
89
+
90
+ function formatSeq(seq) {
91
+ return String(seq).padStart(4, '0');
92
+ }
93
+
94
+ function normalizeId(id, fallbackPlatform, seq) {
95
+ const cleaned = toSlug(id || '');
96
+ if (cleaned) return cleaned;
97
+ const platform = normalizePlatform(fallbackPlatform);
98
+ const tag = platform === 'xiaohongshu' ? 'xhs' : (toSlug(platform).split('-')[0] || 'acct');
99
+ return `${tag}-${formatSeq(seq)}`;
100
+ }
101
+
102
+ function ensureSafeName(name, field) {
103
+ const value = String(name || '').trim();
104
+ if (!value) throw new Error(`${field} is required`);
105
+ if (value.includes('/') || value.includes('\\')) throw new Error(`${field} is invalid`);
106
+ if (value === '.' || value === '..') throw new Error(`${field} is invalid`);
107
+ return value;
108
+ }
109
+
110
+ function resolvePortableRoot() {
111
+ const root = String(process.env.WEBAUTO_PORTABLE_ROOT || process.env.WEBAUTO_ROOT || '').trim();
112
+ return root ? path.join(root, '.webauto') : '';
113
+ }
114
+
115
+ export function resolveWebautoRoot() {
116
+ const portableRoot = resolvePortableRoot();
117
+ if (portableRoot) return portableRoot;
118
+ const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
119
+ return path.join(home, '.webauto');
120
+ }
121
+
122
+ export function resolveAccountsRoot() {
123
+ const envRoot = String(process.env.WEBAUTO_PATHS_ACCOUNTS || '').trim();
124
+ if (envRoot) return envRoot;
125
+ return path.join(resolveWebautoRoot(), 'accounts');
126
+ }
127
+
128
+ function resolveIndexPath() {
129
+ return path.join(resolveAccountsRoot(), INDEX_FILE);
130
+ }
131
+
132
+ function resolveAccountDir(id) {
133
+ return path.join(resolveAccountsRoot(), id);
134
+ }
135
+
136
+ function isWithinDir(rootDir, targetPath) {
137
+ const root = path.resolve(rootDir);
138
+ const target = path.resolve(targetPath);
139
+ return target === root || target.startsWith(`${root}${path.sep}`);
140
+ }
141
+
142
+ function loadIndex() {
143
+ const fallback = { version: 1, nextSeq: 1, updatedAt: null, accounts: [] };
144
+ const raw = readJson(resolveIndexPath(), fallback);
145
+ const accounts = Array.isArray(raw?.accounts) ? raw.accounts : [];
146
+ const maxSeq = accounts.reduce((max, account) => {
147
+ const seq = Number(account?.seq);
148
+ return Number.isFinite(seq) ? Math.max(max, seq) : max;
149
+ }, 0);
150
+ const nextSeq = Number.isFinite(Number(raw?.nextSeq)) && Number(raw?.nextSeq) > maxSeq
151
+ ? Number(raw.nextSeq)
152
+ : maxSeq + 1;
153
+ return {
154
+ version: 1,
155
+ nextSeq,
156
+ updatedAt: raw?.updatedAt || null,
157
+ accounts,
158
+ };
159
+ }
160
+
161
+ function saveIndex(index) {
162
+ const payload = {
163
+ version: 1,
164
+ nextSeq: Number(index?.nextSeq) || 1,
165
+ updatedAt: nowIso(),
166
+ accounts: Array.isArray(index?.accounts) ? index.accounts : [],
167
+ };
168
+ writeJson(resolveIndexPath(), payload);
169
+ return payload;
170
+ }
171
+
172
+ function resolveProfilePrefix(platform) {
173
+ if (platform === 'xiaohongshu') return 'xiaohongshu-batch';
174
+ const slug = toSlug(platform) || 'account';
175
+ return `${slug}-account`;
176
+ }
177
+
178
+ function ensureAliasUnique(accounts, alias, exceptId = '') {
179
+ if (!alias) return;
180
+ const target = alias.toLowerCase();
181
+ for (const account of accounts) {
182
+ if (!account || account.id === exceptId) continue;
183
+ const candidate = String(account.alias || '').trim().toLowerCase();
184
+ if (candidate && candidate === target) {
185
+ throw new Error(`alias already exists: ${alias}`);
186
+ }
187
+ }
188
+ }
189
+
190
+ function resolveAccountOrThrow(index, key) {
191
+ const idOrAlias = String(key || '').trim();
192
+ if (!idOrAlias) throw new Error('account id or alias is required');
193
+ const byId = index.accounts.find((item) => item?.id === idOrAlias);
194
+ if (byId) return byId;
195
+ const target = idOrAlias.toLowerCase();
196
+ const byAlias = index.accounts.filter((item) => String(item?.alias || '').trim().toLowerCase() === target);
197
+ if (byAlias.length === 1) return byAlias[0];
198
+ if (byAlias.length > 1) throw new Error(`alias is not unique: ${idOrAlias}`);
199
+ throw new Error(`account not found: ${idOrAlias}`);
200
+ }
201
+
202
+ function resolveAccountByProfile(index, profileId) {
203
+ const value = String(profileId || '').trim();
204
+ if (!value) return null;
205
+ let matched = null;
206
+ for (const item of index.accounts) {
207
+ if (String(item?.profileId || '').trim() !== value) continue;
208
+ matched = pickNewerRecord(matched, item);
209
+ }
210
+ return matched;
211
+ }
212
+
213
+ function resolveAccountByAccountId(index, accountId) {
214
+ const value = normalizeText(accountId);
215
+ if (!value) return null;
216
+ let matched = null;
217
+ for (const item of index.accounts) {
218
+ if (normalizeText(item?.accountId) !== value) continue;
219
+ matched = pickNewerRecord(matched, item);
220
+ }
221
+ return matched;
222
+ }
223
+
224
+ function persistAccountMeta(account) {
225
+ const dir = resolveAccountDir(account.id);
226
+ writeJson(path.join(dir, META_FILE), account);
227
+ }
228
+
229
+ function deleteAccountMeta(id) {
230
+ const dir = resolveAccountDir(id);
231
+ if (fs.existsSync(dir)) {
232
+ fs.rmSync(dir, { recursive: true, force: true });
233
+ }
234
+ }
235
+
236
+ function resolveAccountName(inputName, platform, seq) {
237
+ const provided = normalizeText(inputName);
238
+ if (provided) return provided.slice(0, 120);
239
+ return `${platform}-account-${formatSeq(seq)}`;
240
+ }
241
+
242
+ export function listAccounts() {
243
+ const index = loadIndex();
244
+ const accounts = [...index.accounts].sort((a, b) => Number(a?.seq || 0) - Number(b?.seq || 0));
245
+ return { root: resolveAccountsRoot(), count: accounts.length, accounts };
246
+ }
247
+
248
+ function buildProfileAccountView(profileId, record = null) {
249
+ const accountId = normalizeText(record?.accountId);
250
+ const status = normalizeStatus(record?.status || (accountId ? STATUS_VALID : STATUS_INVALID));
251
+ const valid = status === STATUS_VALID && Boolean(accountId);
252
+ return {
253
+ profileId,
254
+ accountRecordId: record?.id || null,
255
+ accountId,
256
+ alias: normalizeText(record?.alias),
257
+ name: normalizeText(record?.name),
258
+ status,
259
+ valid,
260
+ reason: normalizeText(record?.reason) || (valid ? null : 'missing_account_id'),
261
+ updatedAt: record?.updatedAt || null,
262
+ };
263
+ }
264
+
265
+ export function listAccountProfiles() {
266
+ const index = loadIndex();
267
+ const byAccountId = new Map();
268
+ for (const record of index.accounts) {
269
+ const accountId = normalizeText(record?.accountId);
270
+ if (!accountId) continue;
271
+ byAccountId.set(accountId, pickNewerRecord(byAccountId.get(accountId), record));
272
+ }
273
+ const deduped = [];
274
+ for (const record of index.accounts) {
275
+ const accountId = normalizeText(record?.accountId);
276
+ if (accountId) {
277
+ if (byAccountId.get(accountId) !== record) continue;
278
+ }
279
+ deduped.push(record);
280
+ }
281
+ const byProfile = new Map();
282
+ for (const record of deduped) {
283
+ const profileId = normalizeText(record?.profileId);
284
+ if (!profileId) continue;
285
+ byProfile.set(profileId, pickNewerRecord(byProfile.get(profileId), record));
286
+ }
287
+ const rows = Array.from(byProfile.entries())
288
+ .sort((a, b) => (Number(a[1]?.seq || 0) - Number(b[1]?.seq || 0)))
289
+ .map(([profileId, record]) => buildProfileAccountView(profileId, record));
290
+ const validProfiles = rows.filter((item) => item.valid).map((item) => item.profileId);
291
+ const invalidProfiles = rows.filter((item) => !item.valid).map((item) => item.profileId);
292
+ return {
293
+ root: resolveAccountsRoot(),
294
+ count: rows.length,
295
+ profiles: rows,
296
+ validProfiles,
297
+ invalidProfiles,
298
+ };
299
+ }
300
+
301
+ export function getAccount(idOrAlias) {
302
+ const index = loadIndex();
303
+ return resolveAccountOrThrow(index, idOrAlias);
304
+ }
305
+
306
+ export async function addAccount(input = {}) {
307
+ const index = loadIndex();
308
+ const seq = Number(index.nextSeq) || 1;
309
+ const platform = normalizePlatform(input.platform);
310
+ const id = ensureSafeName(normalizeId(input.id, platform, seq), 'id');
311
+ if (index.accounts.some((item) => item?.id === id)) {
312
+ throw new Error(`account id already exists: ${id}`);
313
+ }
314
+
315
+ const alias = normalizeAlias(input.alias) || normalizeAlias(input.username);
316
+ ensureAliasUnique(index.accounts, alias);
317
+
318
+ const profileId = normalizeText(input.profileId)
319
+ || resolveNextProfileId(resolveProfilePrefix(platform));
320
+ await ensureProfile(profileId);
321
+
322
+ const fingerprintId = normalizeText(input.fingerprintId) || profileId;
323
+ const createdAt = nowIso();
324
+ const accountId = normalizeText(input.accountId || input.platformAccountId || null);
325
+ const status = accountId ? STATUS_VALID : STATUS_INVALID;
326
+ const account = {
327
+ id,
328
+ seq,
329
+ platform,
330
+ status,
331
+ valid: status === STATUS_VALID,
332
+ reason: status === STATUS_VALID ? null : 'missing_account_id',
333
+ accountId,
334
+ name: accountId || resolveAccountName(input.name, platform, seq),
335
+ alias,
336
+ username: normalizeText(input.username),
337
+ profileId,
338
+ fingerprintId,
339
+ createdAt,
340
+ updatedAt: createdAt,
341
+ aliasSource: alias ? (normalizeAlias(input.alias) ? 'manual' : 'username') : null,
342
+ };
343
+
344
+ index.accounts.push(account);
345
+ index.nextSeq = seq + 1;
346
+ saveIndex(index);
347
+ persistAccountMeta(account);
348
+
349
+ return {
350
+ root: resolveAccountsRoot(),
351
+ account,
352
+ };
353
+ }
354
+
355
+ export async function updateAccount(idOrAlias, patch = {}) {
356
+ const index = loadIndex();
357
+ const account = resolveAccountOrThrow(index, idOrAlias);
358
+ const next = { ...account };
359
+
360
+ if (Object.prototype.hasOwnProperty.call(patch, 'platform')) {
361
+ next.platform = normalizePlatform(patch.platform);
362
+ }
363
+ if (Object.prototype.hasOwnProperty.call(patch, 'name')) {
364
+ const name = normalizeText(patch.name);
365
+ if (name) next.name = name.slice(0, 120);
366
+ }
367
+ if (Object.prototype.hasOwnProperty.call(patch, 'username')) {
368
+ next.username = normalizeText(patch.username);
369
+ }
370
+ if (Object.prototype.hasOwnProperty.call(patch, 'status')) {
371
+ next.status = normalizeStatus(patch.status);
372
+ }
373
+ if (Object.prototype.hasOwnProperty.call(patch, 'accountId')) {
374
+ next.accountId = normalizeText(patch.accountId);
375
+ }
376
+ if (Object.prototype.hasOwnProperty.call(patch, 'reason')) {
377
+ next.reason = normalizeText(patch.reason);
378
+ }
379
+ if (Object.prototype.hasOwnProperty.call(patch, 'profileId')) {
380
+ const profileId = ensureSafeName(normalizeText(patch.profileId), 'profileId');
381
+ if (profileId !== next.profileId) {
382
+ await ensureProfile(profileId);
383
+ next.profileId = profileId;
384
+ if (!Object.prototype.hasOwnProperty.call(patch, 'fingerprintId')) {
385
+ next.fingerprintId = profileId;
386
+ }
387
+ }
388
+ }
389
+ if (Object.prototype.hasOwnProperty.call(patch, 'fingerprintId')) {
390
+ const fp = ensureSafeName(normalizeText(patch.fingerprintId), 'fingerprintId');
391
+ next.fingerprintId = fp;
392
+ }
393
+ if (Object.prototype.hasOwnProperty.call(patch, 'alias')) {
394
+ const alias = normalizeAlias(patch.alias);
395
+ ensureAliasUnique(index.accounts, alias, next.id);
396
+ next.alias = alias;
397
+ }
398
+ if (Object.prototype.hasOwnProperty.call(patch, 'aliasSource')) {
399
+ next.aliasSource = normalizeText(patch.aliasSource);
400
+ }
401
+ if (Object.prototype.hasOwnProperty.call(patch, 'valid')) {
402
+ next.valid = patch.valid === true;
403
+ }
404
+
405
+ if (next.accountId && (!next.name || String(next.name).startsWith(`${next.platform}-account-`))) {
406
+ next.name = String(next.accountId);
407
+ }
408
+ if (!next.accountId) {
409
+ next.status = STATUS_INVALID;
410
+ next.valid = false;
411
+ if (!next.reason) next.reason = 'missing_account_id';
412
+ } else if (next.status !== 'disabled' && next.status !== 'archived') {
413
+ next.status = next.valid === false ? STATUS_INVALID : STATUS_VALID;
414
+ }
415
+ if (next.status === STATUS_VALID) {
416
+ next.valid = true;
417
+ next.reason = null;
418
+ } else if (next.status === STATUS_INVALID) {
419
+ next.valid = false;
420
+ next.reason = next.reason || 'invalid';
421
+ }
422
+
423
+ next.updatedAt = nowIso();
424
+
425
+ const idx = index.accounts.findIndex((item) => item?.id === account.id);
426
+ if (idx < 0) throw new Error(`account not found: ${idOrAlias}`);
427
+ index.accounts[idx] = next;
428
+ saveIndex(index);
429
+ persistAccountMeta(next);
430
+ return next;
431
+ }
432
+
433
+ export function upsertProfileAccountState(input = {}) {
434
+ const profileId = ensureSafeName(normalizeText(input.profileId), 'profileId');
435
+ const platform = normalizePlatform(input.platform);
436
+ const accountId = normalizeText(input.accountId || input.platformAccountId || null);
437
+ const alias = normalizeAlias(input.alias);
438
+ const reason = normalizeText(input.reason);
439
+ const detectedAt = normalizeText(input.detectedAt) || nowIso();
440
+ const status = accountId ? STATUS_VALID : STATUS_INVALID;
441
+
442
+ const index = loadIndex();
443
+ const existingByProfile = resolveAccountByProfile(index, profileId);
444
+ const existingByAccountId = accountId ? resolveAccountByAccountId(index, accountId) : null;
445
+ let target = existingByAccountId || existingByProfile || null;
446
+ const purgeIds = new Set();
447
+
448
+ if (!target && !accountId) {
449
+ return buildProfileAccountView(profileId, {
450
+ profileId,
451
+ accountId: null,
452
+ status: STATUS_INVALID,
453
+ valid: false,
454
+ reason: reason || 'missing_account_id',
455
+ updatedAt: detectedAt,
456
+ });
457
+ }
458
+
459
+ if (!target) {
460
+ const seq = Number(index.nextSeq) || 1;
461
+ const id = ensureSafeName(normalizeId(`${platform}-${profileId}`, platform, seq), 'id');
462
+ const createdAt = nowIso();
463
+ const record = {
464
+ id,
465
+ seq,
466
+ platform,
467
+ status,
468
+ valid: status === STATUS_VALID,
469
+ reason: status === STATUS_VALID ? null : (reason || 'missing_account_id'),
470
+ accountId: accountId || null,
471
+ name: accountId || `${platform}-${profileId}`,
472
+ alias: alias || null,
473
+ username: null,
474
+ profileId,
475
+ fingerprintId: profileId,
476
+ createdAt,
477
+ updatedAt: createdAt,
478
+ detectedAt,
479
+ aliasSource: alias ? 'auto' : null,
480
+ };
481
+ index.accounts.push(record);
482
+ index.nextSeq = seq + 1;
483
+ saveIndex(index);
484
+ persistAccountMeta(record);
485
+ return buildProfileAccountView(profileId, record);
486
+ }
487
+
488
+ if (
489
+ accountId
490
+ && existingByProfile
491
+ && existingByAccountId
492
+ && existingByProfile.id !== existingByAccountId.id
493
+ ) {
494
+ purgeIds.add(existingByProfile.id);
495
+ target = existingByAccountId;
496
+ }
497
+
498
+ if (accountId) {
499
+ for (const row of index.accounts) {
500
+ if (!row || row.id === target.id) continue;
501
+ if (normalizeText(row.accountId) === accountId) {
502
+ purgeIds.add(row.id);
503
+ }
504
+ }
505
+ }
506
+
507
+ const next = {
508
+ ...target,
509
+ platform,
510
+ profileId,
511
+ fingerprintId: profileId,
512
+ accountId: accountId || null,
513
+ status,
514
+ valid: status === STATUS_VALID,
515
+ reason: status === STATUS_VALID ? null : (reason || 'missing_account_id'),
516
+ detectedAt,
517
+ updatedAt: nowIso(),
518
+ };
519
+ if (accountId) {
520
+ next.name = accountId;
521
+ }
522
+ if (alias) {
523
+ for (const row of index.accounts) {
524
+ if (!row) continue;
525
+ if (row.id === target.id || purgeIds.has(row.id)) continue;
526
+ const candidate = String(row.alias || '').trim().toLowerCase();
527
+ if (candidate && candidate === alias.toLowerCase()) {
528
+ throw new Error(`alias already exists: ${alias}`);
529
+ }
530
+ }
531
+ next.alias = alias;
532
+ next.aliasSource = 'auto';
533
+ }
534
+
535
+ index.accounts = index.accounts.filter((item) => item && !purgeIds.has(item.id));
536
+ const rowIndex = index.accounts.findIndex((item) => item?.id === target.id);
537
+ if (rowIndex < 0) throw new Error(`account not found: ${target.id}`);
538
+ index.accounts[rowIndex] = next;
539
+ saveIndex(index);
540
+ for (const id of purgeIds) {
541
+ if (id !== next.id) deleteAccountMeta(id);
542
+ }
543
+ persistAccountMeta(next);
544
+ return buildProfileAccountView(profileId, next);
545
+ }
546
+
547
+ export function markProfileInvalid(profileId, reason = 'login_guard') {
548
+ const id = ensureSafeName(normalizeText(profileId), 'profileId');
549
+ return upsertProfileAccountState({
550
+ profileId: id,
551
+ accountId: null,
552
+ reason,
553
+ });
554
+ }
555
+
556
+ export function removeAccount(idOrAlias, options = {}) {
557
+ const index = loadIndex();
558
+ const account = resolveAccountOrThrow(index, idOrAlias);
559
+ const idx = index.accounts.findIndex((item) => item?.id === account.id);
560
+ if (idx < 0) throw new Error(`account not found: ${idOrAlias}`);
561
+ index.accounts.splice(idx, 1);
562
+ saveIndex(index);
563
+ deleteAccountMeta(account.id);
564
+
565
+ const profileDeleted = Boolean(options.deleteProfile);
566
+ const fingerprintDeleted = Boolean(options.deleteFingerprint);
567
+ if (profileDeleted) {
568
+ const profilesRoot = resolveProfilesRoot();
569
+ const profilePath = path.join(profilesRoot, account.profileId);
570
+ if (isWithinDir(profilesRoot, profilePath)) {
571
+ fs.rmSync(profilePath, { recursive: true, force: true });
572
+ }
573
+ }
574
+ if (fingerprintDeleted) {
575
+ const fpRoot = resolveFingerprintsRoot();
576
+ const fpPath = path.join(fpRoot, `${account.fingerprintId}.json`);
577
+ if (isWithinDir(fpRoot, fpPath)) {
578
+ fs.rmSync(fpPath, { force: true });
579
+ }
580
+ }
581
+
582
+ return {
583
+ removed: account,
584
+ profileDeleted,
585
+ fingerprintDeleted,
586
+ };
587
+ }
@@ -15,7 +15,7 @@ export function resolveProfilesRoot() {
15
15
  return path.join(process.env.HOME || os.homedir(), '.webauto', 'profiles');
16
16
  }
17
17
 
18
- function resolveFingerprintsRoot() {
18
+ export function resolveFingerprintsRoot() {
19
19
  const envFps = String(process.env.WEBAUTO_PATHS_FINGERPRINTS || '').trim();
20
20
  if (envFps) return envFps;
21
21
  const portableRoot = resolvePortableRoot();
@@ -1,11 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import minimist from 'minimist';
3
3
  import { spawnSync } from 'node:child_process';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { existsSync } from 'node:fs';
4
7
 
5
8
  function run(cmd, args) {
6
9
  return spawnSync(cmd, args, { encoding: 'utf8' });
7
10
  }
8
11
 
12
+ function resolveNpxBin() {
13
+ return process.platform === 'win32' ? 'npx.cmd' : 'npx';
14
+ }
15
+
9
16
  function checkCamoufoxInstalled() {
10
17
  const cmd = process.platform === 'win32' ? 'python' : 'python3';
11
18
  const ret = run(cmd, ['-m', 'camoufox', 'path']);
@@ -13,7 +20,16 @@ function checkCamoufoxInstalled() {
13
20
  }
14
21
 
15
22
  function installCamoufox() {
16
- const ret = run('npx', ['camoufox', 'fetch']);
23
+ const ret = run(resolveNpxBin(), ['--yes', '--package=camoufox', 'camoufox', 'fetch']);
24
+ return ret.status === 0;
25
+ }
26
+
27
+ function checkGeoIPInstalled() {
28
+ return existsSync(path.join(os.homedir(), '.webauto', 'geoip', 'GeoLite2-City.mmdb'));
29
+ }
30
+
31
+ function installGeoIP() {
32
+ const ret = run(resolveNpxBin(), ['--yes', '--package=@web-auto/camo', 'camo', 'init', 'geoip']);
17
33
  return ret.status === 0;
18
34
  }
19
35
 
@@ -29,13 +45,18 @@ async function checkBackendHealth() {
29
45
  async function main() {
30
46
  const argv = minimist(process.argv.slice(2));
31
47
  const download = argv['download-browser'] === true;
48
+ const downloadGeoip = argv['download-geoip'] === true;
32
49
  const ensureBackend = argv['ensure-backend'] === true;
33
50
  const provider = String(process.env.WEBAUTO_BROWSER_PROVIDER || 'camo').trim().toLowerCase();
34
51
  let camoufoxInstalled = checkCamoufoxInstalled();
52
+ let geoipInstalled = checkGeoIPInstalled();
35
53
 
36
54
  if (!camoufoxInstalled && download) {
37
55
  camoufoxInstalled = installCamoufox();
38
56
  }
57
+ if (!geoipInstalled && downloadGeoip) {
58
+ geoipInstalled = installGeoIP();
59
+ }
39
60
 
40
61
  let backendEnsured = false;
41
62
  let ensureBackendError = null;
@@ -50,7 +71,9 @@ async function main() {
50
71
  }
51
72
 
52
73
  const backendHealthy = await checkBackendHealth();
53
- const ok = camoufoxInstalled || backendHealthy;
74
+ const dependencyReady = camoufoxInstalled || backendHealthy;
75
+ const geoipReady = downloadGeoip ? geoipInstalled : true;
76
+ const ok = dependencyReady && geoipReady;
54
77
 
55
78
  const result = {
56
79
  ok,
@@ -59,7 +82,8 @@ async function main() {
59
82
  backendEnsured,
60
83
  ensureBackendError,
61
84
  backendHealthy,
62
- message: ok ? 'Camo 后端就绪' : 'Camoufox 未安装',
85
+ geoipInstalled,
86
+ message: ok ? 'Camo 后端就绪' : (downloadGeoip && !geoipInstalled ? 'GeoIP 未安装' : 'Camoufox 未安装'),
63
87
  };
64
88
 
65
89
  console.log(JSON.stringify(result));