@jackwener/opencli 0.9.6 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +83 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.yml +42 -0
  4. package/.github/ISSUE_TEMPLATE/new_site_adapter.yml +57 -0
  5. package/.github/dependabot.yml +27 -0
  6. package/.github/pull_request_template.md +24 -0
  7. package/.github/workflows/ci.yml +14 -8
  8. package/.github/workflows/e2e-headed.yml +6 -2
  9. package/.github/workflows/pkg-pr-new.yml +2 -2
  10. package/.github/workflows/release-please.yml +25 -0
  11. package/.github/workflows/release.yml +2 -2
  12. package/.github/workflows/security.yml +36 -0
  13. package/CLI-ELECTRON.md +89 -36
  14. package/CONTRIBUTING.md +167 -0
  15. package/README.md +98 -32
  16. package/README.zh-CN.md +99 -33
  17. package/dist/browser/discover.js +22 -7
  18. package/dist/browser.test.js +23 -0
  19. package/dist/build-manifest.d.ts +26 -0
  20. package/dist/build-manifest.js +132 -60
  21. package/dist/build-manifest.test.d.ts +1 -0
  22. package/dist/build-manifest.test.js +26 -0
  23. package/dist/cli-manifest.json +1415 -29
  24. package/dist/clis/bilibili/download.d.ts +10 -0
  25. package/dist/clis/bilibili/download.js +135 -0
  26. package/dist/clis/chatwise/ask.d.ts +1 -0
  27. package/dist/clis/chatwise/ask.js +76 -0
  28. package/dist/clis/chatwise/export.d.ts +1 -0
  29. package/dist/clis/chatwise/export.js +46 -0
  30. package/dist/clis/chatwise/history.d.ts +1 -0
  31. package/dist/clis/chatwise/history.js +43 -0
  32. package/dist/clis/chatwise/model.d.ts +1 -0
  33. package/dist/clis/chatwise/model.js +81 -0
  34. package/dist/clis/chatwise/new.d.ts +1 -0
  35. package/dist/clis/chatwise/new.js +18 -0
  36. package/dist/clis/chatwise/read.d.ts +1 -0
  37. package/dist/clis/chatwise/read.js +39 -0
  38. package/dist/clis/chatwise/screenshot.d.ts +1 -0
  39. package/dist/clis/chatwise/screenshot.js +27 -0
  40. package/dist/clis/chatwise/send.d.ts +1 -0
  41. package/dist/clis/chatwise/send.js +45 -0
  42. package/dist/clis/chatwise/status.d.ts +1 -0
  43. package/dist/clis/chatwise/status.js +22 -0
  44. package/dist/clis/discord-app/channels.d.ts +1 -0
  45. package/dist/clis/discord-app/channels.js +45 -0
  46. package/dist/clis/discord-app/members.d.ts +1 -0
  47. package/dist/clis/discord-app/members.js +38 -0
  48. package/dist/clis/discord-app/read.d.ts +1 -0
  49. package/dist/clis/discord-app/read.js +45 -0
  50. package/dist/clis/discord-app/search.d.ts +1 -0
  51. package/dist/clis/discord-app/search.js +56 -0
  52. package/dist/clis/discord-app/send.d.ts +1 -0
  53. package/dist/clis/discord-app/send.js +27 -0
  54. package/dist/clis/discord-app/servers.d.ts +1 -0
  55. package/dist/clis/discord-app/servers.js +36 -0
  56. package/dist/clis/discord-app/status.d.ts +1 -0
  57. package/dist/clis/discord-app/status.js +16 -0
  58. package/dist/clis/feishu/new.d.ts +1 -0
  59. package/dist/clis/feishu/new.js +27 -0
  60. package/dist/clis/feishu/read.d.ts +1 -0
  61. package/dist/clis/feishu/read.js +40 -0
  62. package/dist/clis/feishu/search.d.ts +1 -0
  63. package/dist/clis/feishu/search.js +30 -0
  64. package/dist/clis/feishu/send.d.ts +1 -0
  65. package/dist/clis/feishu/send.js +39 -0
  66. package/dist/clis/feishu/status.d.ts +1 -0
  67. package/dist/clis/feishu/status.js +28 -0
  68. package/dist/clis/grok/ask.d.ts +1 -0
  69. package/dist/clis/grok/ask.js +82 -0
  70. package/dist/clis/grok/debug.d.ts +1 -0
  71. package/dist/clis/grok/debug.js +45 -0
  72. package/dist/clis/jimeng/generate.yaml +84 -0
  73. package/dist/clis/jimeng/history.yaml +47 -0
  74. package/dist/clis/linux-do/categories.yaml +41 -0
  75. package/dist/clis/linux-do/category.yaml +49 -0
  76. package/dist/clis/linux-do/hot.yaml +50 -0
  77. package/dist/clis/linux-do/latest.yaml +40 -0
  78. package/dist/clis/linux-do/search.yaml +45 -0
  79. package/dist/clis/linux-do/topic.yaml +38 -0
  80. package/dist/clis/notion/export.d.ts +1 -0
  81. package/dist/clis/notion/export.js +31 -0
  82. package/dist/clis/notion/favorites.d.ts +1 -0
  83. package/dist/clis/notion/favorites.js +84 -0
  84. package/dist/clis/notion/new.d.ts +1 -0
  85. package/dist/clis/notion/new.js +34 -0
  86. package/dist/clis/notion/read.d.ts +1 -0
  87. package/dist/clis/notion/read.js +30 -0
  88. package/dist/clis/notion/search.d.ts +1 -0
  89. package/dist/clis/notion/search.js +46 -0
  90. package/dist/clis/notion/sidebar.d.ts +1 -0
  91. package/dist/clis/notion/sidebar.js +41 -0
  92. package/dist/clis/notion/status.d.ts +1 -0
  93. package/dist/clis/notion/status.js +16 -0
  94. package/dist/clis/notion/write.d.ts +1 -0
  95. package/dist/clis/notion/write.js +40 -0
  96. package/dist/clis/twitter/download.d.ts +8 -0
  97. package/dist/clis/twitter/download.js +204 -0
  98. package/dist/clis/wechat/chats.d.ts +1 -0
  99. package/dist/clis/wechat/chats.js +28 -0
  100. package/dist/clis/wechat/contacts.d.ts +1 -0
  101. package/dist/clis/wechat/contacts.js +28 -0
  102. package/dist/clis/wechat/read.d.ts +1 -0
  103. package/dist/clis/wechat/read.js +58 -0
  104. package/dist/clis/wechat/search.d.ts +1 -0
  105. package/dist/clis/wechat/search.js +31 -0
  106. package/dist/clis/wechat/send.d.ts +1 -0
  107. package/dist/clis/wechat/send.js +42 -0
  108. package/dist/clis/wechat/status.d.ts +1 -0
  109. package/dist/clis/wechat/status.js +29 -0
  110. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +10 -0
  111. package/dist/clis/xiaohongshu/creator-note-detail.js +88 -0
  112. package/dist/clis/xiaohongshu/creator-notes.d.ts +11 -0
  113. package/dist/clis/xiaohongshu/creator-notes.js +109 -0
  114. package/dist/clis/xiaohongshu/creator-profile.d.ts +10 -0
  115. package/dist/clis/xiaohongshu/creator-profile.js +54 -0
  116. package/dist/clis/xiaohongshu/creator-stats.d.ts +10 -0
  117. package/dist/clis/xiaohongshu/creator-stats.js +74 -0
  118. package/dist/clis/xiaohongshu/download.d.ts +7 -0
  119. package/dist/clis/xiaohongshu/download.js +155 -0
  120. package/dist/clis/xiaohongshu/search.js +1 -1
  121. package/dist/clis/xiaohongshu/user-helpers.d.ts +15 -0
  122. package/dist/clis/xiaohongshu/user-helpers.js +67 -0
  123. package/dist/clis/xiaohongshu/user-helpers.test.d.ts +1 -0
  124. package/dist/clis/xiaohongshu/user-helpers.test.js +81 -0
  125. package/dist/clis/xiaohongshu/user.js +46 -29
  126. package/dist/clis/zhihu/download.d.ts +11 -0
  127. package/dist/clis/zhihu/download.js +186 -0
  128. package/dist/clis/zhihu/download.test.d.ts +1 -0
  129. package/dist/clis/zhihu/download.test.js +10 -0
  130. package/dist/download/index.d.ts +79 -0
  131. package/dist/download/index.js +325 -0
  132. package/dist/download/progress.d.ts +36 -0
  133. package/dist/download/progress.js +111 -0
  134. package/dist/engine.test.js +15 -0
  135. package/dist/main.js +16 -3
  136. package/dist/pipeline/registry.js +2 -0
  137. package/dist/pipeline/steps/download.d.ts +34 -0
  138. package/dist/pipeline/steps/download.js +251 -0
  139. package/dist/pipeline/template.js +28 -0
  140. package/package.json +4 -3
  141. package/scripts/test-site.mjs +70 -0
  142. package/src/browser/discover.ts +23 -7
  143. package/src/browser.test.ts +23 -0
  144. package/src/build-manifest.test.ts +28 -0
  145. package/src/build-manifest.ts +147 -57
  146. package/src/clis/bilibili/download.ts +161 -0
  147. package/src/clis/chatwise/README.md +38 -0
  148. package/src/clis/chatwise/README.zh-CN.md +38 -0
  149. package/src/clis/chatwise/ask.ts +87 -0
  150. package/src/clis/chatwise/export.ts +51 -0
  151. package/src/clis/chatwise/history.ts +47 -0
  152. package/src/clis/chatwise/model.ts +87 -0
  153. package/src/clis/chatwise/new.ts +21 -0
  154. package/src/clis/chatwise/read.ts +42 -0
  155. package/src/clis/chatwise/screenshot.ts +33 -0
  156. package/src/clis/chatwise/send.ts +50 -0
  157. package/src/clis/chatwise/status.ts +25 -0
  158. package/src/clis/discord-app/README.md +28 -0
  159. package/src/clis/discord-app/README.zh-CN.md +28 -0
  160. package/src/clis/discord-app/channels.ts +48 -0
  161. package/src/clis/discord-app/members.ts +41 -0
  162. package/src/clis/discord-app/read.ts +49 -0
  163. package/src/clis/discord-app/search.ts +64 -0
  164. package/src/clis/discord-app/send.ts +32 -0
  165. package/src/clis/discord-app/servers.ts +39 -0
  166. package/src/clis/discord-app/status.ts +18 -0
  167. package/src/clis/feishu/README.md +20 -0
  168. package/src/clis/feishu/README.zh-CN.md +20 -0
  169. package/src/clis/feishu/new.ts +32 -0
  170. package/src/clis/feishu/read.ts +48 -0
  171. package/src/clis/feishu/search.ts +35 -0
  172. package/src/clis/feishu/send.ts +46 -0
  173. package/src/clis/feishu/status.ts +34 -0
  174. package/src/clis/grok/ask.ts +90 -0
  175. package/src/clis/grok/debug.ts +49 -0
  176. package/src/clis/jimeng/generate.yaml +84 -0
  177. package/src/clis/jimeng/history.yaml +47 -0
  178. package/src/clis/linux-do/categories.yaml +41 -0
  179. package/src/clis/linux-do/category.yaml +49 -0
  180. package/src/clis/linux-do/hot.yaml +50 -0
  181. package/src/clis/linux-do/latest.yaml +40 -0
  182. package/src/clis/linux-do/search.yaml +45 -0
  183. package/src/clis/linux-do/topic.yaml +38 -0
  184. package/src/clis/notion/README.md +29 -0
  185. package/src/clis/notion/README.zh-CN.md +29 -0
  186. package/src/clis/notion/export.ts +36 -0
  187. package/src/clis/notion/favorites.ts +87 -0
  188. package/src/clis/notion/new.ts +39 -0
  189. package/src/clis/notion/read.ts +33 -0
  190. package/src/clis/notion/search.ts +54 -0
  191. package/src/clis/notion/sidebar.ts +44 -0
  192. package/src/clis/notion/status.ts +18 -0
  193. package/src/clis/notion/write.ts +45 -0
  194. package/src/clis/twitter/download.ts +227 -0
  195. package/src/clis/wechat/README.md +28 -0
  196. package/src/clis/wechat/README.zh-CN.md +28 -0
  197. package/src/clis/wechat/chats.ts +33 -0
  198. package/src/clis/wechat/contacts.ts +33 -0
  199. package/src/clis/wechat/read.ts +72 -0
  200. package/src/clis/wechat/search.ts +36 -0
  201. package/src/clis/wechat/send.ts +49 -0
  202. package/src/clis/wechat/status.ts +35 -0
  203. package/src/clis/xiaohongshu/creator-note-detail.ts +95 -0
  204. package/src/clis/xiaohongshu/creator-notes.ts +116 -0
  205. package/src/clis/xiaohongshu/creator-profile.ts +60 -0
  206. package/src/clis/xiaohongshu/creator-stats.ts +81 -0
  207. package/src/clis/xiaohongshu/download.ts +173 -0
  208. package/src/clis/xiaohongshu/search.ts +1 -1
  209. package/src/clis/xiaohongshu/user-helpers.test.ts +106 -0
  210. package/src/clis/xiaohongshu/user-helpers.ts +85 -0
  211. package/src/clis/xiaohongshu/user.ts +52 -32
  212. package/src/clis/zhihu/download.test.ts +12 -0
  213. package/src/clis/zhihu/download.ts +223 -0
  214. package/src/download/index.ts +395 -0
  215. package/src/download/progress.ts +125 -0
  216. package/src/engine.test.ts +17 -0
  217. package/src/main.ts +12 -3
  218. package/src/pipeline/registry.ts +2 -0
  219. package/src/pipeline/steps/download.ts +310 -0
  220. package/src/pipeline/template.ts +26 -0
  221. package/tests/e2e/browser-auth.test.ts +25 -0
