@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,299 @@
1
+ #!/usr/bin/env node
2
+ import minimist from 'minimist';
3
+ import { spawn } from 'node:child_process';
4
+ import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'node:fs';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const APP_ROOT = path.resolve(__dirname, '../..');
10
+ const DIST_MAIN = path.join(APP_ROOT, 'dist', 'main', 'index.mjs');
11
+
12
+ const args = minimist(process.argv.slice(2), {
13
+ boolean: ['build', 'install', 'check', 'help', 'headless', 'no-daemon'],
14
+ string: ['profile', 'keyword', 'target', 'scenario', 'output'],
15
+ alias: { h: 'help', p: 'profile', k: 'keyword', t: 'target', o: 'output' }
16
+ });
17
+
18
+ function printHelp() {
19
+ console.log(`webauto ui console
20
+
21
+ Usage:
22
+ webauto ui console [--build] [--install] [--check] [--no-daemon]
23
+ webauto ui test <scenario> [options]
24
+
25
+ Test Scenarios:
26
+ env-check - Environment check automation test
27
+ account-flow - Account creation/login flow test
28
+ config-save - Config save/load test
29
+ crawl-run - Full crawl flow test
30
+
31
+ Options:
32
+ --check Check build/dep status only
33
+ --build Auto-build if missing
34
+ --install Auto-install if missing deps
35
+ --no-daemon Run in foreground mode
36
+ --scenario Test scenario name
37
+ --profile Test profile ID
38
+ --keyword Test keyword
39
+ --target Target count
40
+ --headless Headless mode
41
+ --output Output report path
42
+ `);
43
+ }
44
+
45
+ function checkBuildStatus() {
46
+ return existsSync(DIST_MAIN);
47
+ }
48
+
49
+ async function build() {
50
+ return new Promise((resolve, reject) => {
51
+ console.log('[ui-console] Building...');
52
+ const child = spawn('npm', ['run', 'build'], {
53
+ cwd: APP_ROOT,
54
+ stdio: 'inherit',
55
+ shell: true
56
+ });
57
+ child.on('close', (code) => {
58
+ if (code === 0) resolve();
59
+ else reject(new Error(`Build failed with code ${code}`));
60
+ });
61
+ });
62
+ }
63
+
64
+ async function install() {
65
+ return new Promise((resolve, reject) => {
66
+ console.log('[ui-console] Installing dependencies...');
67
+ const child = spawn('npm', ['install'], {
68
+ cwd: APP_ROOT,
69
+ stdio: 'inherit',
70
+ shell: true
71
+ });
72
+ child.on('close', (code) => {
73
+ if (code === 0) resolve();
74
+ else reject(new Error(`Install failed with code ${code}`));
75
+ });
76
+ });
77
+ }
78
+
79
+ async function startConsole(noDaemon = false) {
80
+ if (!checkBuildStatus()) {
81
+ console.log('[ui-console] Build not found, building...');
82
+ await build();
83
+ }
84
+
85
+ console.log('[ui-console] Starting Desktop Console...');
86
+ const env = { ...process.env };
87
+ if (noDaemon) env.WEBAUTO_NO_DAEMON = '1';
88
+
89
+ const child = spawn('npx', ['electron', DIST_MAIN], {
90
+ cwd: APP_ROOT,
91
+ env,
92
+ stdio: 'inherit',
93
+ detached: !noDaemon
94
+ });
95
+
96
+ if (noDaemon) {
97
+ child.on('close', (code) => {
98
+ console.log(`[ui-console] Exited with code ${code}`);
99
+ process.exit(code);
100
+ });
101
+ } else {
102
+ child.unref();
103
+ console.log(`[ui-console] Started (PID: ${child.pid})`);
104
+ }
105
+ }
106
+
107
+ class UITestRunner {
108
+ constructor(options = {}) {
109
+ this.profile = options.profile || 'test-profile';
110
+ this.keyword = options.keyword || '自动化测试';
111
+ this.target = options.target || 5;
112
+ this.headless = options.headless || false;
113
+ this.output = options.output || null;
114
+ this.results = [];
115
+ this.startTime = Date.now();
116
+ }
117
+
118
+ log(message, type = 'info') {
119
+ const ts = new Date().toISOString();
120
+ const line = `[${ts}] [${type}] ${message}`;
121
+ console.log(line);
122
+ this.results.push({ ts, type, message });
123
+ }
124
+
125
+ async runCommand(cmd, args, timeoutMs = 30000) {
126
+ return new Promise((resolve, reject) => {
127
+ const child = spawn(cmd, args, { shell: true, stdio: 'pipe' });
128
+ let stdout = '';
129
+ let stderr = '';
130
+ const timer = setTimeout(() => {
131
+ child.kill('SIGTERM');
132
+ reject(new Error(`Command timeout: ${cmd}`));
133
+ }, timeoutMs);
134
+ child.stdout.on('data', (data) => { stdout += data.toString(); });
135
+ child.stderr.on('data', (data) => { stderr += data.toString(); });
136
+ child.on('close', (code) => {
137
+ clearTimeout(timer);
138
+ if (code === 0) resolve({ ok: true, stdout, stderr });
139
+ else reject(new Error(stderr || `Exit code: ${code}`));
140
+ });
141
+ });
142
+ }
143
+
144
+ async testEnvCheck() {
145
+ this.log('Starting environment check test', 'test');
146
+ try {
147
+ this.log('Testing: camo CLI');
148
+ await this.runCommand('which camo');
149
+ this.log('PASS: camo CLI found', 'pass');
150
+
151
+ this.log('Testing: Unified API');
152
+ const apiRes = await fetch('http://127.0.0.1:7701/health');
153
+ if (!apiRes.ok) throw new Error('Unified API not responding');
154
+ this.log('PASS: Unified API running', 'pass');
155
+
156
+ this.log('Testing: Browser Service');
157
+ const bsRes = await fetch('http://127.0.0.1:7704/health');
158
+ if (!bsRes.ok) throw new Error('Browser Service not responding');
159
+ this.log('PASS: Browser Service running', 'pass');
160
+
161
+ this.log('Environment check PASSED', 'success');
162
+ return { passed: true };
163
+ } catch (err) {
164
+ this.log(`FAILED: ${err.message}`, 'fail');
165
+ return { passed: false, error: err.message };
166
+ }
167
+ }
168
+
169
+ async testAccountFlow() {
170
+ this.log('Starting account flow test', 'test');
171
+ try {
172
+ this.log(`Testing: Create profile ${this.profile}`);
173
+ const result = await this.runCommand('node', [
174
+ path.join(process.cwd(), 'apps/webauto/entry/profilepool.mjs'),
175
+ 'add', 'test', '--json'
176
+ ], 60000);
177
+ const json = JSON.parse(result.stdout);
178
+ if (json.ok && json.profileId) {
179
+ this.log(`PASS: Profile created: ${json.profileId}`, 'pass');
180
+ } else {
181
+ throw new Error('Failed to create profile');
182
+ }
183
+ this.log('Account flow PASSED', 'success');
184
+ return { passed: true, profileId: json.profileId };
185
+ } catch (err) {
186
+ this.log(`FAILED: ${err.message}`, 'fail');
187
+ return { passed: false, error: err.message };
188
+ }
189
+ }
190
+
191
+ async testConfigSave() {
192
+ this.log('Starting config save test', 'test');
193
+ try {
194
+ const testConfig = {
195
+ keyword: this.keyword, target: this.target, env: 'debug',
196
+ fetchBody: true, fetchComments: true, maxComments: 50,
197
+ autoLike: false, likeKeywords: '', headless: this.headless, dryRun: true
198
+ };
199
+ const configPath = path.join(process.cwd(), 'test-config.json');
200
+ writeFileSync(configPath, JSON.stringify(testConfig, null, 2));
201
+ this.log(`PASS: Config exported`, 'pass');
202
+ const imported = JSON.parse(readFileSync(configPath, 'utf8'));
203
+ if (imported.keyword === this.keyword) {
204
+ this.log('PASS: Config imported', 'pass');
205
+ }
206
+ import('node:fs').then(fs => fs.unlinkSync(configPath));
207
+ this.log('Config save PASSED', 'success');
208
+ return { passed: true };
209
+ } catch (err) {
210
+ this.log(`FAILED: ${err.message}`, 'fail');
211
+ return { passed: false, error: err.message };
212
+ }
213
+ }
214
+
215
+ async testCrawlRun() {
216
+ this.log('Starting crawl run test', 'test');
217
+ try {
218
+ this.log(`Testing: Dry-run crawl (keyword=${this.keyword}, target=${this.target})`);
219
+ const args = [
220
+ path.join(process.cwd(), 'apps/webauto/entry/xhs-unified.mjs'),
221
+ '--profile', this.profile,
222
+ '--keyword', this.keyword,
223
+ '--target', String(this.target),
224
+ '--env', 'debug',
225
+ '--dry-run', 'true'
226
+ ];
227
+ if (this.headless) args.push('--headless', 'true');
228
+ await this.runCommand('node', args, 120000);
229
+ this.log('PASS: Crawl run completed', 'pass');
230
+ this.log('Crawl run PASSED', 'success');
231
+ return { passed: true };
232
+ } catch (err) {
233
+ this.log(`FAILED: ${err.message}`, 'fail');
234
+ return { passed: false, error: err.message };
235
+ }
236
+ }
237
+
238
+ async runScenario(scenario) {
239
+ this.log(`Running test scenario: ${scenario}`, 'info');
240
+ let result;
241
+ switch (scenario) {
242
+ case 'env-check': result = await this.testEnvCheck(); break;
243
+ case 'account-flow': result = await this.testAccountFlow(); break;
244
+ case 'config-save': result = await this.testConfigSave(); break;
245
+ case 'crawl-run': result = await this.testCrawlRun(); break;
246
+ default: throw new Error(`Unknown scenario: ${scenario}`);
247
+ }
248
+ result.duration = Date.now() - this.startTime;
249
+ if (this.output) {
250
+ const report = { scenario, ...result, results: this.results };
251
+ mkdirSync(path.dirname(this.output), { recursive: true });
252
+ writeFileSync(this.output, JSON.stringify(report, null, 2));
253
+ this.log(`Report saved to ${this.output}`, 'info');
254
+ }
255
+ return result;
256
+ }
257
+ }
258
+
259
+ async function main() {
260
+ if (args.help || args.h) { printHelp(); process.exit(0); }
261
+ if (args.check) {
262
+ const buildOk = checkBuildStatus();
263
+ console.log(`Build: ${buildOk ? 'OK' : 'MISSING'}`);
264
+ process.exit(buildOk ? 0 : 1);
265
+ }
266
+ if (args.build) { await build(); process.exit(0); }
267
+ if (args.install) { await install(); process.exit(0); }
268
+
269
+ const testIndex = process.argv.indexOf('test');
270
+ if (testIndex !== -1 && process.argv[testIndex + 1]) {
271
+ const scenario = process.argv[testIndex + 1];
272
+ const runner = new UITestRunner({
273
+ profile: args.profile, keyword: args.keyword,
274
+ target: parseInt(args.target) || 5,
275
+ headless: args.headless, output: args.output
276
+ });
277
+ try {
278
+ const result = await runner.runScenario(scenario);
279
+ if (result.passed) {
280
+ console.log(`\n✅ Test PASSED (${result.duration}ms)`);
281
+ process.exit(0);
282
+ } else {
283
+ console.log(`\n❌ Test FAILED: ${result.error}`);
284
+ process.exit(1);
285
+ }
286
+ } catch (err) {
287
+ console.log(`\n❌ Test ERROR: ${err.message}`);
288
+ process.exit(1);
289
+ }
290
+ return;
291
+ }
292
+
293
+ await startConsole(args['no-daemon']);
294
+ }
295
+
296
+ main().catch((err) => {
297
+ console.error('[ui-console] Error:', err.message);
298
+ process.exit(1);
299
+ });
@@ -0,0 +1,356 @@
1
+ #!/usr/bin/env node
2
+ import minimist from 'minimist';
3
+ import path from 'node:path';
4
+ import { existsSync } from 'node:fs';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { fileURLToPath } from 'node:url';
7
+ import {
8
+ addAccount,
9
+ getAccount,
10
+ listAccountProfiles,
11
+ listAccounts,
12
+ removeAccount,
13
+ updateAccount,
14
+ } from './lib/account-store.mjs';
15
+ import { ensureProfile } from './lib/profilepool.mjs';
16
+ import { syncXhsAccountByProfile, syncXhsAccountsByProfiles } from './lib/account-detect.mjs';
17
+
18
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
19
+ const XHS_HOME_URL = 'https://www.xiaohongshu.com';
20
+
21
+ function output(payload, jsonMode) {
22
+ if (jsonMode) {
23
+ console.log(JSON.stringify(payload));
24
+ return;
25
+ }
26
+ console.log(JSON.stringify(payload, null, 2));
27
+ }
28
+
29
+ function parseBoolean(value, fallback = false) {
30
+ if (value === undefined || value === null) return fallback;
31
+ const normalized = String(value).trim().toLowerCase();
32
+ if (!normalized) return fallback;
33
+ if (normalized === '1' || normalized === 'true' || normalized === 'yes') return true;
34
+ if (normalized === '0' || normalized === 'false' || normalized === 'no') return false;
35
+ return fallback;
36
+ }
37
+
38
+ function getCamoRunner() {
39
+ const local = path.join(ROOT, 'node_modules', '.bin', process.platform === 'win32' ? 'camo.cmd' : 'camo');
40
+ if (existsSync(local)) return { cmd: local, prefix: [] };
41
+ return { cmd: process.platform === 'win32' ? 'npx.cmd' : 'npx', prefix: ['@web-auto/camo'] };
42
+ }
43
+
44
+ function runCamo(args) {
45
+ const runner = getCamoRunner();
46
+ const ret = spawnSync(runner.cmd, [...runner.prefix, ...args], {
47
+ cwd: ROOT,
48
+ env: process.env,
49
+ encoding: 'utf8',
50
+ });
51
+ const stdout = String(ret.stdout || '').trim();
52
+ const stderr = String(ret.stderr || '').trim();
53
+ let parsed = null;
54
+ if (stdout) {
55
+ const lines = stdout.split('\n').map((line) => line.trim()).filter(Boolean).reverse();
56
+ for (const line of lines) {
57
+ try {
58
+ parsed = JSON.parse(line);
59
+ break;
60
+ } catch {
61
+ continue;
62
+ }
63
+ }
64
+ }
65
+ return {
66
+ ok: ret.status === 0,
67
+ code: ret.status,
68
+ stdout,
69
+ stderr,
70
+ json: parsed,
71
+ };
72
+ }
73
+
74
+ function inferLoginUrl(platform) {
75
+ const value = String(platform || '').trim().toLowerCase();
76
+ if (!value || value === 'xiaohongshu' || value === 'xhs') return XHS_HOME_URL;
77
+ return 'https://example.com';
78
+ }
79
+
80
+ function normalizeAlias(input) {
81
+ const value = String(input ?? '').trim();
82
+ return value || null;
83
+ }
84
+
85
+ async function detectAliasFromActivePage(profileId, selector) {
86
+ const { callAPI } = await import('../../../modules/camo-runtime/src/utils/browser-service.mjs');
87
+ const script = `(() => {
88
+ const requested = ${JSON.stringify(String(selector || '').trim())};
89
+ const defaultSelectors = [
90
+ '[class*="user"] [class*="name"]',
91
+ '[class*="nickname"]',
92
+ '[class*="account"] [class*="name"]',
93
+ 'a[href*="/user/profile"] span',
94
+ 'header a[href*="/user"] span',
95
+ 'nav a[href*="/user"] span'
96
+ ];
97
+ const selectors = requested ? [requested] : defaultSelectors;
98
+ const clean = (value) => String(value || '').replace(/\\s+/g, ' ').trim();
99
+ const candidates = [];
100
+ for (const sel of selectors) {
101
+ const nodes = Array.from(document.querySelectorAll(sel)).slice(0, 6);
102
+ for (const node of nodes) {
103
+ const text = clean(node.textContent || '');
104
+ if (!text) continue;
105
+ candidates.push({ text, selector: sel });
106
+ }
107
+ }
108
+ const title = clean(document.title || '');
109
+ if (title) candidates.push({ text: title, selector: 'document.title' });
110
+ const bad = ['小红书', '登录', '注册', '搜索'];
111
+ const picked = candidates.find((item) => {
112
+ if (!item?.text) return false;
113
+ if (item.text.length < 2) return false;
114
+ return bad.every((word) => !item.text.includes(word));
115
+ }) || null;
116
+ return {
117
+ alias: picked?.text || null,
118
+ source: picked?.selector || null,
119
+ candidates
120
+ };
121
+ })()`;
122
+
123
+ const result = await callAPI('evaluate', { profileId, script });
124
+ const payload = result?.result || result || {};
125
+ const alias = normalizeAlias(payload.alias);
126
+ if (!alias) {
127
+ throw new Error('unable to detect alias from current page');
128
+ }
129
+ return {
130
+ alias,
131
+ source: String(payload.source || 'page').trim() || 'page',
132
+ candidates: Array.isArray(payload.candidates) ? payload.candidates : [],
133
+ };
134
+ }
135
+
136
+ function printHelp() {
137
+ console.log(`webauto account
138
+
139
+ Usage:
140
+ webauto account --help
141
+ webauto account list [--json]
142
+ webauto account list --records [--json]
143
+ webauto account add [--platform <name>] [--alias <alias>] [--name <name>] [--username <username>] [--profile <id>] [--fingerprint <id>] [--json]
144
+ webauto account get <id|alias> [--json]
145
+ webauto account update <id|alias> [--alias <alias>|--clear-alias] [--name <name>] [--username <name>] [--profile <id>] [--fingerprint <id>] [--status active|disabled|archived] [--json]
146
+ webauto account delete <id|alias> [--delete-profile] [--delete-fingerprint] [--json]
147
+ webauto account login <id|alias> [--url <url>] [--sync-alias] [--json]
148
+ webauto account sync-alias <id|alias> [--selector <css>] [--alias <value>] [--json]
149
+ webauto account sync <profileId|all> [--json]
150
+
151
+ Notes:
152
+ - 账号数据默认保存到 ~/.webauto/accounts(可用 WEBAUTO_PATHS_ACCOUNTS 覆盖)
153
+ - list 默认按 profile 展示账号有效态(valid/invalid)
154
+ - add 会自动创建并关联 profile/fingerprint(未指定时自动编号)
155
+ - login 会通过 @web-auto/camo 拉起浏览器并绑定账号 profile
156
+ - 只有识别到账号 id 的 profile 才会进入 valid 状态
157
+
158
+ Examples:
159
+ webauto account add --platform xiaohongshu --alias 主号
160
+ webauto account list
161
+ webauto account sync all
162
+ webauto account login xhs-0001 --url https://www.xiaohongshu.com
163
+ webauto account sync-alias xhs-0001
164
+ webauto account update xhs-0001 --alias 运营1号
165
+ webauto account delete xhs-0001 --delete-profile --delete-fingerprint
166
+ `);
167
+ }
168
+
169
+ async function cmdList(jsonMode) {
170
+ const result = listAccountProfiles();
171
+ output({ ok: true, ...result }, jsonMode);
172
+ }
173
+
174
+ async function cmdListRecords(jsonMode) {
175
+ const result = listAccounts();
176
+ output({ ok: true, ...result }, jsonMode);
177
+ }
178
+
179
+ async function cmdAdd(argv, jsonMode) {
180
+ const result = await addAccount({
181
+ id: argv.id,
182
+ platform: argv.platform,
183
+ alias: argv.alias,
184
+ name: argv.name,
185
+ username: argv.username,
186
+ profileId: argv.profile || argv['profile-id'],
187
+ fingerprintId: argv.fingerprint || argv['fingerprint-id'],
188
+ status: argv.status,
189
+ });
190
+ output({ ok: true, ...result }, jsonMode);
191
+ }
192
+
193
+ async function cmdGet(idOrAlias, jsonMode) {
194
+ const account = getAccount(idOrAlias);
195
+ output({ ok: true, account }, jsonMode);
196
+ }
197
+
198
+ async function cmdUpdate(idOrAlias, argv, jsonMode) {
199
+ const patch = {};
200
+ if (argv.platform !== undefined) patch.platform = argv.platform;
201
+ if (argv.name !== undefined) patch.name = argv.name;
202
+ if (argv.username !== undefined) patch.username = argv.username;
203
+ if (argv.status !== undefined) patch.status = argv.status;
204
+ if (argv.profile !== undefined || argv['profile-id'] !== undefined) {
205
+ patch.profileId = argv.profile || argv['profile-id'];
206
+ }
207
+ if (argv.fingerprint !== undefined || argv['fingerprint-id'] !== undefined) {
208
+ patch.fingerprintId = argv.fingerprint || argv['fingerprint-id'];
209
+ }
210
+ if (argv['clear-alias']) {
211
+ patch.alias = null;
212
+ patch.aliasSource = 'manual';
213
+ } else if (argv.alias !== undefined) {
214
+ patch.alias = argv.alias;
215
+ patch.aliasSource = 'manual';
216
+ }
217
+ if (!Object.keys(patch).length) {
218
+ throw new Error('no update fields provided');
219
+ }
220
+ const account = await updateAccount(idOrAlias, patch);
221
+ output({ ok: true, account }, jsonMode);
222
+ }
223
+
224
+ async function cmdDelete(idOrAlias, argv, jsonMode) {
225
+ const result = removeAccount(idOrAlias, {
226
+ deleteProfile: argv['delete-profile'] === true,
227
+ deleteFingerprint: argv['delete-fingerprint'] === true,
228
+ });
229
+ output({ ok: true, ...result }, jsonMode);
230
+ }
231
+
232
+ async function cmdLogin(idOrAlias, argv, jsonMode) {
233
+ const account = getAccount(idOrAlias);
234
+ await ensureProfile(account.profileId);
235
+ const url = String(argv.url || inferLoginUrl(account.platform)).trim();
236
+
237
+ const initResult = runCamo(['init']);
238
+ if (!initResult.ok) {
239
+ output({
240
+ ok: false,
241
+ step: 'init',
242
+ code: initResult.code,
243
+ error: initResult.stderr || initResult.stdout || 'camo init failed',
244
+ }, jsonMode);
245
+ process.exit(1);
246
+ }
247
+
248
+ const startResult = runCamo(['start', account.profileId, '--url', url]);
249
+ if (!startResult.ok) {
250
+ output({
251
+ ok: false,
252
+ step: 'start',
253
+ code: startResult.code,
254
+ error: startResult.stderr || startResult.stdout || 'camo start failed',
255
+ account,
256
+ }, jsonMode);
257
+ process.exit(1);
258
+ }
259
+
260
+ let aliasSync = null;
261
+ if (parseBoolean(argv['sync-alias'], false)) {
262
+ try {
263
+ const detected = await detectAliasFromActivePage(account.profileId, argv.selector);
264
+ const updated = await updateAccount(account.id, {
265
+ alias: detected.alias,
266
+ aliasSource: `auto:${detected.source}`,
267
+ });
268
+ aliasSync = { ok: true, alias: updated.alias, source: detected.source };
269
+ } catch (error) {
270
+ aliasSync = { ok: false, error: error?.message || String(error) };
271
+ }
272
+ }
273
+
274
+ const accountSync = await syncXhsAccountByProfile(account.profileId).catch((error) => ({
275
+ profileId: account.profileId,
276
+ valid: false,
277
+ status: 'invalid',
278
+ reason: error?.message || String(error),
279
+ }));
280
+
281
+ output({
282
+ ok: true,
283
+ account,
284
+ profileId: account.profileId,
285
+ url,
286
+ camo: startResult.json || startResult.stdout || null,
287
+ aliasSync,
288
+ accountSync,
289
+ }, jsonMode);
290
+ }
291
+
292
+ async function cmdSyncAlias(idOrAlias, argv, jsonMode) {
293
+ const account = getAccount(idOrAlias);
294
+ let alias = normalizeAlias(argv.alias);
295
+ let source = 'manual';
296
+ let candidates = [];
297
+ if (!alias) {
298
+ const detected = await detectAliasFromActivePage(account.profileId, argv.selector);
299
+ alias = detected.alias;
300
+ source = `auto:${detected.source}`;
301
+ candidates = detected.candidates;
302
+ }
303
+ const updated = await updateAccount(account.id, { alias, aliasSource: source });
304
+ output({
305
+ ok: true,
306
+ account: updated,
307
+ alias,
308
+ source,
309
+ candidates,
310
+ }, jsonMode);
311
+ }
312
+
313
+ async function cmdSync(target, jsonMode) {
314
+ const value = String(target || '').trim().toLowerCase();
315
+ if (!value || value === 'all') {
316
+ const rows = listAccountProfiles().profiles;
317
+ const profileIds = rows.map((item) => item.profileId);
318
+ const synced = await syncXhsAccountsByProfiles(profileIds);
319
+ output({ ok: true, count: synced.length, profiles: synced }, jsonMode);
320
+ return;
321
+ }
322
+ const synced = await syncXhsAccountByProfile(target);
323
+ output({ ok: true, profile: synced }, jsonMode);
324
+ }
325
+
326
+ async function main() {
327
+ const argv = minimist(process.argv.slice(2), {
328
+ boolean: ['help', 'json', 'clear-alias', 'delete-profile', 'delete-fingerprint', 'sync-alias', 'records'],
329
+ alias: { h: 'help' },
330
+ });
331
+ const cmd = String(argv._[0] || '').trim();
332
+ const arg1 = String(argv._[1] || '').trim();
333
+ const jsonMode = argv.json === true;
334
+
335
+ if (!cmd || cmd === 'help' || argv.help) {
336
+ printHelp();
337
+ return;
338
+ }
339
+
340
+ if (cmd === 'list') return argv.records ? cmdListRecords(jsonMode) : cmdList(jsonMode);
341
+ if (cmd === 'add') return cmdAdd(argv, jsonMode);
342
+ if (cmd === 'get') return cmdGet(arg1, jsonMode);
343
+ if (cmd === 'update') return cmdUpdate(arg1, argv, jsonMode);
344
+ if (cmd === 'delete' || cmd === 'remove' || cmd === 'rm') return cmdDelete(arg1, argv, jsonMode);
345
+ if (cmd === 'login') return cmdLogin(arg1, argv, jsonMode);
346
+ if (cmd === 'sync-alias') return cmdSyncAlias(arg1, argv, jsonMode);
347
+ if (cmd === 'sync') return cmdSync(arg1, jsonMode);
348
+
349
+ throw new Error(`unknown account command: ${cmd}`);
350
+ }
351
+
352
+ main().catch((error) => {
353
+ const message = error?.message || String(error);
354
+ console.error(message);
355
+ process.exit(1);
356
+ });