@jackwener/opencli 1.1.1 → 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 (509) hide show
  1. package/CONTRIBUTING.md +39 -1
  2. package/README.md +9 -10
  3. package/README.zh-CN.md +39 -17
  4. package/SKILL.md +10 -5
  5. package/dist/browser/cdp.d.ts +4 -4
  6. package/dist/browser/cdp.js +39 -16
  7. package/dist/browser/daemon-client.d.ts +2 -1
  8. package/dist/browser/dom-helpers.js +38 -7
  9. package/dist/browser/dom-snapshot.d.ts +86 -0
  10. package/dist/browser/dom-snapshot.js +729 -0
  11. package/dist/browser/dom-snapshot.test.d.ts +11 -0
  12. package/dist/browser/dom-snapshot.test.js +212 -0
  13. package/dist/browser/index.d.ts +2 -0
  14. package/dist/browser/index.js +1 -0
  15. package/dist/browser/page.d.ts +14 -24
  16. package/dist/browser/page.js +37 -4
  17. package/dist/build-manifest.d.ts +11 -4
  18. package/dist/build-manifest.js +59 -21
  19. package/dist/build-manifest.test.js +58 -2
  20. package/dist/cli-manifest.json +3856 -1509
  21. package/dist/cli.js +66 -0
  22. package/dist/clis/barchart/greeks.js +1 -1
  23. package/dist/clis/barchart/options.js +1 -1
  24. package/dist/clis/barchart/quote.js +1 -1
  25. package/dist/clis/bilibili/download.js +1 -1
  26. package/dist/clis/bilibili/following.js +1 -1
  27. package/dist/clis/bilibili/subtitle.js +1 -1
  28. package/dist/clis/bilibili/user-videos.js +1 -1
  29. package/dist/clis/boss/batchgreet.js +10 -97
  30. package/dist/clis/boss/chatlist.js +8 -25
  31. package/dist/clis/boss/chatmsg.js +11 -42
  32. package/dist/clis/boss/common.d.ts +92 -0
  33. package/dist/clis/boss/common.js +223 -0
  34. package/dist/clis/boss/detail.js +7 -49
  35. package/dist/clis/boss/exchange.js +13 -79
  36. package/dist/clis/boss/greet.js +18 -145
  37. package/dist/clis/boss/invite.js +26 -121
  38. package/dist/clis/boss/joblist.js +6 -31
  39. package/dist/clis/boss/mark.js +12 -85
  40. package/dist/clis/boss/recommend.js +10 -49
  41. package/dist/clis/boss/resume.js +18 -118
  42. package/dist/clis/boss/search.js +12 -60
  43. package/dist/clis/boss/send.js +17 -151
  44. package/dist/clis/boss/stats.js +18 -69
  45. package/dist/clis/coupang/add-to-cart.js +1 -1
  46. package/dist/clis/devto/tag.yaml +34 -0
  47. package/dist/clis/devto/top.yaml +29 -0
  48. package/dist/clis/devto/user.yaml +33 -0
  49. package/dist/clis/douban/book-hot.d.ts +1 -0
  50. package/dist/clis/douban/book-hot.js +14 -0
  51. package/dist/clis/douban/marks.d.ts +1 -0
  52. package/dist/clis/douban/marks.js +115 -0
  53. package/dist/clis/douban/movie-hot.d.ts +1 -0
  54. package/dist/clis/douban/movie-hot.js +14 -0
  55. package/dist/clis/douban/reviews.d.ts +1 -0
  56. package/dist/clis/douban/reviews.js +106 -0
  57. package/dist/clis/douban/search.d.ts +1 -0
  58. package/dist/clis/douban/search.js +16 -0
  59. package/dist/clis/douban/shared.d.ts +4 -0
  60. package/dist/clis/douban/shared.js +155 -0
  61. package/dist/clis/douban/subject.yaml +76 -0
  62. package/dist/clis/douban/top250.yaml +70 -0
  63. package/dist/clis/douban/utils.d.ts +35 -0
  64. package/dist/clis/douban/utils.js +48 -0
  65. package/dist/clis/facebook/add-friend.yaml +43 -0
  66. package/dist/clis/facebook/events.yaml +44 -0
  67. package/dist/clis/facebook/feed.yaml +63 -0
  68. package/dist/clis/facebook/friends.yaml +42 -0
  69. package/dist/clis/facebook/groups.yaml +50 -0
  70. package/dist/clis/facebook/join-group.yaml +44 -0
  71. package/dist/clis/facebook/memories.yaml +39 -0
  72. package/dist/clis/facebook/notifications.yaml +40 -0
  73. package/dist/clis/facebook/profile.yaml +37 -0
  74. package/dist/clis/facebook/search.yaml +46 -0
  75. package/dist/clis/google/news.d.ts +5 -0
  76. package/dist/clis/google/news.js +58 -0
  77. package/dist/clis/google/search.d.ts +10 -0
  78. package/dist/clis/google/search.js +127 -0
  79. package/dist/clis/google/suggest.d.ts +5 -0
  80. package/dist/clis/google/suggest.js +34 -0
  81. package/dist/clis/google/trends.d.ts +5 -0
  82. package/dist/clis/google/trends.js +38 -0
  83. package/dist/clis/google/utils.d.ts +9 -0
  84. package/dist/clis/google/utils.js +23 -0
  85. package/dist/clis/google/utils.test.d.ts +1 -0
  86. package/dist/clis/google/utils.test.js +75 -0
  87. package/dist/clis/grok/ask.d.ts +14 -0
  88. package/dist/clis/grok/ask.js +257 -65
  89. package/dist/clis/grok/ask.test.d.ts +1 -0
  90. package/dist/clis/grok/ask.test.js +36 -0
  91. package/dist/clis/instagram/comment.yaml +52 -0
  92. package/dist/clis/instagram/explore.yaml +43 -0
  93. package/dist/clis/instagram/follow.yaml +41 -0
  94. package/dist/clis/instagram/followers.yaml +51 -0
  95. package/dist/clis/instagram/following.yaml +51 -0
  96. package/dist/clis/instagram/like.yaml +46 -0
  97. package/dist/clis/instagram/profile.yaml +42 -0
  98. package/dist/clis/instagram/save.yaml +46 -0
  99. package/dist/clis/instagram/saved.yaml +40 -0
  100. package/dist/clis/instagram/search.yaml +43 -0
  101. package/dist/clis/instagram/unfollow.yaml +38 -0
  102. package/dist/clis/instagram/unlike.yaml +46 -0
  103. package/dist/clis/instagram/unsave.yaml +46 -0
  104. package/dist/clis/instagram/user.yaml +54 -0
  105. package/dist/clis/jike/repost.js +1 -1
  106. package/dist/clis/jimeng/generate.yaml +1 -0
  107. package/dist/clis/linux-do/category.yaml +1 -0
  108. package/dist/clis/lobsters/active.yaml +29 -0
  109. package/dist/clis/lobsters/hot.yaml +29 -0
  110. package/dist/clis/lobsters/newest.yaml +29 -0
  111. package/dist/clis/lobsters/tag.yaml +34 -0
  112. package/dist/clis/medium/feed.d.ts +1 -0
  113. package/dist/clis/medium/feed.js +15 -0
  114. package/dist/clis/medium/search.d.ts +1 -0
  115. package/dist/clis/medium/search.js +15 -0
  116. package/dist/clis/medium/shared.d.ts +5 -0
  117. package/dist/clis/medium/shared.js +78 -0
  118. package/dist/clis/medium/user.d.ts +1 -0
  119. package/dist/clis/medium/user.js +15 -0
  120. package/dist/clis/reddit/comment.js +1 -1
  121. package/dist/clis/reddit/read.js +1 -1
  122. package/dist/clis/reddit/save.js +1 -1
  123. package/dist/clis/reddit/subreddit.yaml +1 -0
  124. package/dist/clis/reddit/subscribe.js +1 -1
  125. package/dist/clis/reddit/upvote.js +1 -1
  126. package/dist/clis/sinablog/article.d.ts +1 -0
  127. package/dist/clis/sinablog/article.js +14 -0
  128. package/dist/clis/sinablog/hot.d.ts +1 -0
  129. package/dist/clis/sinablog/hot.js +14 -0
  130. package/dist/clis/sinablog/search.d.ts +1 -0
  131. package/dist/clis/sinablog/search.js +51 -0
  132. package/dist/clis/sinablog/shared.d.ts +7 -0
  133. package/dist/clis/sinablog/shared.js +187 -0
  134. package/dist/clis/sinablog/user.d.ts +1 -0
  135. package/dist/clis/sinablog/user.js +15 -0
  136. package/dist/clis/substack/feed.d.ts +1 -0
  137. package/dist/clis/substack/feed.js +15 -0
  138. package/dist/clis/substack/publication.d.ts +1 -0
  139. package/dist/clis/substack/publication.js +15 -0
  140. package/dist/clis/substack/search.d.ts +1 -0
  141. package/dist/clis/substack/search.js +77 -0
  142. package/dist/clis/substack/shared.d.ts +4 -0
  143. package/dist/clis/substack/shared.js +129 -0
  144. package/dist/clis/tiktok/comment.yaml +66 -0
  145. package/dist/clis/tiktok/explore.yaml +39 -0
  146. package/dist/clis/tiktok/follow.yaml +39 -0
  147. package/dist/clis/tiktok/following.yaml +46 -0
  148. package/dist/clis/tiktok/friends.yaml +47 -0
  149. package/dist/clis/tiktok/like.yaml +38 -0
  150. package/dist/clis/tiktok/live.yaml +51 -0
  151. package/dist/clis/tiktok/notifications.yaml +52 -0
  152. package/dist/clis/tiktok/profile.yaml +45 -0
  153. package/dist/clis/tiktok/save.yaml +34 -0
  154. package/dist/clis/tiktok/search.yaml +46 -0
  155. package/dist/clis/tiktok/unfollow.yaml +44 -0
  156. package/dist/clis/tiktok/unlike.yaml +38 -0
  157. package/dist/clis/tiktok/unsave.yaml +36 -0
  158. package/dist/clis/tiktok/user.yaml +44 -0
  159. package/dist/clis/twitter/download.d.ts +1 -1
  160. package/dist/clis/twitter/download.js +3 -3
  161. package/dist/clis/twitter/followers.js +1 -1
  162. package/dist/clis/twitter/following.js +1 -1
  163. package/dist/clis/twitter/thread.js +1 -1
  164. package/dist/clis/twitter/timeline.d.ts +23 -0
  165. package/dist/clis/twitter/timeline.js +42 -14
  166. package/dist/clis/twitter/timeline.test.d.ts +1 -0
  167. package/dist/clis/twitter/timeline.test.js +102 -0
  168. package/dist/clis/wikipedia/random.d.ts +1 -0
  169. package/dist/clis/wikipedia/random.js +19 -0
  170. package/dist/clis/wikipedia/search.js +3 -3
  171. package/dist/clis/wikipedia/summary.js +4 -9
  172. package/dist/clis/wikipedia/trending.d.ts +1 -0
  173. package/dist/clis/wikipedia/trending.js +35 -0
  174. package/dist/clis/wikipedia/utils.d.ts +28 -0
  175. package/dist/clis/wikipedia/utils.js +13 -0
  176. package/dist/clis/xiaohongshu/creator-note-detail.js +1 -1
  177. package/dist/clis/xiaohongshu/creator-note-detail.test.js +2 -0
  178. package/dist/clis/xiaohongshu/creator-notes.test.js +2 -0
  179. package/dist/clis/xiaohongshu/download.js +1 -1
  180. package/dist/clis/xueqiu/earnings-date.yaml +69 -0
  181. package/dist/clis/xueqiu/search.yaml +2 -1
  182. package/dist/clis/xueqiu/stock.yaml +2 -0
  183. package/dist/clis/yahoo-finance/quote.js +1 -1
  184. package/dist/commanderAdapter.js +13 -7
  185. package/dist/discovery.d.ts +8 -0
  186. package/dist/discovery.js +105 -19
  187. package/dist/doctor.js +3 -1
  188. package/dist/doctor.test.js +46 -2
  189. package/dist/engine.test.d.ts +0 -3
  190. package/dist/engine.test.js +74 -6
  191. package/dist/execution.d.ts +4 -2
  192. package/dist/execution.js +31 -7
  193. package/dist/explore.d.ts +76 -3
  194. package/dist/explore.js +11 -4
  195. package/dist/generate.d.ts +41 -2
  196. package/dist/generate.js +5 -4
  197. package/dist/main.js +2 -1
  198. package/dist/pipeline/executor.d.ts +2 -2
  199. package/dist/pipeline/executor.js +2 -2
  200. package/dist/pipeline/executor.test.js +33 -6
  201. package/dist/pipeline/registry.d.ts +1 -1
  202. package/dist/pipeline/steps/browser.d.ts +7 -7
  203. package/dist/pipeline/steps/browser.js +15 -7
  204. package/dist/pipeline/steps/fetch.d.ts +1 -1
  205. package/dist/pipeline/steps/fetch.js +11 -7
  206. package/dist/pipeline/steps/transform.d.ts +6 -5
  207. package/dist/pipeline/steps/transform.js +30 -9
  208. package/dist/pipeline/template.d.ts +6 -6
  209. package/dist/pipeline/template.js +43 -5
  210. package/dist/pipeline/template.test.js +18 -0
  211. package/dist/pipeline/transform.test.js +11 -0
  212. package/dist/plugin.d.ts +31 -0
  213. package/dist/plugin.js +216 -0
  214. package/dist/plugin.test.d.ts +4 -0
  215. package/dist/plugin.test.js +76 -0
  216. package/dist/registry-api.d.ts +11 -0
  217. package/dist/registry-api.js +9 -0
  218. package/dist/registry.d.ts +11 -0
  219. package/dist/registry.js +6 -1
  220. package/dist/synthesize.d.ts +94 -4
  221. package/dist/synthesize.js +5 -4
  222. package/dist/types.d.ts +39 -26
  223. package/dist/validate.js +8 -2
  224. package/docs/.vitepress/config.mts +6 -4
  225. package/docs/adapters/browser/barchart.md +6 -5
  226. package/docs/adapters/browser/bilibili.md +9 -0
  227. package/docs/adapters/browser/devto.md +35 -0
  228. package/docs/adapters/browser/douban.md +38 -0
  229. package/docs/adapters/browser/facebook.md +36 -0
  230. package/docs/adapters/browser/google.md +62 -0
  231. package/docs/adapters/browser/grok.md +26 -8
  232. package/docs/adapters/browser/instagram.md +46 -0
  233. package/docs/adapters/browser/lobsters.md +32 -0
  234. package/docs/adapters/browser/medium.md +32 -0
  235. package/docs/adapters/browser/reddit.md +9 -0
  236. package/docs/adapters/browser/sinablog.md +36 -0
  237. package/docs/adapters/browser/substack.md +38 -0
  238. package/docs/adapters/browser/tiktok.md +68 -0
  239. package/docs/adapters/browser/wikipedia.md +11 -2
  240. package/docs/adapters/browser/xueqiu.md +10 -0
  241. package/docs/adapters/browser/yahoo-finance.md +6 -5
  242. package/docs/adapters/desktop/antigravity.md +6 -0
  243. package/docs/adapters/desktop/chatgpt.md +2 -1
  244. package/docs/adapters/desktop/codex.md +5 -1
  245. package/docs/adapters/desktop/cursor.md +4 -0
  246. package/docs/adapters/desktop/discord.md +7 -7
  247. package/docs/adapters/index.md +1 -4
  248. package/docs/guide/getting-started.md +1 -0
  249. package/docs/guide/plugins.md +153 -0
  250. package/docs/zh/guide/plugins.md +107 -0
  251. package/extension/src/background.ts +18 -11
  252. package/package.json +10 -5
  253. package/scripts/clean-dist.cjs +13 -0
  254. package/src/browser/cdp.ts +71 -31
  255. package/src/browser/daemon-client.ts +2 -1
  256. package/src/browser/dom-helpers.ts +38 -7
  257. package/src/browser/dom-snapshot.test.ts +249 -0
  258. package/src/browser/dom-snapshot.ts +770 -0
  259. package/src/browser/index.ts +2 -0
  260. package/src/browser/page.ts +50 -19
  261. package/src/build-manifest.test.ts +70 -2
  262. package/src/build-manifest.ts +94 -26
  263. package/src/cli.ts +71 -2
  264. package/src/clis/barchart/greeks.ts +1 -1
  265. package/src/clis/barchart/options.ts +1 -1
  266. package/src/clis/barchart/quote.ts +1 -1
  267. package/src/clis/bilibili/download.ts +1 -1
  268. package/src/clis/bilibili/following.ts +1 -1
  269. package/src/clis/bilibili/subtitle.ts +1 -1
  270. package/src/clis/bilibili/user-videos.ts +1 -1
  271. package/src/clis/boss/batchgreet.ts +14 -106
  272. package/src/clis/boss/chatlist.ts +12 -26
  273. package/src/clis/boss/chatmsg.ts +16 -40
  274. package/src/clis/boss/common.ts +287 -0
  275. package/src/clis/boss/detail.ts +8 -54
  276. package/src/clis/boss/exchange.ts +15 -89
  277. package/src/clis/boss/greet.ts +23 -160
  278. package/src/clis/boss/invite.ts +36 -133
  279. package/src/clis/boss/joblist.ts +7 -36
  280. package/src/clis/boss/mark.ts +13 -94
  281. package/src/clis/boss/recommend.ts +12 -57
  282. package/src/clis/boss/resume.ts +19 -124
  283. package/src/clis/boss/search.ts +13 -66
  284. package/src/clis/boss/send.ts +21 -161
  285. package/src/clis/boss/stats.ts +19 -74
  286. package/src/clis/coupang/add-to-cart.ts +1 -1
  287. package/src/clis/devto/tag.yaml +34 -0
  288. package/src/clis/devto/top.yaml +29 -0
  289. package/src/clis/devto/user.yaml +33 -0
  290. package/src/clis/douban/book-hot.ts +15 -0
  291. package/src/clis/douban/marks.ts +135 -0
  292. package/src/clis/douban/movie-hot.ts +15 -0
  293. package/src/clis/douban/reviews.ts +127 -0
  294. package/src/clis/douban/search.ts +17 -0
  295. package/src/clis/douban/shared.ts +165 -0
  296. package/src/clis/douban/subject.yaml +76 -0
  297. package/src/clis/douban/top250.yaml +70 -0
  298. package/src/clis/douban/utils.ts +81 -0
  299. package/src/clis/facebook/add-friend.yaml +43 -0
  300. package/src/clis/facebook/events.yaml +44 -0
  301. package/src/clis/facebook/feed.yaml +63 -0
  302. package/src/clis/facebook/friends.yaml +42 -0
  303. package/src/clis/facebook/groups.yaml +50 -0
  304. package/src/clis/facebook/join-group.yaml +44 -0
  305. package/src/clis/facebook/memories.yaml +39 -0
  306. package/src/clis/facebook/notifications.yaml +40 -0
  307. package/src/clis/facebook/profile.yaml +37 -0
  308. package/src/clis/facebook/search.yaml +46 -0
  309. package/src/clis/google/news.ts +66 -0
  310. package/src/clis/google/search.ts +133 -0
  311. package/src/clis/google/suggest.ts +40 -0
  312. package/src/clis/google/trends.ts +44 -0
  313. package/src/clis/google/utils.test.ts +82 -0
  314. package/src/clis/google/utils.ts +24 -0
  315. package/src/clis/grok/ask.test.ts +53 -0
  316. package/src/clis/grok/ask.ts +300 -69
  317. package/src/clis/instagram/comment.yaml +52 -0
  318. package/src/clis/instagram/explore.yaml +43 -0
  319. package/src/clis/instagram/follow.yaml +41 -0
  320. package/src/clis/instagram/followers.yaml +51 -0
  321. package/src/clis/instagram/following.yaml +51 -0
  322. package/src/clis/instagram/like.yaml +46 -0
  323. package/src/clis/instagram/profile.yaml +42 -0
  324. package/src/clis/instagram/save.yaml +46 -0
  325. package/src/clis/instagram/saved.yaml +40 -0
  326. package/src/clis/instagram/search.yaml +43 -0
  327. package/src/clis/instagram/unfollow.yaml +38 -0
  328. package/src/clis/instagram/unlike.yaml +46 -0
  329. package/src/clis/instagram/unsave.yaml +46 -0
  330. package/src/clis/instagram/user.yaml +54 -0
  331. package/src/clis/jike/repost.ts +1 -1
  332. package/src/clis/jimeng/generate.yaml +1 -0
  333. package/src/clis/linux-do/category.yaml +1 -0
  334. package/src/clis/lobsters/active.yaml +29 -0
  335. package/src/clis/lobsters/hot.yaml +29 -0
  336. package/src/clis/lobsters/newest.yaml +29 -0
  337. package/src/clis/lobsters/tag.yaml +34 -0
  338. package/src/clis/medium/feed.ts +16 -0
  339. package/src/clis/medium/search.ts +16 -0
  340. package/src/clis/medium/shared.ts +83 -0
  341. package/src/clis/medium/user.ts +16 -0
  342. package/src/clis/reddit/comment.ts +1 -1
  343. package/src/clis/reddit/read.ts +1 -1
  344. package/src/clis/reddit/save.ts +1 -1
  345. package/src/clis/reddit/subreddit.yaml +1 -0
  346. package/src/clis/reddit/subscribe.ts +1 -1
  347. package/src/clis/reddit/upvote.ts +1 -1
  348. package/src/clis/sinablog/article.ts +15 -0
  349. package/src/clis/sinablog/hot.ts +15 -0
  350. package/src/clis/sinablog/search.ts +56 -0
  351. package/src/clis/sinablog/shared.ts +198 -0
  352. package/src/clis/sinablog/user.ts +16 -0
  353. package/src/clis/substack/feed.ts +16 -0
  354. package/src/clis/substack/publication.ts +16 -0
  355. package/src/clis/substack/search.ts +91 -0
  356. package/src/clis/substack/shared.ts +132 -0
  357. package/src/clis/tiktok/comment.yaml +66 -0
  358. package/src/clis/tiktok/explore.yaml +39 -0
  359. package/src/clis/tiktok/follow.yaml +39 -0
  360. package/src/clis/tiktok/following.yaml +46 -0
  361. package/src/clis/tiktok/friends.yaml +47 -0
  362. package/src/clis/tiktok/like.yaml +38 -0
  363. package/src/clis/tiktok/live.yaml +51 -0
  364. package/src/clis/tiktok/notifications.yaml +52 -0
  365. package/src/clis/tiktok/profile.yaml +45 -0
  366. package/src/clis/tiktok/save.yaml +34 -0
  367. package/src/clis/tiktok/search.yaml +46 -0
  368. package/src/clis/tiktok/unfollow.yaml +44 -0
  369. package/src/clis/tiktok/unlike.yaml +38 -0
  370. package/src/clis/tiktok/unsave.yaml +36 -0
  371. package/src/clis/tiktok/user.yaml +44 -0
  372. package/src/clis/twitter/download.ts +3 -3
  373. package/src/clis/twitter/followers.ts +1 -1
  374. package/src/clis/twitter/following.ts +1 -1
  375. package/src/clis/twitter/thread.ts +1 -1
  376. package/src/clis/twitter/timeline.test.ts +109 -0
  377. package/src/clis/twitter/timeline.ts +59 -19
  378. package/src/clis/wikipedia/random.ts +19 -0
  379. package/src/clis/wikipedia/search.ts +10 -4
  380. package/src/clis/wikipedia/summary.ts +4 -9
  381. package/src/clis/wikipedia/trending.ts +41 -0
  382. package/src/clis/wikipedia/utils.ts +31 -0
  383. package/src/clis/xiaohongshu/creator-note-detail.test.ts +2 -0
  384. package/src/clis/xiaohongshu/creator-note-detail.ts +1 -1
  385. package/src/clis/xiaohongshu/creator-notes.test.ts +2 -0
  386. package/src/clis/xiaohongshu/download.ts +1 -1
  387. package/src/clis/xueqiu/earnings-date.yaml +69 -0
  388. package/src/clis/xueqiu/search.yaml +2 -1
  389. package/src/clis/xueqiu/stock.yaml +2 -0
  390. package/src/clis/yahoo-finance/quote.ts +1 -1
  391. package/src/commanderAdapter.ts +17 -10
  392. package/src/discovery.ts +134 -24
  393. package/src/doctor.test.ts +59 -2
  394. package/src/doctor.ts +4 -2
  395. package/src/engine.test.ts +79 -6
  396. package/src/execution.ts +42 -16
  397. package/src/explore.ts +77 -9
  398. package/src/generate.ts +58 -9
  399. package/src/main.ts +2 -1
  400. package/src/pipeline/executor.test.ts +35 -6
  401. package/src/pipeline/executor.ts +11 -7
  402. package/src/pipeline/registry.ts +3 -3
  403. package/src/pipeline/steps/browser.ts +24 -15
  404. package/src/pipeline/steps/fetch.ts +18 -13
  405. package/src/pipeline/steps/transform.ts +40 -15
  406. package/src/pipeline/template.test.ts +18 -0
  407. package/src/pipeline/template.ts +86 -13
  408. package/src/pipeline/transform.test.ts +15 -2
  409. package/src/plugin.test.ts +86 -0
  410. package/src/plugin.ts +254 -0
  411. package/src/registry-api.ts +12 -0
  412. package/src/registry.ts +19 -1
  413. package/src/synthesize.ts +102 -21
  414. package/src/types.ts +44 -12
  415. package/src/validate.ts +19 -4
  416. package/tests/e2e/browser-public.test.ts +11 -0
  417. package/tests/e2e/public-commands.test.ts +64 -0
  418. package/dist/clis/feishu/new.d.ts +0 -1
  419. package/dist/clis/feishu/new.js +0 -27
  420. package/dist/clis/feishu/read.d.ts +0 -1
  421. package/dist/clis/feishu/read.js +0 -40
  422. package/dist/clis/feishu/search.d.ts +0 -1
  423. package/dist/clis/feishu/search.js +0 -30
  424. package/dist/clis/feishu/send.d.ts +0 -1
  425. package/dist/clis/feishu/send.js +0 -39
  426. package/dist/clis/feishu/status.d.ts +0 -1
  427. package/dist/clis/feishu/status.js +0 -28
  428. package/dist/clis/neteasemusic/like.d.ts +0 -1
  429. package/dist/clis/neteasemusic/like.js +0 -25
  430. package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
  431. package/dist/clis/neteasemusic/lyrics.js +0 -47
  432. package/dist/clis/neteasemusic/next.d.ts +0 -1
  433. package/dist/clis/neteasemusic/next.js +0 -26
  434. package/dist/clis/neteasemusic/play.d.ts +0 -1
  435. package/dist/clis/neteasemusic/play.js +0 -26
  436. package/dist/clis/neteasemusic/playing.d.ts +0 -1
  437. package/dist/clis/neteasemusic/playing.js +0 -59
  438. package/dist/clis/neteasemusic/playlist.d.ts +0 -1
  439. package/dist/clis/neteasemusic/playlist.js +0 -46
  440. package/dist/clis/neteasemusic/prev.d.ts +0 -1
  441. package/dist/clis/neteasemusic/prev.js +0 -25
  442. package/dist/clis/neteasemusic/search.d.ts +0 -1
  443. package/dist/clis/neteasemusic/search.js +0 -52
  444. package/dist/clis/neteasemusic/status.d.ts +0 -1
  445. package/dist/clis/neteasemusic/status.js +0 -16
  446. package/dist/clis/neteasemusic/volume.d.ts +0 -1
  447. package/dist/clis/neteasemusic/volume.js +0 -54
  448. package/dist/clis/wechat/chats.d.ts +0 -1
  449. package/dist/clis/wechat/chats.js +0 -28
  450. package/dist/clis/wechat/contacts.d.ts +0 -1
  451. package/dist/clis/wechat/contacts.js +0 -28
  452. package/dist/clis/wechat/read.d.ts +0 -1
  453. package/dist/clis/wechat/read.js +0 -58
  454. package/dist/clis/wechat/search.d.ts +0 -1
  455. package/dist/clis/wechat/search.js +0 -31
  456. package/dist/clis/wechat/send.d.ts +0 -1
  457. package/dist/clis/wechat/send.js +0 -42
  458. package/dist/clis/wechat/status.d.ts +0 -1
  459. package/dist/clis/wechat/status.js +0 -29
  460. package/dist/pipeline.d.ts +0 -7
  461. package/dist/pipeline.js +0 -7
  462. package/docs/adapters/browser/github.md +0 -26
  463. package/docs/adapters/desktop/feishu.md +0 -20
  464. package/docs/adapters/desktop/neteasemusic.md +0 -31
  465. package/docs/adapters/desktop/wechat.md +0 -28
  466. package/src/clis/antigravity/README.md +0 -5
  467. package/src/clis/antigravity/README.zh-CN.md +0 -51
  468. package/src/clis/chaoxing/README.md +0 -14
  469. package/src/clis/chaoxing/README.zh-CN.md +0 -35
  470. package/src/clis/chatgpt/README.md +0 -5
  471. package/src/clis/chatgpt/README.zh-CN.md +0 -44
  472. package/src/clis/chatwise/README.md +0 -5
  473. package/src/clis/chatwise/README.zh-CN.md +0 -38
  474. package/src/clis/codex/README.md +0 -5
  475. package/src/clis/codex/README.zh-CN.md +0 -33
  476. package/src/clis/cursor/README.md +0 -5
  477. package/src/clis/cursor/README.zh-CN.md +0 -33
  478. package/src/clis/discord-app/README.md +0 -5
  479. package/src/clis/discord-app/README.zh-CN.md +0 -28
  480. package/src/clis/feishu/README.md +0 -5
  481. package/src/clis/feishu/README.zh-CN.md +0 -20
  482. package/src/clis/feishu/new.ts +0 -32
  483. package/src/clis/feishu/read.ts +0 -48
  484. package/src/clis/feishu/search.ts +0 -35
  485. package/src/clis/feishu/send.ts +0 -46
  486. package/src/clis/feishu/status.ts +0 -34
  487. package/src/clis/neteasemusic/README.md +0 -5
  488. package/src/clis/neteasemusic/README.zh-CN.md +0 -31
  489. package/src/clis/neteasemusic/like.ts +0 -28
  490. package/src/clis/neteasemusic/lyrics.ts +0 -53
  491. package/src/clis/neteasemusic/next.ts +0 -30
  492. package/src/clis/neteasemusic/play.ts +0 -30
  493. package/src/clis/neteasemusic/playing.ts +0 -62
  494. package/src/clis/neteasemusic/playlist.ts +0 -51
  495. package/src/clis/neteasemusic/prev.ts +0 -29
  496. package/src/clis/neteasemusic/search.ts +0 -58
  497. package/src/clis/neteasemusic/status.ts +0 -18
  498. package/src/clis/neteasemusic/volume.ts +0 -61
  499. package/src/clis/notion/README.md +0 -5
  500. package/src/clis/notion/README.zh-CN.md +0 -29
  501. package/src/clis/wechat/README.md +0 -5
  502. package/src/clis/wechat/README.zh-CN.md +0 -28
  503. package/src/clis/wechat/chats.ts +0 -33
  504. package/src/clis/wechat/contacts.ts +0 -33
  505. package/src/clis/wechat/read.ts +0 -72
  506. package/src/clis/wechat/search.ts +0 -36
  507. package/src/clis/wechat/send.ts +0 -49
  508. package/src/clis/wechat/status.ts +0 -35
  509. package/src/pipeline.ts +0 -8