@@ -0,0 +1,167 @@
1
+ # Contributing to OpenCLI
2
+
3
+ Thanks for your interest in contributing to OpenCLI.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # 1. Fork & clone
9
+ git clone git@github.com:<your-username>/opencli.git
10
+ cd opencli
11
+
12
+ # 2. Install dependencies
13
+ npm install
14
+
15
+ # 3. Build
16
+ npm run build
17
+
18
+ # 4. Run a few checks
19
+ npx tsc --noEmit
20
+ npx vitest run src/
21
+
22
+ # 5. Link globally (optional, for testing `opencli` command)
23
+ npm link
24
+ ```
25
+
26
+ ## Adding a New Site Adapter
27
+
28
+ This is the most common type of contribution. Start with YAML when possible, and use TypeScript only when you need browser-side logic or multi-step flows.
29
+
30
+ ### YAML Adapter (Recommended for data-fetching commands)
31
+
32
+ Create a file like `src/clis/<site>/<command>.yaml`:
33
+
34
+ ```yaml
35
+ site: mysite
36
+ name: trending
37
+ description: Trending posts on MySite
38
+ domain: www.mysite.com
39
+ strategy: public # public | cookie | header
40
+ browser: false # true if browser session is needed
41
+
42
+ args:
43
+ limit:
44
+ type: int
45
+ default: 20
46
+ description: Number of items
47
+
48
+ pipeline:
49
+ - fetch:
50
+ url: https://api.mysite.com/trending
51
+
52
+ - map:
53
+ rank: ${{ index + 1 }}
54
+ title: ${{ item.title }}
55
+ score: ${{ item.score }}
56
+ url: ${{ item.url }}
57
+
58
+ - limit: ${{ args.limit }}
59
+
60
+ columns: [rank, title, score, url]
61
+ ```
62
+
63
+ See [`hackernews/top.yaml`](src/clis/hackernews/top.yaml) for a real example.
64
+
65
+ ### TypeScript Adapter (For complex browser interactions)
66
+
67
+ Create a file like `src/clis/<site>/<command>.ts`:
68
+
69
+ ```typescript
70
+ import { cli, Strategy } from '../../registry.js';
71
+
72
+ cli({
73
+ site: 'mysite',
74
+ name: 'search',
75
+ description: 'Search MySite',
76
+ domain: 'www.mysite.com',
77
+ strategy: Strategy.COOKIE,
78
+ args: [
79
+ { name: 'query', required: true, help: 'Search query' },
80
+ { name: 'limit', type: 'int', default: 10, help: 'Max results' },
81
+ ],
82
+ columns: ['title', 'url', 'date'],
83
+
84
+ func: async (page, kwargs) => {
85
+ const { query, limit = 10 } = kwargs;
86
+ await page.goto('https://www.mysite.com');
87
+
88
+ const data = await page.evaluate(`
89
+ (async () => {
90
+ const res = await fetch('/api/search?q=${encodeURIComponent(query)}', {
91
+ credentials: 'include'
92
+ });
93
+ return (await res.json()).results;
94
+ })()
95
+ `);
96
+
97
+ return data.slice(0, Number(limit)).map((item: any) => ({
98
+ title: item.title,
99
+ url: item.url,
100
+ date: item.created_at,
101
+ }));
102
+ },
103
+ });
104
+ ```
105
+
106
+ Use `opencli explore <url>` to discover APIs and see [CLI-EXPLORER.md](./CLI-EXPLORER.md) if you need the full adapter workflow.
107
+
108
+ ### Validate Your Adapter
109
+
110
+ ```bash
111
+ # Validate YAML syntax and schema
112
+ opencli validate
113
+
114
+ # Test your command
115
+ opencli <site> <command> --limit 3 -f json
116
+
117
+ # Verbose mode for debugging
118
+ opencli <site> <command> -v
119
+ ```
120
+
121
+ ## Testing
122
+
123
+ See [TESTING.md](./TESTING.md) for the full guide and exact test locations.
124
+
125
+ ```bash
126
+ npx vitest run src/ # Unit tests
127
+ npx vitest run tests/e2e/ # E2E tests
128
+ npx vitest run # All tests
129
+ ```
130
+
131
+ ## Code Style
132
+
133
+ - **TypeScript strict mode** — avoid `any` where possible.
134
+ - **ES Modules** — use `.js` extensions in imports (TypeScript output).
135
+ - **Naming**: `kebab-case` for files, `camelCase` for variables/functions, `PascalCase` for types/classes.
136
+ - **No default exports** — use named exports.
137
+
138
+ ## Commit Convention
139
+
140
+ We use [Conventional Commits](https://www.conventionalcommits.org/):
141
+
142
+ ```
143
+ feat(twitter): add thread command
144
+ fix(browser): handle CDP timeout gracefully
145
+ docs: update CONTRIBUTING.md
146
+ test(reddit): add e2e test for save command
147
+ chore: bump vitest to v4
148
+ ```
149
+
150
+ Common scopes: site name (`twitter`, `reddit`) or module name (`browser`, `pipeline`, `engine`).
151
+
152
+ ## Submitting a Pull Request
153
+
154
+ 1. Create a feature branch: `git checkout -b feat/mysite-trending`
155
+ 2. Make your changes and add tests when relevant
156
+ 3. Run the checks that apply:
157
+ ```bash
158
+ npx tsc --noEmit # Type check
159
+ npx vitest run src/ # Unit tests
160
+ opencli validate # YAML validation (if applicable)
161
+ ```
162
+ 4. Commit using conventional commit format
163
+ 5. Push and open a PR
164
+
165
+ ## License
166
+
167
+ By contributing, you agree that your contributions will be licensed under the [Apache-2.0 License](./LICENSE).
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # OpenCLI
2
2
 
3
3
  > **Make any website or Electron App your CLI.**
4
- > Zero risk · Reuse Chrome login · AI-powered discovery · 80+ commands · 19 sites
4
+ > Zero risk · Reuse Chrome login · AI-powered discovery · Browser + Desktop automation
5
5
 
6
6
  [中文文档](./README.zh-CN.md)
7
7
 
@@ -22,6 +22,7 @@ Turn ANY Electron application into a CLI tool! Recombine, script, and extend app
22
22
  - [Prerequisites](#prerequisites)
23
23
  - [Quick Start](#quick-start)
24
24
  - [Built-in Commands](#built-in-commands)
25
+ - [Download Support](#download-support)
25
26
  - [Output Formats](#output-formats)
26
27
  - [For AI Agents (Developer Guide)](#for-ai-agents-developer-guide)
27
28
  - [Remote Chrome (Server/Headless)](#remote-chrome-serverheadless)
@@ -43,7 +44,7 @@ Turn ANY Electron application into a CLI tool! Recombine, script, and extend app
43
44
 
44
45
  ## Prerequisites
45
46
 
46
- - **Node.js**: >= 18.0.0
47
+ - **Node.js**: >= 20.0.0
47
48
  - **Chrome** running **and logged into the target site** (e.g. bilibili.com, zhihu.com, xiaohongshu.com).
48
49
 
49
50
  > **⚠️ Important**: Browser commands reuse your Chrome login session. You must be logged into the target website in Chrome before running commands. If you get empty data or errors, check your login status first.
@@ -143,34 +144,99 @@ npm install -g @jackwener/opencli@latest
143
144
 
144
145
  ## Built-in Commands
145
146
 
146
- **26 sites · 128 commands** — run `opencli list` for the live registry.
147
-
148
- | Site | Commands | Count | Mode |
149
- |------|----------|:-----:|------|
150
- | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` | 18 | 🔐 Browser |
151
- | **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 15 | 🔐 Browser |
152
- | **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` | 11 | 🔐 Browser |
153
- | **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 12 | 🖥️ Desktop |
154
- | **codex** | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` | 10 | 🖥️ Desktop |
155
- | **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 6 | 🌐 / 🔐 |
156
- | **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 6 | 🔐 Browser |
157
- | **antigravity** | `status` `send` `read` `new` `evaluate` | 5 | 🖥️ Desktop |
158
- | **xiaohongshu** | `search` `notifications` `feed` `me` `user` | 5 | 🔐 Browser |
159
- | **chatgpt** | `status` `new` `send` `read` `ask` | 5 | 🖥️ Desktop |
160
- | **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 3 | 🌐 Public |
161
- | **youtube** | `search` `video` `transcript` | 3 | 🔐 Browser |
162
- | **zhihu** | `hot` `search` `question` | 3 | 🔐 Browser |
163
- | **boss** | `search` `detail` | 2 | 🔐 Browser |
164
- | **coupang** | `search` `add-to-cart` | 2 | 🔐 Browser |
165
- | **bbc** | `news` | 1 | 🌐 Public |
166
- | **ctrip** | `search` | 1 | 🔐 Browser |
167
- | **github** | `search` | 1 | 🌐 Public |
168
- | **hackernews** | `top` | 1 | 🌐 Public |
169
- | **linkedin** | `search` | 1 | 🔐 Browser |
170
- | **reuters** | `search` | 1 | 🔐 Browser |
171
- | **smzdm** | `search` | 1 | 🔐 Browser |
172
- | **weibo** | `hot` | 1 | 🔐 Browser |
173
- | **yahoo-finance** | `quote` | 1 | 🔐 Browser |
147
+ Run `opencli list` for the live registry.
148
+
149
+ | Site | Commands | Mode |
150
+ |------|----------|------|
151
+ | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` | 🔐 Browser |
152
+ | **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 Browser |
153
+ | **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 🖥️ Desktop |
154
+ | **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 🔐 Browser |
155
+ | **codex** | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` | 🖥️ Desktop |
156
+ | **chatwise** | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` | 🖥️ Desktop |
157
+ | **notion** | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` | 🖥️ Desktop |
158
+ | **discord-app** | `status` `send` `read` `channels` `servers` `search` `members` | 🖥️ Desktop |
159
+ | **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 🌐 / 🔐 |
160
+ | **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 🔐 Browser |
161
+ | **antigravity** | `status` `send` `read` `new` `evaluate` | 🖥️ Desktop |
162
+ | **chatgpt** | `status` `new` `send` `read` `ask` | 🖥️ Desktop |
163
+ | **xiaohongshu** | `search` `notifications` `feed` `me` `user` `download` | 🔐 Browser |
164
+ | **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 🌐 Public |
165
+ | **zhihu** | `hot` `search` `question` `download` | 🔐 Browser |
166
+ | **youtube** | `search` `video` `transcript` | 🔐 Browser |
167
+ | **boss** | `search` `detail` | 🔐 Browser |
168
+ | **coupang** | `search` `add-to-cart` | 🔐 Browser |
169
+ | **bbc** | `news` | 🌐 Public |
170
+ | **ctrip** | `search` | 🔐 Browser |
171
+ | **github** | `search` | 🌐 Public |
172
+ | **hackernews** | `top` | 🌐 Public |
173
+ | **linkedin** | `search` | 🔐 Browser |
174
+ | **reuters** | `search` | 🔐 Browser |
175
+ | **smzdm** | `search` | 🔐 Browser |
176
+ | **weibo** | `hot` | 🔐 Browser |
177
+ | **yahoo-finance** | `quote` | 🔐 Browser |
178
+
179
+ ## Download Support
180
+
181
+ OpenCLI supports downloading images, videos, and articles from supported platforms.
182
+
183
+ ### Supported Platforms
184
+
185
+ | Platform | Content Types | Notes |
186
+ |----------|---------------|-------|
187
+ | **xiaohongshu** | Images, Videos | Downloads all media from a note |
188
+ | **bilibili** | Videos | Requires `yt-dlp` installed |
189
+ | **twitter** | Images, Videos | Downloads from user media tab or single tweet |
190
+ | **zhihu** | Articles (Markdown) | Exports articles with optional image download |
191
+
192
+ ### Prerequisites
193
+
194
+ For video downloads from streaming platforms, you need to install `yt-dlp`:
195
+
196
+ ```bash
197
+ # Install yt-dlp
198
+ pip install yt-dlp
199
+ # or
200
+ brew install yt-dlp
201
+ ```
202
+
203
+ ### Usage Examples
204
+
205
+ ```bash
206
+ # Download images/videos from Xiaohongshu note
207
+ opencli xiaohongshu download --note-id abc123 --output ./xhs
208
+
209
+ # Download Bilibili video (requires yt-dlp)
210
+ opencli bilibili download --bvid BV1xxx --output ./bilibili
211
+ opencli bilibili download --bvid BV1xxx --quality 1080p # Specify quality
212
+
213
+ # Download Twitter media from user
214
+ opencli twitter download --username elonmusk --limit 20 --output ./twitter
215
+
216
+ # Download single tweet media
217
+ opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
218
+
219
+ # Export Zhihu article to Markdown
220
+ opencli zhihu download --url "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
221
+
222
+ # Export with local images
223
+ opencli zhihu download --url "https://zhuanlan.zhihu.com/p/xxx" --download-images
224
+ ```
225
+
226
+ ### Pipeline Step (for YAML adapters)
227
+
228
+ The `download` step can be used in YAML pipelines:
229
+
230
+ ```yaml
231
+ pipeline:
232
+ - fetch: https://api.example.com/media
233
+ - download:
234
+ url: ${{ item.imageUrl }}
235
+ dir: ./downloads
236
+ filename: ${{ item.title | sanitize }}.jpg
237
+ concurrency: 5
238
+ skip_existing: true
239
+ ```
174
240
 
