@jackwener/opencli 1.1.0 → 1.2.0

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 (769) hide show
  1. package/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
  2. package/.github/pull_request_template.md +7 -0
  3. package/.github/workflows/doc-check.yml +36 -0
  4. package/.github/workflows/docs.yml +7 -42
  5. package/CHANGELOG.md +23 -0
  6. package/CLI-EXPLORER.md +9 -8
  7. package/CONTRIBUTING.md +39 -1
  8. package/README.md +33 -19
  9. package/README.zh-CN.md +64 -27
  10. package/SKILL.md +102 -33
  11. package/dist/browser/cdp.d.ts +4 -4
  12. package/dist/browser/cdp.js +45 -17
  13. package/dist/browser/daemon-client.d.ts +2 -1
  14. package/dist/browser/dom-helpers.js +38 -7
  15. package/dist/browser/dom-snapshot.d.ts +86 -0
  16. package/dist/browser/dom-snapshot.js +729 -0
  17. package/dist/browser/dom-snapshot.test.d.ts +11 -0
  18. package/dist/browser/dom-snapshot.test.js +212 -0
  19. package/dist/browser/index.d.ts +2 -0
  20. package/dist/browser/index.js +1 -0
  21. package/dist/browser/page.d.ts +18 -25
  22. package/dist/browser/page.js +44 -5
  23. package/dist/build-manifest.d.ts +11 -4
  24. package/dist/build-manifest.js +79 -34
  25. package/dist/build-manifest.test.js +58 -2
  26. package/dist/cli-manifest.json +4273 -1771
  27. package/dist/cli.d.ts +6 -0
  28. package/dist/cli.js +255 -162
  29. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  30. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  31. package/dist/clis/apple-podcasts/search.js +2 -2
  32. package/dist/clis/apple-podcasts/top.js +9 -2
  33. package/dist/clis/arxiv/search.js +1 -1
  34. package/dist/clis/barchart/greeks.js +1 -1
  35. package/dist/clis/barchart/options.js +1 -1
  36. package/dist/clis/barchart/quote.js +1 -1
  37. package/dist/clis/bilibili/download.js +1 -1
  38. package/dist/clis/bilibili/dynamic.js +1 -1
  39. package/dist/clis/bilibili/favorite.js +1 -1
  40. package/dist/clis/bilibili/feed.js +1 -1
  41. package/dist/clis/bilibili/following.js +2 -2
  42. package/dist/clis/bilibili/history.js +1 -1
  43. package/dist/clis/bilibili/me.js +1 -1
  44. package/dist/clis/bilibili/ranking.js +1 -1
  45. package/dist/clis/bilibili/search.js +3 -3
  46. package/dist/clis/bilibili/subtitle.js +2 -2
  47. package/dist/clis/bilibili/user-videos.js +2 -2
  48. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  49. package/dist/clis/bloomberg/businessweek.js +17 -0
  50. package/dist/clis/bloomberg/economics.js +17 -0
  51. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  52. package/dist/clis/bloomberg/feeds.js +15 -0
  53. package/dist/clis/bloomberg/industries.d.ts +1 -0
  54. package/dist/clis/bloomberg/industries.js +17 -0
  55. package/dist/clis/bloomberg/main.d.ts +1 -0
  56. package/dist/clis/bloomberg/main.js +17 -0
  57. package/dist/clis/bloomberg/markets.d.ts +1 -0
  58. package/dist/clis/bloomberg/markets.js +17 -0
  59. package/dist/clis/bloomberg/news.d.ts +1 -0
  60. package/dist/clis/bloomberg/news.js +105 -0
  61. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  62. package/dist/clis/bloomberg/opinions.js +17 -0
  63. package/dist/clis/bloomberg/politics.d.ts +1 -0
  64. package/dist/clis/bloomberg/politics.js +17 -0
  65. package/dist/clis/bloomberg/tech.d.ts +1 -0
  66. package/dist/clis/bloomberg/tech.js +17 -0
  67. package/dist/clis/bloomberg/utils.d.ts +34 -0
  68. package/dist/clis/bloomberg/utils.js +364 -0
  69. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  70. package/dist/clis/bloomberg/utils.test.js +129 -0
  71. package/dist/clis/boss/batchgreet.js +12 -99
  72. package/dist/clis/boss/chatlist.js +9 -26
  73. package/dist/clis/boss/chatmsg.js +11 -42
  74. package/dist/clis/boss/common.d.ts +92 -0
  75. package/dist/clis/boss/common.js +223 -0
  76. package/dist/clis/boss/detail.js +8 -50
  77. package/dist/clis/boss/exchange.js +13 -79
  78. package/dist/clis/boss/greet.js +20 -147
  79. package/dist/clis/boss/invite.js +26 -121
  80. package/dist/clis/boss/joblist.js +6 -31
  81. package/dist/clis/boss/mark.js +12 -85
  82. package/dist/clis/boss/recommend.js +10 -49
  83. package/dist/clis/boss/resume.js +18 -118
  84. package/dist/clis/boss/search.js +13 -61
  85. package/dist/clis/boss/send.js +18 -152
  86. package/dist/clis/boss/stats.js +20 -71
  87. package/dist/clis/chaoxing/assignments.js +1 -1
  88. package/dist/clis/chaoxing/exams.js +1 -1
  89. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  90. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  91. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  92. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  93. package/dist/clis/chatgpt/read.js +1 -1
  94. package/dist/clis/chatwise/export.js +1 -1
  95. package/dist/clis/chatwise/model.js +2 -2
  96. package/dist/clis/chatwise/screenshot.js +1 -1
  97. package/dist/clis/codex/export.js +1 -1
  98. package/dist/clis/codex/model.js +2 -2
  99. package/dist/clis/codex/screenshot.js +1 -1
  100. package/dist/clis/coupang/add-to-cart.js +3 -4
  101. package/dist/clis/coupang/search.js +2 -4
  102. package/dist/clis/coupang/utils.test.d.ts +1 -0
  103. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  104. package/dist/clis/ctrip/search.js +1 -1
  105. package/dist/clis/cursor/export.js +1 -1
  106. package/dist/clis/cursor/model.js +2 -2
  107. package/dist/clis/cursor/screenshot.js +1 -1
  108. package/dist/clis/devto/tag.yaml +34 -0
  109. package/dist/clis/devto/top.yaml +29 -0
  110. package/dist/clis/devto/user.yaml +33 -0
  111. package/dist/clis/douban/book-hot.d.ts +1 -0
  112. package/dist/clis/douban/book-hot.js +14 -0
  113. package/dist/clis/douban/marks.d.ts +1 -0
  114. package/dist/clis/douban/marks.js +115 -0
  115. package/dist/clis/douban/movie-hot.d.ts +1 -0
  116. package/dist/clis/douban/movie-hot.js +14 -0
  117. package/dist/clis/douban/reviews.d.ts +1 -0
  118. package/dist/clis/douban/reviews.js +106 -0
  119. package/dist/clis/douban/search.d.ts +1 -0
  120. package/dist/clis/douban/search.js +16 -0
  121. package/dist/clis/douban/shared.d.ts +4 -0
  122. package/dist/clis/douban/shared.js +155 -0
  123. package/dist/clis/douban/subject.yaml +76 -0
  124. package/dist/clis/douban/top250.yaml +70 -0
  125. package/dist/clis/douban/utils.d.ts +35 -0
  126. package/dist/clis/douban/utils.js +48 -0
  127. package/dist/clis/facebook/add-friend.yaml +43 -0
  128. package/dist/clis/facebook/events.yaml +44 -0
  129. package/dist/clis/facebook/feed.yaml +63 -0
  130. package/dist/clis/facebook/friends.yaml +42 -0
  131. package/dist/clis/facebook/groups.yaml +50 -0
  132. package/dist/clis/facebook/join-group.yaml +44 -0
  133. package/dist/clis/facebook/memories.yaml +39 -0
  134. package/dist/clis/facebook/notifications.yaml +40 -0
  135. package/dist/clis/facebook/profile.yaml +37 -0
  136. package/dist/clis/facebook/search.yaml +46 -0
  137. package/dist/clis/google/news.d.ts +5 -0
  138. package/dist/clis/google/news.js +58 -0
  139. package/dist/clis/google/search.d.ts +10 -0
  140. package/dist/clis/google/search.js +127 -0
  141. package/dist/clis/google/suggest.d.ts +5 -0
  142. package/dist/clis/google/suggest.js +34 -0
  143. package/dist/clis/google/trends.d.ts +5 -0
  144. package/dist/clis/google/trends.js +38 -0
  145. package/dist/clis/google/utils.d.ts +9 -0
  146. package/dist/clis/google/utils.js +23 -0
  147. package/dist/clis/google/utils.test.d.ts +1 -0
  148. package/dist/clis/google/utils.test.js +75 -0
  149. package/dist/clis/grok/ask.d.ts +14 -0
  150. package/dist/clis/grok/ask.js +257 -65
  151. package/dist/clis/grok/ask.test.d.ts +1 -0
  152. package/dist/clis/grok/ask.test.js +36 -0
  153. package/dist/clis/instagram/comment.yaml +52 -0
  154. package/dist/clis/instagram/explore.yaml +43 -0
  155. package/dist/clis/instagram/follow.yaml +41 -0
  156. package/dist/clis/instagram/followers.yaml +51 -0
  157. package/dist/clis/instagram/following.yaml +51 -0
  158. package/dist/clis/instagram/like.yaml +46 -0
  159. package/dist/clis/instagram/profile.yaml +42 -0
  160. package/dist/clis/instagram/save.yaml +46 -0
  161. package/dist/clis/instagram/saved.yaml +40 -0
  162. package/dist/clis/instagram/search.yaml +43 -0
  163. package/dist/clis/instagram/unfollow.yaml +38 -0
  164. package/dist/clis/instagram/unlike.yaml +46 -0
  165. package/dist/clis/instagram/unsave.yaml +46 -0
  166. package/dist/clis/instagram/user.yaml +54 -0
  167. package/dist/clis/jike/comment.js +2 -3
  168. package/dist/clis/jike/create.js +1 -2
  169. package/dist/clis/jike/feed.js +0 -1
  170. package/dist/clis/jike/like.js +1 -2
  171. package/dist/clis/jike/notifications.js +0 -1
  172. package/dist/clis/jike/post.yaml +1 -0
  173. package/dist/clis/jike/repost.js +2 -3
  174. package/dist/clis/jike/search.js +2 -3
  175. package/dist/clis/jike/topic.yaml +1 -0
  176. package/dist/clis/jike/user.yaml +1 -0
  177. package/dist/clis/jimeng/generate.yaml +1 -0
  178. package/dist/clis/jimeng/history.yaml +0 -1
  179. package/dist/clis/linkedin/search.js +7 -7
  180. package/dist/clis/linux-do/category.yaml +2 -0
  181. package/dist/clis/linux-do/search.yaml +4 -3
  182. package/dist/clis/linux-do/topic.yaml +1 -0
  183. package/dist/clis/lobsters/active.yaml +29 -0
  184. package/dist/clis/lobsters/hot.yaml +29 -0
  185. package/dist/clis/lobsters/newest.yaml +29 -0
  186. package/dist/clis/lobsters/tag.yaml +34 -0
  187. package/dist/clis/medium/feed.d.ts +1 -0
  188. package/dist/clis/medium/feed.js +15 -0
  189. package/dist/clis/medium/search.d.ts +1 -0
  190. package/dist/clis/medium/search.js +15 -0
  191. package/dist/clis/medium/shared.d.ts +5 -0
  192. package/dist/clis/medium/shared.js +78 -0
  193. package/dist/clis/medium/user.d.ts +1 -0
  194. package/dist/clis/medium/user.js +15 -0
  195. package/dist/clis/notion/export.js +1 -1
  196. package/dist/clis/reddit/comment.js +3 -4
  197. package/dist/clis/reddit/read.js +4 -5
  198. package/dist/clis/reddit/save.js +2 -3
  199. package/dist/clis/reddit/saved.js +0 -1
  200. package/dist/clis/reddit/search.yaml +1 -0
  201. package/dist/clis/reddit/subreddit.yaml +1 -0
  202. package/dist/clis/reddit/subscribe.js +1 -2
  203. package/dist/clis/reddit/upvote.js +2 -3
  204. package/dist/clis/reddit/upvoted.js +0 -1
  205. package/dist/clis/reddit/user-comments.yaml +1 -0
  206. package/dist/clis/reddit/user-posts.yaml +1 -0
  207. package/dist/clis/reddit/user.yaml +1 -0
  208. package/dist/clis/reuters/search.js +1 -1
  209. package/dist/clis/sinablog/article.d.ts +1 -0
  210. package/dist/clis/sinablog/article.js +14 -0
  211. package/dist/clis/sinablog/hot.d.ts +1 -0
  212. package/dist/clis/sinablog/hot.js +14 -0
  213. package/dist/clis/sinablog/search.d.ts +1 -0
  214. package/dist/clis/sinablog/search.js +51 -0
  215. package/dist/clis/sinablog/shared.d.ts +7 -0
  216. package/dist/clis/sinablog/shared.js +187 -0
  217. package/dist/clis/sinablog/user.d.ts +1 -0
  218. package/dist/clis/sinablog/user.js +15 -0
  219. package/dist/clis/smzdm/search.js +2 -3
  220. package/dist/clis/stackoverflow/search.yaml +1 -0
  221. package/dist/clis/steam/top-sellers.yaml +29 -0
  222. package/dist/clis/substack/feed.d.ts +1 -0
  223. package/dist/clis/substack/feed.js +15 -0
  224. package/dist/clis/substack/publication.d.ts +1 -0
  225. package/dist/clis/substack/publication.js +15 -0
  226. package/dist/clis/substack/search.d.ts +1 -0
  227. package/dist/clis/substack/search.js +77 -0
  228. package/dist/clis/substack/shared.d.ts +4 -0
  229. package/dist/clis/substack/shared.js +129 -0
  230. package/dist/clis/tiktok/comment.yaml +66 -0
  231. package/dist/clis/tiktok/explore.yaml +39 -0
  232. package/dist/clis/tiktok/follow.yaml +39 -0
  233. package/dist/clis/tiktok/following.yaml +46 -0
  234. package/dist/clis/tiktok/friends.yaml +47 -0
  235. package/dist/clis/tiktok/like.yaml +38 -0
  236. package/dist/clis/tiktok/live.yaml +51 -0
  237. package/dist/clis/tiktok/notifications.yaml +52 -0
  238. package/dist/clis/tiktok/profile.yaml +45 -0
  239. package/dist/clis/tiktok/save.yaml +34 -0
  240. package/dist/clis/tiktok/search.yaml +46 -0
  241. package/dist/clis/tiktok/unfollow.yaml +44 -0
  242. package/dist/clis/tiktok/unlike.yaml +38 -0
  243. package/dist/clis/tiktok/unsave.yaml +36 -0
  244. package/dist/clis/tiktok/user.yaml +44 -0
  245. package/dist/clis/twitter/accept.js +2 -2
  246. package/dist/clis/twitter/article.js +2 -2
  247. package/dist/clis/twitter/block.d.ts +1 -0
  248. package/dist/clis/twitter/block.js +88 -0
  249. package/dist/clis/twitter/delete.js +1 -1
  250. package/dist/clis/twitter/download.d.ts +1 -1
  251. package/dist/clis/twitter/download.js +3 -3
  252. package/dist/clis/twitter/followers.js +1 -1
  253. package/dist/clis/twitter/following.js +1 -1
  254. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  255. package/dist/clis/twitter/hide-reply.js +66 -0
  256. package/dist/clis/twitter/like.js +1 -1
  257. package/dist/clis/twitter/post.js +1 -1
  258. package/dist/clis/twitter/reply-dm.js +1 -1
  259. package/dist/clis/twitter/reply.js +2 -2
  260. package/dist/clis/twitter/search.js +1 -1
  261. package/dist/clis/twitter/thread.js +2 -2
  262. package/dist/clis/twitter/timeline.d.ts +23 -0
  263. package/dist/clis/twitter/timeline.js +42 -14
  264. package/dist/clis/twitter/timeline.test.d.ts +1 -0
  265. package/dist/clis/twitter/timeline.test.js +102 -0
  266. package/dist/clis/twitter/trending.d.ts +1 -0
  267. package/dist/clis/twitter/trending.js +91 -0
  268. package/dist/clis/twitter/unblock.d.ts +1 -0
  269. package/dist/clis/twitter/unblock.js +71 -0
  270. package/dist/clis/v2ex/topic.yaml +1 -0
  271. package/dist/clis/weibo/hot.js +0 -1
  272. package/dist/clis/weread/book.js +1 -1
  273. package/dist/clis/weread/highlights.js +1 -1
  274. package/dist/clis/weread/notes.js +1 -1
  275. package/dist/clis/weread/search.js +1 -1
  276. package/dist/clis/wikipedia/random.d.ts +1 -0
  277. package/dist/clis/wikipedia/random.js +19 -0
  278. package/dist/clis/wikipedia/search.js +4 -4
  279. package/dist/clis/wikipedia/summary.js +4 -9
  280. package/dist/clis/wikipedia/trending.d.ts +1 -0
  281. package/dist/clis/wikipedia/trending.js +35 -0
  282. package/dist/clis/wikipedia/utils.d.ts +28 -0
  283. package/dist/clis/wikipedia/utils.js +13 -0
  284. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +15 -0
  285. package/dist/clis/xiaohongshu/creator-note-detail.js +69 -5
  286. package/dist/clis/xiaohongshu/creator-note-detail.test.js +82 -33
  287. package/dist/clis/xiaohongshu/creator-notes.js +35 -5
  288. package/dist/clis/xiaohongshu/creator-notes.test.js +37 -6
  289. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  290. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  291. package/dist/clis/xiaohongshu/download.js +2 -3
  292. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  293. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  294. package/dist/clis/xiaohongshu/search.js +2 -2
  295. package/dist/clis/xiaohongshu/user.js +1 -2
  296. package/dist/clis/xueqiu/earnings-date.yaml +69 -0
  297. package/dist/clis/xueqiu/search.yaml +2 -1
  298. package/dist/clis/xueqiu/stock.yaml +2 -0
  299. package/dist/clis/yahoo-finance/quote.js +1 -2
  300. package/dist/clis/youtube/search.js +1 -1
  301. package/dist/clis/youtube/transcript.js +1 -1
  302. package/dist/clis/youtube/video.js +1 -1
  303. package/dist/clis/zhihu/download.js +1 -2
  304. package/dist/clis/zhihu/question.js +1 -1
  305. package/dist/clis/zhihu/search.yaml +4 -3
  306. package/dist/commanderAdapter.d.ts +21 -0
  307. package/dist/commanderAdapter.js +117 -0
  308. package/dist/{engine.d.ts → discovery.d.ts} +6 -4
  309. package/dist/{engine.js → discovery.js} +93 -104
  310. package/dist/doctor.js +3 -1
  311. package/dist/doctor.test.js +46 -2
  312. package/dist/download/index.d.ts +2 -6
  313. package/dist/download/index.js +19 -46
  314. package/dist/engine.test.d.ts +0 -3
  315. package/dist/engine.test.js +80 -11
  316. package/dist/execution.d.ts +24 -0
  317. package/dist/execution.js +153 -0
  318. package/dist/explore.d.ts +76 -3
  319. package/dist/explore.js +132 -111
  320. package/dist/external-clis.yaml +48 -0
  321. package/dist/external.d.ts +7 -2
  322. package/dist/external.js +11 -14
  323. package/dist/generate.d.ts +41 -2
  324. package/dist/generate.js +5 -4
  325. package/dist/main.js +2 -1
  326. package/dist/pipeline/executor.d.ts +2 -2
  327. package/dist/pipeline/executor.js +2 -2
  328. package/dist/pipeline/executor.test.js +33 -6
  329. package/dist/pipeline/registry.d.ts +1 -1
  330. package/dist/pipeline/steps/browser.d.ts +7 -7
  331. package/dist/pipeline/steps/browser.js +21 -7
  332. package/dist/pipeline/steps/fetch.d.ts +1 -1
  333. package/dist/pipeline/steps/fetch.js +11 -7
  334. package/dist/pipeline/steps/transform.d.ts +6 -5
  335. package/dist/pipeline/steps/transform.js +30 -9
  336. package/dist/pipeline/template.d.ts +6 -6
  337. package/dist/pipeline/template.js +43 -5
  338. package/dist/pipeline/template.test.js +18 -0
  339. package/dist/pipeline/transform.test.js +11 -0
  340. package/dist/plugin.d.ts +31 -0
  341. package/dist/plugin.js +216 -0
  342. package/dist/plugin.test.d.ts +4 -0
  343. package/dist/plugin.test.js +76 -0
  344. package/dist/registry-api.d.ts +11 -0
  345. package/dist/registry-api.js +9 -0
  346. package/dist/registry.d.ts +13 -0
  347. package/dist/registry.js +8 -1
  348. package/dist/runtime.d.ts +5 -0
  349. package/dist/runtime.js +8 -0
  350. package/dist/serialization.d.ts +34 -0
  351. package/dist/serialization.js +63 -0
  352. package/dist/synthesize.d.ts +94 -4
  353. package/dist/synthesize.js +5 -4
  354. package/dist/types.d.ts +43 -27
  355. package/dist/validate.js +8 -2
  356. package/docs/.vitepress/config.mts +20 -7
  357. package/docs/adapters/browser/arxiv.md +27 -0
  358. package/docs/adapters/browser/barchart.md +33 -0
  359. package/docs/adapters/browser/bilibili.md +9 -0
  360. package/docs/adapters/browser/bloomberg.md +70 -0
  361. package/docs/adapters/browser/chaoxing.md +39 -0
  362. package/docs/adapters/browser/devto.md +35 -0
  363. package/docs/adapters/browser/douban.md +38 -0
  364. package/docs/adapters/browser/facebook.md +36 -0
  365. package/docs/adapters/browser/google.md +62 -0
  366. package/docs/adapters/browser/grok.md +53 -0
  367. package/docs/adapters/browser/hf.md +42 -0
  368. package/docs/adapters/browser/instagram.md +46 -0
  369. package/docs/adapters/browser/jike.md +45 -0
  370. package/docs/adapters/browser/jimeng.md +39 -0
  371. package/docs/adapters/browser/linux-do.md +45 -0
  372. package/docs/adapters/browser/lobsters.md +32 -0
  373. package/docs/adapters/browser/medium.md +32 -0
  374. package/docs/adapters/browser/reddit.md +9 -0
  375. package/docs/adapters/browser/sinablog.md +36 -0
  376. package/docs/adapters/browser/sinafinance.md +35 -0
  377. package/docs/adapters/browser/stackoverflow.md +35 -0
  378. package/docs/adapters/browser/steam.md +26 -0
  379. package/docs/adapters/browser/substack.md +38 -0
  380. package/docs/adapters/browser/tiktok.md +68 -0
  381. package/docs/adapters/browser/twitter.md +3 -0
  382. package/docs/adapters/browser/weread.md +48 -0
  383. package/docs/adapters/browser/wikipedia.md +39 -0
  384. package/docs/adapters/browser/xiaohongshu.md +5 -1
  385. package/docs/adapters/browser/xueqiu.md +10 -0
  386. package/docs/adapters/browser/yahoo-finance.md +6 -5
  387. package/docs/adapters/desktop/antigravity.md +6 -0
  388. package/docs/adapters/desktop/chatgpt.md +5 -4
  389. package/docs/adapters/desktop/codex.md +5 -1
  390. package/docs/adapters/desktop/cursor.md +4 -0
  391. package/docs/adapters/desktop/discord.md +7 -7
  392. package/docs/adapters/index.md +14 -4
  393. package/docs/advanced/download.md +4 -4
  394. package/docs/developer/architecture.md +17 -4
  395. package/docs/guide/getting-started.md +1 -0
  396. package/docs/guide/plugins.md +153 -0
  397. package/docs/zh/guide/plugins.md +107 -0
  398. package/extension/src/background.ts +18 -11
  399. package/package.json +10 -5
  400. package/scripts/check-doc-coverage.sh +69 -0
  401. package/scripts/clean-dist.cjs +13 -0
  402. package/scripts/copy-yaml.cjs +7 -0
  403. package/src/browser/cdp.ts +77 -32
  404. package/src/browser/daemon-client.ts +2 -1
  405. package/src/browser/dom-helpers.ts +38 -7
  406. package/src/browser/dom-snapshot.test.ts +249 -0
  407. package/src/browser/dom-snapshot.ts +770 -0
  408. package/src/browser/index.ts +2 -0
  409. package/src/browser/page.ts +57 -20
  410. package/src/build-manifest.test.ts +70 -2
  411. package/src/build-manifest.ts +114 -40
  412. package/src/cli.ts +287 -139
  413. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  414. package/src/clis/apple-podcasts/search.ts +2 -2
  415. package/src/clis/apple-podcasts/top.ts +12 -2
  416. package/src/clis/arxiv/search.ts +1 -1
  417. package/src/clis/barchart/greeks.ts +1 -1
  418. package/src/clis/barchart/options.ts +1 -1
  419. package/src/clis/barchart/quote.ts +1 -1
  420. package/src/clis/bilibili/download.ts +1 -1
  421. package/src/clis/bilibili/dynamic.ts +1 -1
  422. package/src/clis/bilibili/favorite.ts +1 -1
  423. package/src/clis/bilibili/feed.ts +1 -1
  424. package/src/clis/bilibili/following.ts +2 -2
  425. package/src/clis/bilibili/history.ts +1 -1
  426. package/src/clis/bilibili/me.ts +1 -1
  427. package/src/clis/bilibili/ranking.ts +1 -1
  428. package/src/clis/bilibili/search.ts +3 -3
  429. package/src/clis/bilibili/subtitle.ts +2 -2
  430. package/src/clis/bilibili/user-videos.ts +2 -2
  431. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  432. package/src/clis/bloomberg/businessweek.ts +18 -0
  433. package/src/clis/bloomberg/economics.ts +18 -0
  434. package/src/clis/bloomberg/feeds.ts +16 -0
  435. package/src/clis/bloomberg/industries.ts +18 -0
  436. package/src/clis/bloomberg/main.ts +18 -0
  437. package/src/clis/bloomberg/markets.ts +18 -0
  438. package/src/clis/bloomberg/news.ts +136 -0
  439. package/src/clis/bloomberg/opinions.ts +18 -0
  440. package/src/clis/bloomberg/politics.ts +18 -0
  441. package/src/clis/bloomberg/tech.ts +18 -0
  442. package/src/clis/bloomberg/utils.test.ts +135 -0
  443. package/src/clis/bloomberg/utils.ts +429 -0
  444. package/src/clis/boss/batchgreet.ts +16 -108
  445. package/src/clis/boss/chatlist.ts +13 -27
  446. package/src/clis/boss/chatmsg.ts +16 -40
  447. package/src/clis/boss/common.ts +287 -0
  448. package/src/clis/boss/detail.ts +9 -55
  449. package/src/clis/boss/exchange.ts +15 -89
  450. package/src/clis/boss/greet.ts +25 -162
  451. package/src/clis/boss/invite.ts +36 -133
  452. package/src/clis/boss/joblist.ts +7 -36
  453. package/src/clis/boss/mark.ts +13 -94
  454. package/src/clis/boss/recommend.ts +12 -57
  455. package/src/clis/boss/resume.ts +19 -124
  456. package/src/clis/boss/search.ts +14 -67
  457. package/src/clis/boss/send.ts +22 -162
  458. package/src/clis/boss/stats.ts +21 -76
  459. package/src/clis/chaoxing/assignments.ts +1 -1
  460. package/src/clis/chaoxing/exams.ts +1 -1
  461. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  462. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  463. package/src/clis/chatgpt/read.ts +1 -1
  464. package/src/clis/chatwise/export.ts +1 -1
  465. package/src/clis/chatwise/model.ts +2 -2
  466. package/src/clis/chatwise/screenshot.ts +1 -1
  467. package/src/clis/codex/export.ts +1 -1
  468. package/src/clis/codex/model.ts +2 -2
  469. package/src/clis/codex/screenshot.ts +1 -1
  470. package/src/clis/coupang/add-to-cart.ts +3 -4
  471. package/src/clis/coupang/search.ts +2 -4
  472. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  473. package/src/clis/ctrip/search.ts +1 -1
  474. package/src/clis/cursor/export.ts +1 -1
  475. package/src/clis/cursor/model.ts +2 -2
  476. package/src/clis/cursor/screenshot.ts +1 -1
  477. package/src/clis/devto/tag.yaml +34 -0
  478. package/src/clis/devto/top.yaml +29 -0
  479. package/src/clis/devto/user.yaml +33 -0
  480. package/src/clis/douban/book-hot.ts +15 -0
  481. package/src/clis/douban/marks.ts +135 -0
  482. package/src/clis/douban/movie-hot.ts +15 -0
  483. package/src/clis/douban/reviews.ts +127 -0
  484. package/src/clis/douban/search.ts +17 -0
  485. package/src/clis/douban/shared.ts +165 -0
  486. package/src/clis/douban/subject.yaml +76 -0
  487. package/src/clis/douban/top250.yaml +70 -0
  488. package/src/clis/douban/utils.ts +81 -0
  489. package/src/clis/facebook/add-friend.yaml +43 -0
  490. package/src/clis/facebook/events.yaml +44 -0
  491. package/src/clis/facebook/feed.yaml +63 -0
  492. package/src/clis/facebook/friends.yaml +42 -0
  493. package/src/clis/facebook/groups.yaml +50 -0
  494. package/src/clis/facebook/join-group.yaml +44 -0
  495. package/src/clis/facebook/memories.yaml +39 -0
  496. package/src/clis/facebook/notifications.yaml +40 -0
  497. package/src/clis/facebook/profile.yaml +37 -0
  498. package/src/clis/facebook/search.yaml +46 -0
  499. package/src/clis/google/news.ts +66 -0
  500. package/src/clis/google/search.ts +133 -0
  501. package/src/clis/google/suggest.ts +40 -0
  502. package/src/clis/google/trends.ts +44 -0
  503. package/src/clis/google/utils.test.ts +82 -0
  504. package/src/clis/google/utils.ts +24 -0
  505. package/src/clis/grok/ask.test.ts +53 -0
  506. package/src/clis/grok/ask.ts +300 -69
  507. package/src/clis/instagram/comment.yaml +52 -0
  508. package/src/clis/instagram/explore.yaml +43 -0
  509. package/src/clis/instagram/follow.yaml +41 -0
  510. package/src/clis/instagram/followers.yaml +51 -0
  511. package/src/clis/instagram/following.yaml +51 -0
  512. package/src/clis/instagram/like.yaml +46 -0
  513. package/src/clis/instagram/profile.yaml +42 -0
  514. package/src/clis/instagram/save.yaml +46 -0
  515. package/src/clis/instagram/saved.yaml +40 -0
  516. package/src/clis/instagram/search.yaml +43 -0
  517. package/src/clis/instagram/unfollow.yaml +38 -0
  518. package/src/clis/instagram/unlike.yaml +46 -0
  519. package/src/clis/instagram/unsave.yaml +46 -0
  520. package/src/clis/instagram/user.yaml +54 -0
  521. package/src/clis/jike/comment.ts +2 -3
  522. package/src/clis/jike/create.ts +1 -2
  523. package/src/clis/jike/feed.ts +0 -1
  524. package/src/clis/jike/like.ts +1 -2
  525. package/src/clis/jike/notifications.ts +0 -1
  526. package/src/clis/jike/post.yaml +1 -0
  527. package/src/clis/jike/repost.ts +2 -3
  528. package/src/clis/jike/search.ts +2 -3
  529. package/src/clis/jike/topic.yaml +1 -0
  530. package/src/clis/jike/user.yaml +1 -0
  531. package/src/clis/jimeng/generate.yaml +1 -0
  532. package/src/clis/jimeng/history.yaml +0 -1
  533. package/src/clis/linkedin/search.ts +7 -7
  534. package/src/clis/linux-do/category.yaml +2 -0
  535. package/src/clis/linux-do/search.yaml +4 -3
  536. package/src/clis/linux-do/topic.yaml +1 -0
  537. package/src/clis/lobsters/active.yaml +29 -0
  538. package/src/clis/lobsters/hot.yaml +29 -0
  539. package/src/clis/lobsters/newest.yaml +29 -0
  540. package/src/clis/lobsters/tag.yaml +34 -0
  541. package/src/clis/medium/feed.ts +16 -0
  542. package/src/clis/medium/search.ts +16 -0
  543. package/src/clis/medium/shared.ts +83 -0
  544. package/src/clis/medium/user.ts +16 -0
  545. package/src/clis/notion/export.ts +1 -1
  546. package/src/clis/reddit/comment.ts +3 -4
  547. package/src/clis/reddit/read.ts +4 -5
  548. package/src/clis/reddit/save.ts +2 -3
  549. package/src/clis/reddit/saved.ts +0 -1
  550. package/src/clis/reddit/search.yaml +1 -0
  551. package/src/clis/reddit/subreddit.yaml +1 -0
  552. package/src/clis/reddit/subscribe.ts +1 -2
  553. package/src/clis/reddit/upvote.ts +2 -3
  554. package/src/clis/reddit/upvoted.ts +0 -1
  555. package/src/clis/reddit/user-comments.yaml +1 -0
  556. package/src/clis/reddit/user-posts.yaml +1 -0
  557. package/src/clis/reddit/user.yaml +1 -0
  558. package/src/clis/reuters/search.ts +1 -1
  559. package/src/clis/sinablog/article.ts +15 -0
  560. package/src/clis/sinablog/hot.ts +15 -0
  561. package/src/clis/sinablog/search.ts +56 -0
  562. package/src/clis/sinablog/shared.ts +198 -0
  563. package/src/clis/sinablog/user.ts +16 -0
  564. package/src/clis/smzdm/search.ts +2 -3
  565. package/src/clis/stackoverflow/search.yaml +1 -0
  566. package/src/clis/steam/top-sellers.yaml +29 -0
  567. package/src/clis/substack/feed.ts +16 -0
  568. package/src/clis/substack/publication.ts +16 -0
  569. package/src/clis/substack/search.ts +91 -0
  570. package/src/clis/substack/shared.ts +132 -0
  571. package/src/clis/tiktok/comment.yaml +66 -0
  572. package/src/clis/tiktok/explore.yaml +39 -0
  573. package/src/clis/tiktok/follow.yaml +39 -0
  574. package/src/clis/tiktok/following.yaml +46 -0
  575. package/src/clis/tiktok/friends.yaml +47 -0
  576. package/src/clis/tiktok/like.yaml +38 -0
  577. package/src/clis/tiktok/live.yaml +51 -0
  578. package/src/clis/tiktok/notifications.yaml +52 -0
  579. package/src/clis/tiktok/profile.yaml +45 -0
  580. package/src/clis/tiktok/save.yaml +34 -0
  581. package/src/clis/tiktok/search.yaml +46 -0
  582. package/src/clis/tiktok/unfollow.yaml +44 -0
  583. package/src/clis/tiktok/unlike.yaml +38 -0
  584. package/src/clis/tiktok/unsave.yaml +36 -0
  585. package/src/clis/tiktok/user.yaml +44 -0
  586. package/src/clis/twitter/accept.ts +2 -2
  587. package/src/clis/twitter/article.ts +2 -2
  588. package/src/clis/twitter/block.ts +92 -0
  589. package/src/clis/twitter/delete.ts +1 -1
  590. package/src/clis/twitter/download.ts +3 -3
  591. package/src/clis/twitter/followers.ts +1 -1
  592. package/src/clis/twitter/following.ts +1 -1
  593. package/src/clis/twitter/hide-reply.ts +70 -0
  594. package/src/clis/twitter/like.ts +1 -1
  595. package/src/clis/twitter/post.ts +1 -1
  596. package/src/clis/twitter/reply-dm.ts +1 -1
  597. package/src/clis/twitter/reply.ts +2 -2
  598. package/src/clis/twitter/search.ts +1 -1
  599. package/src/clis/twitter/thread.ts +2 -2
  600. package/src/clis/twitter/timeline.test.ts +109 -0
  601. package/src/clis/twitter/timeline.ts +59 -19
  602. package/src/clis/twitter/trending.ts +113 -0
  603. package/src/clis/twitter/unblock.ts +75 -0
  604. package/src/clis/v2ex/topic.yaml +1 -0
  605. package/src/clis/weibo/hot.ts +0 -1
  606. package/src/clis/weread/book.ts +1 -1
  607. package/src/clis/weread/highlights.ts +1 -1
  608. package/src/clis/weread/notes.ts +1 -1
  609. package/src/clis/weread/search.ts +1 -1
  610. package/src/clis/wikipedia/random.ts +19 -0
  611. package/src/clis/wikipedia/search.ts +11 -5
  612. package/src/clis/wikipedia/summary.ts +4 -9
  613. package/src/clis/wikipedia/trending.ts +41 -0
  614. package/src/clis/wikipedia/utils.ts +31 -0
  615. package/src/clis/xiaohongshu/creator-note-detail.test.ts +84 -33
  616. package/src/clis/xiaohongshu/creator-note-detail.ts +89 -5
  617. package/src/clis/xiaohongshu/creator-notes.test.ts +41 -6
  618. package/src/clis/xiaohongshu/creator-notes.ts +44 -5
  619. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  620. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  621. package/src/clis/xiaohongshu/download.ts +2 -3
  622. package/src/clis/xiaohongshu/feed.yaml +0 -1
  623. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  624. package/src/clis/xiaohongshu/search.ts +2 -2
  625. package/src/clis/xiaohongshu/user.ts +1 -2
  626. package/src/clis/xueqiu/earnings-date.yaml +69 -0
  627. package/src/clis/xueqiu/search.yaml +2 -1
  628. package/src/clis/xueqiu/stock.yaml +2 -0
  629. package/src/clis/yahoo-finance/quote.ts +1 -2
  630. package/src/clis/youtube/search.ts +1 -1
  631. package/src/clis/youtube/transcript.ts +1 -1
  632. package/src/clis/youtube/video.ts +1 -1
  633. package/src/clis/zhihu/download.ts +1 -2
  634. package/src/clis/zhihu/question.ts +1 -1
  635. package/src/clis/zhihu/search.yaml +4 -3
  636. package/src/commanderAdapter.ts +120 -0
  637. package/src/discovery.ts +277 -0
  638. package/src/doctor.test.ts +59 -2
  639. package/src/doctor.ts +4 -2
  640. package/src/download/index.ts +21 -54
  641. package/src/engine.test.ts +85 -11
  642. package/src/execution.ts +164 -0
  643. package/src/explore.ts +211 -117
  644. package/src/external-clis.yaml +9 -0
  645. package/src/external.ts +15 -12
  646. package/src/generate.ts +58 -9
  647. package/src/main.ts +2 -1
  648. package/src/pipeline/executor.test.ts +35 -6
  649. package/src/pipeline/executor.ts +11 -7
  650. package/src/pipeline/registry.ts +3 -3
  651. package/src/pipeline/steps/browser.ts +29 -15
  652. package/src/pipeline/steps/fetch.ts +18 -13
  653. package/src/pipeline/steps/transform.ts +40 -15
  654. package/src/pipeline/template.test.ts +18 -0
  655. package/src/pipeline/template.ts +86 -13
  656. package/src/pipeline/transform.test.ts +15 -2
  657. package/src/plugin.test.ts +86 -0
  658. package/src/plugin.ts +254 -0
  659. package/src/registry-api.ts +12 -0
  660. package/src/registry.ts +24 -1
  661. package/src/runtime.ts +9 -0
  662. package/src/serialization.ts +79 -0
  663. package/src/synthesize.ts +102 -21
  664. package/src/types.ts +45 -13
  665. package/src/validate.ts +19 -4
  666. package/tests/e2e/browser-public.test.ts +36 -0
  667. package/tests/e2e/public-commands.test.ts +119 -1
  668. package/dist/clis/feishu/new.d.ts +0 -1
  669. package/dist/clis/feishu/new.js +0 -27
  670. package/dist/clis/feishu/read.d.ts +0 -1
  671. package/dist/clis/feishu/read.js +0 -40
  672. package/dist/clis/feishu/search.d.ts +0 -1
  673. package/dist/clis/feishu/search.js +0 -30
  674. package/dist/clis/feishu/send.d.ts +0 -1
  675. package/dist/clis/feishu/send.js +0 -39
  676. package/dist/clis/feishu/status.d.ts +0 -1
  677. package/dist/clis/feishu/status.js +0 -28
  678. package/dist/clis/neteasemusic/like.d.ts +0 -1
  679. package/dist/clis/neteasemusic/like.js +0 -25
  680. package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
  681. package/dist/clis/neteasemusic/lyrics.js +0 -47
  682. package/dist/clis/neteasemusic/next.d.ts +0 -1
  683. package/dist/clis/neteasemusic/next.js +0 -26
  684. package/dist/clis/neteasemusic/play.d.ts +0 -1
  685. package/dist/clis/neteasemusic/play.js +0 -26
  686. package/dist/clis/neteasemusic/playing.d.ts +0 -1
  687. package/dist/clis/neteasemusic/playing.js +0 -59
  688. package/dist/clis/neteasemusic/playlist.d.ts +0 -1
  689. package/dist/clis/neteasemusic/playlist.js +0 -46
  690. package/dist/clis/neteasemusic/prev.d.ts +0 -1
  691. package/dist/clis/neteasemusic/prev.js +0 -25
  692. package/dist/clis/neteasemusic/search.d.ts +0 -1
  693. package/dist/clis/neteasemusic/search.js +0 -52
  694. package/dist/clis/neteasemusic/status.d.ts +0 -1
  695. package/dist/clis/neteasemusic/status.js +0 -16
  696. package/dist/clis/neteasemusic/volume.d.ts +0 -1
  697. package/dist/clis/neteasemusic/volume.js +0 -54
  698. package/dist/clis/twitter/trending.yaml +0 -46
  699. package/dist/clis/wechat/chats.d.ts +0 -1
  700. package/dist/clis/wechat/chats.js +0 -28
  701. package/dist/clis/wechat/contacts.d.ts +0 -1
  702. package/dist/clis/wechat/contacts.js +0 -28
  703. package/dist/clis/wechat/read.d.ts +0 -1
  704. package/dist/clis/wechat/read.js +0 -58
  705. package/dist/clis/wechat/search.d.ts +0 -1
  706. package/dist/clis/wechat/search.js +0 -31
  707. package/dist/clis/wechat/send.d.ts +0 -1
  708. package/dist/clis/wechat/send.js +0 -42
  709. package/dist/clis/wechat/status.d.ts +0 -1
  710. package/dist/clis/wechat/status.js +0 -29
  711. package/dist/pipeline.d.ts +0 -7
  712. package/dist/pipeline.js +0 -7
  713. package/docs/adapters/browser/github.md +0 -26
  714. package/docs/adapters/desktop/feishu.md +0 -20
  715. package/docs/adapters/desktop/neteasemusic.md +0 -31
  716. package/docs/adapters/desktop/wechat.md +0 -28
  717. package/docs/public/CNAME +0 -1
  718. package/src/clis/antigravity/README.md +0 -5
  719. package/src/clis/antigravity/README.zh-CN.md +0 -51
  720. package/src/clis/chaoxing/README.md +0 -14
  721. package/src/clis/chaoxing/README.zh-CN.md +0 -35
  722. package/src/clis/chatgpt/README.md +0 -5
  723. package/src/clis/chatgpt/README.zh-CN.md +0 -44
  724. package/src/clis/chatwise/README.md +0 -5
  725. package/src/clis/chatwise/README.zh-CN.md +0 -38
  726. package/src/clis/codex/README.md +0 -5
  727. package/src/clis/codex/README.zh-CN.md +0 -33
  728. package/src/clis/cursor/README.md +0 -5
  729. package/src/clis/cursor/README.zh-CN.md +0 -33
  730. package/src/clis/discord-app/README.md +0 -5
  731. package/src/clis/discord-app/README.zh-CN.md +0 -28
  732. package/src/clis/feishu/README.md +0 -5
  733. package/src/clis/feishu/README.zh-CN.md +0 -20
  734. package/src/clis/feishu/new.ts +0 -32
  735. package/src/clis/feishu/read.ts +0 -48
  736. package/src/clis/feishu/search.ts +0 -35
  737. package/src/clis/feishu/send.ts +0 -46
  738. package/src/clis/feishu/status.ts +0 -34
  739. package/src/clis/neteasemusic/README.md +0 -5
  740. package/src/clis/neteasemusic/README.zh-CN.md +0 -31
  741. package/src/clis/neteasemusic/like.ts +0 -28
  742. package/src/clis/neteasemusic/lyrics.ts +0 -53
  743. package/src/clis/neteasemusic/next.ts +0 -30
  744. package/src/clis/neteasemusic/play.ts +0 -30
  745. package/src/clis/neteasemusic/playing.ts +0 -62
  746. package/src/clis/neteasemusic/playlist.ts +0 -51
  747. package/src/clis/neteasemusic/prev.ts +0 -29
  748. package/src/clis/neteasemusic/search.ts +0 -58
  749. package/src/clis/neteasemusic/status.ts +0 -18
  750. package/src/clis/neteasemusic/volume.ts +0 -61
  751. package/src/clis/notion/README.md +0 -5
  752. package/src/clis/notion/README.zh-CN.md +0 -29
  753. package/src/clis/twitter/trending.yaml +0 -46
  754. package/src/clis/wechat/README.md +0 -5
  755. package/src/clis/wechat/README.zh-CN.md +0 -28
  756. package/src/clis/wechat/chats.ts +0 -33
  757. package/src/clis/wechat/contacts.ts +0 -33
  758. package/src/clis/wechat/read.ts +0 -72
  759. package/src/clis/wechat/search.ts +0 -36
  760. package/src/clis/wechat/send.ts +0 -49
  761. package/src/clis/wechat/status.ts +0 -35
  762. package/src/engine.ts +0 -274
  763. package/src/pipeline.ts +0 -8
  764. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  765. /package/dist/{chaoxing.test.d.ts → clis/bloomberg/businessweek.d.ts} +0 -0
  766. /package/dist/{coupang.test.d.ts → clis/bloomberg/economics.d.ts} +0 -0
  767. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  768. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  769. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