@@ -1,158 +1,63 @@
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';
5
+ import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './common.js';
8
6
  cli({
9
7
  site: 'boss',
10
8
  name: 'invite',
11
9
  description: 'BOSS直聘发送面试邀请',
12
10
  domain: 'www.zhipin.com',
13
11
  strategy: Strategy.COOKIE,
12
+ navigateBefore: false,
14
13
  browser: true,
15
14
  args: [
16
- { name: 'uid', required: true, help: 'Encrypted UID of the candidate' },
15
+ { name: 'uid', positional: true, required: true, help: 'Encrypted UID of the candidate' },
17
16
  { name: 'time', required: true, help: 'Interview time (e.g. 2025-04-01 14:00)' },
18
17
  { name: 'address', default: '', help: 'Interview address (uses saved address if empty)' },
19
18
  { name: 'contact', default: '', help: 'Contact person name (uses saved contact if empty)' },
20
19
  ],
21
20
  columns: ['status', 'detail'],
22
21
  func: async (page, kwargs) => {
23
- if (!page)
24
- throw new Error('Browser page required');
25
- const uid = kwargs.uid;
26
- const timeStr = kwargs.time;
27
- const address = kwargs.address;
28
- const contact = kwargs.contact;
29
- if (process.env.OPENCLI_VERBOSE) {
30
- console.error(`[opencli:boss] Sending interview invitation to ${uid}...`);
31
- }
32
- await page.goto('https://www.zhipin.com/web/chat/index');
33
- await page.wait({ time: 2 });
34
- // Get candidate info
35
- let friend = null;
36
- // Check greet list first
37
- const greetData = await page.evaluate(`
38
- async () => {
39
- return new Promise((resolve, reject) => {
40
- const xhr = new XMLHttpRequest();
41
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/greetRecSortList', true);
42
- xhr.withCredentials = true;
43
- xhr.timeout = 15000;
44
- xhr.setRequestHeader('Accept', 'application/json');
45
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
46
- xhr.onerror = () => reject(new Error('Network Error'));
47
- xhr.send();
48
- });
49
- }
50
- `);
51
- if (greetData.code === 0) {
52
- friend = (greetData.zpData?.friendList || []).find((f) => f.encryptUid === uid);
53
- }
54
- if (!friend) {
55
- const friendData = await page.evaluate(`
56
- async () => {
57
- return new Promise((resolve, reject) => {
58
- const xhr = new XMLHttpRequest();
59
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/getBossFriendListV2.json?page=1&status=0&jobId=0', true);
60
- xhr.withCredentials = true;
61
- xhr.timeout = 15000;
62
- xhr.setRequestHeader('Accept', 'application/json');
63
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
64
- xhr.onerror = () => reject(new Error('Network Error'));
65
- xhr.send();
66
- });
67
- }
68
- `);
69
- if (friendData.code === 0) {
70
- friend = (friendData.zpData?.friendList || []).find((f) => f.encryptUid === uid);
71
- }
72
- }
73
- if (!friend) {
22
+ requirePage(page);
23
+ verbose(`Sending interview invitation to ${kwargs.uid}...`);
24
+ await navigateToChat(page);
25
+ const friend = await findFriendByUid(page, kwargs.uid, { checkGreetList: true });
26
+ if (!friend)
74
27
  throw new Error('未找到该候选人');
75
- }
76
- const numericUid = friend.uid;
77
28
  const friendName = friend.name || '候选人';
78
- const securityId = friend.securityId || '';
79
- const encJobId = friend.encryptJobId || '';
80
29
  // Get saved contact info
81
- const contactData = await page.evaluate(`
82
- async () => {
83
- return new Promise((resolve, reject) => {
84
- const xhr = new XMLHttpRequest();
85
- xhr.open('GET', 'https://www.zhipin.com/wapi/zpinterview/boss/interview/contactInit', true);
86
- xhr.withCredentials = true;
87
- xhr.timeout = 10000;
88
- xhr.setRequestHeader('Accept', 'application/json');
89
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
90
- xhr.onerror = () => reject(new Error('Network Error'));
91
- xhr.send();
92
- });
93
- }
94
- `);
95
- const contactId = contactData.zpData?.contactId || '';
96
- const contactName = contact || contactData.zpData?.contactName || '';
30
+ const contactData = await bossFetch(page, 'https://www.zhipin.com/wapi/zpinterview/boss/interview/contactInit', { allowNonZero: true, timeout: 10_000 });
31
+ const contactName = kwargs.contact || contactData.zpData?.contactName || '';
97
32
  const contactPhone = contactData.zpData?.contactPhone || '';
33
+ const contactId = contactData.zpData?.contactId || '';
98
34
  // Get saved address
99
- const addressData = await page.evaluate(`
100
- async () => {
101
- return new Promise((resolve, reject) => {
102
- const xhr = new XMLHttpRequest();
103
- xhr.open('GET', 'https://www.zhipin.com/wapi/zpinterview/boss/interview/listAddress', true);
104
- xhr.withCredentials = true;
105
- xhr.timeout = 10000;
106
- xhr.setRequestHeader('Accept', 'application/json');
107
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
108
- xhr.onerror = () => reject(new Error('Network Error'));
109
- xhr.send();
110
- });
111
- }
112
- `);
35
+ const addressData = await bossFetch(page, 'https://www.zhipin.com/wapi/zpinterview/boss/interview/listAddress', { allowNonZero: true, timeout: 10_000 });
113
36
  const savedAddress = addressData.zpData?.list?.[0] || {};
114
- const addressText = address || savedAddress.cityAddressText || savedAddress.addressText || '';
37
+ const addressText = kwargs.address || savedAddress.cityAddressText || savedAddress.addressText || '';
115
38
  // Parse interview time
116
- const interviewTime = new Date(timeStr).getTime();
39
+ const interviewTime = new Date(kwargs.time).getTime();
117
40
  if (isNaN(interviewTime)) {
118
- throw new Error(`时间格式错误: ${timeStr},请使用格式如 2025-04-01 14:00`);
41
+ throw new Error(`时间格式错误: ${kwargs.time},请使用格式如 2025-04-01 14:00`);
119
42
  }
120
- // Send interview invitation
121
43
  const params = new URLSearchParams({
122
- uid: String(numericUid),
123
- securityId: securityId,
124
- encryptJobId: encJobId,
44
+ uid: String(friend.uid),
45
+ securityId: friend.securityId || '',
46
+ encryptJobId: friend.encryptJobId || '',
125
47
  interviewTime: String(interviewTime),
126
- contactId: contactId,
127
- contactName: contactName,
128
- contactPhone: contactPhone,
48
+ contactId,
49
+ contactName,
50
+ contactPhone,
129
51
  address: addressText,
130
- interviewType: '1', // 1 = onsite
52
+ interviewType: '1',
131
53
  });
132
- const data = await page.evaluate(`
133
- async () => {
134
- return new Promise((resolve, reject) => {
135
- const xhr = new XMLHttpRequest();
136
- xhr.open('POST', 'https://www.zhipin.com/wapi/zpinterview/boss/interview/invite.json', true);
137
- xhr.withCredentials = true;
138
- xhr.timeout = 15000;
139
- xhr.setRequestHeader('Accept', 'application/json');
140
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
141
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
142
- xhr.onerror = () => reject(new Error('Network Error'));
143
- xhr.send(${JSON.stringify(params.toString())});
54
+ await bossFetch(page, 'https://www.zhipin.com/wapi/zpinterview/boss/interview/invite.json', {
55
+ method: 'POST',
56
+ body: params.toString(),
144
57
  });
145
- }
146
- `);
147
- if (data.code !== 0) {
148
- if (data.code === 7 || data.code === 37) {
149
- throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
150
- }
151
- throw new Error(`面试邀请发送失败: ${data.message} (code=${data.code})`);
152
- }
153
58
  return [{
154
59
  status: '✅ 面试邀请已发送',
155
- detail: `已向 ${friendName} 发送面试邀请\n时间: ${timeStr}\n地点: ${addressText}\n联系人: ${contactName}`,
60
+ detail: `已向 ${friendName} 发送面试邀请\n时间: ${kwargs.time}\n地点: ${addressText}\n联系人: ${contactName}`,
156
61
  }];
157
62
  },
158
63
  });
