@jackwener/opencli 0.7.2 → 0.7.4

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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # OpenCLI
2
2
 
3
3
  > **Make any website your CLI.**
4
- > Zero risk · Reuse Chrome login · AI-powered discovery
4
+ > Zero risk · Reuse Chrome login · AI-powered discovery · 80+ commands · 19 sites
5
5
 
6
6
  [中文文档](./README.zh-CN.md)
7
7
 
@@ -9,7 +9,7 @@
9
9
  [![Node.js Version](https://img.shields.io/node/v/@jackwener/opencli?style=flat-square)](https://nodejs.org)
10
10
  [![License](https://img.shields.io/npm/l/@jackwener/opencli?style=flat-square)](./LICENSE)
11
11
 
12
- A CLI tool that turns **any website** into a command-line interface — bilibili, zhihu, xiaohongshu, twitter, reddit, and many more — powered by browser session reuse and AI-native discovery.
12
+ A CLI tool that turns **any website** into a command-line interface — Bilibili, Zhihu, 小红书, Twitter/X, Reddit, YouTube, and [many more](#built-in-commands) — powered by browser session reuse and AI-native discovery.
13
13
 
14
14
  ---
15
15
 
@@ -32,8 +32,9 @@ A CLI tool that turns **any website** into a command-line interface — bilibili
32
32
 
33
33
  - **Account-safe** — Reuses Chrome's logged-in state; your credentials never leave the browser.
34
34
  - **AI Agent ready** — `explore` discovers APIs, `synthesize` generates adapters, `cascade` finds auth strategies.
35
+ - **Self-healing setup** — `opencli setup` auto-discovers tokens; `opencli doctor` diagnoses config across 10+ tools; `--fix` repairs them all.
35
36
  - **Dynamic Loader** — Simply drop `.ts` or `.yaml` adapters into the `clis/` folder for auto-registration.
36
- - **Dual-Engine Architecture** — Supports both YAML declarative data pipelines and robust browser runtime typescript injections.
37
+ - **Dual-Engine Architecture** — Supports both YAML declarative data pipelines and robust browser runtime TypeScript injections.
37
38
 
38
39
  ## Prerequisites
39
40
 
@@ -85,10 +86,12 @@ export PLAYWRIGHT_MCP_EXTENSION_TOKEN="<your-token-here>"
85
86
 
86
87
  </details>
87
88
 
88
- Verify with `opencli doctor` — shows colored status for all config locations:
89
+ Verify with `opencli doctor` — shows colored status for extension install, token consistency, and all config locations:
89
90
 
90
91
  ```bash
91
- opencli doctor
92
+ opencli doctor # Token & config diagnosis
93
+ opencli doctor --live # Also test live browser connectivity
94
+ opencli doctor --fix -y # Auto-fix all mismatched configs
92
95
  ```
93
96
 
94
97
  ## Quick Start
@@ -130,26 +133,29 @@ npm install -g @jackwener/opencli@latest
130
133
 
131
134
  ## Built-in Commands
132
135
 
133
- | Site | Commands | Mode |
134
- |------|----------|------|
135
- | **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` | 🔐 Browser |
136
- | **zhihu** | `hot` `search` `question` | 🔐 Browser |
137
- | **xiaohongshu** | `search` `notifications` `feed` `user` | 🔐 Browser |
138
- | **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 🔐 Browser |
139
- | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` | 🔐 Browser |
140
- | **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 Browser |
141
- | **weibo** | `hot` | 🔐 Browser |
142
- | **boss** | `search` `detail` | 🔐 Browser |
143
- | **coupang** | `search` `add-to-cart` | 🔐 Browser |
144
- | **youtube** | `search` | 🔐 Browser |
145
- | **yahoo-finance** | `quote` | 🔐 Browser |
146
- | **reuters** | `search` | 🔐 Browser |
147
- | **smzdm** | `search` | 🔐 Browser |
148
- | **ctrip** | `search` | 🔐 Browser |
149
- | **github** | `search` | 🌐 Public |
150
- | **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 🌐 Public / 🔐 Browser |
151
- | **hackernews** | `top` | 🌐 Public |
152
- | **bbc** | `news` | 🌐 Public |
136
+ **19 sites · 80+ commands** run `opencli list` for the live registry.
137
+
138
+ | Site | Commands | Count | Mode |
139
+ |------|----------|:-----:|------|
140
+ | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` | 18 | 🔐 Browser |
141
+ | **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 15 | 🔐 Browser |
142
+ | **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` | 11 | 🔐 Browser |
143
+ | **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 6 | 🌐 / 🔐 |
144
+ | **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 6 | 🔐 Browser |
145
+ | **xiaohongshu** | `search` `notifications` `feed` `me` `user` | 5 | 🔐 Browser |
146
+ | **youtube** | `search` `video` `transcript` | 3 | 🔐 Browser |
147
+ | **zhihu** | `hot` `search` `question` | 3 | 🔐 Browser |
148
+ | **boss** | `search` `detail` | 2 | 🔐 Browser |
149
+ | **coupang** | `search` `add-to-cart` | 2 | 🔐 Browser |
150
+ | **bbc** | `news` | 1 | 🌐 Public |
151
+ | **ctrip** | `search` | 1 | 🔐 Browser |
152
+ | **github** | `search` | 1 | 🌐 Public |
153
+ | **hackernews** | `top` | 1 | 🌐 Public |
154
+ | **linkedin** | `search` | 1 | 🔐 Browser |
155
+ | **reuters** | `search` | 1 | 🔐 Browser |
156
+ | **smzdm** | `search` | 1 | 🔐 Browser |
157
+ | **weibo** | `hot` | 1 | 🔐 Browser |
158
+ | **yahoo-finance** | `quote` | 1 | 🔐 Browser |
153
159
 
154
160
  ## Output Formats
155
161
 
@@ -194,7 +200,7 @@ Explore outputs to `.opencli/explore/<site>/` (manifest.json, endpoints.json, ca
194
200
 
195
201
  See **[TESTING.md](./TESTING.md)** for the full testing guide, including:
196
202
 
197
- - Current test coverage (unit + ~52 E2E tests across all 18 sites)
203
+ - Current test coverage (unit + E2E tests across 19 sites)
198
204
  - How to run tests locally
199
205
  - How to add tests when creating new adapters
200
206
  - CI/CD pipeline with sharding
package/README.zh-CN.md CHANGED
@@ -141,6 +141,7 @@ npm install -g @jackwener/opencli@latest
141
141
  | **boss** | `search` `detail` | 🔐 浏览器 |
142
142
  | **coupang** | `search` `add-to-cart` | 🔐 浏览器 |
143
143
  | **youtube** | `search` | 🔐 浏览器 |
144
+ | **linkedin** | `search` | 🔐 浏览器 |
144
145
  | **yahoo-finance** | `quote` | 🔐 浏览器 |
145
146
  | **reuters** | `search` | 🔐 浏览器 |
146
147
  | **smzdm** | `search` | 🔐 浏览器 |
package/SKILL.md CHANGED
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: opencli
3
- description: "OpenCLI — Make any website your CLI. Zero risk, AI-powered, reuse Chrome login."
4
- version: 0.7.0
3
+ description: "OpenCLI — Make any website your CLI. Zero risk, AI-powered, reuse Chrome login. 80+ commands across 19 sites."
4
+ version: 0.7.3
5
5
  author: jackwener
6
- tags: [cli, browser, web, mcp, playwright, bilibili, zhihu, twitter, github, v2ex, hackernews, reddit, xiaohongshu, xueqiu, AI, agent]
6
+ tags: [cli, browser, web, mcp, playwright, bilibili, zhihu, twitter, github, v2ex, hackernews, reddit, xiaohongshu, xueqiu, youtube, boss, coupang, AI, agent]
7
7
  ---
8
8
 
9
9
  # OpenCLI
@@ -68,6 +68,7 @@ opencli zhihu question --id 34816524 # 问题详情和回答
68
68
  opencli xiaohongshu search --keyword "美食" # 搜索笔记
69
69
  opencli xiaohongshu notifications # 通知(mentions/likes/connections)
70
70
  opencli xiaohongshu feed --limit 10 # 推荐 Feed
71
+ opencli xiaohongshu me # 我的信息
71
72
  opencli xiaohongshu user --uid xxx # 用户主页
72
73
 
73
74
  # 雪球 Xueqiu (browser)
@@ -131,9 +132,13 @@ opencli weibo hot --limit 10 # 微博热搜
131
132
 
132
133
  # BOSS直聘 (browser)
133
134
  opencli boss search --query "AI agent" # 搜索职位
135
+ opencli boss detail --securityId xxx # 职位详情
134
136
 
135
137
  # YouTube (browser)
136
138
  opencli youtube search --query "rust" # 搜索视频
139
+ opencli youtube video --url "https://www.youtube.com/watch?v=xxx" # 视频元数据(标题、播放量、描述等)
140
+ opencli youtube transcript --url "https://www.youtube.com/watch?v=xxx" # 获取视频字幕/转录
141
+ opencli youtube transcript --url "xxx" --lang zh-Hans --mode raw # 指定语言 + 原始时间戳模式
137
142
 
138
143
  # Yahoo Finance (browser)
139
144
  opencli yahoo-finance quote --symbol AAPL # 股票行情
@@ -157,7 +162,8 @@ opencli list -f yaml # YAML output
157
162
  opencli validate # Validate all CLI definitions
158
163
  opencli validate bilibili # Validate specific site
159
164
  opencli setup # Interactive token setup (auto-discover + TUI checkbox)
160
- opencli doctor # Diagnose token config across all tools
165
+ opencli doctor # Diagnose token & extension config across all tools
166
+ opencli doctor --live # Also test live browser connectivity
161
167
  opencli doctor --fix -y # Auto-fix all config files (non-interactive)
162
168
  ```
163
169
 
@@ -680,6 +680,90 @@
680
680
  ],
681
681
  "type": "yaml"
682
682
  },
683
+ {
684
+ "site": "linkedin",
685
+ "name": "search",
686
+ "description": "",
687
+ "strategy": "header",
688
+ "browser": true,
689
+ "args": [
690
+ {
691
+ "name": "query",
692
+ "type": "string",
693
+ "required": true,
694
+ "help": "Job search keywords"
695
+ },
696
+ {
697
+ "name": "location",
698
+ "type": "string",
699
+ "required": false,
700
+ "help": "Location text such as San Francisco Bay Area"
701
+ },
702
+ {
703
+ "name": "limit",
704
+ "type": "int",
705
+ "default": 10,
706
+ "required": false,
707
+ "help": "Number of jobs to return (max 100)"
708
+ },
709
+ {
710
+ "name": "start",
711
+ "type": "int",
712
+ "default": 0,
713
+ "required": false,
714
+ "help": "Result offset for pagination"
715
+ },
716
+ {
717
+ "name": "details",
718
+ "type": "bool",
719
+ "default": false,
720
+ "required": false,
721
+ "help": "Include full job description and apply URL (slower)"
722
+ },
723
+ {
724
+ "name": "company",
725
+ "type": "string",
726
+ "required": false,
727
+ "help": "Comma-separated company names or LinkedIn company IDs"
728
+ },
729
+ {
730
+ "name": "experience_level",
731
+ "type": "string",
732
+ "required": false,
733
+ "help": "Comma-separated: internship, entry, associate, mid-senior, director, executive"
734
+ },
735
+ {
736
+ "name": "job_type",
737
+ "type": "string",
738
+ "required": false,
739
+ "help": "Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other"
740
+ },
741
+ {
742
+ "name": "date_posted",
743
+ "type": "string",
744
+ "required": false,
745
+ "help": "One of: any, month, week, 24h"
746
+ },
747
+ {
748
+ "name": "remote",
749
+ "type": "string",
750
+ "required": false,
751
+ "help": "Comma-separated: on-site, hybrid, remote"
752
+ }
753
+ ],
754
+ "type": "ts",
755
+ "modulePath": "linkedin/search.js",
756
+ "domain": "www.linkedin.com",
757
+ "columns": [
758
+ "rank",
759
+ "title",
760
+ "company",
761
+ "location",
762
+ "listed",
763
+ "salary",
764
+ "url"
765
+ ]
766
+ },
683
767
  {
684
768
  "site": "reddit",
685
769
  "name": "comment",
@@ -858,19 +942,18 @@
858
942
  "site": "reddit",
859
943
  "name": "read",
860
944
  "description": "Read a Reddit post and its comments",
861
- "domain": "reddit.com",
862
945
  "strategy": "cookie",
863
946
  "browser": true,
864
947
  "args": [
865
948
  {
866
949
  "name": "post_id",
867
- "type": "string",
950
+ "type": "str",
868
951
  "required": true,
869
952
  "help": "Post ID (e.g. 1abc123) or full URL"
870
953
  },
871
954
  {
872
955
  "name": "sort",
873
- "type": "string",
956
+ "type": "str",
874
957
  "default": "best",
875
958
  "required": false,
876
959
  "help": "Comment sort: best, top, new, controversial, old, qa"
@@ -880,32 +963,39 @@
880
963
  "type": "int",
881
964
  "default": 25,
882
965
  "required": false,
883
- "help": "Number of top-level comments to fetch"
884
- }
885
- ],
886
- "columns": [
887
- "type",
888
- "author",
889
- "score",
890
- "text"
891
- ],
892
- "pipeline": [
966
+ "help": "Number of top-level comments"
967
+ },
893
968
  {
894
- "navigate": "https://www.reddit.com"
969
+ "name": "depth",
970
+ "type": "int",
971
+ "default": 2,
972
+ "required": false,
973
+ "help": "Max reply depth (1=no replies, 2=one level of replies, etc.)"
895
974
  },
896
975
  {
897
- "evaluate": "(async () => {\n let postId = ${{ args.post_id | json }};\n const urlMatch = postId.match(/comments\\/([a-z0-9]+)/);\n if (urlMatch) postId = urlMatch[1];\n\n const sort = ${{ args.sort | json }};\n const limit = ${{ args.limit }};\n const res = await fetch('/comments/' + postId + '.json?sort=' + sort + '&limit=' + limit + '&raw_json=1', {\n credentials: 'include'\n });\n const data = await res.json();\n if (!Array.isArray(data) || data.length < 1) return [];\n\n const results = [];\n\n // First element: post itself\n const post = data[0]?.data?.children?.[0]?.data;\n if (post) {\n let body = post.selftext || '';\n if (body.length > 2000) body = body.slice(0, 2000) + '\\n... [truncated]';\n results.push({\n type: '📰 POST',\n author: post.author,\n score: post.score,\n text: post.title + (body ? '\\n\\n' + body : '') + (post.url && !post.is_self ? '\\n🔗 ' + post.url : ''),\n });\n }\n\n // Second element: comments\n const comments = data[1]?.data?.children || [];\n for (const c of comments) {\n if (c.kind !== 't1') continue;\n const d = c.data;\n let body = d.body || '';\n if (body.length > 500) body = body.slice(0, 500) + '...';\n results.push({\n type: '💬 COMMENT',\n author: d.author || '[deleted]',\n score: d.score || 0,\n text: body,\n });\n }\n\n return results;\n})()\n"
976
+ "name": "replies",
977
+ "type": "int",
978
+ "default": 5,
979
+ "required": false,
980
+ "help": "Max replies shown per comment at each level (sorted by score)"
898
981
  },
899
982
  {
900
- "map": {
901
- "type": "${{ item.type }}",
902
- "author": "${{ item.author }}",
903
- "score": "${{ item.score }}",
904
- "text": "${{ item.text }}"
905
- }
983
+ "name": "max_length",
984
+ "type": "int",
985
+ "default": 2000,
986
+ "required": false,
987
+ "help": "Max characters per comment body (min 100)"
906
988
  }
907
989
  ],
908
- "type": "yaml"
990
+ "type": "ts",
991
+ "modulePath": "reddit/read.js",
992
+ "domain": "reddit.com",
993
+ "columns": [
994
+ "type",
995
+ "author",
996
+ "score",
997
+ "text"
998
+ ]
909
999
  },
910
1000
  {
911
1001
  "site": "reddit",
@@ -2639,6 +2729,89 @@
2639
2729
  "url"
2640
2730
  ]
2641
2731
  },
2732
+ {
2733
+ "site": "youtube",
2734
+ "name": "transcript-group",
2735
+ "description": "",
2736
+ "strategy": "cookie",
2737
+ "browser": true,
2738
+ "args": [],
2739
+ "type": "ts",
2740
+ "modulePath": "youtube/transcript-group.js"
2741
+ },
2742
+ {
2743
+ "site": "youtube",
2744
+ "name": "transcript-group.test",
2745
+ "description": "",
2746
+ "strategy": "cookie",
2747
+ "browser": true,
2748
+ "args": [],
2749
+ "type": "ts",
2750
+ "modulePath": "youtube/transcript-group.test.js"
2751
+ },
2752
+ {
2753
+ "site": "youtube",
2754
+ "name": "transcript",
2755
+ "description": "Get YouTube video transcript/subtitles",
2756
+ "strategy": "cookie",
2757
+ "browser": true,
2758
+ "args": [
2759
+ {
2760
+ "name": "url",
2761
+ "type": "str",
2762
+ "required": true,
2763
+ "help": "YouTube video URL or video ID"
2764
+ },
2765
+ {
2766
+ "name": "lang",
2767
+ "type": "str",
2768
+ "required": false,
2769
+ "help": "Language code (e.g. en, zh-Hans). Omit to auto-select"
2770
+ },
2771
+ {
2772
+ "name": "mode",
2773
+ "type": "str",
2774
+ "default": "grouped",
2775
+ "required": false,
2776
+ "help": "Output mode: grouped (readable paragraphs) or raw (every segment)"
2777
+ }
2778
+ ],
2779
+ "type": "ts",
2780
+ "modulePath": "youtube/transcript.js",
2781
+ "domain": "www.youtube.com"
2782
+ },
2783
+ {
2784
+ "site": "youtube",
2785
+ "name": "utils",
2786
+ "description": "",
2787
+ "strategy": "cookie",
2788
+ "browser": true,
2789
+ "args": [],
2790
+ "type": "ts",
2791
+ "modulePath": "youtube/utils.js"
2792
+ },
2793
+ {
2794
+ "site": "youtube",
2795
+ "name": "video",
2796
+ "description": "Get YouTube video metadata (title, views, description, etc.)",
2797
+ "strategy": "cookie",
2798
+ "browser": true,
2799
+ "args": [
2800
+ {
2801
+ "name": "url",
2802
+ "type": "str",
2803
+ "required": true,
2804
+ "help": "YouTube video URL or video ID"
2805
+ }
2806
+ ],
2807
+ "type": "ts",
2808
+ "modulePath": "youtube/video.js",
2809
+ "domain": "www.youtube.com",
2810
+ "columns": [
2811
+ "field",
2812
+ "value"
2813
+ ]
2814
+ },
2642
2815
  {
2643
2816
  "site": "zhihu",
2644
2817
  "name": "hot",
@@ -0,0 +1 @@
1
+ export {};