@@ -1,15 +1,11 @@
1
1
  /**
2
2
  * BOSS直聘 greet — send greeting to a new candidate (initiate chat).
3
- *
4
- * This is different from send.ts which messages existing contacts.
5
- * For new candidates (from recommend list), we navigate to their chat page
6
- * and use UI automation to send the greeting message.
7
- *
8
- * The greetRecSortList provides candidates who have applied or been recommended.
9
- * We click on them in the list and send a greeting.
10
3
  */
11
4
  import { cli, Strategy } from '../../registry.js';
12
- import type { IPage } from '../../types.js';
5
+ import {
6
+ requirePage, navigateToChat, findFriendByUid,
7
+ clickCandidateInList, typeAndSendMessage, verbose,
8
+ } from './common.js';
13
9
 
14
10
  cli({
15
11
  site: 'boss',
@@ -17,180 +13,47 @@ cli({
17
13
  description: 'BOSS直聘向新候选人发送招呼(开始聊天)',
18
14
  domain: 'www.zhipin.com',
19
15
  strategy: Strategy.COOKIE,
16
+ navigateBefore: false,
20
17
  browser: true,
21
18
  args: [
22
- { name: 'uid', required: true, help: 'Encrypted UID of the candidate (from recommend)' },
23
- { name: 'security_id', required: true, help: 'Security ID of the candidate' },
24
- { name: 'job_id', required: true, help: 'Encrypted job ID' },
19
+ { name: 'uid', positional: true, required: true, help: 'Encrypted UID of the candidate (from recommend)' },
20
+ { name: 'security-id', required: true, help: 'Security ID of the candidate' },
21
+ { name: 'job-id', required: true, help: 'Encrypted job ID' },
25
22
  { name: 'text', default: '', help: 'Custom greeting message (uses default template if empty)' },
26
23
  ],
27
24
  columns: ['status', 'detail'],
28
- func: async (page: IPage | null, kwargs) => {
29
- if (!page) throw new Error('Browser page required');
25
+ func: async (page, kwargs) => {
26
+ requirePage(page);
27
+ verbose(`Greeting candidate ${kwargs.uid}...`);
30
28
 
31
- const uid = kwargs.uid;
32
- const securityId = kwargs.security_id;
33
- const jobId = kwargs.job_id;
34
- const text = kwargs.text;
29
+ await navigateToChat(page, 3);
35
30
 
36
- if (process.env.OPENCLI_VERBOSE) {
37
- console.error(`[opencli:boss] Greeting candidate ${uid}...`);
38
- }
39
-
40
- // Navigate to chat page
41
- await page.goto('https://www.zhipin.com/web/chat/index');
42
- await page.wait({ time: 3 });
43
-
44
- // Find the candidate in the greet list by encryptUid
45
- const listData: any = await page.evaluate(`
46
- async () => {
47
- return new Promise((resolve, reject) => {
48
- const xhr = new XMLHttpRequest();
49
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/greetRecSortList', true);
50
- xhr.withCredentials = true;
51
- xhr.timeout = 15000;
52
- xhr.setRequestHeader('Accept', 'application/json');
53
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
54
- xhr.onerror = () => reject(new Error('Network Error'));
55
- xhr.send();
56
- });
57
- }
58
- `);
59
-
60
- if (listData.code !== 0) {
61
- if (listData.code === 7 || listData.code === 37) {
62
- throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
63
- }
64
- throw new Error(`获取候选人列表失败: ${listData.message}`);
65
- }
66
-
67
- // Also check the regular friend list
68
- let target: any = null;
69
- const greetList = listData.zpData?.friendList || [];
70
- target = greetList.find((f: any) => f.encryptUid === uid);
71
-
72
- let numericUid: string | null = null;
73
- let friendName = '候选人';
74
-
75
- if (target) {
76
- numericUid = target.uid;
77
- friendName = target.name || friendName;
78
- }
31
+ // Find candidate in greet list or friend list
32
+ const friend = await findFriendByUid(page, kwargs.uid, {
33
+ maxPages: 1,
34
+ checkGreetList: true,
35
+ });
79
36
 
80
- if (!numericUid) {
81
- // Try to find in friend list
82
- const friendData: any = await page.evaluate(`
83
- async () => {
84
- return new Promise((resolve, reject) => {
85
- const xhr = new XMLHttpRequest();
86
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/getBossFriendListV2.json?page=1&status=0&jobId=0', true);
87
- xhr.withCredentials = true;
88
- xhr.timeout = 15000;
89
- xhr.setRequestHeader('Accept', 'application/json');
90
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
91
- xhr.onerror = () => reject(new Error('Network Error'));
92
- xhr.send();
93
- });
94
- }
95
- `);
96
-
97
- if (friendData.code === 0) {
98
- const allFriends = friendData.zpData?.friendList || [];
99
- const found = allFriends.find((f: any) => f.encryptUid === uid);
100
- if (found) {
101
- numericUid = found.uid;
102
- friendName = found.name || friendName;
103
- }
104
- }
105
- }
106
-
107
- if (!numericUid) {
37
+ if (!friend) {
108
38
  throw new Error('未找到该候选人,请确认 uid 是否正确(可从 recommend 命令获取)');
109
39
  }
110
40
 
111
- // Click on the candidate in the chat list
112
- const clicked: any = await page.evaluate(`
113
- async () => {
114
- const item = document.querySelector('#_${numericUid}-0') || document.querySelector('[id^="_${numericUid}"]');
115
- if (item) {
116
- item.click();
117
- return { clicked: true, id: item.id };
118
- }
119
- const items = document.querySelectorAll('.geek-item');
120
- for (const el of items) {
121
- if (el.id && el.id.startsWith('_${numericUid}')) {
122
- el.click();
123
- return { clicked: true, id: el.id };
124
- }
125
- }
126
- return { clicked: false };
127
- }
128
- `);
41
+ const numericUid = friend.uid;
42
+ const friendName = friend.name || '候选人';
129
43
 
130
- if (!clicked.clicked) {
44
+ const clicked = await clickCandidateInList(page, numericUid);
45
+ if (!clicked) {
131
46
  throw new Error('无法在聊天列表中找到该用户,候选人可能不在当前列表中');
132
47
  }
133
48
 
134
49
  await page.wait({ time: 2 });
135
50
 
136
- // Type the message
137
- const msgText = text || '你好,请问您对这个职位感兴趣吗?';
138
-
139
- const typed: any = await page.evaluate(`
140
- async () => {
141
- const selectors = [
142
- '.chat-editor [contenteditable="true"]',
143
- '.chat-input [contenteditable="true"]',
144
- '.message-editor [contenteditable="true"]',
145
- '.chat-conversation [contenteditable="true"]',
146
- '[contenteditable="true"]',
147
- 'textarea',
148
- ];
149
-
150
- for (const sel of selectors) {
151
- const el = document.querySelector(sel);
152
- if (el && el.offsetParent !== null) {
153
- el.focus();
154
- if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') {
155
- el.value = ${JSON.stringify(msgText)};
156
- el.dispatchEvent(new Event('input', { bubbles: true }));
157
- } else {
158
- el.textContent = '';
159
- el.focus();
160
- document.execCommand('insertText', false, ${JSON.stringify(msgText)});
161
- el.dispatchEvent(new Event('input', { bubbles: true }));
162
- }
163
- return { found: true, selector: sel };
164
- }
165
- }
166
- return { found: false };
167
- }
168
- `);
169
-
170
- if (!typed.found) {
51
+ const msgText = kwargs.text || '你好,请问您对这个职位感兴趣吗?';
52
+ const sent = await typeAndSendMessage(page, msgText);
53
+ if (!sent) {
171
54
  throw new Error('找不到消息输入框');
172
55
  }
173
56
 
174
- await page.wait({ time: 0.5 });
175
-
176
- // Click send button
177
- const sent: any = await page.evaluate(`
178
- async () => {
179
- const btn = document.querySelector('.conversation-editor .submit')
180
- || document.querySelector('.submit-content .submit')
181
- || document.querySelector('.conversation-operate .submit');
182
- if (btn) {
183
- btn.click();
184
- return { clicked: true };
185
- }
186
- return { clicked: false };
187
- }
188
- `);
189
-
190
- if (!sent.clicked) {
191
- await page.pressKey('Enter');
192
- }
193
-
194
57
  await page.wait({ time: 1 });
195
58
 
196
59
  return [{ status: '✅ 招呼已发送', detail: `已向 ${friendName} 发送: ${msgText}` }];
@@ -1,11 +1,8 @@
1
1
  /**
2
2
  * BOSS直聘 invite — send interview invitation to a candidate.
3
- *
4
- * Uses POST /wapi/zpinterview/boss/interview/invite to send interview invitations.
5
- * Address and contact info come from the boss's saved settings.
6
3
  */
7
4
  import { cli, Strategy } from '../../registry.js';
8
- import type { IPage } from '../../types.js';
5
+ import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './common.js';
9
6
 
10
7
  cli({
11
8
  site: 'boss',
@@ -13,165 +10,71 @@ cli({
13
10
  description: 'BOSS直聘发送面试邀请',
14
11
  domain: 'www.zhipin.com',
15
12
  strategy: Strategy.COOKIE,
13
+ navigateBefore: false,
16
14
  browser: true,
17
15
  args: [
18
- { name: 'uid', required: true, help: 'Encrypted UID of the candidate' },
16
+ { name: 'uid', positional: true, required: true, help: 'Encrypted UID of the candidate' },
19
17
  { name: 'time', required: true, help: 'Interview time (e.g. 2025-04-01 14:00)' },
20
18
  { name: 'address', default: '', help: 'Interview address (uses saved address if empty)' },
21
19
  { name: 'contact', default: '', help: 'Contact person name (uses saved contact if empty)' },
22
20
  ],
23
21
  columns: ['status', 'detail'],
24
- func: async (page: IPage | null, kwargs) => {
25
- if (!page) throw new Error('Browser page required');
22
+ func: async (page, kwargs) => {
23
+ requirePage(page);
24
+ verbose(`Sending interview invitation to ${kwargs.uid}...`);
26
25
 
27
- const uid = kwargs.uid;
28
- const timeStr = kwargs.time;
29
- const address = kwargs.address;
30
- const contact = kwargs.contact;
26
+ await navigateToChat(page);
31
27
 
32
- if (process.env.OPENCLI_VERBOSE) {
33
- console.error(`[opencli:boss] Sending interview invitation to ${uid}...`);
34
- }
35
-
36
- await page.goto('https://www.zhipin.com/web/chat/index');
37
- await page.wait({ time: 2 });
38
-
39
- // Get candidate info
40
- let friend: any = null;
28
+ const friend = await findFriendByUid(page, kwargs.uid, { checkGreetList: true });
29
+ if (!friend) throw new Error('未找到该候选人');
41
30
 
42
- // Check greet list first
43
- const greetData: any = await page.evaluate(`
44
- async () => {
45
- return new Promise((resolve, reject) => {
46
- const xhr = new XMLHttpRequest();
47
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/greetRecSortList', true);
48
- xhr.withCredentials = true;
49
- xhr.timeout = 15000;
50
- xhr.setRequestHeader('Accept', 'application/json');
51
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
52
- xhr.onerror = () => reject(new Error('Network Error'));
53
- xhr.send();
54
- });
55
- }
56
- `);
57
-
58
- if (greetData.code === 0) {
59
- friend = (greetData.zpData?.friendList || []).find((f: any) => f.encryptUid === uid);
60
- }
61
-
62
- if (!friend) {
63
- const friendData: any = await page.evaluate(`
64
- async () => {
65
- return new Promise((resolve, reject) => {
66
- const xhr = new XMLHttpRequest();
67
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/getBossFriendListV2.json?page=1&status=0&jobId=0', true);
68
- xhr.withCredentials = true;
69
- xhr.timeout = 15000;
70
- xhr.setRequestHeader('Accept', 'application/json');
71
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
72
- xhr.onerror = () => reject(new Error('Network Error'));
73
- xhr.send();
74
- });
75
- }
76
- `);
77
- if (friendData.code === 0) {
78
- friend = (friendData.zpData?.friendList || []).find((f: any) => f.encryptUid === uid);
79
- }
80
- }
81
-
82
- if (!friend) {
83
- throw new Error('未找到该候选人');
84
- }
85
-
86
- const numericUid = friend.uid;
87
31
  const friendName = friend.name || '候选人';
88
- const securityId = friend.securityId || '';
89
- const encJobId = friend.encryptJobId || '';
90
32
 
91
33
  // Get saved contact info
92
- const contactData: any = await page.evaluate(`
93
- async () => {
94
- return new Promise((resolve, reject) => {
95
- const xhr = new XMLHttpRequest();
96
- xhr.open('GET', 'https://www.zhipin.com/wapi/zpinterview/boss/interview/contactInit', true);
97
- xhr.withCredentials = true;
98
- xhr.timeout = 10000;
99
- xhr.setRequestHeader('Accept', 'application/json');
100
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
101
- xhr.onerror = () => reject(new Error('Network Error'));
102
- xhr.send();
103
- });
104
- }
105
- `);
106
-
107
- const contactId = contactData.zpData?.contactId || '';
108
- const contactName = contact || contactData.zpData?.contactName || '';
34
+ const contactData = await bossFetch(
35
+ page,
36
+ 'https://www.zhipin.com/wapi/zpinterview/boss/interview/contactInit',
37
+ { allowNonZero: true, timeout: 10_000 },
38
+ );
39
+ const contactName = kwargs.contact || contactData.zpData?.contactName || '';
109
40
  const contactPhone = contactData.zpData?.contactPhone || '';
41
+ const contactId = contactData.zpData?.contactId || '';
110
42
 
111
43
  // Get saved address
112
- const addressData: any = await page.evaluate(`
113
- async () => {
114
- return new Promise((resolve, reject) => {
115
- const xhr = new XMLHttpRequest();
116
- xhr.open('GET', 'https://www.zhipin.com/wapi/zpinterview/boss/interview/listAddress', true);
117
- xhr.withCredentials = true;
118
- xhr.timeout = 10000;
119
- xhr.setRequestHeader('Accept', 'application/json');
120
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
121
- xhr.onerror = () => reject(new Error('Network Error'));
122
- xhr.send();
123
- });
124
- }
125
- `);
126
-
44
+ const addressData = await bossFetch(
45
+ page,
46
+ 'https://www.zhipin.com/wapi/zpinterview/boss/interview/listAddress',
47
+ { allowNonZero: true, timeout: 10_000 },
48
+ );
127
49
  const savedAddress = addressData.zpData?.list?.[0] || {};
128
- const addressText = address || savedAddress.cityAddressText || savedAddress.addressText || '';
50
+ const addressText = kwargs.address || savedAddress.cityAddressText || savedAddress.addressText || '';
129
51
 
130
52
  // Parse interview time
131
- const interviewTime = new Date(timeStr).getTime();
53
+ const interviewTime = new Date(kwargs.time).getTime();
132
54
  if (isNaN(interviewTime)) {
133
- throw new Error(`时间格式错误: ${timeStr},请使用格式如 2025-04-01 14:00`);
55
+ throw new Error(`时间格式错误: ${kwargs.time},请使用格式如 2025-04-01 14:00`);
134
56
  }
135
57
 
136
- // Send interview invitation
137
58
  const params = new URLSearchParams({
138
- uid: String(numericUid),
139
- securityId: securityId,
140
- encryptJobId: encJobId,
59
+ uid: String(friend.uid),
60
+ securityId: friend.securityId || '',
61
+ encryptJobId: friend.encryptJobId || '',
141
62
  interviewTime: String(interviewTime),
142
- contactId: contactId,
143
- contactName: contactName,
144
- contactPhone: contactPhone,
63
+ contactId,
64
+ contactName,
65
+ contactPhone,
145
66
  address: addressText,
146
- interviewType: '1', // 1 = onsite
67
+ interviewType: '1',
147
68
  });
148
69
 
149
- const data: any = await page.evaluate(`
150
- async () => {
151
- return new Promise((resolve, reject) => {
152
- const xhr = new XMLHttpRequest();
153
- xhr.open('POST', 'https://www.zhipin.com/wapi/zpinterview/boss/interview/invite.json', true);
154
- xhr.withCredentials = true;
155
- xhr.timeout = 15000;
156
- xhr.setRequestHeader('Accept', 'application/json');
157
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
158
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
159
- xhr.onerror = () => reject(new Error('Network Error'));
160
- xhr.send(${JSON.stringify(params.toString())});
161
- });
162
- }
163
- `);
164
-
165
- if (data.code !== 0) {
166
- if (data.code === 7 || data.code === 37) {
167
- throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
168
- }
169
- throw new Error(`面试邀请发送失败: ${data.message} (code=${data.code})`);
170
- }
70
+ await bossFetch(page, 'https://www.zhipin.com/wapi/zpinterview/boss/interview/invite.json', {
71
+ method: 'POST',
72
+ body: params.toString(),
73
+ });
171
74
 
172
75
  return [{
173
76
  status: '✅ 面试邀请已发送',
174
- detail: `已向 ${friendName} 发送面试邀请\n时间: ${timeStr}\n地点: ${addressText}\n联系人: ${contactName}`,
77
+ detail: `已向 ${friendName} 发送面试邀请\n时间: ${kwargs.time}\n地点: ${addressText}\n联系人: ${contactName}`,
175
78
  }];
176
79
  },
177
80
  });
@@ -1,10 +1,8 @@
1
1
  /**
2
2
  * BOSS直聘 job list — list my published jobs via boss API.
3
- *
4
- * Uses /wapi/zpjob/job/chatted/jobList to get job list with status info.
5
3
  */
6
4
  import { cli, Strategy } from '../../registry.js';
7
- import type { IPage } from '../../types.js';
5
+ import { requirePage, navigateToChat, bossFetch, verbose } from './common.js';
8
6
 
9
7
  cli({
10
8
  site: 'boss',
@@ -12,46 +10,19 @@ cli({
12
10
  description: 'BOSS直聘查看我发布的职位列表',
13
11
  domain: 'www.zhipin.com',
14
12
  strategy: Strategy.COOKIE,
13
+ navigateBefore: false,
15
14
  browser: true,
16
15
  args: [],
17
16
  columns: ['job_name', 'salary', 'city', 'status', 'encrypt_job_id'],
18
- func: async (page: IPage | null, kwargs) => {
19
- if (!page) throw new Error('Browser page required');
17
+ func: async (page, kwargs) => {
18
+ requirePage(page);
19
+ verbose('Fetching job list...');
20
20
 
21
- if (process.env.OPENCLI_VERBOSE) {
22
- console.error('[opencli:boss] Fetching job list...');
23
- }
21
+ await navigateToChat(page);
24
22
 
25
- await page.goto('https://www.zhipin.com/web/chat/index');
26
- await page.wait({ time: 2 });
27
-
28
- const targetUrl = 'https://www.zhipin.com/wapi/zpjob/job/chatted/jobList';
29
-
30
- const data: any = await page.evaluate(`
31
- async () => {
32
- return new Promise((resolve, reject) => {
33
- const xhr = new XMLHttpRequest();
34
- xhr.open('GET', '${targetUrl}', true);
35
- xhr.withCredentials = true;
36
- xhr.timeout = 15000;
37
- xhr.setRequestHeader('Accept', 'application/json');
38
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
39
- xhr.onerror = () => reject(new Error('Network Error'));
40
- xhr.ontimeout = () => reject(new Error('Timeout'));
41
- xhr.send();
42
- });
43
- }
44
- `);
45
-
46
- if (data.code !== 0) {
47
- if (data.code === 7 || data.code === 37) {
48
- throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
49
- }
50
- throw new Error(`API error: ${data.message} (code=${data.code})`);
51
- }
23
+ const data = await bossFetch(page, 'https://www.zhipin.com/wapi/zpjob/job/chatted/jobList');
52
24
 
53
25
  const jobs = data.zpData || [];
54
-
55
26
  return jobs.map((j: any) => ({
56
27
  job_name: j.jobName || '',
57
28
  salary: j.salaryDesc || '',
@@ -1,15 +1,12 @@
1
1
  /**
2
2
  * BOSS直聘 mark — label/mark a candidate.
3
3
  *
4
- * Uses /wapi/zprelation/friend/label/addMark to add a label to a candidate,
5
- * and /wapi/zprelation/friend/label/deleteMark to remove one.
6
- *
7
- * Available labels (from /wapi/zprelation/friend/label/get):
4
+ * Available labels:
8
5
  * 1=新招呼, 2=沟通中, 3=已约面, 4=已获取简历, 5=已交换电话,
9
6
  * 6=已交换微信, 7=不合适, 8=牛人发起, 11=收藏
10
7
  */
11
8
  import { cli, Strategy } from '../../registry.js';
12
- import type { IPage } from '../../types.js';
9
+ import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './common.js';
13
10
 
14
11
  const LABEL_MAP: Record<string, number> = {
15
12
  '新招呼': 1, '沟通中': 2, '已约面': 3, '已获取简历': 4,
@@ -22,17 +19,17 @@ cli({
22
19
  description: 'BOSS直聘给候选人添加标签',
23
20
  domain: 'www.zhipin.com',
24
21
  strategy: Strategy.COOKIE,
22
+ navigateBefore: false,
25
23
  browser: true,
26
24
  args: [
27
- { name: 'uid', required: true, help: 'Encrypted UID of the candidate' },
25
+ { name: 'uid', positional: true, required: true, help: 'Encrypted UID of the candidate' },
28
26
  { name: 'label', required: true, help: 'Label name (新招呼/沟通中/已约面/已获取简历/已交换电话/已交换微信/不合适/收藏) or label ID' },
29
27
  { name: 'remove', type: 'boolean', default: false, help: 'Remove the label instead of adding' },
30
28
  ],
31
29
  columns: ['status', 'detail'],
32
- func: async (page: IPage | null, kwargs) => {
33
- if (!page) throw new Error('Browser page required');
30
+ func: async (page, kwargs) => {
31
+ requirePage(page);
34
32
 
35
- const uid = kwargs.uid;
36
33
  const labelInput = kwargs.label;
37
34
  const remove = kwargs.remove || false;
38
35
 
@@ -43,7 +40,6 @@ cli({
43
40
  } else if (!isNaN(Number(labelInput))) {
44
41
  labelId = Number(labelInput);
45
42
  } else {
46
- // Try partial match
47
43
  const entry = Object.entries(LABEL_MAP).find(([k]) => k.includes(labelInput));
48
44
  if (entry) {
49
45
  labelId = entry[1];
@@ -52,99 +48,22 @@ cli({
52
48
  }
53
49
  }
54
50
 
55
- if (process.env.OPENCLI_VERBOSE) {
56
- console.error(`[opencli:boss] ${remove ? 'Removing' : 'Adding'} label ${labelId} for ${uid}...`);
57
- }
51
+ verbose(`${remove ? 'Removing' : 'Adding'} label ${labelId} for ${kwargs.uid}...`);
58
52
 
59
- await page.goto('https://www.zhipin.com/web/chat/index');
60
- await page.wait({ time: 2 });
53
+ await navigateToChat(page);
61
54
 
62
- // First get numeric UID from friend list
63
- const friendData: any = await page.evaluate(`
64
- async () => {
65
- return new Promise((resolve, reject) => {
66
- const xhr = new XMLHttpRequest();
67
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/getBossFriendListV2.json?page=1&status=0&jobId=0', true);
68
- xhr.withCredentials = true;
69
- xhr.timeout = 15000;
70
- xhr.setRequestHeader('Accept', 'application/json');
71
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
72
- xhr.onerror = () => reject(new Error('Network Error'));
73
- xhr.send();
74
- });
75
- }
76
- `);
55
+ const friend = await findFriendByUid(page, kwargs.uid, { checkGreetList: true });
56
+ if (!friend) throw new Error('未找到该候选人');
77
57
 
78
- if (friendData.code !== 0) {
79
- if (friendData.code === 7 || friendData.code === 37) {
80
- throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
81
- }
82
- throw new Error(`获取好友列表失败: ${friendData.message}`);
83
- }
84
-
85
- // Find in friend list (check multiple pages)
86
- let friend: any = null;
87
- let allFriends = friendData.zpData?.friendList || [];
88
- friend = allFriends.find((f: any) => f.encryptUid === uid);
89
-
90
- if (!friend) {
91
- // Also check greetRecSortList
92
- const greetData: any = await page.evaluate(`
93
- async () => {
94
- return new Promise((resolve, reject) => {
95
- const xhr = new XMLHttpRequest();
96
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/greetRecSortList', true);
97
- xhr.withCredentials = true;
98
- xhr.timeout = 15000;
99
- xhr.setRequestHeader('Accept', 'application/json');
100
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
101
- xhr.onerror = () => reject(new Error('Network Error'));
102
- xhr.send();
103
- });
104
- }
105
- `);
106
- if (greetData.code === 0) {
107
- friend = (greetData.zpData?.friendList || []).find((f: any) => f.encryptUid === uid);
108
- }
109
- }
110
-
111
- if (!friend) {
112
- throw new Error('未找到该候选人');
113
- }
114
-
115
- const numericUid = friend.uid;
116
58
  const friendName = friend.name || '候选人';
117
- const friendSource = friend.friendSource ?? 0;
118
-
119
59
  const action = remove ? 'deleteMark' : 'addMark';
120
- const targetUrl = `https://www.zhipin.com/wapi/zprelation/friend/label/${action}`;
121
-
122
- // The API uses friendId + friendSource + labelId (discovered from JS bundles)
123
60
  const params = new URLSearchParams({
124
- friendId: String(numericUid),
125
- friendSource: String(friendSource),
61
+ friendId: String(friend.uid),
62
+ friendSource: String(friend.friendSource ?? 0),
126
63
  labelId: String(labelId),
127
64
  });
128
65
 
129
- // Try GET first (the N() wrapper in boss JS uses GET with query params)
130
- const data: any = await page.evaluate(`
131
- async () => {
132
- return new Promise((resolve, reject) => {
133
- const xhr = new XMLHttpRequest();
134
- xhr.open('GET', '${targetUrl}?${params.toString()}', true);
135
- xhr.withCredentials = true;
136
- xhr.timeout = 15000;
137
- xhr.setRequestHeader('Accept', 'application/json');
138
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
139
- xhr.onerror = () => reject(new Error('Network Error'));
140
- xhr.send();
141
- });
142
- }
143
- `);
144
-
145
- if (data.code !== 0) {
146
- throw new Error(`标签操作失败: ${data.message} (code=${data.code})`);
147
- }
66
+ await bossFetch(page, `https://www.zhipin.com/wapi/zprelation/friend/label/${action}?${params.toString()}`);
148
67
 
149
68
  const labelName = Object.entries(LABEL_MAP).find(([, v]) => v === labelId)?.[0] || String(labelId);
150
69
  return [{