@@ -1,48 +1,23 @@
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';
5
+ import { requirePage, navigateToChat, bossFetch, verbose } from './common.js';
7
6
  cli({
8
7
  site: 'boss',
9
8
  name: 'joblist',
10
9
  description: 'BOSS直聘查看我发布的职位列表',
11
10
  domain: 'www.zhipin.com',
12
11
  strategy: Strategy.COOKIE,
12
+ navigateBefore: false,
13
13
  browser: true,
14
14
  args: [],
15
15
  columns: ['job_name', 'salary', 'city', 'status', 'encrypt_job_id'],
16
16
  func: async (page, kwargs) => {
17
- if (!page)
18
- throw new Error('Browser page required');
19
- if (process.env.OPENCLI_VERBOSE) {
20
- console.error('[opencli:boss] Fetching job list...');
21
- }
22
- await page.goto('https://www.zhipin.com/web/chat/index');
23
- await page.wait({ time: 2 });
24
- const targetUrl = 'https://www.zhipin.com/wapi/zpjob/job/chatted/jobList';
25
- const data = await page.evaluate(`
26
- async () => {
27
- return new Promise((resolve, reject) => {
28
- const xhr = new XMLHttpRequest();
29
- xhr.open('GET', '${targetUrl}', true);
30
- xhr.withCredentials = true;
31
- xhr.timeout = 15000;
32
- xhr.setRequestHeader('Accept', 'application/json');
33
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
34
- xhr.onerror = () => reject(new Error('Network Error'));
35
- xhr.ontimeout = () => reject(new Error('Timeout'));
36
- xhr.send();
37
- });
38
- }
39
- `);
40
- if (data.code !== 0) {
41
- if (data.code === 7 || data.code === 37) {
42
- throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
43
- }
44
- throw new Error(`API error: ${data.message} (code=${data.code})`);
45
- }
17
+ requirePage(page);
18
+ verbose('Fetching job list...');
19
+ await navigateToChat(page);
20
+ const data = await bossFetch(page, 'https://www.zhipin.com/wapi/zpjob/job/chatted/jobList');
46
21
  const jobs = data.zpData || [];
47
22
  return jobs.map((j) => ({
48
23
  job_name: j.jobName || '',
@@ -1,14 +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';
9
+ import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './common.js';
12
10
  const LABEL_MAP = {
13
11
  '新招呼': 1, '沟通中': 2, '已约面': 3, '已获取简历': 4,
14
12
  '已交换电话': 5, '已交换微信': 6, '不合适': 7, '牛人发起': 8, '收藏': 11,
@@ -19,17 +17,16 @@ cli({
19
17
  description: 'BOSS直聘给候选人添加标签',
20
18
  domain: 'www.zhipin.com',
21
19
  strategy: Strategy.COOKIE,
20
+ navigateBefore: false,
22
21
  browser: true,
23
22
  args: [
24
- { name: 'uid', required: true, help: 'Encrypted UID of the candidate' },
23
+ { name: 'uid', positional: true, required: true, help: 'Encrypted UID of the candidate' },
25
24
  { name: 'label', required: true, help: 'Label name (新招呼/沟通中/已约面/已获取简历/已交换电话/已交换微信/不合适/收藏) or label ID' },
26
25
  { name: 'remove', type: 'boolean', default: false, help: 'Remove the label instead of adding' },
27
26
  ],
28
27
  columns: ['status', 'detail'],
29
28
  func: async (page, kwargs) => {
30
- if (!page)
31
- throw new Error('Browser page required');
32
- const uid = kwargs.uid;
29
+ requirePage(page);
33
30
  const labelInput = kwargs.label;
34
31
  const remove = kwargs.remove || false;
35
32
  // Resolve label to ID
@@ -41,7 +38,6 @@ cli({
41
38
  labelId = Number(labelInput);
42
39
  }
43
40
  else {
44
- // Try partial match
45
41
  const entry = Object.entries(LABEL_MAP).find(([k]) => k.includes(labelInput));
46
42
  if (entry) {
47
43
  labelId = entry[1];
@@ -50,88 +46,19 @@ cli({
50
46
  throw new Error(`未知标签: ${labelInput}。可用标签: ${Object.keys(LABEL_MAP).join(', ')}`);
51
47
  }
52
48
  }
53
- if (process.env.OPENCLI_VERBOSE) {
54
- console.error(`[opencli:boss] ${remove ? 'Removing' : 'Adding'} label ${labelId} for ${uid}...`);
55
- }
56
- await page.goto('https://www.zhipin.com/web/chat/index');
57
- await page.wait({ time: 2 });
58
- // First get numeric UID from friend list
59
- const friendData = await page.evaluate(`
60
- async () => {
61
- return new Promise((resolve, reject) => {
62
- const xhr = new XMLHttpRequest();
63
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/getBossFriendListV2.json?page=1&status=0&jobId=0', true);
64
- xhr.withCredentials = true;
65
- xhr.timeout = 15000;
66
- xhr.setRequestHeader('Accept', 'application/json');
67
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
68
- xhr.onerror = () => reject(new Error('Network Error'));
69
- xhr.send();
70
- });
71
- }
72
- `);
73
- if (friendData.code !== 0) {
74
- if (friendData.code === 7 || friendData.code === 37) {
75
- throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
76
- }
77
- throw new Error(`获取好友列表失败: ${friendData.message}`);
78
- }
79
- // Find in friend list (check multiple pages)
80
- let friend = null;
81
- let allFriends = friendData.zpData?.friendList || [];
82
- friend = allFriends.find((f) => f.encryptUid === uid);
83
- if (!friend) {
84
- // Also check greetRecSortList
85
- const greetData = await page.evaluate(`
86
- async () => {
87
- return new Promise((resolve, reject) => {
88
- const xhr = new XMLHttpRequest();
89
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/greetRecSortList', true);
90
- xhr.withCredentials = true;
91
- xhr.timeout = 15000;
92
- xhr.setRequestHeader('Accept', 'application/json');
93
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
94
- xhr.onerror = () => reject(new Error('Network Error'));
95
- xhr.send();
96
- });
97
- }
98
- `);
99
- if (greetData.code === 0) {
100
- friend = (greetData.zpData?.friendList || []).find((f) => f.encryptUid === uid);
101
- }
102
- }
103
- if (!friend) {
49
+ verbose(`${remove ? 'Removing' : 'Adding'} label ${labelId} for ${kwargs.uid}...`);
50
+ await navigateToChat(page);
51
+ const friend = await findFriendByUid(page, kwargs.uid, { checkGreetList: true });
52
+ if (!friend)
104
53
  throw new Error('未找到该候选人');
105
- }
106
- const numericUid = friend.uid;
107
54
  const friendName = friend.name || '候选人';
108
- const friendSource = friend.friendSource ?? 0;
109
55
  const action = remove ? 'deleteMark' : 'addMark';
110
- const targetUrl = `https://www.zhipin.com/wapi/zprelation/friend/label/${action}`;
111
- // The API uses friendId + friendSource + labelId (discovered from JS bundles)
112
56
  const params = new URLSearchParams({
113
- friendId: String(numericUid),
114
- friendSource: String(friendSource),
57
+ friendId: String(friend.uid),
58
+ friendSource: String(friend.friendSource ?? 0),
115
59
  labelId: String(labelId),
116
60
  });
117
- // Try GET first (the N() wrapper in boss JS uses GET with query params)
118
- const data = await page.evaluate(`
119
- async () => {
120
- return new Promise((resolve, reject) => {
121
- const xhr = new XMLHttpRequest();
122
- xhr.open('GET', '${targetUrl}?${params.toString()}', true);
123
- xhr.withCredentials = true;
124
- xhr.timeout = 15000;
125
- xhr.setRequestHeader('Accept', 'application/json');
126
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
127
- xhr.onerror = () => reject(new Error('Network Error'));
128
- xhr.send();
129
- });
130
- }
131
- `);
132
- if (data.code !== 0) {
133
- throw new Error(`标签操作失败: ${data.message} (code=${data.code})`);
134
- }
61
+ await bossFetch(page, `https://www.zhipin.com/wapi/zprelation/friend/label/${action}?${params.toString()}`);
135
62
  const labelName = Object.entries(LABEL_MAP).find(([, v]) => v === labelId)?.[0] || String(labelId);
136
63
  return [{
137
64
  status: remove ? '✅ 标签已移除' : '✅ 标签已添加',
@@ -1,76 +1,37 @@
1
1
  /**
2
2
  * BOSS直聘 recommend — view recommended candidates (新招呼/greet sort list).
3
- *
4
- * Uses /wapi/zprelation/friend/greetRecSortList to get system-recommended candidates.
5
- * These are candidates who have greeted or been recommended by the system.
6
3
  */
7
4
  import { cli, Strategy } from '../../registry.js';
5
+ import { requirePage, navigateToChat, bossFetch, fetchRecommendList, verbose } from './common.js';
8
6
  cli({
9
7
  site: 'boss',
10
8
  name: 'recommend',
11
9
  description: 'BOSS直聘查看推荐候选人(新招呼列表)',
12
10
  domain: 'www.zhipin.com',
13
11
  strategy: Strategy.COOKIE,
12
+ navigateBefore: false,
14
13
  browser: true,
15
14
  args: [
16
15
  { name: 'limit', type: 'int', default: 20, help: 'Number of results to return' },
17
16
  ],
18
17
  columns: ['name', 'job_name', 'last_time', 'labels', 'encrypt_uid', 'security_id', 'encrypt_job_id'],
19
18
  func: async (page, kwargs) => {
20
- if (!page)
21
- throw new Error('Browser page required');
22
- const limit = kwargs.limit || 20;
23
- if (process.env.OPENCLI_VERBOSE) {
24
- console.error('[opencli:boss] Fetching recommended candidates...');
25
- }
26
- await page.goto('https://www.zhipin.com/web/chat/index');
27
- await page.wait({ time: 2 });
19
+ requirePage(page);
20
+ verbose('Fetching recommended candidates...');
21
+ await navigateToChat(page);
28
22
  // Get label definitions for mapping
29
- const labelData = await page.evaluate(`
30
- async () => {
31
- return new Promise((resolve, reject) => {
32
- const xhr = new XMLHttpRequest();
33
- xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/label/get', true);
34
- xhr.withCredentials = true;
35
- xhr.timeout = 10000;
36
- xhr.setRequestHeader('Accept', 'application/json');
37
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { resolve({}); } };
38
- xhr.onerror = () => resolve({});
39
- xhr.send();
23
+ const labelData = await bossFetch(page, 'https://www.zhipin.com/wapi/zprelation/friend/label/get', {
24
+ allowNonZero: true,
40
25
  });
41
- }
42
- `);
43
26
  const labelMap = {};
44
27
  if (labelData.code === 0 && labelData.zpData?.labels) {
45
28
  for (const l of labelData.zpData.labels) {
46
29
  labelMap[l.labelId] = l.label;
47
30
  }
48
31
  }
49
- // Get recommended candidates
50
- const targetUrl = 'https://www.zhipin.com/wapi/zprelation/friend/greetRecSortList';
51
- const data = await page.evaluate(`
52
- async () => {
53
- return new Promise((resolve, reject) => {
54
- const xhr = new XMLHttpRequest();
55
- xhr.open('GET', '${targetUrl}', true);
56
- xhr.withCredentials = true;
57
- xhr.timeout = 15000;
58
- xhr.setRequestHeader('Accept', 'application/json');
59
- xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
60
- xhr.onerror = () => reject(new Error('Network Error'));
61
- xhr.ontimeout = () => reject(new Error('Timeout'));
62
- xhr.send();
63
- });
64
- }
65
- `);
66
- if (data.code !== 0) {
67
- if (data.code === 7 || data.code === 37) {
68
- throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
69
- }
70
- throw new Error(`API error: ${data.message} (code=${data.code})`);
71
- }
72
- const friends = (data.zpData?.friendList || []).slice(0, limit);
73
- return friends.map((f) => ({
32
+ const friends = await fetchRecommendList(page);
33
+ const limit = kwargs.limit || 20;
34
+ return friends.slice(0, limit).map((f) => ({
74
35
  name: f.name || '',
75
36
  job_name: f.jobName || '',
76
37
  last_time: f.lastTime || '',