@jackwener/opencli 1.0.6 → 1.1.1
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/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
- package/.github/pull_request_template.md +7 -0
- package/.github/workflows/doc-check.yml +36 -0
- package/.github/workflows/docs.yml +7 -42
- package/CHANGELOG.md +23 -0
- package/CLI-EXPLORER.md +9 -8
- package/README.md +51 -10
- package/README.zh-CN.md +29 -11
- package/SKILL.md +102 -33
- package/dist/browser/cdp.js +6 -1
- package/dist/browser/page.d.ts +4 -1
- package/dist/browser/page.js +7 -1
- package/dist/build-manifest.js +23 -16
- package/dist/cli-manifest.json +951 -296
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +225 -148
- package/dist/clis/antigravity/serve.js +296 -47
- package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
- package/dist/clis/apple-podcasts/commands.test.js +76 -0
- package/dist/clis/apple-podcasts/search.js +2 -2
- package/dist/clis/apple-podcasts/top.js +9 -2
- package/dist/clis/arxiv/paper.js +21 -0
- package/dist/clis/arxiv/search.js +24 -0
- package/dist/clis/arxiv/utils.d.ts +18 -0
- package/dist/clis/arxiv/utils.js +49 -0
- package/dist/clis/bilibili/dynamic.js +1 -1
- package/dist/clis/bilibili/favorite.js +1 -1
- package/dist/clis/bilibili/feed.js +1 -1
- package/dist/clis/bilibili/following.js +1 -1
- package/dist/clis/bilibili/history.js +1 -1
- package/dist/clis/bilibili/me.js +1 -1
- package/dist/clis/bilibili/ranking.js +1 -1
- package/dist/clis/bilibili/search.js +3 -3
- package/dist/clis/bilibili/subtitle.js +1 -1
- package/dist/clis/bilibili/user-videos.js +1 -1
- package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
- package/dist/clis/bloomberg/businessweek.d.ts +1 -0
- package/dist/clis/bloomberg/businessweek.js +17 -0
- package/dist/clis/bloomberg/economics.d.ts +1 -0
- package/dist/clis/bloomberg/economics.js +17 -0
- package/dist/clis/bloomberg/feeds.d.ts +1 -0
- package/dist/clis/bloomberg/feeds.js +15 -0
- package/dist/clis/bloomberg/industries.d.ts +1 -0
- package/dist/clis/bloomberg/industries.js +17 -0
- package/dist/clis/bloomberg/main.d.ts +1 -0
- package/dist/clis/bloomberg/main.js +17 -0
- package/dist/clis/bloomberg/markets.d.ts +1 -0
- package/dist/clis/bloomberg/markets.js +17 -0
- package/dist/clis/bloomberg/news.d.ts +1 -0
- package/dist/clis/bloomberg/news.js +105 -0
- package/dist/clis/bloomberg/opinions.d.ts +1 -0
- package/dist/clis/bloomberg/opinions.js +17 -0
- package/dist/clis/bloomberg/politics.d.ts +1 -0
- package/dist/clis/bloomberg/politics.js +17 -0
- package/dist/clis/bloomberg/tech.d.ts +1 -0
- package/dist/clis/bloomberg/tech.js +17 -0
- package/dist/clis/bloomberg/utils.d.ts +34 -0
- package/dist/clis/bloomberg/utils.js +364 -0
- package/dist/clis/bloomberg/utils.test.d.ts +1 -0
- package/dist/clis/bloomberg/utils.test.js +129 -0
- package/dist/clis/boss/batchgreet.d.ts +1 -0
- package/dist/clis/boss/batchgreet.js +147 -0
- package/dist/clis/boss/chatlist.js +2 -2
- package/dist/clis/boss/detail.js +2 -2
- package/dist/clis/boss/exchange.d.ts +1 -0
- package/dist/clis/boss/exchange.js +111 -0
- package/dist/clis/boss/greet.d.ts +1 -0
- package/dist/clis/boss/greet.js +175 -0
- package/dist/clis/boss/invite.d.ts +1 -0
- package/dist/clis/boss/invite.js +158 -0
- package/dist/clis/boss/joblist.d.ts +1 -0
- package/dist/clis/boss/joblist.js +55 -0
- package/dist/clis/boss/mark.d.ts +1 -0
- package/dist/clis/boss/mark.js +141 -0
- package/dist/clis/boss/recommend.d.ts +1 -0
- package/dist/clis/boss/recommend.js +83 -0
- package/dist/clis/boss/search.js +1 -1
- package/dist/clis/boss/send.js +1 -1
- package/dist/clis/boss/stats.d.ts +1 -0
- package/dist/clis/boss/stats.js +116 -0
- package/dist/clis/chaoxing/assignments.js +1 -1
- package/dist/clis/chaoxing/exams.js +1 -1
- package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
- package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
- package/dist/clis/chaoxing/utils.test.d.ts +1 -0
- package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
- package/dist/clis/chatgpt/read.js +1 -1
- package/dist/clis/chatwise/export.js +1 -1
- package/dist/clis/chatwise/model.js +2 -2
- package/dist/clis/chatwise/screenshot.js +1 -1
- package/dist/clis/codex/export.js +1 -1
- package/dist/clis/codex/model.js +2 -2
- package/dist/clis/codex/screenshot.js +1 -1
- package/dist/clis/coupang/add-to-cart.js +3 -4
- package/dist/clis/coupang/search.js +2 -4
- package/dist/clis/coupang/utils.test.d.ts +1 -0
- package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
- package/dist/clis/ctrip/search.js +1 -1
- package/dist/clis/cursor/export.js +1 -1
- package/dist/clis/cursor/model.js +2 -2
- package/dist/clis/cursor/screenshot.js +1 -1
- package/dist/clis/jike/comment.js +2 -3
- package/dist/clis/jike/create.js +1 -2
- package/dist/clis/jike/feed.js +0 -1
- package/dist/clis/jike/like.js +1 -2
- package/dist/clis/jike/notifications.js +0 -1
- package/dist/clis/jike/post.yaml +1 -0
- package/dist/clis/jike/repost.js +1 -2
- package/dist/clis/jike/search.js +2 -3
- package/dist/clis/jike/topic.yaml +1 -0
- package/dist/clis/jike/user.yaml +1 -0
- package/dist/clis/jimeng/history.yaml +0 -1
- package/dist/clis/linkedin/search.js +7 -7
- package/dist/clis/linux-do/category.yaml +1 -0
- package/dist/clis/linux-do/search.yaml +4 -3
- package/dist/clis/linux-do/topic.yaml +1 -0
- package/dist/clis/notion/export.js +1 -1
- package/dist/clis/reddit/comment.js +3 -4
- package/dist/clis/reddit/read.js +4 -5
- package/dist/clis/reddit/save.js +2 -3
- package/dist/clis/reddit/saved.js +0 -1
- package/dist/clis/reddit/search.yaml +1 -0
- package/dist/clis/reddit/subscribe.js +0 -1
- package/dist/clis/reddit/upvote.js +2 -3
- package/dist/clis/reddit/upvoted.js +0 -1
- package/dist/clis/reddit/user-comments.yaml +1 -0
- package/dist/clis/reddit/user-posts.yaml +1 -0
- package/dist/clis/reddit/user.yaml +1 -0
- package/dist/clis/reuters/search.js +1 -1
- package/dist/clis/sinafinance/news.d.ts +7 -0
- package/dist/clis/sinafinance/news.js +61 -0
- package/dist/clis/smzdm/search.js +2 -3
- package/dist/clis/stackoverflow/search.yaml +1 -0
- package/dist/clis/steam/top-sellers.yaml +29 -0
- package/dist/clis/twitter/accept.js +2 -2
- package/dist/clis/twitter/article.js +2 -2
- package/dist/clis/twitter/block.d.ts +1 -0
- package/dist/clis/twitter/block.js +88 -0
- package/dist/clis/twitter/delete.js +1 -1
- package/dist/clis/twitter/hide-reply.d.ts +1 -0
- package/dist/clis/twitter/hide-reply.js +66 -0
- package/dist/clis/twitter/like.js +1 -1
- package/dist/clis/twitter/post.js +1 -1
- package/dist/clis/twitter/reply-dm.js +1 -1
- package/dist/clis/twitter/reply.js +2 -2
- package/dist/clis/twitter/search.js +1 -1
- package/dist/clis/twitter/thread.js +2 -2
- package/dist/clis/twitter/trending.d.ts +1 -0
- package/dist/clis/twitter/trending.js +91 -0
- package/dist/clis/twitter/unblock.d.ts +1 -0
- package/dist/clis/twitter/unblock.js +71 -0
- package/dist/clis/v2ex/topic.yaml +1 -0
- package/dist/clis/weibo/hot.js +0 -1
- package/dist/clis/weread/book.js +1 -1
- package/dist/clis/weread/highlights.js +1 -1
- package/dist/clis/weread/notes.js +1 -1
- package/dist/clis/weread/search.js +1 -1
- package/dist/clis/wikipedia/search.d.ts +1 -0
- package/dist/clis/wikipedia/search.js +30 -0
- package/dist/clis/wikipedia/summary.d.ts +1 -0
- package/dist/clis/wikipedia/summary.js +28 -0
- package/dist/clis/wikipedia/utils.d.ts +8 -0
- package/dist/clis/wikipedia/utils.js +18 -0
- package/dist/clis/xiaohongshu/creator-note-detail.d.ts +79 -5
- package/dist/clis/xiaohongshu/creator-note-detail.js +323 -70
- package/dist/clis/xiaohongshu/creator-note-detail.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/creator-note-detail.test.js +258 -0
- package/dist/clis/xiaohongshu/creator-notes-summary.d.ts +28 -0
- package/dist/clis/xiaohongshu/creator-notes-summary.js +92 -0
- package/dist/clis/xiaohongshu/creator-notes-summary.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/creator-notes-summary.test.js +49 -0
- package/dist/clis/xiaohongshu/creator-notes.d.ts +18 -5
- package/dist/clis/xiaohongshu/creator-notes.js +189 -71
- package/dist/clis/xiaohongshu/creator-notes.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/creator-notes.test.js +191 -0
- package/dist/clis/xiaohongshu/creator-profile.js +0 -1
- package/dist/clis/xiaohongshu/creator-stats.js +0 -1
- package/dist/clis/xiaohongshu/download.js +2 -3
- package/dist/clis/xiaohongshu/feed.yaml +0 -1
- package/dist/clis/xiaohongshu/notifications.yaml +0 -1
- package/dist/clis/xiaohongshu/search.js +2 -2
- package/dist/clis/xiaohongshu/user.js +1 -2
- package/dist/clis/yahoo-finance/quote.js +0 -1
- package/dist/clis/youtube/search.js +1 -1
- package/dist/clis/youtube/transcript.js +1 -1
- package/dist/clis/youtube/video.js +1 -1
- package/dist/clis/zhihu/download.js +1 -2
- package/dist/clis/zhihu/question.js +1 -1
- package/dist/clis/zhihu/search.yaml +4 -3
- package/dist/commanderAdapter.d.ts +21 -0
- package/dist/commanderAdapter.js +111 -0
- package/dist/{engine.d.ts → discovery.d.ts} +0 -6
- package/dist/{engine.js → discovery.js} +1 -98
- package/dist/download/index.d.ts +2 -6
- package/dist/download/index.js +19 -46
- package/dist/engine.test.d.ts +1 -1
- package/dist/engine.test.js +8 -7
- package/dist/execution.d.ts +22 -0
- package/dist/execution.js +129 -0
- package/dist/explore.js +121 -107
- package/dist/external-clis.yaml +48 -0
- package/dist/external.d.ts +25 -0
- package/dist/external.js +156 -0
- package/dist/main.js +1 -1
- package/dist/pipeline/steps/browser.js +8 -2
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +2 -0
- package/dist/runtime.d.ts +5 -0
- package/dist/runtime.js +8 -0
- package/dist/serialization.d.ts +34 -0
- package/dist/serialization.js +63 -0
- package/dist/types.d.ts +4 -1
- package/docs/.vitepress/config.mts +14 -3
- package/docs/adapters/browser/arxiv.md +27 -0
- package/docs/adapters/browser/barchart.md +32 -0
- package/docs/adapters/browser/bloomberg.md +70 -0
- package/docs/adapters/browser/chaoxing.md +39 -0
- package/docs/adapters/browser/grok.md +35 -0
- package/docs/adapters/browser/hf.md +42 -0
- package/docs/adapters/browser/jike.md +45 -0
- package/docs/adapters/browser/jimeng.md +39 -0
- package/docs/adapters/browser/linux-do.md +45 -0
- package/docs/adapters/browser/sinafinance.md +35 -0
- package/docs/adapters/browser/stackoverflow.md +35 -0
- package/docs/adapters/browser/steam.md +26 -0
- package/docs/adapters/browser/twitter.md +3 -0
- package/docs/adapters/browser/weread.md +48 -0
- package/docs/adapters/browser/wikipedia.md +30 -0
- package/docs/adapters/browser/xiaohongshu.md +5 -1
- package/docs/adapters/desktop/chatgpt.md +3 -3
- package/docs/adapters/index.md +13 -0
- package/docs/advanced/download.md +4 -4
- package/docs/developer/architecture.md +17 -4
- package/package.json +1 -1
- package/scripts/check-doc-coverage.sh +69 -0
- package/scripts/copy-yaml.cjs +7 -0
- package/src/browser/cdp.ts +9 -4
- package/src/browser/page.ts +7 -1
- package/src/build-manifest.ts +25 -19
- package/src/cli.ts +253 -119
- package/src/clis/antigravity/serve.ts +323 -50
- package/src/clis/apple-podcasts/commands.test.ts +95 -0
- package/src/clis/apple-podcasts/search.ts +2 -2
- package/src/clis/apple-podcasts/top.ts +12 -2
- package/src/clis/arxiv/paper.ts +21 -0
- package/src/clis/arxiv/search.ts +24 -0
- package/src/clis/arxiv/utils.ts +63 -0
- package/src/clis/bilibili/dynamic.ts +1 -1
- package/src/clis/bilibili/favorite.ts +1 -1
- package/src/clis/bilibili/feed.ts +1 -1
- package/src/clis/bilibili/following.ts +1 -1
- package/src/clis/bilibili/history.ts +1 -1
- package/src/clis/bilibili/me.ts +1 -1
- package/src/clis/bilibili/ranking.ts +1 -1
- package/src/clis/bilibili/search.ts +3 -3
- package/src/clis/bilibili/subtitle.ts +1 -1
- package/src/clis/bilibili/user-videos.ts +1 -1
- package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
- package/src/clis/bloomberg/businessweek.ts +18 -0
- package/src/clis/bloomberg/economics.ts +18 -0
- package/src/clis/bloomberg/feeds.ts +16 -0
- package/src/clis/bloomberg/industries.ts +18 -0
- package/src/clis/bloomberg/main.ts +18 -0
- package/src/clis/bloomberg/markets.ts +18 -0
- package/src/clis/bloomberg/news.ts +136 -0
- package/src/clis/bloomberg/opinions.ts +18 -0
- package/src/clis/bloomberg/politics.ts +18 -0
- package/src/clis/bloomberg/tech.ts +18 -0
- package/src/clis/bloomberg/utils.test.ts +135 -0
- package/src/clis/bloomberg/utils.ts +429 -0
- package/src/clis/boss/batchgreet.ts +167 -0
- package/src/clis/boss/chatlist.ts +2 -2
- package/src/clis/boss/detail.ts +2 -2
- package/src/clis/boss/exchange.ts +126 -0
- package/src/clis/boss/greet.ts +198 -0
- package/src/clis/boss/invite.ts +177 -0
- package/src/clis/boss/joblist.ts +63 -0
- package/src/clis/boss/mark.ts +155 -0
- package/src/clis/boss/recommend.ts +94 -0
- package/src/clis/boss/search.ts +1 -1
- package/src/clis/boss/send.ts +1 -1
- package/src/clis/boss/stats.ts +130 -0
- package/src/clis/chaoxing/assignments.ts +1 -1
- package/src/clis/chaoxing/exams.ts +1 -1
- package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
- package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
- package/src/clis/chatgpt/README.zh-CN.md +3 -3
- package/src/clis/chatgpt/read.ts +1 -1
- package/src/clis/chatwise/export.ts +1 -1
- package/src/clis/chatwise/model.ts +2 -2
- package/src/clis/chatwise/screenshot.ts +1 -1
- package/src/clis/codex/export.ts +1 -1
- package/src/clis/codex/model.ts +2 -2
- package/src/clis/codex/screenshot.ts +1 -1
- package/src/clis/coupang/add-to-cart.ts +3 -4
- package/src/clis/coupang/search.ts +2 -4
- package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
- package/src/clis/ctrip/search.ts +1 -1
- package/src/clis/cursor/export.ts +1 -1
- package/src/clis/cursor/model.ts +2 -2
- package/src/clis/cursor/screenshot.ts +1 -1
- package/src/clis/jike/comment.ts +2 -3
- package/src/clis/jike/create.ts +1 -2
- package/src/clis/jike/feed.ts +0 -1
- package/src/clis/jike/like.ts +1 -2
- package/src/clis/jike/notifications.ts +0 -1
- package/src/clis/jike/post.yaml +1 -0
- package/src/clis/jike/repost.ts +1 -2
- package/src/clis/jike/search.ts +2 -3
- package/src/clis/jike/topic.yaml +1 -0
- package/src/clis/jike/user.yaml +1 -0
- package/src/clis/jimeng/history.yaml +0 -1
- package/src/clis/linkedin/search.ts +7 -7
- package/src/clis/linux-do/category.yaml +1 -0
- package/src/clis/linux-do/search.yaml +4 -3
- package/src/clis/linux-do/topic.yaml +1 -0
- package/src/clis/notion/export.ts +1 -1
- package/src/clis/reddit/comment.ts +3 -4
- package/src/clis/reddit/read.ts +4 -5
- package/src/clis/reddit/save.ts +2 -3
- package/src/clis/reddit/saved.ts +0 -1
- package/src/clis/reddit/search.yaml +1 -0
- package/src/clis/reddit/subscribe.ts +0 -1
- package/src/clis/reddit/upvote.ts +2 -3
- package/src/clis/reddit/upvoted.ts +0 -1
- package/src/clis/reddit/user-comments.yaml +1 -0
- package/src/clis/reddit/user-posts.yaml +1 -0
- package/src/clis/reddit/user.yaml +1 -0
- package/src/clis/reuters/search.ts +1 -1
- package/src/clis/sinafinance/news.ts +76 -0
- package/src/clis/smzdm/search.ts +2 -3
- package/src/clis/stackoverflow/search.yaml +1 -0
- package/src/clis/steam/top-sellers.yaml +29 -0
- package/src/clis/twitter/accept.ts +2 -2
- package/src/clis/twitter/article.ts +2 -2
- package/src/clis/twitter/block.ts +92 -0
- package/src/clis/twitter/delete.ts +1 -1
- package/src/clis/twitter/hide-reply.ts +70 -0
- package/src/clis/twitter/like.ts +1 -1
- package/src/clis/twitter/post.ts +1 -1
- package/src/clis/twitter/reply-dm.ts +1 -1
- package/src/clis/twitter/reply.ts +2 -2
- package/src/clis/twitter/search.ts +1 -1
- package/src/clis/twitter/thread.ts +2 -2
- package/src/clis/twitter/trending.ts +113 -0
- package/src/clis/twitter/unblock.ts +75 -0
- package/src/clis/v2ex/topic.yaml +1 -0
- package/src/clis/weibo/hot.ts +0 -1
- package/src/clis/weread/book.ts +1 -1
- package/src/clis/weread/highlights.ts +1 -1
- package/src/clis/weread/notes.ts +1 -1
- package/src/clis/weread/search.ts +1 -1
- package/src/clis/wikipedia/search.ts +32 -0
- package/src/clis/wikipedia/summary.ts +28 -0
- package/src/clis/wikipedia/utils.ts +20 -0
- package/src/clis/xiaohongshu/creator-note-detail.test.ts +272 -0
- package/src/clis/xiaohongshu/creator-note-detail.ts +425 -73
- package/src/clis/xiaohongshu/creator-notes-summary.test.ts +54 -0
- package/src/clis/xiaohongshu/creator-notes-summary.ts +120 -0
- package/src/clis/xiaohongshu/creator-notes.test.ts +211 -0
- package/src/clis/xiaohongshu/creator-notes.ts +254 -75
- package/src/clis/xiaohongshu/creator-profile.ts +0 -1
- package/src/clis/xiaohongshu/creator-stats.ts +0 -1
- package/src/clis/xiaohongshu/download.ts +2 -3
- package/src/clis/xiaohongshu/feed.yaml +0 -1
- package/src/clis/xiaohongshu/notifications.yaml +0 -1
- package/src/clis/xiaohongshu/search.ts +2 -2
- package/src/clis/xiaohongshu/user.ts +1 -2
- package/src/clis/yahoo-finance/quote.ts +0 -1
- package/src/clis/youtube/search.ts +1 -1
- package/src/clis/youtube/transcript.ts +1 -1
- package/src/clis/youtube/video.ts +1 -1
- package/src/clis/zhihu/download.ts +1 -2
- package/src/clis/zhihu/question.ts +1 -1
- package/src/clis/zhihu/search.yaml +4 -3
- package/src/commanderAdapter.ts +113 -0
- package/src/daemon.ts +3 -3
- package/src/{engine.ts → discovery.ts} +1 -108
- package/src/download/index.ts +21 -54
- package/src/engine.test.ts +8 -7
- package/src/execution.ts +138 -0
- package/src/explore.ts +135 -109
- package/src/external-clis.yaml +48 -0
- package/src/external.ts +185 -0
- package/src/main.ts +1 -1
- package/src/pipeline/steps/browser.ts +7 -2
- package/src/registry.ts +5 -0
- package/src/runtime.ts +9 -0
- package/src/serialization.ts +79 -0
- package/src/types.ts +1 -1
- package/tests/e2e/browser-public.test.ts +25 -0
- package/tests/e2e/public-commands.test.ts +55 -1
- package/dist/clis/twitter/trending.yaml +0 -46
- package/src/clis/twitter/trending.yaml +0 -46
- /package/dist/{chaoxing.test.d.ts → clis/arxiv/paper.d.ts} +0 -0
- /package/dist/{coupang.test.d.ts → clis/arxiv/search.d.ts} +0 -0
- /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
- /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
- /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
- /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOSS直聘 mark — label/mark a candidate.
|
|
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):
|
|
8
|
+
* 1=新招呼, 2=沟通中, 3=已约面, 4=已获取简历, 5=已交换电话,
|
|
9
|
+
* 6=已交换微信, 7=不合适, 8=牛人发起, 11=收藏
|
|
10
|
+
*/
|
|
11
|
+
import { cli, Strategy } from '../../registry.js';
|
|
12
|
+
import type { IPage } from '../../types.js';
|
|
13
|
+
|
|
14
|
+
const LABEL_MAP: Record<string, number> = {
|
|
15
|
+
'新招呼': 1, '沟通中': 2, '已约面': 3, '已获取简历': 4,
|
|
16
|
+
'已交换电话': 5, '已交换微信': 6, '不合适': 7, '牛人发起': 8, '收藏': 11,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
cli({
|
|
20
|
+
site: 'boss',
|
|
21
|
+
name: 'mark',
|
|
22
|
+
description: 'BOSS直聘给候选人添加标签',
|
|
23
|
+
domain: 'www.zhipin.com',
|
|
24
|
+
strategy: Strategy.COOKIE,
|
|
25
|
+
browser: true,
|
|
26
|
+
args: [
|
|
27
|
+
{ name: 'uid', required: true, help: 'Encrypted UID of the candidate' },
|
|
28
|
+
{ name: 'label', required: true, help: 'Label name (新招呼/沟通中/已约面/已获取简历/已交换电话/已交换微信/不合适/收藏) or label ID' },
|
|
29
|
+
{ name: 'remove', type: 'boolean', default: false, help: 'Remove the label instead of adding' },
|
|
30
|
+
],
|
|
31
|
+
columns: ['status', 'detail'],
|
|
32
|
+
func: async (page: IPage | null, kwargs) => {
|
|
33
|
+
if (!page) throw new Error('Browser page required');
|
|
34
|
+
|
|
35
|
+
const uid = kwargs.uid;
|
|
36
|
+
const labelInput = kwargs.label;
|
|
37
|
+
const remove = kwargs.remove || false;
|
|
38
|
+
|
|
39
|
+
// Resolve label to ID
|
|
40
|
+
let labelId: number;
|
|
41
|
+
if (LABEL_MAP[labelInput]) {
|
|
42
|
+
labelId = LABEL_MAP[labelInput];
|
|
43
|
+
} else if (!isNaN(Number(labelInput))) {
|
|
44
|
+
labelId = Number(labelInput);
|
|
45
|
+
} else {
|
|
46
|
+
// Try partial match
|
|
47
|
+
const entry = Object.entries(LABEL_MAP).find(([k]) => k.includes(labelInput));
|
|
48
|
+
if (entry) {
|
|
49
|
+
labelId = entry[1];
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error(`未知标签: ${labelInput}。可用标签: ${Object.keys(LABEL_MAP).join(', ')}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (process.env.OPENCLI_VERBOSE) {
|
|
56
|
+
console.error(`[opencli:boss] ${remove ? 'Removing' : 'Adding'} label ${labelId} for ${uid}...`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await page.goto('https://www.zhipin.com/web/chat/index');
|
|
60
|
+
await page.wait({ time: 2 });
|
|
61
|
+
|
|
62
|
+
// First get numeric UID from friend list
|
|
63
|
+
const friendData: any = await page.evaluate(`
|
|
64
|
+
async () => {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const xhr = new XMLHttpRequest();
|
|
67
|
+
xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/getBossFriendListV2.json?page=1&status=0&jobId=0', true);
|
|
68
|
+
xhr.withCredentials = true;
|
|
69
|
+
xhr.timeout = 15000;
|
|
70
|
+
xhr.setRequestHeader('Accept', 'application/json');
|
|
71
|
+
xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(e); } };
|
|
72
|
+
xhr.onerror = () => reject(new Error('Network Error'));
|
|
73
|
+
xhr.send();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
`);
|
|
77
|
+
|
|
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
|
+
const friendName = friend.name || '候选人';
|
|
117
|
+
const friendSource = friend.friendSource ?? 0;
|
|
118
|
+
|
|
119
|
+
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
|
+
const params = new URLSearchParams({
|
|
124
|
+
friendId: String(numericUid),
|
|
125
|
+
friendSource: String(friendSource),
|
|
126
|
+
labelId: String(labelId),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Try GET first (the N() wrapper in boss JS uses GET with query params)
|
|
130
|
+
const data: any = await page.evaluate(`
|
|
131
|
+
async () => {
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
const xhr = new XMLHttpRequest();
|
|
134
|
+
xhr.open('GET', '${targetUrl}?${params.toString()}', true);
|
|
135
|
+
xhr.withCredentials = true;
|
|
136
|
+
xhr.timeout = 15000;
|
|
137
|
+
xhr.setRequestHeader('Accept', 'application/json');
|
|
138
|
+
xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
|
|
139
|
+
xhr.onerror = () => reject(new Error('Network Error'));
|
|
140
|
+
xhr.send();
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
`);
|
|
144
|
+
|
|
145
|
+
if (data.code !== 0) {
|
|
146
|
+
throw new Error(`标签操作失败: ${data.message} (code=${data.code})`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const labelName = Object.entries(LABEL_MAP).find(([, v]) => v === labelId)?.[0] || String(labelId);
|
|
150
|
+
return [{
|
|
151
|
+
status: remove ? '✅ 标签已移除' : '✅ 标签已添加',
|
|
152
|
+
detail: `${friendName}: ${remove ? '移除' : '添加'}标签「${labelName}」`,
|
|
153
|
+
}];
|
|
154
|
+
},
|
|
155
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
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
|
+
*/
|
|
7
|
+
import { cli, Strategy } from '../../registry.js';
|
|
8
|
+
import type { IPage } from '../../types.js';
|
|
9
|
+
|
|
10
|
+
cli({
|
|
11
|
+
site: 'boss',
|
|
12
|
+
name: 'recommend',
|
|
13
|
+
description: 'BOSS直聘查看推荐候选人(新招呼列表)',
|
|
14
|
+
domain: 'www.zhipin.com',
|
|
15
|
+
strategy: Strategy.COOKIE,
|
|
16
|
+
browser: true,
|
|
17
|
+
args: [
|
|
18
|
+
{ name: 'limit', type: 'int', default: 20, help: 'Number of results to return' },
|
|
19
|
+
],
|
|
20
|
+
columns: ['name', 'job_name', 'last_time', 'labels', 'encrypt_uid', 'security_id', 'encrypt_job_id'],
|
|
21
|
+
func: async (page: IPage | null, kwargs) => {
|
|
22
|
+
if (!page) throw new Error('Browser page required');
|
|
23
|
+
|
|
24
|
+
const limit = kwargs.limit || 20;
|
|
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 });
|
|
32
|
+
|
|
33
|
+
// Get label definitions for mapping
|
|
34
|
+
const labelData: any = await page.evaluate(`
|
|
35
|
+
async () => {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
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
|
+
|
|
49
|
+
const labelMap: Record<number, string> = {};
|
|
50
|
+
if (labelData.code === 0 && labelData.zpData?.labels) {
|
|
51
|
+
for (const l of labelData.zpData.labels) {
|
|
52
|
+
labelMap[l.labelId] = l.label;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Get recommended candidates
|
|
57
|
+
const targetUrl = 'https://www.zhipin.com/wapi/zprelation/friend/greetRecSortList';
|
|
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);
|
|
83
|
+
|
|
84
|
+
return friends.map((f: any) => ({
|
|
85
|
+
name: f.name || '',
|
|
86
|
+
job_name: f.jobName || '',
|
|
87
|
+
last_time: f.lastTime || '',
|
|
88
|
+
labels: (f.relationLabelList || []).map((id: number) => labelMap[id] || String(id)).join(', '),
|
|
89
|
+
encrypt_uid: f.encryptUid || '',
|
|
90
|
+
security_id: f.securityId || '',
|
|
91
|
+
encrypt_job_id: f.encryptJobId || '',
|
|
92
|
+
}));
|
|
93
|
+
},
|
|
94
|
+
});
|
package/src/clis/boss/search.ts
CHANGED
|
@@ -72,7 +72,7 @@ cli({
|
|
|
72
72
|
|
|
73
73
|
browser: true,
|
|
74
74
|
args: [
|
|
75
|
-
{ name: 'query', required: true, help: 'Search keyword (e.g. AI agent, 前端)' },
|
|
75
|
+
{ name: 'query', required: true, positional: true, help: 'Search keyword (e.g. AI agent, 前端)' },
|
|
76
76
|
{ name: 'city', default: '北京', help: 'City name or code (e.g. 杭州, 上海, 101010100)' },
|
|
77
77
|
{ name: 'experience', default: '', help: 'Experience: 应届/1年以内/1-3年/3-5年/5-10年/10年以上' },
|
|
78
78
|
{ name: 'degree', default: '', help: 'Degree: 大专/本科/硕士/博士' },
|
package/src/clis/boss/send.ts
CHANGED
|
@@ -17,7 +17,7 @@ cli({
|
|
|
17
17
|
browser: true,
|
|
18
18
|
args: [
|
|
19
19
|
{ name: 'uid', required: true, help: 'Encrypted UID of the candidate (from chatlist)' },
|
|
20
|
-
{ name: 'text', required: true, help: 'Message text to send' },
|
|
20
|
+
{ name: 'text', required: true, positional: true, help: 'Message text to send' },
|
|
21
21
|
],
|
|
22
22
|
columns: ['status', 'detail'],
|
|
23
23
|
func: async (page: IPage | null, kwargs) => {
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOSS直聘 stats — job statistics overview.
|
|
3
|
+
*
|
|
4
|
+
* Uses /wapi/zpchat/chatHelper/statistics for total friend count,
|
|
5
|
+
* and /wapi/zpjob/job/chatted/jobList for per-job info.
|
|
6
|
+
* Since BOSS doesn't expose detailed per-job stats via API,
|
|
7
|
+
* we show what's available: job status, chat info, and total stats.
|
|
8
|
+
*/
|
|
9
|
+
import { cli, Strategy } from '../../registry.js';
|
|
10
|
+
import type { IPage } from '../../types.js';
|
|
11
|
+
|
|
12
|
+
cli({
|
|
13
|
+
site: 'boss',
|
|
14
|
+
name: 'stats',
|
|
15
|
+
description: 'BOSS直聘职位数据统计',
|
|
16
|
+
domain: 'www.zhipin.com',
|
|
17
|
+
strategy: Strategy.COOKIE,
|
|
18
|
+
browser: true,
|
|
19
|
+
args: [
|
|
20
|
+
{ name: 'job-id', default: '', help: 'Encrypted job ID (show all if empty)' },
|
|
21
|
+
],
|
|
22
|
+
columns: ['job_name', 'salary', 'city', 'status', 'total_chats', 'encrypt_job_id'],
|
|
23
|
+
func: async (page: IPage | null, kwargs) => {
|
|
24
|
+
if (!page) throw new Error('Browser page required');
|
|
25
|
+
|
|
26
|
+
const filterJobId = kwargs['job-id'] || '';
|
|
27
|
+
|
|
28
|
+
if (process.env.OPENCLI_VERBOSE) {
|
|
29
|
+
console.error('[opencli:boss] Fetching job statistics...');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await page.goto('https://www.zhipin.com/web/chat/index');
|
|
33
|
+
await page.wait({ time: 2 });
|
|
34
|
+
|
|
35
|
+
// Get job list
|
|
36
|
+
const jobData: any = await page.evaluate(`
|
|
37
|
+
async () => {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const xhr = new XMLHttpRequest();
|
|
40
|
+
xhr.open('GET', 'https://www.zhipin.com/wapi/zpjob/job/chatted/jobList', true);
|
|
41
|
+
xhr.withCredentials = true;
|
|
42
|
+
xhr.timeout = 15000;
|
|
43
|
+
xhr.setRequestHeader('Accept', 'application/json');
|
|
44
|
+
xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
|
|
45
|
+
xhr.onerror = () => reject(new Error('Network Error'));
|
|
46
|
+
xhr.ontimeout = () => reject(new Error('Timeout'));
|
|
47
|
+
xhr.send();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
`);
|
|
51
|
+
|
|
52
|
+
if (jobData.code !== 0) {
|
|
53
|
+
if (jobData.code === 7 || jobData.code === 37) {
|
|
54
|
+
throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`API error: ${jobData.message} (code=${jobData.code})`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Get total chat stats
|
|
60
|
+
const chatStats: any = await page.evaluate(`
|
|
61
|
+
async () => {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const xhr = new XMLHttpRequest();
|
|
64
|
+
xhr.open('GET', 'https://www.zhipin.com/wapi/zpchat/chatHelper/statistics', true);
|
|
65
|
+
xhr.withCredentials = true;
|
|
66
|
+
xhr.timeout = 10000;
|
|
67
|
+
xhr.setRequestHeader('Accept', 'application/json');
|
|
68
|
+
xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { resolve({}); } };
|
|
69
|
+
xhr.onerror = () => resolve({});
|
|
70
|
+
xhr.send();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
`);
|
|
74
|
+
|
|
75
|
+
const totalFriends = chatStats.zpData?.totalFriendCount || 0;
|
|
76
|
+
|
|
77
|
+
// Get per-job chat counts from friend list
|
|
78
|
+
const friendData: any = await page.evaluate(`
|
|
79
|
+
async () => {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const xhr = new XMLHttpRequest();
|
|
82
|
+
xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/getBossFriendListV2.json?page=1&status=0&jobId=0', true);
|
|
83
|
+
xhr.withCredentials = true;
|
|
84
|
+
xhr.timeout = 15000;
|
|
85
|
+
xhr.setRequestHeader('Accept', 'application/json');
|
|
86
|
+
xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { resolve({}); } };
|
|
87
|
+
xhr.onerror = () => resolve({});
|
|
88
|
+
xhr.send();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
`);
|
|
92
|
+
|
|
93
|
+
// Count chats per job
|
|
94
|
+
const jobChatCounts: Record<string, number> = {};
|
|
95
|
+
if (friendData.code === 0) {
|
|
96
|
+
for (const f of (friendData.zpData?.friendList || [])) {
|
|
97
|
+
const jobName = f.jobName || 'unknown';
|
|
98
|
+
jobChatCounts[jobName] = (jobChatCounts[jobName] || 0) + 1;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let jobs = jobData.zpData || [];
|
|
103
|
+
if (filterJobId) {
|
|
104
|
+
jobs = jobs.filter((j: any) => j.encryptJobId === filterJobId);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const results = jobs.map((j: any) => ({
|
|
108
|
+
job_name: j.jobName || '',
|
|
109
|
+
salary: j.salaryDesc || '',
|
|
110
|
+
city: j.address || '',
|
|
111
|
+
status: j.jobOnlineStatus === 1 ? '在线' : '已关闭',
|
|
112
|
+
total_chats: String(jobChatCounts[j.jobName] || 0),
|
|
113
|
+
encrypt_job_id: j.encryptJobId || '',
|
|
114
|
+
}));
|
|
115
|
+
|
|
116
|
+
// Add summary row
|
|
117
|
+
if (!filterJobId && results.length > 0) {
|
|
118
|
+
results.push({
|
|
119
|
+
job_name: '--- 总计 ---',
|
|
120
|
+
salary: '',
|
|
121
|
+
city: '',
|
|
122
|
+
status: `${jobs.length} 个职位`,
|
|
123
|
+
total_chats: String(totalFriends),
|
|
124
|
+
encrypt_job_id: '',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return results;
|
|
129
|
+
},
|
|
130
|
+
});
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* course pages loaded as iframes.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { IPage } from '
|
|
9
|
+
import type { IPage } from '../../types.js';
|
|
10
10
|
|
|
11
11
|
// ── Utilities ────────────────────────────────────────────────────────
|
|
12
12
|
|
|
@@ -96,7 +96,6 @@ export async function getCourses(page: IPage): Promise<ChaoxingCourse[]> {
|
|
|
96
96
|
/** Navigate to the interaction page to establish a Chaoxing session. */
|
|
97
97
|
export async function initSession(page: IPage): Promise<void> {
|
|
98
98
|
await page.goto('https://mooc2-ans.chaoxing.com/mooc2-ans/visit/interaction');
|
|
99
|
-
await page.wait(3);
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
/**
|
|
@@ -108,7 +107,6 @@ export async function enterCourse(page: IPage, course: ChaoxingCourse): Promise<
|
|
|
108
107
|
`https://mooc1.chaoxing.com/visit/stucoursemiddle` +
|
|
109
108
|
`?courseid=${course.courseId}&clazzid=${course.classId}&cpi=${course.cpi}&ismooc2=1&v=2`;
|
|
110
109
|
await page.goto(url);
|
|
111
|
-
await page.wait(3);
|
|
112
110
|
}
|
|
113
111
|
|
|
114
112
|
/**
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- `opencli chatgpt status`:检查 ChatGPT 应用是否在运行。
|
|
15
15
|
- `opencli chatgpt new`:激活 ChatGPT 并按 `Cmd+N` 开始新对话。
|
|
16
16
|
- `opencli chatgpt send "消息"`:将消息复制到剪贴板,激活 ChatGPT,粘贴并提交。
|
|
17
|
-
- `opencli chatgpt read
|
|
17
|
+
- `opencli chatgpt read`:通过当前聚焦 ChatGPT 窗口的辅助功能树读取最后一条可见消息并返回文本。
|
|
18
18
|
|
|
19
19
|
## 方式二:CDP(高级,Electron 调试模式)
|
|
20
20
|
|
|
@@ -34,11 +34,11 @@ export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9224"
|
|
|
34
34
|
|
|
35
35
|
## 工作原理
|
|
36
36
|
|
|
37
|
-
- **AppleScript 模式**:使用 `osascript`
|
|
37
|
+
- **AppleScript 模式**:使用 `osascript` 控制 ChatGPT,发送消息时借助 `pbcopy`/`pbpaste` 粘贴文本,读取消息时通过 macOS 辅助功能树获取当前可见聊天内容。
|
|
38
38
|
- **CDP 模式**:通过 Chrome DevTools Protocol 连接到 Electron 渲染进程,直接操作 DOM。
|
|
39
39
|
|
|
40
40
|
## 限制
|
|
41
41
|
|
|
42
42
|
- 仅支持 macOS(AppleScript 依赖)
|
|
43
43
|
- AppleScript 模式需要辅助功能权限
|
|
44
|
-
- `read`
|
|
44
|
+
- `read` 返回的是当前聚焦 ChatGPT 窗口里的最后一条可见消息;如果目标消息不在可见区域,需先手动滚动
|
package/src/clis/chatgpt/read.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { getVisibleChatMessages } from './ax.js';
|
|
|
6
6
|
export const readCommand = cli({
|
|
7
7
|
site: 'chatgpt',
|
|
8
8
|
name: 'read',
|
|
9
|
-
description: '
|
|
9
|
+
description: 'Read the last visible message from the focused ChatGPT Desktop window',
|
|
10
10
|
domain: 'localhost',
|
|
11
11
|
strategy: Strategy.PUBLIC,
|
|
12
12
|
browser: false,
|
|
@@ -10,7 +10,7 @@ export const exportCommand = cli({
|
|
|
10
10
|
strategy: Strategy.UI,
|
|
11
11
|
browser: true,
|
|
12
12
|
args: [
|
|
13
|
-
{ name: 'output', required: false,
|
|
13
|
+
{ name: 'output', required: false, help: 'Output file (default: /tmp/chatwise-export.md)' },
|
|
14
14
|
],
|
|
15
15
|
columns: ['Status', 'File', 'Messages'],
|
|
16
16
|
func: async (page: IPage, kwargs: any) => {
|
|
@@ -9,11 +9,11 @@ export const modelCommand = cli({
|
|
|
9
9
|
strategy: Strategy.UI,
|
|
10
10
|
browser: true,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: '
|
|
12
|
+
{ name: 'model-name', required: false, positional: true, help: 'Model to switch to (e.g. gpt-4, claude-3)' },
|
|
13
13
|
],
|
|
14
14
|
columns: ['Status', 'Model'],
|
|
15
15
|
func: async (page: IPage, kwargs: any) => {
|
|
16
|
-
const desiredModel = kwargs
|
|
16
|
+
const desiredModel = kwargs['model-name'] as string | undefined;
|
|
17
17
|
|
|
18
18
|
if (!desiredModel) {
|
|
19
19
|
// Read current model
|
|
@@ -10,7 +10,7 @@ export const screenshotCommand = cli({
|
|
|
10
10
|
strategy: Strategy.UI,
|
|
11
11
|
browser: true,
|
|
12
12
|
args: [
|
|
13
|
-
{ name: 'output', required: false,
|
|
13
|
+
{ name: 'output', required: false, help: 'Output file path (default: /tmp/chatwise-snapshot)' },
|
|
14
14
|
],
|
|
15
15
|
columns: ['Status', 'File'],
|
|
16
16
|
func: async (page: IPage, kwargs: any) => {
|
package/src/clis/codex/export.ts
CHANGED
|
@@ -10,7 +10,7 @@ export const exportCommand = cli({
|
|
|
10
10
|
strategy: Strategy.UI,
|
|
11
11
|
browser: true,
|
|
12
12
|
args: [
|
|
13
|
-
{ name: 'output', required: false,
|
|
13
|
+
{ name: 'output', required: false, help: 'Output file (default: /tmp/codex-export.md)' },
|
|
14
14
|
],
|
|
15
15
|
columns: ['Status', 'File', 'Messages'],
|
|
16
16
|
func: async (page: IPage, kwargs: any) => {
|
package/src/clis/codex/model.ts
CHANGED
|
@@ -9,11 +9,11 @@ export const modelCommand = cli({
|
|
|
9
9
|
strategy: Strategy.UI,
|
|
10
10
|
browser: true,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: '
|
|
12
|
+
{ name: 'model-name', required: false, positional: true, help: 'The ID of the model to switch to (e.g. gpt-4)' }
|
|
13
13
|
],
|
|
14
14
|
columns: ['Status', 'Model'],
|
|
15
15
|
func: async (page: IPage, kwargs: any) => {
|
|
16
|
-
const desiredModel = kwargs
|
|
16
|
+
const desiredModel = kwargs['model-name'] as string | undefined;
|
|
17
17
|
|
|
18
18
|
if (!desiredModel) {
|
|
19
19
|
// Just read the current model. We traverse iframes/webviews if needed.
|
|
@@ -10,7 +10,7 @@ export const screenshotCommand = cli({
|
|
|
10
10
|
strategy: Strategy.UI,
|
|
11
11
|
browser: true,
|
|
12
12
|
args: [
|
|
13
|
-
{ name: 'output', required: false,
|
|
13
|
+
{ name: 'output', required: false, help: 'Output file path (default: /tmp/codex-snapshot.txt)' },
|
|
14
14
|
],
|
|
15
15
|
columns: ['Status', 'File'],
|
|
16
16
|
func: async (page: IPage, kwargs: any) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
-
import { canonicalizeProductUrl, normalizeProductId } from '
|
|
2
|
+
import { canonicalizeProductUrl, normalizeProductId } from './utils.js';
|
|
3
3
|
|
|
4
4
|
function escapeJsString(value: string): string {
|
|
5
5
|
return JSON.stringify(value);
|
|
@@ -102,12 +102,12 @@ cli({
|
|
|
102
102
|
strategy: Strategy.COOKIE,
|
|
103
103
|
browser: true,
|
|
104
104
|
args: [
|
|
105
|
-
{ name: '
|
|
105
|
+
{ name: 'product-id', required: false, help: 'Coupang product ID' },
|
|
106
106
|
{ name: 'url', required: false, help: 'Canonical product URL' },
|
|
107
107
|
],
|
|
108
108
|
columns: ['ok', 'product_id', 'url', 'message'],
|
|
109
109
|
func: async (page, kwargs) => {
|
|
110
|
-
const rawProductId = kwargs
|
|
110
|
+
const rawProductId = kwargs['product-id'] ?? kwargs['product-id'];
|
|
111
111
|
const productId = normalizeProductId(rawProductId);
|
|
112
112
|
const targetUrl = canonicalizeProductUrl(kwargs.url, productId);
|
|
113
113
|
|
|
@@ -117,7 +117,6 @@ cli({
|
|
|
117
117
|
|
|
118
118
|
const finalUrl = targetUrl || canonicalizeProductUrl('', productId);
|
|
119
119
|
await page.goto(finalUrl);
|
|
120
|
-
await page.wait(3);
|
|
121
120
|
|
|
122
121
|
const result = await page.evaluate(buildAddToCartEvaluate(productId));
|
|
123
122
|
const loginHints = result?.loginHints ?? {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
-
import { mergeSearchItems, normalizeSearchItem, sanitizeSearchItems } from '
|
|
2
|
+
import { mergeSearchItems, normalizeSearchItem, sanitizeSearchItems } from './utils.js';
|
|
3
3
|
|
|
4
4
|
function escapeJsString(value: string): string {
|
|
5
5
|
return JSON.stringify(value);
|
|
@@ -409,7 +409,7 @@ cli({
|
|
|
409
409
|
strategy: Strategy.COOKIE,
|
|
410
410
|
browser: true,
|
|
411
411
|
args: [
|
|
412
|
-
{ name: 'query', required: true, help: 'Search keyword' },
|
|
412
|
+
{ name: 'query', required: true, positional: true, help: 'Search keyword' },
|
|
413
413
|
{ name: 'page', type: 'int', default: 1, help: 'Search result page number' },
|
|
414
414
|
{ name: 'limit', type: 'int', default: 20, help: 'Max results (max 50)' },
|
|
415
415
|
{ name: 'filter', required: false, help: 'Optional search filter (currently supports: rocket)' },
|
|
@@ -425,7 +425,6 @@ cli({
|
|
|
425
425
|
const initialPage = filter ? 1 : pageNumber;
|
|
426
426
|
const url = `https://www.coupang.com/np/search?q=${encodeURIComponent(query)}&channel=user&page=${initialPage}`;
|
|
427
427
|
await page.goto(url);
|
|
428
|
-
await page.wait(3);
|
|
429
428
|
if (filter) {
|
|
430
429
|
const filterResult = await page.evaluate(buildApplyFilterEvaluate(filter));
|
|
431
430
|
if (!filterResult?.ok) {
|
|
@@ -437,7 +436,6 @@ cli({
|
|
|
437
436
|
const filteredUrl = new URL(locationInfo?.href || url);
|
|
438
437
|
filteredUrl.searchParams.set('page', String(pageNumber));
|
|
439
438
|
await page.goto(filteredUrl.toString());
|
|
440
|
-
await page.wait(3);
|
|
441
439
|
}
|
|
442
440
|
}
|
|
443
441
|
await page.autoScroll({ times: filter ? 3 : 2, delayMs: 1500 });
|
package/src/clis/ctrip/search.ts
CHANGED
|
@@ -11,7 +11,7 @@ cli({
|
|
|
11
11
|
domain: 'www.ctrip.com',
|
|
12
12
|
strategy: Strategy.COOKIE,
|
|
13
13
|
args: [
|
|
14
|
-
{ name: 'query', required: true, help: 'Search keyword (city or attraction)' },
|
|
14
|
+
{ name: 'query', required: true, positional: true, help: 'Search keyword (city or attraction)' },
|
|
15
15
|
{ name: 'limit', type: 'int', default: 15, help: 'Number of results' },
|
|
16
16
|
],
|
|
17
17
|
columns: ['rank', 'name', 'type', 'score', 'price', 'url'],
|
|
@@ -11,7 +11,7 @@ function makeExportCommand(site: string, readSelector: string) {
|
|
|
11
11
|
strategy: Strategy.UI,
|
|
12
12
|
browser: true,
|
|
13
13
|
args: [
|
|
14
|
-
{ name: 'output', required: false,
|
|
14
|
+
{ name: 'output', required: false, help: `Output file (default: /tmp/${site}-export.md)` },
|
|
15
15
|
],
|
|
16
16
|
columns: ['Status', 'File', 'Messages'],
|
|
17
17
|
func: async (page: IPage, kwargs: any) => {
|