@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.
- package/CONTRIBUTING.md +39 -1
- package/README.md +9 -10
- package/README.zh-CN.md +39 -17
- package/SKILL.md +10 -5
- package/dist/browser/cdp.d.ts +4 -4
- package/dist/browser/cdp.js +39 -16
- package/dist/browser/daemon-client.d.ts +2 -1
- package/dist/browser/dom-helpers.js +38 -7
- package/dist/browser/dom-snapshot.d.ts +86 -0
- package/dist/browser/dom-snapshot.js +729 -0
- package/dist/browser/dom-snapshot.test.d.ts +11 -0
- package/dist/browser/dom-snapshot.test.js +212 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/page.d.ts +14 -24
- package/dist/browser/page.js +37 -4
- package/dist/build-manifest.d.ts +11 -4
- package/dist/build-manifest.js +59 -21
- package/dist/build-manifest.test.js +58 -2
- package/dist/cli-manifest.json +3856 -1509
- package/dist/cli.js +66 -0
- package/dist/clis/barchart/greeks.js +1 -1
- package/dist/clis/barchart/options.js +1 -1
- package/dist/clis/barchart/quote.js +1 -1
- package/dist/clis/bilibili/download.js +1 -1
- package/dist/clis/bilibili/following.js +1 -1
- package/dist/clis/bilibili/subtitle.js +1 -1
- package/dist/clis/bilibili/user-videos.js +1 -1
- package/dist/clis/boss/batchgreet.js +10 -97
- package/dist/clis/boss/chatlist.js +8 -25
- package/dist/clis/boss/chatmsg.js +11 -42
- package/dist/clis/boss/common.d.ts +92 -0
- package/dist/clis/boss/common.js +223 -0
- package/dist/clis/boss/detail.js +7 -49
- package/dist/clis/boss/exchange.js +13 -79
- package/dist/clis/boss/greet.js +18 -145
- package/dist/clis/boss/invite.js +26 -121
- package/dist/clis/boss/joblist.js +6 -31
- package/dist/clis/boss/mark.js +12 -85
- package/dist/clis/boss/recommend.js +10 -49
- package/dist/clis/boss/resume.js +18 -118
- package/dist/clis/boss/search.js +12 -60
- package/dist/clis/boss/send.js +17 -151
- package/dist/clis/boss/stats.js +18 -69
- package/dist/clis/coupang/add-to-cart.js +1 -1
- package/dist/clis/devto/tag.yaml +34 -0
- package/dist/clis/devto/top.yaml +29 -0
- package/dist/clis/devto/user.yaml +33 -0
- package/dist/clis/douban/book-hot.d.ts +1 -0
- package/dist/clis/douban/book-hot.js +14 -0
- package/dist/clis/douban/marks.d.ts +1 -0
- package/dist/clis/douban/marks.js +115 -0
- package/dist/clis/douban/movie-hot.d.ts +1 -0
- package/dist/clis/douban/movie-hot.js +14 -0
- package/dist/clis/douban/reviews.d.ts +1 -0
- package/dist/clis/douban/reviews.js +106 -0
- package/dist/clis/douban/search.d.ts +1 -0
- package/dist/clis/douban/search.js +16 -0
- package/dist/clis/douban/shared.d.ts +4 -0
- package/dist/clis/douban/shared.js +155 -0
- package/dist/clis/douban/subject.yaml +76 -0
- package/dist/clis/douban/top250.yaml +70 -0
- package/dist/clis/douban/utils.d.ts +35 -0
- package/dist/clis/douban/utils.js +48 -0
- package/dist/clis/facebook/add-friend.yaml +43 -0
- package/dist/clis/facebook/events.yaml +44 -0
- package/dist/clis/facebook/feed.yaml +63 -0
- package/dist/clis/facebook/friends.yaml +42 -0
- package/dist/clis/facebook/groups.yaml +50 -0
- package/dist/clis/facebook/join-group.yaml +44 -0
- package/dist/clis/facebook/memories.yaml +39 -0
- package/dist/clis/facebook/notifications.yaml +40 -0
- package/dist/clis/facebook/profile.yaml +37 -0
- package/dist/clis/facebook/search.yaml +46 -0
- package/dist/clis/google/news.d.ts +5 -0
- package/dist/clis/google/news.js +58 -0
- package/dist/clis/google/search.d.ts +10 -0
- package/dist/clis/google/search.js +127 -0
- package/dist/clis/google/suggest.d.ts +5 -0
- package/dist/clis/google/suggest.js +34 -0
- package/dist/clis/google/trends.d.ts +5 -0
- package/dist/clis/google/trends.js +38 -0
- package/dist/clis/google/utils.d.ts +9 -0
- package/dist/clis/google/utils.js +23 -0
- package/dist/clis/google/utils.test.d.ts +1 -0
- package/dist/clis/google/utils.test.js +75 -0
- package/dist/clis/grok/ask.d.ts +14 -0
- package/dist/clis/grok/ask.js +257 -65
- package/dist/clis/grok/ask.test.d.ts +1 -0
- package/dist/clis/grok/ask.test.js +36 -0
- package/dist/clis/instagram/comment.yaml +52 -0
- package/dist/clis/instagram/explore.yaml +43 -0
- package/dist/clis/instagram/follow.yaml +41 -0
- package/dist/clis/instagram/followers.yaml +51 -0
- package/dist/clis/instagram/following.yaml +51 -0
- package/dist/clis/instagram/like.yaml +46 -0
- package/dist/clis/instagram/profile.yaml +42 -0
- package/dist/clis/instagram/save.yaml +46 -0
- package/dist/clis/instagram/saved.yaml +40 -0
- package/dist/clis/instagram/search.yaml +43 -0
- package/dist/clis/instagram/unfollow.yaml +38 -0
- package/dist/clis/instagram/unlike.yaml +46 -0
- package/dist/clis/instagram/unsave.yaml +46 -0
- package/dist/clis/instagram/user.yaml +54 -0
- package/dist/clis/jike/repost.js +1 -1
- package/dist/clis/jimeng/generate.yaml +1 -0
- package/dist/clis/linux-do/category.yaml +1 -0
- package/dist/clis/lobsters/active.yaml +29 -0
- package/dist/clis/lobsters/hot.yaml +29 -0
- package/dist/clis/lobsters/newest.yaml +29 -0
- package/dist/clis/lobsters/tag.yaml +34 -0
- package/dist/clis/medium/feed.d.ts +1 -0
- package/dist/clis/medium/feed.js +15 -0
- package/dist/clis/medium/search.d.ts +1 -0
- package/dist/clis/medium/search.js +15 -0
- package/dist/clis/medium/shared.d.ts +5 -0
- package/dist/clis/medium/shared.js +78 -0
- package/dist/clis/medium/user.d.ts +1 -0
- package/dist/clis/medium/user.js +15 -0
- package/dist/clis/reddit/comment.js +1 -1
- package/dist/clis/reddit/read.js +1 -1
- package/dist/clis/reddit/save.js +1 -1
- package/dist/clis/reddit/subreddit.yaml +1 -0
- package/dist/clis/reddit/subscribe.js +1 -1
- package/dist/clis/reddit/upvote.js +1 -1
- package/dist/clis/sinablog/article.d.ts +1 -0
- package/dist/clis/sinablog/article.js +14 -0
- package/dist/clis/sinablog/hot.d.ts +1 -0
- package/dist/clis/sinablog/hot.js +14 -0
- package/dist/clis/sinablog/search.d.ts +1 -0
- package/dist/clis/sinablog/search.js +51 -0
- package/dist/clis/sinablog/shared.d.ts +7 -0
- package/dist/clis/sinablog/shared.js +187 -0
- package/dist/clis/sinablog/user.d.ts +1 -0
- package/dist/clis/sinablog/user.js +15 -0
- package/dist/clis/substack/feed.d.ts +1 -0
- package/dist/clis/substack/feed.js +15 -0
- package/dist/clis/substack/publication.d.ts +1 -0
- package/dist/clis/substack/publication.js +15 -0
- package/dist/clis/substack/search.d.ts +1 -0
- package/dist/clis/substack/search.js +77 -0
- package/dist/clis/substack/shared.d.ts +4 -0
- package/dist/clis/substack/shared.js +129 -0
- package/dist/clis/tiktok/comment.yaml +66 -0
- package/dist/clis/tiktok/explore.yaml +39 -0
- package/dist/clis/tiktok/follow.yaml +39 -0
- package/dist/clis/tiktok/following.yaml +46 -0
- package/dist/clis/tiktok/friends.yaml +47 -0
- package/dist/clis/tiktok/like.yaml +38 -0
- package/dist/clis/tiktok/live.yaml +51 -0
- package/dist/clis/tiktok/notifications.yaml +52 -0
- package/dist/clis/tiktok/profile.yaml +45 -0
- package/dist/clis/tiktok/save.yaml +34 -0
- package/dist/clis/tiktok/search.yaml +46 -0
- package/dist/clis/tiktok/unfollow.yaml +44 -0
- package/dist/clis/tiktok/unlike.yaml +38 -0
- package/dist/clis/tiktok/unsave.yaml +36 -0
- package/dist/clis/tiktok/user.yaml +44 -0
- package/dist/clis/twitter/download.d.ts +1 -1
- package/dist/clis/twitter/download.js +3 -3
- package/dist/clis/twitter/followers.js +1 -1
- package/dist/clis/twitter/following.js +1 -1
- package/dist/clis/twitter/thread.js +1 -1
- package/dist/clis/twitter/timeline.d.ts +23 -0
- package/dist/clis/twitter/timeline.js +42 -14
- package/dist/clis/twitter/timeline.test.d.ts +1 -0
- package/dist/clis/twitter/timeline.test.js +102 -0
- package/dist/clis/wikipedia/random.d.ts +1 -0
- package/dist/clis/wikipedia/random.js +19 -0
- package/dist/clis/wikipedia/search.js +3 -3
- package/dist/clis/wikipedia/summary.js +4 -9
- package/dist/clis/wikipedia/trending.d.ts +1 -0
- package/dist/clis/wikipedia/trending.js +35 -0
- package/dist/clis/wikipedia/utils.d.ts +28 -0
- package/dist/clis/wikipedia/utils.js +13 -0
- package/dist/clis/xiaohongshu/creator-note-detail.js +1 -1
- package/dist/clis/xiaohongshu/creator-note-detail.test.js +2 -0
- package/dist/clis/xiaohongshu/creator-notes.test.js +2 -0
- package/dist/clis/xiaohongshu/download.js +1 -1
- package/dist/clis/xueqiu/earnings-date.yaml +69 -0
- package/dist/clis/xueqiu/search.yaml +2 -1
- package/dist/clis/xueqiu/stock.yaml +2 -0
- package/dist/clis/yahoo-finance/quote.js +1 -1
- package/dist/commanderAdapter.js +13 -7
- package/dist/discovery.d.ts +8 -0
- package/dist/discovery.js +105 -19
- package/dist/doctor.js +3 -1
- package/dist/doctor.test.js +46 -2
- package/dist/engine.test.d.ts +0 -3
- package/dist/engine.test.js +74 -6
- package/dist/execution.d.ts +4 -2
- package/dist/execution.js +31 -7
- package/dist/explore.d.ts +76 -3
- package/dist/explore.js +11 -4
- package/dist/generate.d.ts +41 -2
- package/dist/generate.js +5 -4
- package/dist/main.js +2 -1
- package/dist/pipeline/executor.d.ts +2 -2
- package/dist/pipeline/executor.js +2 -2
- package/dist/pipeline/executor.test.js +33 -6
- package/dist/pipeline/registry.d.ts +1 -1
- package/dist/pipeline/steps/browser.d.ts +7 -7
- package/dist/pipeline/steps/browser.js +15 -7
- package/dist/pipeline/steps/fetch.d.ts +1 -1
- package/dist/pipeline/steps/fetch.js +11 -7
- package/dist/pipeline/steps/transform.d.ts +6 -5
- package/dist/pipeline/steps/transform.js +30 -9
- package/dist/pipeline/template.d.ts +6 -6
- package/dist/pipeline/template.js +43 -5
- package/dist/pipeline/template.test.js +18 -0
- package/dist/pipeline/transform.test.js +11 -0
- package/dist/plugin.d.ts +31 -0
- package/dist/plugin.js +216 -0
- package/dist/plugin.test.d.ts +4 -0
- package/dist/plugin.test.js +76 -0
- package/dist/registry-api.d.ts +11 -0
- package/dist/registry-api.js +9 -0
- package/dist/registry.d.ts +11 -0
- package/dist/registry.js +6 -1
- package/dist/synthesize.d.ts +94 -4
- package/dist/synthesize.js +5 -4
- package/dist/types.d.ts +39 -26
- package/dist/validate.js +8 -2
- package/docs/.vitepress/config.mts +6 -4
- package/docs/adapters/browser/barchart.md +6 -5
- package/docs/adapters/browser/bilibili.md +9 -0
- package/docs/adapters/browser/devto.md +35 -0
- package/docs/adapters/browser/douban.md +38 -0
- package/docs/adapters/browser/facebook.md +36 -0
- package/docs/adapters/browser/google.md +62 -0
- package/docs/adapters/browser/grok.md +26 -8
- package/docs/adapters/browser/instagram.md +46 -0
- package/docs/adapters/browser/lobsters.md +32 -0
- package/docs/adapters/browser/medium.md +32 -0
- package/docs/adapters/browser/reddit.md +9 -0
- package/docs/adapters/browser/sinablog.md +36 -0
- package/docs/adapters/browser/substack.md +38 -0
- package/docs/adapters/browser/tiktok.md +68 -0
- package/docs/adapters/browser/wikipedia.md +11 -2
- package/docs/adapters/browser/xueqiu.md +10 -0
- package/docs/adapters/browser/yahoo-finance.md +6 -5
- package/docs/adapters/desktop/antigravity.md +6 -0
- package/docs/adapters/desktop/chatgpt.md +2 -1
- package/docs/adapters/desktop/codex.md +5 -1
- package/docs/adapters/desktop/cursor.md +4 -0
- package/docs/adapters/desktop/discord.md +7 -7
- package/docs/adapters/index.md +1 -4
- package/docs/guide/getting-started.md +1 -0
- package/docs/guide/plugins.md +153 -0
- package/docs/zh/guide/plugins.md +107 -0
- package/extension/src/background.ts +18 -11
- package/package.json +10 -5
- package/scripts/clean-dist.cjs +13 -0
- package/src/browser/cdp.ts +71 -31
- package/src/browser/daemon-client.ts +2 -1
- package/src/browser/dom-helpers.ts +38 -7
- package/src/browser/dom-snapshot.test.ts +249 -0
- package/src/browser/dom-snapshot.ts +770 -0
- package/src/browser/index.ts +2 -0
- package/src/browser/page.ts +50 -19
- package/src/build-manifest.test.ts +70 -2
- package/src/build-manifest.ts +94 -26
- package/src/cli.ts +71 -2
- package/src/clis/barchart/greeks.ts +1 -1
- package/src/clis/barchart/options.ts +1 -1
- package/src/clis/barchart/quote.ts +1 -1
- package/src/clis/bilibili/download.ts +1 -1
- package/src/clis/bilibili/following.ts +1 -1
- package/src/clis/bilibili/subtitle.ts +1 -1
- package/src/clis/bilibili/user-videos.ts +1 -1
- package/src/clis/boss/batchgreet.ts +14 -106
- package/src/clis/boss/chatlist.ts +12 -26
- package/src/clis/boss/chatmsg.ts +16 -40
- package/src/clis/boss/common.ts +287 -0
- package/src/clis/boss/detail.ts +8 -54
- package/src/clis/boss/exchange.ts +15 -89
- package/src/clis/boss/greet.ts +23 -160
- package/src/clis/boss/invite.ts +36 -133
- package/src/clis/boss/joblist.ts +7 -36
- package/src/clis/boss/mark.ts +13 -94
- package/src/clis/boss/recommend.ts +12 -57
- package/src/clis/boss/resume.ts +19 -124
- package/src/clis/boss/search.ts +13 -66
- package/src/clis/boss/send.ts +21 -161
- package/src/clis/boss/stats.ts +19 -74
- package/src/clis/coupang/add-to-cart.ts +1 -1
- package/src/clis/devto/tag.yaml +34 -0
- package/src/clis/devto/top.yaml +29 -0
- package/src/clis/devto/user.yaml +33 -0
- package/src/clis/douban/book-hot.ts +15 -0
- package/src/clis/douban/marks.ts +135 -0
- package/src/clis/douban/movie-hot.ts +15 -0
- package/src/clis/douban/reviews.ts +127 -0
- package/src/clis/douban/search.ts +17 -0
- package/src/clis/douban/shared.ts +165 -0
- package/src/clis/douban/subject.yaml +76 -0
- package/src/clis/douban/top250.yaml +70 -0
- package/src/clis/douban/utils.ts +81 -0
- package/src/clis/facebook/add-friend.yaml +43 -0
- package/src/clis/facebook/events.yaml +44 -0
- package/src/clis/facebook/feed.yaml +63 -0
- package/src/clis/facebook/friends.yaml +42 -0
- package/src/clis/facebook/groups.yaml +50 -0
- package/src/clis/facebook/join-group.yaml +44 -0
- package/src/clis/facebook/memories.yaml +39 -0
- package/src/clis/facebook/notifications.yaml +40 -0
- package/src/clis/facebook/profile.yaml +37 -0
- package/src/clis/facebook/search.yaml +46 -0
- package/src/clis/google/news.ts +66 -0
- package/src/clis/google/search.ts +133 -0
- package/src/clis/google/suggest.ts +40 -0
- package/src/clis/google/trends.ts +44 -0
- package/src/clis/google/utils.test.ts +82 -0
- package/src/clis/google/utils.ts +24 -0
- package/src/clis/grok/ask.test.ts +53 -0
- package/src/clis/grok/ask.ts +300 -69
- package/src/clis/instagram/comment.yaml +52 -0
- package/src/clis/instagram/explore.yaml +43 -0
- package/src/clis/instagram/follow.yaml +41 -0
- package/src/clis/instagram/followers.yaml +51 -0
- package/src/clis/instagram/following.yaml +51 -0
- package/src/clis/instagram/like.yaml +46 -0
- package/src/clis/instagram/profile.yaml +42 -0
- package/src/clis/instagram/save.yaml +46 -0
- package/src/clis/instagram/saved.yaml +40 -0
- package/src/clis/instagram/search.yaml +43 -0
- package/src/clis/instagram/unfollow.yaml +38 -0
- package/src/clis/instagram/unlike.yaml +46 -0
- package/src/clis/instagram/unsave.yaml +46 -0
- package/src/clis/instagram/user.yaml +54 -0
- package/src/clis/jike/repost.ts +1 -1
- package/src/clis/jimeng/generate.yaml +1 -0
- package/src/clis/linux-do/category.yaml +1 -0
- package/src/clis/lobsters/active.yaml +29 -0
- package/src/clis/lobsters/hot.yaml +29 -0
- package/src/clis/lobsters/newest.yaml +29 -0
- package/src/clis/lobsters/tag.yaml +34 -0
- package/src/clis/medium/feed.ts +16 -0
- package/src/clis/medium/search.ts +16 -0
- package/src/clis/medium/shared.ts +83 -0
- package/src/clis/medium/user.ts +16 -0
- package/src/clis/reddit/comment.ts +1 -1
- package/src/clis/reddit/read.ts +1 -1
- package/src/clis/reddit/save.ts +1 -1
- package/src/clis/reddit/subreddit.yaml +1 -0
- package/src/clis/reddit/subscribe.ts +1 -1
- package/src/clis/reddit/upvote.ts +1 -1
- package/src/clis/sinablog/article.ts +15 -0
- package/src/clis/sinablog/hot.ts +15 -0
- package/src/clis/sinablog/search.ts +56 -0
- package/src/clis/sinablog/shared.ts +198 -0
- package/src/clis/sinablog/user.ts +16 -0
- package/src/clis/substack/feed.ts +16 -0
- package/src/clis/substack/publication.ts +16 -0
- package/src/clis/substack/search.ts +91 -0
- package/src/clis/substack/shared.ts +132 -0
- package/src/clis/tiktok/comment.yaml +66 -0
- package/src/clis/tiktok/explore.yaml +39 -0
- package/src/clis/tiktok/follow.yaml +39 -0
- package/src/clis/tiktok/following.yaml +46 -0
- package/src/clis/tiktok/friends.yaml +47 -0
- package/src/clis/tiktok/like.yaml +38 -0
- package/src/clis/tiktok/live.yaml +51 -0
- package/src/clis/tiktok/notifications.yaml +52 -0
- package/src/clis/tiktok/profile.yaml +45 -0
- package/src/clis/tiktok/save.yaml +34 -0
- package/src/clis/tiktok/search.yaml +46 -0
- package/src/clis/tiktok/unfollow.yaml +44 -0
- package/src/clis/tiktok/unlike.yaml +38 -0
- package/src/clis/tiktok/unsave.yaml +36 -0
- package/src/clis/tiktok/user.yaml +44 -0
- package/src/clis/twitter/download.ts +3 -3
- package/src/clis/twitter/followers.ts +1 -1
- package/src/clis/twitter/following.ts +1 -1
- package/src/clis/twitter/thread.ts +1 -1
- package/src/clis/twitter/timeline.test.ts +109 -0
- package/src/clis/twitter/timeline.ts +59 -19
- package/src/clis/wikipedia/random.ts +19 -0
- package/src/clis/wikipedia/search.ts +10 -4
- package/src/clis/wikipedia/summary.ts +4 -9
- package/src/clis/wikipedia/trending.ts +41 -0
- package/src/clis/wikipedia/utils.ts +31 -0
- package/src/clis/xiaohongshu/creator-note-detail.test.ts +2 -0
- package/src/clis/xiaohongshu/creator-note-detail.ts +1 -1
- package/src/clis/xiaohongshu/creator-notes.test.ts +2 -0
- package/src/clis/xiaohongshu/download.ts +1 -1
- package/src/clis/xueqiu/earnings-date.yaml +69 -0
- package/src/clis/xueqiu/search.yaml +2 -1
- package/src/clis/xueqiu/stock.yaml +2 -0
- package/src/clis/yahoo-finance/quote.ts +1 -1
- package/src/commanderAdapter.ts +17 -10
- package/src/discovery.ts +134 -24
- package/src/doctor.test.ts +59 -2
- package/src/doctor.ts +4 -2
- package/src/engine.test.ts +79 -6
- package/src/execution.ts +42 -16
- package/src/explore.ts +77 -9
- package/src/generate.ts +58 -9
- package/src/main.ts +2 -1
- package/src/pipeline/executor.test.ts +35 -6
- package/src/pipeline/executor.ts +11 -7
- package/src/pipeline/registry.ts +3 -3
- package/src/pipeline/steps/browser.ts +24 -15
- package/src/pipeline/steps/fetch.ts +18 -13
- package/src/pipeline/steps/transform.ts +40 -15
- package/src/pipeline/template.test.ts +18 -0
- package/src/pipeline/template.ts +86 -13
- package/src/pipeline/transform.test.ts +15 -2
- package/src/plugin.test.ts +86 -0
- package/src/plugin.ts +254 -0
- package/src/registry-api.ts +12 -0
- package/src/registry.ts +19 -1
- package/src/synthesize.ts +102 -21
- package/src/types.ts +44 -12
- package/src/validate.ts +19 -4
- package/tests/e2e/browser-public.test.ts +11 -0
- package/tests/e2e/public-commands.test.ts +64 -0
- package/dist/clis/feishu/new.d.ts +0 -1
- package/dist/clis/feishu/new.js +0 -27
- package/dist/clis/feishu/read.d.ts +0 -1
- package/dist/clis/feishu/read.js +0 -40
- package/dist/clis/feishu/search.d.ts +0 -1
- package/dist/clis/feishu/search.js +0 -30
- package/dist/clis/feishu/send.d.ts +0 -1
- package/dist/clis/feishu/send.js +0 -39
- package/dist/clis/feishu/status.d.ts +0 -1
- package/dist/clis/feishu/status.js +0 -28
- package/dist/clis/neteasemusic/like.d.ts +0 -1
- package/dist/clis/neteasemusic/like.js +0 -25
- package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
- package/dist/clis/neteasemusic/lyrics.js +0 -47
- package/dist/clis/neteasemusic/next.d.ts +0 -1
- package/dist/clis/neteasemusic/next.js +0 -26
- package/dist/clis/neteasemusic/play.d.ts +0 -1
- package/dist/clis/neteasemusic/play.js +0 -26
- package/dist/clis/neteasemusic/playing.d.ts +0 -1
- package/dist/clis/neteasemusic/playing.js +0 -59
- package/dist/clis/neteasemusic/playlist.d.ts +0 -1
- package/dist/clis/neteasemusic/playlist.js +0 -46
- package/dist/clis/neteasemusic/prev.d.ts +0 -1
- package/dist/clis/neteasemusic/prev.js +0 -25
- package/dist/clis/neteasemusic/search.d.ts +0 -1
- package/dist/clis/neteasemusic/search.js +0 -52
- package/dist/clis/neteasemusic/status.d.ts +0 -1
- package/dist/clis/neteasemusic/status.js +0 -16
- package/dist/clis/neteasemusic/volume.d.ts +0 -1
- package/dist/clis/neteasemusic/volume.js +0 -54
- package/dist/clis/wechat/chats.d.ts +0 -1
- package/dist/clis/wechat/chats.js +0 -28
- package/dist/clis/wechat/contacts.d.ts +0 -1
- package/dist/clis/wechat/contacts.js +0 -28
- package/dist/clis/wechat/read.d.ts +0 -1
- package/dist/clis/wechat/read.js +0 -58
- package/dist/clis/wechat/search.d.ts +0 -1
- package/dist/clis/wechat/search.js +0 -31
- package/dist/clis/wechat/send.d.ts +0 -1
- package/dist/clis/wechat/send.js +0 -42
- package/dist/clis/wechat/status.d.ts +0 -1
- package/dist/clis/wechat/status.js +0 -29
- package/dist/pipeline.d.ts +0 -7
- package/dist/pipeline.js +0 -7
- package/docs/adapters/browser/github.md +0 -26
- package/docs/adapters/desktop/feishu.md +0 -20
- package/docs/adapters/desktop/neteasemusic.md +0 -31
- package/docs/adapters/desktop/wechat.md +0 -28
- package/src/clis/antigravity/README.md +0 -5
- package/src/clis/antigravity/README.zh-CN.md +0 -51
- package/src/clis/chaoxing/README.md +0 -14
- package/src/clis/chaoxing/README.zh-CN.md +0 -35
- package/src/clis/chatgpt/README.md +0 -5
- package/src/clis/chatgpt/README.zh-CN.md +0 -44
- package/src/clis/chatwise/README.md +0 -5
- package/src/clis/chatwise/README.zh-CN.md +0 -38
- package/src/clis/codex/README.md +0 -5
- package/src/clis/codex/README.zh-CN.md +0 -33
- package/src/clis/cursor/README.md +0 -5
- package/src/clis/cursor/README.zh-CN.md +0 -33
- package/src/clis/discord-app/README.md +0 -5
- package/src/clis/discord-app/README.zh-CN.md +0 -28
- package/src/clis/feishu/README.md +0 -5
- package/src/clis/feishu/README.zh-CN.md +0 -20
- package/src/clis/feishu/new.ts +0 -32
- package/src/clis/feishu/read.ts +0 -48
- package/src/clis/feishu/search.ts +0 -35
- package/src/clis/feishu/send.ts +0 -46
- package/src/clis/feishu/status.ts +0 -34
- package/src/clis/neteasemusic/README.md +0 -5
- package/src/clis/neteasemusic/README.zh-CN.md +0 -31
- package/src/clis/neteasemusic/like.ts +0 -28
- package/src/clis/neteasemusic/lyrics.ts +0 -53
- package/src/clis/neteasemusic/next.ts +0 -30
- package/src/clis/neteasemusic/play.ts +0 -30
- package/src/clis/neteasemusic/playing.ts +0 -62
- package/src/clis/neteasemusic/playlist.ts +0 -51
- package/src/clis/neteasemusic/prev.ts +0 -29
- package/src/clis/neteasemusic/search.ts +0 -58
- package/src/clis/neteasemusic/status.ts +0 -18
- package/src/clis/neteasemusic/volume.ts +0 -61
- package/src/clis/notion/README.md +0 -5
- package/src/clis/notion/README.zh-CN.md +0 -29
- package/src/clis/wechat/README.md +0 -5
- package/src/clis/wechat/README.zh-CN.md +0 -28
- package/src/clis/wechat/chats.ts +0 -33
- package/src/clis/wechat/contacts.ts +0 -33
- package/src/clis/wechat/read.ts +0 -72
- package/src/clis/wechat/search.ts +0 -36
- package/src/clis/wechat/send.ts +0 -49
- package/src/clis/wechat/status.ts +0 -35
- package/src/pipeline.ts +0 -8
package/src/clis/boss/mark.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* BOSS直聘 mark — label/mark a candidate.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
|
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
|
|
33
|
-
|
|
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
|
-
|
|
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
|
|
60
|
-
await page.wait({ time: 2 });
|
|
53
|
+
await navigateToChat(page);
|
|
61
54
|
|
|
62
|
-
|
|
63
|
-
|
|
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(
|
|
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
|
-
|
|
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 [{
|
|
@@ -1,11 +1,8 @@
|
|
|
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';
|
|
8
|
-
import
|
|
5
|
+
import { requirePage, navigateToChat, bossFetch, fetchRecommendList, verbose } from './common.js';
|
|
9
6
|
|
|
10
7
|
cli({
|
|
11
8
|
site: 'boss',
|
|
@@ -13,39 +10,22 @@ 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
16
|
{ name: 'limit', type: 'int', default: 20, help: 'Number of results to return' },
|
|
19
17
|
],
|
|
20
18
|
columns: ['name', 'job_name', 'last_time', 'labels', 'encrypt_uid', 'security_id', 'encrypt_job_id'],
|
|
21
|
-
func: async (page
|
|
22
|
-
|
|
19
|
+
func: async (page, kwargs) => {
|
|
20
|
+
requirePage(page);
|
|
21
|
+
verbose('Fetching recommended candidates...');
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (process.env.OPENCLI_VERBOSE) {
|
|
27
|
-
console.error('[opencli:boss] Fetching recommended candidates...');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
await page.goto('https://www.zhipin.com/web/chat/index');
|
|
31
|
-
await page.wait({ time: 2 });
|
|
23
|
+
await navigateToChat(page);
|
|
32
24
|
|
|
33
25
|
// Get label definitions for mapping
|
|
34
|
-
const labelData
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const xhr = new XMLHttpRequest();
|
|
38
|
-
xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/label/get', true);
|
|
39
|
-
xhr.withCredentials = true;
|
|
40
|
-
xhr.timeout = 10000;
|
|
41
|
-
xhr.setRequestHeader('Accept', 'application/json');
|
|
42
|
-
xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { resolve({}); } };
|
|
43
|
-
xhr.onerror = () => resolve({});
|
|
44
|
-
xhr.send();
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
`);
|
|
48
|
-
|
|
26
|
+
const labelData = await bossFetch(page, 'https://www.zhipin.com/wapi/zprelation/friend/label/get', {
|
|
27
|
+
allowNonZero: true,
|
|
28
|
+
});
|
|
49
29
|
const labelMap: Record<number, string> = {};
|
|
50
30
|
if (labelData.code === 0 && labelData.zpData?.labels) {
|
|
51
31
|
for (const l of labelData.zpData.labels) {
|
|
@@ -53,35 +33,10 @@ cli({
|
|
|
53
33
|
}
|
|
54
34
|
}
|
|
55
35
|
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const data: any = await page.evaluate(`
|
|
60
|
-
async () => {
|
|
61
|
-
return new Promise((resolve, reject) => {
|
|
62
|
-
const xhr = new XMLHttpRequest();
|
|
63
|
-
xhr.open('GET', '${targetUrl}', 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(new Error('JSON parse failed')); } };
|
|
68
|
-
xhr.onerror = () => reject(new Error('Network Error'));
|
|
69
|
-
xhr.ontimeout = () => reject(new Error('Timeout'));
|
|
70
|
-
xhr.send();
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
`);
|
|
74
|
-
|
|
75
|
-
if (data.code !== 0) {
|
|
76
|
-
if (data.code === 7 || data.code === 37) {
|
|
77
|
-
throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
|
|
78
|
-
}
|
|
79
|
-
throw new Error(`API error: ${data.message} (code=${data.code})`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const friends = (data.zpData?.friendList || []).slice(0, limit);
|
|
36
|
+
const friends = await fetchRecommendList(page);
|
|
37
|
+
const limit = kwargs.limit || 20;
|
|
83
38
|
|
|
84
|
-
return friends.map((f: any) => ({
|
|
39
|
+
return friends.slice(0, limit).map((f: any) => ({
|
|
85
40
|
name: f.name || '',
|
|
86
41
|
job_name: f.jobName || '',
|
|
87
42
|
last_time: f.lastTime || '',
|
package/src/clis/boss/resume.ts
CHANGED
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
* BOSS直聘 resume — view candidate resume/profile via chat page UI scraping (boss side).
|
|
3
3
|
*
|
|
4
4
|
* Flow: navigate to chat page → click on candidate → scrape the right panel info.
|
|
5
|
-
* The chat page loads candidate basic info, work experience, and education
|
|
6
|
-
* in the right panel when a candidate is selected.
|
|
7
5
|
*
|
|
8
|
-
* HTML structure
|
|
6
|
+
* Right panel HTML structure:
|
|
9
7
|
* .base-info-single-detial → name, gender, age, experience, degree
|
|
10
8
|
* .experience-content.time-list → time ranges (icon-base-info-work / icon-base-info-edu)
|
|
11
9
|
* .experience-content.detail-list → details (company·position / school·major·degree)
|
|
12
10
|
* .position-content → job being discussed + expectation
|
|
13
11
|
*/
|
|
14
12
|
import { cli, Strategy } from '../../registry.js';
|
|
15
|
-
import
|
|
13
|
+
import { requirePage, navigateToChat, findFriendByUid, clickCandidateInList } from './common.js';
|
|
16
14
|
|
|
17
15
|
cli({
|
|
18
16
|
site: 'boss',
|
|
@@ -20,119 +18,42 @@ cli({
|
|
|
20
18
|
description: 'BOSS直聘查看候选人简历(招聘端)',
|
|
21
19
|
domain: 'www.zhipin.com',
|
|
22
20
|
strategy: Strategy.COOKIE,
|
|
23
|
-
|
|
21
|
+
navigateBefore: false,
|
|
24
22
|
browser: true,
|
|
25
23
|
args: [
|
|
26
|
-
{ name: 'uid', required: true, help: 'Encrypted UID of the candidate (from chatlist)' },
|
|
24
|
+
{ name: 'uid', required: true, positional: true, help: 'Encrypted UID of the candidate (from chatlist)' },
|
|
27
25
|
],
|
|
28
26
|
columns: [
|
|
29
27
|
'name', 'gender', 'age', 'experience', 'degree', 'active_time',
|
|
30
28
|
'work_history', 'education',
|
|
31
29
|
'job_chatting', 'expect',
|
|
32
30
|
],
|
|
33
|
-
func: async (page
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const uid = kwargs.uid;
|
|
37
|
-
|
|
38
|
-
// Step 1: Navigate to chat page
|
|
39
|
-
await page.goto('https://www.zhipin.com/web/chat/index');
|
|
40
|
-
await page.wait({ time: 3 });
|
|
41
|
-
|
|
42
|
-
// Step 2: Get friend list to find candidate's numeric uid
|
|
43
|
-
const friendData: 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/getBossFriendListV2.json?page=1&status=0&jobId=0', 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
|
-
`);
|
|
31
|
+
func: async (page, kwargs) => {
|
|
32
|
+
requirePage(page);
|
|
57
33
|
|
|
58
|
-
|
|
59
|
-
if (friendData.code === 7 || friendData.code === 37) {
|
|
60
|
-
throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
|
|
61
|
-
}
|
|
62
|
-
throw new Error('获取好友列表失败: ' + (friendData.message || friendData.code));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
let friend: any = null;
|
|
66
|
-
const allFriends = friendData.zpData?.friendList || [];
|
|
67
|
-
friend = allFriends.find((f: any) => f.encryptUid === uid);
|
|
68
|
-
|
|
69
|
-
if (!friend) {
|
|
70
|
-
for (let p = 2; p <= 5; p++) {
|
|
71
|
-
const moreUrl = `https://www.zhipin.com/wapi/zprelation/friend/getBossFriendListV2.json?page=${p}&status=0&jobId=0`;
|
|
72
|
-
const moreData: any = await page.evaluate(`
|
|
73
|
-
async () => {
|
|
74
|
-
return new Promise((resolve, reject) => {
|
|
75
|
-
const xhr = new XMLHttpRequest();
|
|
76
|
-
xhr.open('GET', '${moreUrl}', true);
|
|
77
|
-
xhr.withCredentials = true;
|
|
78
|
-
xhr.timeout = 15000;
|
|
79
|
-
xhr.setRequestHeader('Accept', 'application/json');
|
|
80
|
-
xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
|
|
81
|
-
xhr.onerror = () => reject(new Error('Network Error'));
|
|
82
|
-
xhr.send();
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
`);
|
|
86
|
-
if (moreData.code === 0) {
|
|
87
|
-
const list = moreData.zpData?.friendList || [];
|
|
88
|
-
friend = list.find((f: any) => f.encryptUid === uid);
|
|
89
|
-
if (friend) break;
|
|
90
|
-
if (list.length === 0) break;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
34
|
+
await navigateToChat(page, 3);
|
|
94
35
|
|
|
36
|
+
const friend = await findFriendByUid(page, kwargs.uid, { maxPages: 5 });
|
|
95
37
|
if (!friend) throw new Error('未找到该候选人,请确认 uid 是否正确');
|
|
96
38
|
|
|
97
39
|
const numericUid = friend.uid;
|
|
98
40
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
async () => {
|
|
102
|
-
const item = document.querySelector('#_${numericUid}-0') || document.querySelector('[id^="_${numericUid}"]');
|
|
103
|
-
if (item) {
|
|
104
|
-
item.click();
|
|
105
|
-
return { clicked: true };
|
|
106
|
-
}
|
|
107
|
-
const items = document.querySelectorAll('.geek-item');
|
|
108
|
-
for (const el of items) {
|
|
109
|
-
if (el.id && el.id.startsWith('_${numericUid}')) {
|
|
110
|
-
el.click();
|
|
111
|
-
return { clicked: true };
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return { clicked: false };
|
|
115
|
-
}
|
|
116
|
-
`);
|
|
117
|
-
|
|
118
|
-
if (!clicked.clicked) {
|
|
41
|
+
const clicked = await clickCandidateInList(page, numericUid);
|
|
42
|
+
if (!clicked) {
|
|
119
43
|
throw new Error('无法在聊天列表中找到该用户,请确认聊天列表中有此人');
|
|
120
44
|
}
|
|
121
45
|
|
|
122
|
-
// Step 4: Wait for right panel to load
|
|
123
46
|
await page.wait({ time: 2 });
|
|
124
47
|
|
|
125
|
-
//
|
|
48
|
+
// Scrape the right panel
|
|
126
49
|
const resumeInfo: any = await page.evaluate(`
|
|
127
50
|
(() => {
|
|
128
51
|
const container = document.querySelector('.base-info-single-container') || document.querySelector('.base-info-content');
|
|
129
52
|
if (!container) return { error: 'no container found' };
|
|
130
53
|
|
|
131
|
-
// === Basic Info ===
|
|
132
54
|
const nameEl = container.querySelector('.base-name');
|
|
133
55
|
const name = nameEl ? nameEl.textContent.trim() : '';
|
|
134
56
|
|
|
135
|
-
// Gender
|
|
136
57
|
let gender = '';
|
|
137
58
|
const detailDiv = container.querySelector('.base-info-single-detial');
|
|
138
59
|
if (detailDiv) {
|
|
@@ -144,11 +65,9 @@ cli({
|
|
|
144
65
|
}
|
|
145
66
|
}
|
|
146
67
|
|
|
147
|
-
// Active time
|
|
148
68
|
const activeEl = container.querySelector('.active-time');
|
|
149
69
|
const activeTime = activeEl ? activeEl.textContent.trim() : '';
|
|
150
70
|
|
|
151
|
-
// Age, experience, degree — direct child divs of .base-info-single-detial
|
|
152
71
|
let age = '', experience = '', degree = '';
|
|
153
72
|
if (detailDiv) {
|
|
154
73
|
for (const el of detailDiv.children) {
|
|
@@ -163,50 +82,32 @@ cli({
|
|
|
163
82
|
}
|
|
164
83
|
}
|
|
165
84
|
|
|
166
|
-
|
|
167
|
-
// Structure: two .experience-content divs
|
|
168
|
-
// 1. .time-list → <li> items with icon (work/edu) and time span
|
|
169
|
-
// 2. .detail-list → <li> items with icon (work/edu) and detail text
|
|
170
|
-
// Each <li> has a <use> with xlink:href "#icon-base-info-work" or "#icon-base-info-edu"
|
|
171
|
-
|
|
172
|
-
const workTimes = [];
|
|
173
|
-
const eduTimes = [];
|
|
174
|
-
const workDetails = [];
|
|
175
|
-
const eduDetails = [];
|
|
85
|
+
const workTimes = [], eduTimes = [], workDetails = [], eduDetails = [];
|
|
176
86
|
|
|
177
87
|
const timeList = container.querySelector('.experience-content.time-list');
|
|
178
88
|
if (timeList) {
|
|
179
|
-
const
|
|
180
|
-
for (const li of lis) {
|
|
89
|
+
for (const li of timeList.querySelectorAll('li')) {
|
|
181
90
|
const useEl = li.querySelector('use');
|
|
182
91
|
const href = useEl ? (useEl.getAttribute('xlink:href') || useEl.getAttribute('href') || '') : '';
|
|
183
92
|
const timeSpan = li.querySelector('.time');
|
|
184
93
|
const timeText = timeSpan ? timeSpan.textContent.trim() : li.textContent.trim();
|
|
185
|
-
if (href.includes('base-info-edu'))
|
|
186
|
-
|
|
187
|
-
} else {
|
|
188
|
-
workTimes.push(timeText);
|
|
189
|
-
}
|
|
94
|
+
if (href.includes('base-info-edu')) eduTimes.push(timeText);
|
|
95
|
+
else workTimes.push(timeText);
|
|
190
96
|
}
|
|
191
97
|
}
|
|
192
98
|
|
|
193
99
|
const detailList = container.querySelector('.experience-content.detail-list');
|
|
194
100
|
if (detailList) {
|
|
195
|
-
const
|
|
196
|
-
for (const li of lis) {
|
|
101
|
+
for (const li of detailList.querySelectorAll('li')) {
|
|
197
102
|
const useEl = li.querySelector('use');
|
|
198
103
|
const href = useEl ? (useEl.getAttribute('xlink:href') || useEl.getAttribute('href') || '') : '';
|
|
199
104
|
const valueSpan = li.querySelector('.value');
|
|
200
105
|
const valueText = valueSpan ? valueSpan.textContent.trim() : li.textContent.trim();
|
|
201
|
-
if (href.includes('base-info-edu'))
|
|
202
|
-
|
|
203
|
-
} else {
|
|
204
|
-
workDetails.push(valueText);
|
|
205
|
-
}
|
|
106
|
+
if (href.includes('base-info-edu')) eduDetails.push(valueText);
|
|
107
|
+
else workDetails.push(valueText);
|
|
206
108
|
}
|
|
207
109
|
}
|
|
208
110
|
|
|
209
|
-
// Combine times and details
|
|
210
111
|
const workHistory = [];
|
|
211
112
|
for (let i = 0; i < Math.max(workTimes.length, workDetails.length); i++) {
|
|
212
113
|
const parts = [];
|
|
@@ -223,22 +124,16 @@ cli({
|
|
|
223
124
|
if (parts.length) education.push(parts.join(' '));
|
|
224
125
|
}
|
|
225
126
|
|
|
226
|
-
// === Job Chatting & Expect ===
|
|
227
127
|
const positionContent = container.querySelector('.position-content');
|
|
228
128
|
let jobChatting = '', expect = '';
|
|
229
129
|
if (positionContent) {
|
|
230
130
|
const posNameEl = positionContent.querySelector('.position-name');
|
|
231
131
|
if (posNameEl) jobChatting = posNameEl.textContent.trim();
|
|
232
|
-
|
|
233
132
|
const expectEl = positionContent.querySelector('.position-item.expect .value');
|
|
234
133
|
if (expectEl) expect = expectEl.textContent.trim();
|
|
235
134
|
}
|
|
236
135
|
|
|
237
|
-
return {
|
|
238
|
-
name, gender, age, experience, degree, activeTime,
|
|
239
|
-
workHistory, education,
|
|
240
|
-
jobChatting, expect,
|
|
241
|
-
};
|
|
136
|
+
return { name, gender, age, experience, degree, activeTime, workHistory, education, jobChatting, expect };
|
|
242
137
|
})()
|
|
243
138
|
`);
|
|
244
139
|
|
package/src/clis/boss/search.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* BOSS直聘 job search — browser cookie API.
|
|
3
3
|
*/
|
|
4
4
|
import { cli, Strategy } from '../../registry.js';
|
|
5
|
-
import
|
|
5
|
+
import { requirePage, navigateTo, bossFetch, assertOk, verbose } from './common.js';
|
|
6
6
|
|
|
7
7
|
/** City name → BOSS Zhipin city code mapping */
|
|
8
8
|
const CITY_CODES: Record<string, string> = {
|
|
@@ -69,7 +69,7 @@ cli({
|
|
|
69
69
|
description: 'BOSS直聘搜索职位',
|
|
70
70
|
domain: 'www.zhipin.com',
|
|
71
71
|
strategy: Strategy.COOKIE,
|
|
72
|
-
|
|
72
|
+
navigateBefore: false,
|
|
73
73
|
browser: true,
|
|
74
74
|
args: [
|
|
75
75
|
{ name: 'query', required: true, positional: true, help: 'Search keyword (e.g. AI agent, 前端)' },
|
|
@@ -82,19 +82,13 @@ cli({
|
|
|
82
82
|
{ name: 'limit', type: 'int', default: 15, help: 'Number of results' },
|
|
83
83
|
],
|
|
84
84
|
columns: ['name', 'salary', 'company', 'area', 'experience', 'degree', 'skills', 'boss', 'security_id', 'url'],
|
|
85
|
-
func: async (page
|
|
86
|
-
|
|
85
|
+
func: async (page, kwargs) => {
|
|
86
|
+
requirePage(page);
|
|
87
87
|
|
|
88
88
|
const cityCode = resolveCity(kwargs.city);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
// Navigate to the Web search view first to establish proper referrer context
|
|
94
|
-
// This is a lesson learned from boss-cli: referrer is important
|
|
95
|
-
await page.goto(`https://www.zhipin.com/web/geek/job?query=${encodeURIComponent(kwargs.query)}&city=${cityCode}`);
|
|
96
|
-
|
|
97
|
-
// Give the page a tiny bit of time to settle to avoid immediate 403s
|
|
89
|
+
verbose('Navigating to set referrer context...');
|
|
90
|
+
|
|
91
|
+
await navigateTo(page, `https://www.zhipin.com/web/geek/job?query=${encodeURIComponent(kwargs.query)}&city=${cityCode}`);
|
|
98
92
|
await new Promise(r => setTimeout(r, 1000));
|
|
99
93
|
|
|
100
94
|
const expVal = resolveMap(kwargs.experience, EXP_MAP);
|
|
@@ -109,7 +103,6 @@ cli({
|
|
|
109
103
|
|
|
110
104
|
while (allJobs.length < limit) {
|
|
111
105
|
if (allJobs.length > 0) {
|
|
112
|
-
// Human-like pause between page fetches (1-3 seconds)
|
|
113
106
|
await new Promise(r => setTimeout(r, 1000 + Math.random() * 2000));
|
|
114
107
|
}
|
|
115
108
|
|
|
@@ -126,62 +119,19 @@ cli({
|
|
|
126
119
|
if (industryVal) qs.set('industry', industryVal);
|
|
127
120
|
|
|
128
121
|
const targetUrl = `https://www.zhipin.com/wapi/zpgeek/search/joblist.json?${qs.toString()}`;
|
|
122
|
+
verbose(`Fetching page ${currentPage}... (current jobs: ${allJobs.length})`);
|
|
129
123
|
|
|
130
|
-
|
|
131
|
-
console.error(`[opencli:boss] Fetching page ${currentPage}... (current jobs: ${allJobs.length})`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const evaluateScript = `
|
|
135
|
-
async () => {
|
|
136
|
-
return new Promise((resolve, reject) => {
|
|
137
|
-
const xhr = new window.XMLHttpRequest();
|
|
138
|
-
xhr.open('GET', '${targetUrl}', true);
|
|
139
|
-
xhr.withCredentials = true;
|
|
140
|
-
xhr.timeout = 15000; // 15s timeout
|
|
141
|
-
xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
|
|
142
|
-
xhr.onload = () => {
|
|
143
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
144
|
-
try {
|
|
145
|
-
resolve(JSON.parse(xhr.responseText));
|
|
146
|
-
} catch (e) {
|
|
147
|
-
reject(new Error('Failed to parse JSON. Raw (200 chars): ' + xhr.responseText.substring(0, 200)));
|
|
148
|
-
}
|
|
149
|
-
} else {
|
|
150
|
-
reject(new Error('XHR HTTP Status: ' + xhr.status));
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
xhr.onerror = () => reject(new Error('XHR Network Error'));
|
|
154
|
-
xhr.ontimeout = () => reject(new Error('XHR Timeout'));
|
|
155
|
-
xhr.send();
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
`;
|
|
159
|
-
|
|
160
|
-
let data: any;
|
|
161
|
-
try {
|
|
162
|
-
data = await page.evaluate(evaluateScript);
|
|
163
|
-
} catch (e: any) {
|
|
164
|
-
throw new Error('API evaluate failed: ' + e.message);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (data.code !== 0) {
|
|
168
|
-
if (data.code === 37) {
|
|
169
|
-
throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
|
|
170
|
-
}
|
|
171
|
-
throw new Error(`BOSS API error: ${data.message || 'Unknown'} (code=${data.code})\nRaw data: ${JSON.stringify(data)}`);
|
|
172
|
-
}
|
|
124
|
+
const data = await bossFetch(page, targetUrl);
|
|
173
125
|
|
|
174
126
|
const zpData = data.zpData || {};
|
|
175
127
|
const batch = zpData.jobList || [];
|
|
176
|
-
if (batch.length === 0)
|
|
177
|
-
break; // No more results
|
|
178
|
-
}
|
|
128
|
+
if (batch.length === 0) break;
|
|
179
129
|
|
|
180
130
|
let addedInBatch = 0;
|
|
181
131
|
for (const j of batch) {
|
|
182
132
|
if (!j.encryptJobId || seenIds.has(j.encryptJobId)) continue;
|
|
183
133
|
seenIds.add(j.encryptJobId);
|
|
184
|
-
|
|
134
|
+
|
|
185
135
|
allJobs.push({
|
|
186
136
|
name: j.jobName,
|
|
187
137
|
salary: j.salaryDesc,
|
|
@@ -199,14 +149,11 @@ cli({
|
|
|
199
149
|
}
|
|
200
150
|
|
|
201
151
|
if (addedInBatch === 0) {
|
|
202
|
-
|
|
203
|
-
if (process.env.OPENCLI_VERBOSE) console.error(`[opencli:boss] API returned duplicate page, stopping pagination at ${allJobs.length} items`);
|
|
152
|
+
verbose(`API returned duplicate page, stopping pagination at ${allJobs.length} items`);
|
|
204
153
|
break;
|
|
205
154
|
}
|
|
206
155
|
|
|
207
|
-
if (!zpData.hasMore)
|
|
208
|
-
break; // API says no more pages
|
|
209
|
-
}
|
|
156
|
+
if (!zpData.hasMore) break;
|
|
210
157
|
currentPage++;
|
|
211
158
|
}
|
|
212
159
|
|