175
241
  ## Output Formats
176
242
 
@@ -215,7 +281,7 @@ Explore outputs to `.opencli/explore/<site>/` (manifest.json, endpoints.json, ca
215
281
 
216
282
  See **[TESTING.md](./TESTING.md)** for the full testing guide, including:
217
283
 
218
- - Current test coverage (unit + E2E tests across 19 sites)
284
+ - Current test coverage (unit + E2E tests across browser and desktop adapters)
219
285
  - How to run tests locally
220
286
  - How to add tests when creating new adapters
221
287
  - CI/CD pipeline with sharding
@@ -237,7 +303,7 @@ npx vitest run tests/e2e/ # E2E tests
237
303
  - **Empty data returns or 'Unauthorized' error**
238
304
  - Your login session in Chrome might have expired. Open a normal Chrome tab, navigate to the target site, and log in or refresh the page to prove you are human.
239
305
  - **Node API errors**
240
- - Make sure you are using Node.js >= 18. Some dependencies require modern Node APIs.
306
+ - Make sure you are using Node.js >= 20. Some dependencies require modern Node APIs.
241
307
  - **Token issues**
242
308
  - Run `opencli doctor` to diagnose token configuration across all tools.
243
309
 
package/README.zh-CN.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # OpenCLI
2
2
 
3
3
  > **把任何网站或 Electron 应用变成你的命令行工具。**
4
- > 零风控 · 复用 Chrome 登录 · AI 自动发现接口 · 80+ 命令 · 19 站点
4
+ > 零风控 · 复用 Chrome 登录 · AI 自动发现接口 · 浏览器与桌面端自动化
5
5
 
6
6
  [English](./README.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
- OpenCLI 将任何网站或 Electron 应用(如 Antigravity)变成命令行工具 — B站、知乎、小红书、Twitter/X、Reddit、YouTube 等 [19 个站点](#内置命令) — 复用浏览器登录态,AI 驱动探索。
12
+ OpenCLI 将任何网站或 Electron 应用(如 Antigravity)变成命令行工具 — B站、知乎、小红书、Twitter/X、Reddit、YouTube 等[多种站点与应用](#内置命令) — 复用浏览器登录态,AI 驱动探索。
13
13
 
14
14
  🔥 **opencli 支持 CLI 化所有 electron 应用!最强大更新来袭!** 🔥
15
15
  CLI all electron!现在支持把所有 electron 应用 CLI 化,从而组合出各种神奇的能力。
@@ -24,6 +24,7 @@ CLI all electron!现在支持把所有 electron 应用 CLI 化,从而组合
24
24
  - [前置要求](#前置要求)
25
25
  - [快速开始](#快速开始)
26
26
  - [内置命令](#内置命令)
27
+ - [下载支持](#下载支持)
27
28
  - [输出格式](#输出格式)
28
29
  - [致 AI Agent(开发者指南)](#致-ai-agent开发者指南)
29
30
  - [远程 Chrome(服务器/无头环境)](#远程-chrome服务器无头环境)
@@ -36,7 +37,7 @@ CLI all electron!现在支持把所有 electron 应用 CLI 化,从而组合
36
37
  ## 亮点
37
38
 
38
39
  - **CLI All Electron** — 支持把所有 electron 应用(如 Antigravity Ultra)CLI 化,让 AI 控制自己!
39
- - **多站点覆盖** — B站、知乎、小红书、Twitter、Reddit 等 19 个站点,80+ 命令
40
+ - **多站点覆盖** — 覆盖 B站、知乎、小红书、Twitter、Reddit,以及多种桌面应用
40
41
  - **零风控** — 复用 Chrome 登录态,无需存储任何凭证
41
42
  - **自修复配置** — `opencli setup` 自动发现 Token;`opencli doctor` 诊断 10+ 工具配置;`--fix` 一键修复
42
43
  - **AI 原生** — `explore` 自动发现 API,`synthesize` 生成适配器,`cascade` 探测认证策略
@@ -44,7 +45,7 @@ CLI all electron!现在支持把所有 electron 应用 CLI 化,从而组合
44
45
 
45
46
  ## 前置要求
46
47
 
47
- - **Node.js**: >= 18.0.0
48
+ - **Node.js**: >= 20.0.0
48
49
  - **Chrome** 浏览器正在运行,且**已登录目标网站**(如 bilibili.com、zhihu.com、xiaohongshu.com)
49
50
 
50
51
  > **⚠️ 重要**:大多数命令复用你的 Chrome 登录状态。运行命令前,你必须已在 Chrome 中打开目标网站并完成登录。如果获取到空数据或报错,请先检查你的浏览器登录状态。
@@ -144,34 +145,99 @@ npm install -g @jackwener/opencli@latest
144
145
 
145
146
  ## 内置命令
146
147
 
147
- **26 个站点 · 128 命令** — 运行 `opencli list` 查看完整注册表。
148
-
149
- | 站点 | 命令 | 数量 | 模式 |
150
- |------|------|:----:|------|
151
- | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` | 18 | 🔐 浏览器 |
152
- | **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 15 | 🔐 浏览器 |
153
- | **antigravity** | `status` `send` `read` `new` `evaluate` | 5 | 🖥️ 桌面端 |
154
- | **chatgpt** | `status` `new` `send` `read` `ask` | 5 | 🖥️ 桌面端 |
155
- | **codex** | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` | 10 | 🖥️ 桌面端 |
156
- | **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 12 | 🖥️ 桌面端 |
157
- | **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` | 11 | 🔐 浏览器 |
158
- | **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 6 | 🌐 / 🔐 |
159
- | **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 6 | 🔐 浏览器 |
160
- | **xiaohongshu** | `search` `notifications` `feed` `me` `user` | 5 | 🔐 浏览器 |
161
- | **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 3 | 🌐 公开 |
162
- | **youtube** | `search` `video` `transcript` | 3 | 🔐 浏览器 |
163
- | **zhihu** | `hot` `search` `question` | 3 | 🔐 浏览器 |
164
- | **boss** | `search` `detail` | 2 | 🔐 浏览器 |
165
- | **coupang** | `search` `add-to-cart` | 2 | 🔐 浏览器 |
166
- | **bbc** | `news` | 1 | 🌐 公共 API |
167
- | **ctrip** | `search` | 1 | 🔐 浏览器 |
168
- | **github** | `search` | 1 | 🌐 公共 API |
169
- | **hackernews** | `top` | 1 | 🌐 公共 API |
170
- | **linkedin** | `search` | 1 | 🔐 浏览器 |
171
- | **reuters** | `search` | 1 | 🔐 浏览器 |
172
- | **smzdm** | `search` | 1 | 🔐 浏览器 |
173
- | **weibo** | `hot` | 1 | 🔐 浏览器 |
174
- | **yahoo-finance** | `quote` | 1 | 🔐 浏览器 |
148
+ 运行 `opencli list` 查看完整注册表。
149
+
150
+ | 站点 | 命令 | 模式 |
151
+ |------|------|------|
152
+ | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` | 🔐 浏览器 |
153
+ | **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 浏览器 |
154
+ | **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 🖥️ 桌面端 |
155
+ | **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 🔐 浏览器 |
156
+ | **codex** | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` | 🖥️ 桌面端 |
157
+ | **chatwise** | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` | 🖥️ 桌面端 |
158
+ | **notion** | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` | 🖥️ 桌面端 |
159
+ | **discord-app** | `status` `send` `read` `channels` `servers` `search` `members` | 🖥️ 桌面端 |
160
+ | **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 🌐 / 🔐 |
161
+ | **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 🔐 浏览器 |
162
+ | **antigravity** | `status` `send` `read` `new` `evaluate` | 🖥️ 桌面端 |
163
+ | **chatgpt** | `status` `new` `send` `read` `ask` | 🖥️ 桌面端 |
164
+ | **xiaohongshu** | `search` `notifications` `feed` `me` `user` `download` | 🔐 浏览器 |
165
+ | **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 🌐 公开 |
166
+ | **zhihu** | `hot` `search` `question` `download` | 🔐 浏览器 |
167
+ | **youtube** | `search` `video` `transcript` | 🔐 浏览器 |
168
+ | **boss** | `search` `detail` | 🔐 浏览器 |
169
+ | **coupang** | `search` `add-to-cart` | 🔐 浏览器 |
170
+ | **bbc** | `news` | 🌐 公共 API |
171
+ | **ctrip** | `search` | 🔐 浏览器 |
172
+ | **github** | `search` | 🌐 公共 API |
173
+ | **hackernews** | `top` | 🌐 公共 API |
174
+ | **linkedin** | `search` | 🔐 浏览器 |
175
+ | **reuters** | `search` | 🔐 浏览器 |
176
+ | **smzdm** | `search` | 🔐 浏览器 |
177
+ | **weibo** | `hot` | 🔐 浏览器 |
178
+ | **yahoo-finance** | `quote` | 🔐 浏览器 |
179
+
180
+ ## 下载支持
181
+
182
+ OpenCLI 支持从各平台下载图片、视频和文章。
183
+
184
+ ### 支持的平台
185
+
186
+ | 平台 | 内容类型 | 说明 |
187
+ |------|----------|------|
188
+ | **小红书** | 图片、视频 | 下载笔记中的所有媒体文件 |
189
+ | **B站** | 视频 | 需要安装 `yt-dlp` |
190
+ | **Twitter/X** | 图片、视频 | 从用户媒体页或单条推文下载 |
191
+ | **知乎** | 文章(Markdown) | 导出文章,可选下载图片到本地 |
192
+
193
+ ### 前置依赖
194
+
195
+ 下载流媒体平台的视频需要安装 `yt-dlp`:
196
+
197
+ ```bash
198
+ # 安装 yt-dlp
199
+ pip install yt-dlp
200
+ # 或者
201
+ brew install yt-dlp
202
+ ```
203
+
204
+ ### 使用示例
205
+
206
+ ```bash
207
+ # 下载小红书笔记中的图片/视频
208
+ opencli xiaohongshu download --note-id abc123 --output ./xhs
209
+
210
+ # 下载B站视频(需要 yt-dlp)
211
+ opencli bilibili download --bvid BV1xxx --output ./bilibili
212
+ opencli bilibili download --bvid BV1xxx --quality 1080p # 指定画质
213
+
214
+ # 下载 Twitter 用户的媒体
215
+ opencli twitter download --username elonmusk --limit 20 --output ./twitter
216
+
217
+ # 下载单条推文的媒体
218
+ opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
219
+
220
+ # 导出知乎文章为 Markdown
221
+ opencli zhihu download --url "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
222
+
223
+ # 导出文章并下载图片到本地
224
+ opencli zhihu download --url "https://zhuanlan.zhihu.com/p/xxx" --download-images
225
+ ```
226
+
227
+ ### Pipeline Step(用于 YAML 适配器)
228
+
229
+ `download` step 可以在 YAML 管线中使用:
230
+
231
+ ```yaml
232
+ pipeline:
233
+ - fetch: https://api.example.com/media
234
+ - download:
235
+ url: ${{ item.imageUrl }}
236
+ dir: ./downloads
237
+ filename: ${{ item.title | sanitize }}.jpg
238
+ concurrency: 5
239
+ skip_existing: true
240
+ ```
175
241
 
176
242
  ## 输出格式
177
243
 
@@ -220,7 +286,7 @@ opencli cascade https://api.example.com/data
220
286
  - **返回空数据,或者报错 "Unauthorized"**
221
287
  - Chrome 里的登录态可能已经过期(甚至被要求过滑动验证码)。请打开当前 Chrome 页面,在新标签页重新手工登录或刷新该页面。
222
288
  - **Node API 错误 (如 parseArgs, fs 等)**
223
- - 确保 Node.js 版本 `>= 18`。旧版不支持我们使用的现代核心库 API。
289
+ - 确保 Node.js 版本 `>= 20`。旧版不支持我们使用的现代核心库 API。
224
290
  - **Token 问题**
225
291
  - 运行 `opencli doctor` 诊断所有工具的 Token 配置状态。
226
292
  - 使用 `opencli doctor --live` 测试浏览器连通性。
@@ -9,6 +9,18 @@ import * as path from 'node:path';
9
9
  let _cachedMcpServerPath;
10
10
  let _existsSync = fs.existsSync;
11
11
  let _execSync = execSync;
12
+ function isSupportedMcpEntrypoint(candidate) {
13
+ const normalized = candidate.replace(/\\/g, '/').toLowerCase();
14
+ return normalized.endsWith('/@playwright/mcp/cli.js') ||
15
+ normalized.endsWith('/mcp-server-playwright') ||
16
+ normalized.endsWith('/mcp-server-playwright.js');
17
+ }
18
+ function resolveSupportedMcpPath(candidate) {
19
+ const trimmed = candidate?.trim();
20
+ if (!trimmed || !_existsSync(trimmed))
21
+ return null;
22
+ return isSupportedMcpEntrypoint(trimmed) ? trimmed : null;
23
+ }
12
24
  export function resetMcpServerPathCache() {
13
25
  _cachedMcpServerPath = undefined;
14
26
  }
@@ -67,8 +79,9 @@ export function findMcpServerPath() {
67
79
  // Try npx resolution (legacy package name)
68
80
  try {
69
81
  const result = _execSync('npx -y --package=@playwright/mcp which mcp-server-playwright 2>/dev/null', { encoding: 'utf-8', timeout: 10000 }).trim();
70
- if (result && _existsSync(result)) {
71
- _cachedMcpServerPath = result;
82
+ const resolved = resolveSupportedMcpPath(result);
83
+ if (resolved) {
84
+ _cachedMcpServerPath = resolved;
72
85
  return _cachedMcpServerPath;
73
86
  }
74
87
  }
@@ -76,8 +89,9 @@ export function findMcpServerPath() {
76
89
  // Try which
77
90
  try {
78
91
  const result = _execSync('which mcp-server-playwright 2>/dev/null', { encoding: 'utf-8', timeout: 5000 }).trim();
79
- if (result && _existsSync(result)) {
80
- _cachedMcpServerPath = result;
92
+ const resolved = resolveSupportedMcpPath(result);
93
+ if (resolved) {
94
+ _cachedMcpServerPath = resolved;
81
95
  return _cachedMcpServerPath;
82
96
  }
83
97
  }
@@ -87,9 +101,10 @@ export function findMcpServerPath() {
87
101
  if (!_existsSync(base))
88
102
  continue;
89
103
  try {
90
- const found = _execSync(`find "${base}" -name "cli.js" -path "*playwright*mcp*" 2>/dev/null | head -1`, { encoding: 'utf-8', timeout: 5000 }).trim();
91
- if (found) {
92
- _cachedMcpServerPath = found;
104
+ const found = _execSync(`find "${base}" -type f -path "*/@playwright/mcp/cli.js" 2>/dev/null | head -1`, { encoding: 'utf-8', timeout: 5000 }).trim();
105
+ const resolved = resolveSupportedMcpPath(found);
106
+ if (resolved) {
107
+ _cachedMcpServerPath = resolved;
93
108
  return _cachedMcpServerPath;
94
109
  }
95
110
  }
@@ -1,4 +1,6 @@
1
1
  import { afterEach, describe, it, expect, vi } from 'vitest';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
2
4
  import { PlaywrightMCP, __test__ } from './browser/index.js';
3
5
  afterEach(() => {
4
6
  __test__.resetMcpServerPathCache();
@@ -228,6 +230,27 @@ describe('browser helpers', () => {
228
230
  });
229
231
  }
230
232
  });
233
+ it('ignores non-server playwright cli paths discovered from fallback scans', () => {
234
+ const wrongCli = '/root/.npm/_npx/e41f203b7505f1fb/node_modules/playwright/lib/mcp/terminal/cli.js';
235
+ const npxCacheBase = path.join(os.homedir(), '.npm', '_npx');
236
+ const existsSync = vi.fn((candidate) => {
237
+ const value = String(candidate);
238
+ return value === npxCacheBase || value === wrongCli;
239
+ });
240
+ const execSync = vi.fn((command) => {
241
+ if (String(command).includes('npm root -g'))
242
+ return '/missing/global/node_modules\n';
243
+ if (String(command).includes('--package=@playwright/mcp which mcp-server-playwright'))
244
+ return `${wrongCli}\n`;
245
+ if (String(command).includes('which mcp-server-playwright'))
246
+ return '';
247
+ if (String(command).includes(`find "${npxCacheBase}"`))
248
+ return `${wrongCli}\n`;
249
+ throw new Error(`unexpected command: ${String(command)}`);
250
+ });
251
+ __test__.setMcpDiscoveryTestHooks({ existsSync, execSync: execSync });
252
+ expect(__test__.findMcpServerPath()).toBeNull();
253
+ });
231
254
  });
232
255
  describe('PlaywrightMCP state', () => {
233
256
  it('transitions to closed after close()', async () => {
@@ -8,4 +8,30 @@
8
8
  * Usage: npx tsx src/build-manifest.ts
9
9
  * Output: dist/cli-manifest.json
10
10
  */
11
+ interface ManifestEntry {
12
+ site: string;
13
+ name: string;
14
+ description: string;
15
+ domain?: string;
16
+ strategy: string;
17
+ browser: boolean;
18
+ args: Array<{
19
+ name: string;
20
+ type?: string;
21
+ default?: any;
22
+ required?: boolean;
23
+ positional?: boolean;
24
+ help?: string;
25
+ choices?: string[];
26
+ }>;
27
+ columns?: string[];
28
+ pipeline?: any[];
29
+ timeout?: number;
30
+ /** 'yaml' or 'ts' — determines how executeCommand loads the handler */
31
+ type: 'yaml' | 'ts';
32
+ /** Relative path from clis/ dir, e.g. 'bilibili/hot.yaml' or 'bilibili/search.js' */
33
+ modulePath?: string;
34
+ }
35
+ export declare function parseTsArgsBlock(argsBlock: string): ManifestEntry['args'];
36
+ export declare function buildManifest(): ManifestEntry[];
11
37
  export {};