@jackwener/opencli 1.4.1 → 1.5.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/.github/workflows/build-extension.yml +2 -6
- package/.github/workflows/ci.yml +21 -1
- package/README.md +35 -6
- package/README.zh-CN.md +12 -5
- package/SKILL.md +2 -0
- package/dist/browser/cdp.d.ts +2 -1
- package/dist/browser/cdp.js +5 -0
- package/dist/browser/discover.d.ts +4 -1
- package/dist/browser/discover.js +6 -2
- package/dist/browser/errors.d.ts +2 -2
- package/dist/browser/errors.js +4 -12
- package/dist/browser/mcp.d.ts +2 -1
- package/dist/browser/page.d.ts +3 -0
- package/dist/browser/page.js +24 -1
- package/dist/build-manifest.d.ts +2 -0
- package/dist/build-manifest.js +39 -14
- package/dist/build-manifest.test.js +21 -0
- package/dist/capabilityRouting.d.ts +2 -0
- package/dist/capabilityRouting.js +2 -1
- package/dist/cli-manifest.json +1567 -108
- package/dist/cli.js +68 -6
- package/dist/clis/36kr/article.d.ts +1 -0
- package/dist/clis/36kr/article.js +62 -0
- package/dist/clis/36kr/hot.d.ts +3 -0
- package/dist/clis/36kr/hot.js +80 -0
- package/dist/clis/36kr/hot.test.d.ts +1 -0
- package/dist/clis/36kr/hot.test.js +15 -0
- package/dist/clis/36kr/news.d.ts +1 -0
- package/dist/clis/36kr/news.js +51 -0
- package/dist/clis/36kr/news.test.d.ts +1 -0
- package/dist/clis/36kr/news.test.js +85 -0
- package/dist/clis/36kr/search.d.ts +1 -0
- package/dist/clis/36kr/search.js +72 -0
- package/dist/clis/bilibili/comments.d.ts +5 -0
- package/dist/clis/bilibili/comments.js +40 -0
- package/dist/clis/bilibili/comments.test.d.ts +1 -0
- package/dist/clis/bilibili/comments.test.js +82 -0
- package/dist/clis/bluesky/feeds.yaml +29 -0
- package/dist/clis/bluesky/followers.yaml +33 -0
- package/dist/clis/bluesky/following.yaml +33 -0
- package/dist/clis/bluesky/profile.yaml +27 -0
- package/dist/clis/bluesky/search.yaml +34 -0
- package/dist/clis/bluesky/starter-packs.yaml +34 -0
- package/dist/clis/bluesky/thread.yaml +32 -0
- package/dist/clis/bluesky/trending.yaml +27 -0
- package/dist/clis/bluesky/user.yaml +34 -0
- package/dist/clis/chatgpt/ask.js +29 -14
- package/dist/clis/chatgpt/ax.d.ts +6 -0
- package/dist/clis/chatgpt/ax.js +172 -1
- package/dist/clis/chatgpt/model.d.ts +1 -0
- package/dist/clis/chatgpt/model.js +24 -0
- package/dist/clis/chatgpt/send.js +12 -3
- package/dist/clis/douban/download.d.ts +1 -0
- package/dist/clis/douban/download.js +67 -0
- package/dist/clis/douban/download.test.d.ts +1 -0
- package/dist/clis/douban/download.test.js +170 -0
- package/dist/clis/douban/photos.d.ts +1 -0
- package/dist/clis/douban/photos.js +34 -0
- package/dist/clis/douban/utils.d.ts +25 -0
- package/dist/clis/douban/utils.js +190 -1
- package/dist/clis/douban/utils.test.d.ts +1 -0
- package/dist/clis/douban/utils.test.js +64 -0
- package/dist/clis/imdb/person.d.ts +1 -0
- package/dist/clis/imdb/person.js +203 -0
- package/dist/clis/imdb/reviews.d.ts +1 -0
- package/dist/clis/imdb/reviews.js +88 -0
- package/dist/clis/imdb/search.d.ts +1 -0
- package/dist/clis/imdb/search.js +161 -0
- package/dist/clis/imdb/title.d.ts +1 -0
- package/dist/clis/imdb/title.js +93 -0
- package/dist/clis/imdb/top.d.ts +1 -0
- package/dist/clis/imdb/top.js +53 -0
- package/dist/clis/imdb/trending.d.ts +1 -0
- package/dist/clis/imdb/trending.js +52 -0
- package/dist/clis/imdb/utils.d.ts +46 -0
- package/dist/clis/imdb/utils.js +285 -0
- package/dist/clis/imdb/utils.test.d.ts +1 -0
- package/dist/clis/imdb/utils.test.js +88 -0
- package/dist/clis/jd/item.d.ts +4 -0
- package/dist/clis/jd/item.js +16 -15
- package/dist/clis/jd/item.test.js +16 -1
- package/dist/clis/linux-do/categories.yaml +38 -9
- package/dist/clis/linux-do/category.d.ts +1 -0
- package/dist/clis/linux-do/category.js +36 -0
- package/dist/clis/linux-do/feed.d.ts +45 -0
- package/dist/clis/linux-do/feed.js +397 -0
- package/dist/clis/linux-do/feed.test.d.ts +1 -0
- package/dist/clis/linux-do/feed.test.js +118 -0
- package/dist/clis/linux-do/hot.d.ts +1 -0
- package/dist/clis/linux-do/hot.js +25 -0
- package/dist/clis/linux-do/latest.d.ts +1 -0
- package/dist/clis/linux-do/latest.js +18 -0
- package/dist/clis/linux-do/tags.yaml +41 -0
- package/dist/clis/linux-do/topic.yaml +41 -3
- package/dist/clis/linux-do/user-posts.yaml +67 -0
- package/dist/clis/linux-do/user-topics.yaml +54 -0
- package/dist/clis/paperreview/commands.test.d.ts +3 -0
- package/dist/clis/paperreview/commands.test.js +243 -0
- package/dist/clis/paperreview/feedback.d.ts +1 -0
- package/dist/clis/paperreview/feedback.js +52 -0
- package/dist/clis/paperreview/review.d.ts +1 -0
- package/dist/clis/paperreview/review.js +37 -0
- package/dist/clis/paperreview/submit.d.ts +1 -0
- package/dist/clis/paperreview/submit.js +85 -0
- package/dist/clis/paperreview/utils.d.ts +46 -0
- package/dist/clis/paperreview/utils.js +197 -0
- package/dist/clis/paperreview/utils.test.d.ts +1 -0
- package/dist/clis/paperreview/utils.test.js +49 -0
- package/dist/clis/producthunt/browse.d.ts +1 -0
- package/dist/clis/producthunt/browse.js +99 -0
- package/dist/clis/producthunt/hot.d.ts +1 -0
- package/dist/clis/producthunt/hot.js +110 -0
- package/dist/clis/producthunt/posts.d.ts +1 -0
- package/dist/clis/producthunt/posts.js +28 -0
- package/dist/clis/producthunt/today.d.ts +1 -0
- package/dist/clis/producthunt/today.js +35 -0
- package/dist/clis/producthunt/utils.d.ts +29 -0
- package/dist/clis/producthunt/utils.js +99 -0
- package/dist/clis/producthunt/utils.test.d.ts +1 -0
- package/dist/clis/producthunt/utils.test.js +64 -0
- package/dist/clis/twitter/article.js +4 -28
- package/dist/clis/twitter/likes.d.ts +24 -0
- package/dist/clis/twitter/likes.js +217 -0
- package/dist/clis/twitter/likes.test.d.ts +1 -0
- package/dist/clis/twitter/likes.test.js +85 -0
- package/dist/clis/twitter/profile.js +4 -28
- package/dist/clis/twitter/search.js +2 -1
- package/dist/clis/twitter/search.test.js +2 -0
- package/dist/clis/twitter/shared.d.ts +6 -0
- package/dist/clis/twitter/shared.js +35 -0
- package/dist/clis/twitter/timeline.js +2 -13
- package/dist/clis/twitter/trending.js +29 -61
- package/dist/clis/v2ex/hot.yaml +17 -3
- package/dist/clis/weixin/download.d.ts +17 -0
- package/dist/clis/weixin/download.js +88 -20
- package/dist/clis/weread/book.js +2 -2
- package/dist/clis/weread/commands.test.d.ts +3 -0
- package/dist/clis/weread/commands.test.js +43 -0
- package/dist/clis/weread/highlights.js +2 -2
- package/dist/clis/weread/notebooks.js +2 -2
- package/dist/clis/weread/notes.js +3 -3
- package/dist/clis/weread/shelf.js +2 -2
- package/dist/clis/weread/utils.d.ts +4 -4
- package/dist/clis/weread/utils.js +32 -14
- package/dist/clis/weread/utils.test.js +1 -28
- package/dist/clis/xiaohongshu/comments.d.ts +5 -0
- package/dist/clis/xiaohongshu/comments.js +74 -0
- package/dist/clis/xiaohongshu/comments.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/comments.test.js +79 -0
- package/dist/clis/xiaohongshu/publish.js +179 -47
- package/dist/clis/xiaohongshu/publish.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/publish.test.js +131 -0
- package/dist/clis/xiaohongshu/search.d.ts +8 -1
- package/dist/clis/xiaohongshu/search.js +20 -1
- package/dist/clis/xiaohongshu/search.test.d.ts +1 -1
- package/dist/clis/xiaohongshu/search.test.js +32 -1
- package/dist/commanderAdapter.d.ts +1 -0
- package/dist/commanderAdapter.js +176 -29
- package/dist/commanderAdapter.test.d.ts +1 -0
- package/dist/commanderAdapter.test.js +62 -0
- package/dist/daemon.js +17 -1
- package/dist/discovery.js +48 -42
- package/dist/doctor.d.ts +2 -2
- package/dist/doctor.js +11 -4
- package/dist/download/index.js +63 -51
- package/dist/download/index.test.js +17 -4
- package/dist/engine.test.js +42 -0
- package/dist/errors.d.ts +4 -2
- package/dist/errors.js +17 -34
- package/dist/execution.d.ts +1 -3
- package/dist/execution.js +66 -8
- package/dist/execution.test.d.ts +1 -0
- package/dist/execution.test.js +40 -0
- package/dist/external.js +6 -1
- package/dist/hooks.js +2 -0
- package/dist/main.js +6 -0
- package/dist/output.js +5 -1
- package/dist/pipeline/executor.js +3 -4
- package/dist/plugin-manifest.d.ts +70 -0
- package/dist/plugin-manifest.js +160 -0
- package/dist/plugin-manifest.test.d.ts +4 -0
- package/dist/plugin-manifest.test.js +179 -0
- package/dist/plugin-scaffold.d.ts +28 -0
- package/dist/plugin-scaffold.js +142 -0
- package/dist/plugin-scaffold.test.d.ts +4 -0
- package/dist/plugin-scaffold.test.js +83 -0
- package/dist/plugin.d.ts +82 -11
- package/dist/plugin.js +870 -84
- package/dist/plugin.test.js +1032 -17
- package/dist/registry.d.ts +4 -0
- package/dist/registry.js +2 -0
- package/dist/runtime-detect.d.ts +21 -0
- package/dist/runtime-detect.js +32 -0
- package/dist/runtime-detect.test.d.ts +1 -0
- package/dist/runtime-detect.test.js +27 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +2 -2
- package/dist/serialization.d.ts +2 -0
- package/dist/serialization.js +6 -0
- package/dist/types.d.ts +3 -0
- package/dist/update-check.d.ts +22 -0
- package/dist/update-check.js +112 -0
- package/dist/weixin-download.test.d.ts +1 -0
- package/dist/weixin-download.test.js +30 -0
- package/dist/weread-private-api-regression.test.d.ts +1 -0
- package/dist/weread-private-api-regression.test.js +122 -0
- package/dist/yaml-schema.d.ts +3 -0
- package/dist/yaml-schema.js +18 -1
- package/docs/.vitepress/config.mts +4 -0
- package/docs/adapters/browser/36kr.md +47 -0
- package/docs/adapters/browser/bluesky.md +53 -0
- package/docs/adapters/browser/douban.md +14 -0
- package/docs/adapters/browser/imdb.md +47 -0
- package/docs/adapters/browser/jd.md +2 -2
- package/docs/adapters/browser/linux-do.md +181 -20
- package/docs/adapters/browser/paperreview.md +43 -0
- package/docs/adapters/browser/producthunt.md +49 -0
- package/docs/adapters/desktop/chatgpt.md +5 -0
- package/docs/adapters/index.md +6 -2
- package/docs/advanced/download.md +4 -0
- package/docs/advanced/rate-limiter-plugin.md +99 -0
- package/docs/guide/electron-app-cli.md +200 -0
- package/docs/guide/getting-started.md +1 -0
- package/docs/guide/plugins.md +97 -0
- package/docs/zh/guide/electron-app-cli.md +188 -0
- package/docs/zh/guide/getting-started.md +1 -0
- package/docs/zh/guide/plugins.md +65 -0
- package/extension/package.json +1 -0
- package/extension/scripts/package-release.mjs +179 -0
- package/extension/src/background.ts +2 -0
- package/package.json +4 -1
- package/scripts/postinstall.js +10 -0
- package/src/browser/cdp.ts +8 -1
- package/src/browser/discover.ts +8 -3
- package/src/browser/errors.ts +13 -14
- package/src/browser/mcp.ts +2 -1
- package/src/browser/page.ts +24 -1
- package/src/build-manifest.test.ts +23 -0
- package/src/build-manifest.ts +40 -15
- package/src/capabilityRouting.ts +2 -1
- package/src/cli.ts +69 -6
- package/src/clis/36kr/article.ts +69 -0
- package/src/clis/36kr/hot.test.ts +19 -0
- package/src/clis/36kr/hot.ts +100 -0
- package/src/clis/36kr/news.test.ts +90 -0
- package/src/clis/36kr/news.ts +54 -0
- package/src/clis/36kr/search.ts +78 -0
- package/src/clis/bilibili/comments.test.ts +102 -0
- package/src/clis/bilibili/comments.ts +44 -0
- package/src/clis/bluesky/feeds.yaml +29 -0
- package/src/clis/bluesky/followers.yaml +33 -0
- package/src/clis/bluesky/following.yaml +33 -0
- package/src/clis/bluesky/profile.yaml +27 -0
- package/src/clis/bluesky/search.yaml +34 -0
- package/src/clis/bluesky/starter-packs.yaml +34 -0
- package/src/clis/bluesky/thread.yaml +32 -0
- package/src/clis/bluesky/trending.yaml +27 -0
- package/src/clis/bluesky/user.yaml +34 -0
- package/src/clis/chatgpt/ask.ts +28 -14
- package/src/clis/chatgpt/ax.ts +180 -1
- package/src/clis/chatgpt/model.ts +27 -0
- package/src/clis/chatgpt/send.ts +16 -6
- package/src/clis/douban/download.test.ts +196 -0
- package/src/clis/douban/download.ts +78 -0
- package/src/clis/douban/photos.ts +36 -0
- package/src/clis/douban/utils.test.ts +97 -0
- package/src/clis/douban/utils.ts +232 -1
- package/src/clis/imdb/person.ts +232 -0
- package/src/clis/imdb/reviews.ts +111 -0
- package/src/clis/imdb/search.ts +179 -0
- package/src/clis/imdb/title.ts +121 -0
- package/src/clis/imdb/top.ts +67 -0
- package/src/clis/imdb/trending.ts +66 -0
- package/src/clis/imdb/utils.test.ts +117 -0
- package/src/clis/imdb/utils.ts +305 -0
- package/src/clis/jd/item.test.ts +18 -1
- package/src/clis/jd/item.ts +18 -15
- package/src/clis/linux-do/categories.yaml +38 -9
- package/src/clis/linux-do/category.ts +37 -0
- package/src/clis/linux-do/feed.test.ts +132 -0
- package/src/clis/linux-do/feed.ts +501 -0
- package/src/clis/linux-do/hot.ts +26 -0
- package/src/clis/linux-do/latest.ts +19 -0
- package/src/clis/linux-do/tags.yaml +41 -0
- package/src/clis/linux-do/topic.yaml +41 -3
- package/src/clis/linux-do/user-posts.yaml +67 -0
- package/src/clis/linux-do/user-topics.yaml +54 -0
- package/src/clis/paperreview/commands.test.ts +283 -0
- package/src/clis/paperreview/feedback.ts +64 -0
- package/src/clis/paperreview/review.ts +47 -0
- package/src/clis/paperreview/submit.ts +119 -0
- package/src/clis/paperreview/utils.test.ts +68 -0
- package/src/clis/paperreview/utils.ts +276 -0
- package/src/clis/producthunt/browse.ts +109 -0
- package/src/clis/producthunt/hot.ts +127 -0
- package/src/clis/producthunt/posts.ts +29 -0
- package/src/clis/producthunt/today.ts +37 -0
- package/src/clis/producthunt/utils.test.ts +72 -0
- package/src/clis/producthunt/utils.ts +122 -0
- package/src/clis/twitter/article.ts +5 -28
- package/src/clis/twitter/likes.test.ts +91 -0
- package/src/clis/twitter/likes.ts +256 -0
- package/src/clis/twitter/profile.ts +5 -28
- package/src/clis/twitter/search.test.ts +2 -0
- package/src/clis/twitter/search.ts +3 -1
- package/src/clis/twitter/shared.ts +45 -0
- package/src/clis/twitter/timeline.ts +2 -13
- package/src/clis/twitter/trending.ts +29 -77
- package/src/clis/v2ex/hot.yaml +17 -3
- package/src/clis/weixin/download.ts +114 -20
- package/src/clis/weread/book.ts +2 -2
- package/src/clis/weread/commands.test.ts +57 -0
- package/src/clis/weread/highlights.ts +2 -2
- package/src/clis/weread/notebooks.ts +2 -2
- package/src/clis/weread/notes.ts +3 -3
- package/src/clis/weread/shelf.ts +2 -2
- package/src/clis/weread/utils.test.ts +1 -32
- package/src/clis/weread/utils.ts +41 -16
- package/src/clis/xiaohongshu/comments.test.ts +96 -0
- package/src/clis/xiaohongshu/comments.ts +81 -0
- package/src/clis/xiaohongshu/publish.test.ts +151 -0
- package/src/clis/xiaohongshu/publish.ts +206 -54
- package/src/clis/xiaohongshu/search.test.ts +39 -1
- package/src/clis/xiaohongshu/search.ts +19 -1
- package/src/commanderAdapter.test.ts +78 -0
- package/src/commanderAdapter.ts +188 -24
- package/src/daemon.ts +19 -1
- package/src/discovery.ts +49 -48
- package/src/doctor.ts +15 -5
- package/src/download/index.test.ts +14 -4
- package/src/download/index.ts +67 -55
- package/src/engine.test.ts +38 -0
- package/src/errors.ts +26 -63
- package/src/execution.test.ts +47 -0
- package/src/execution.ts +67 -9
- package/src/external.ts +6 -1
- package/src/hooks.ts +1 -0
- package/src/main.ts +7 -0
- package/src/output.ts +3 -1
- package/src/pipeline/executor.ts +4 -6
- package/src/plugin-manifest.test.ts +223 -0
- package/src/plugin-manifest.ts +206 -0
- package/src/plugin-scaffold.test.ts +98 -0
- package/src/plugin-scaffold.ts +170 -0
- package/src/plugin.test.ts +1104 -17
- package/src/plugin.ts +1101 -86
- package/src/registry.ts +6 -1
- package/src/runtime-detect.test.ts +30 -0
- package/src/runtime-detect.ts +36 -0
- package/src/runtime.ts +3 -3
- package/src/serialization.ts +4 -0
- package/src/types.ts +3 -0
- package/src/update-check.ts +114 -0
- package/src/weixin-download.test.ts +64 -0
- package/src/weread-private-api-regression.test.ts +150 -0
- package/src/yaml-schema.ts +20 -0
- package/tests/e2e/browser-auth.test.ts +13 -9
- package/tests/e2e/browser-public-extended.test.ts +1 -1
- package/tests/e2e/browser-public.test.ts +62 -4
- package/tests/e2e/helpers.ts +2 -1
- package/tests/e2e/public-commands.test.ts +37 -3
- package/tests/smoke/api-health.test.ts +1 -1
- package/vitest.config.ts +10 -0
- package/dist/clis/linux-do/category.yaml +0 -51
- package/dist/clis/linux-do/hot.yaml +0 -50
- package/dist/clis/linux-do/latest.yaml +0 -40
- package/src/clis/linux-do/category.yaml +0 -51
- package/src/clis/linux-do/hot.yaml +0 -50
- package/src/clis/linux-do/latest.yaml +0 -40
|
@@ -39,12 +39,8 @@ jobs:
|
|
|
39
39
|
working-directory: extension
|
|
40
40
|
|
|
41
41
|
- name: Prepare extension package
|
|
42
|
-
run:
|
|
43
|
-
|
|
44
|
-
mkdir -p extension-package
|
|
45
|
-
cp extension/manifest.json extension-package/
|
|
46
|
-
cp -R extension/dist extension-package/
|
|
47
|
-
cp -R extension/icons extension-package/
|
|
42
|
+
run: npm run package:release -- --out ../extension-package
|
|
43
|
+
working-directory: extension
|
|
48
44
|
|
|
49
45
|
- name: Create Extension ZIP
|
|
50
46
|
run: |
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -61,6 +61,27 @@ jobs:
|
|
|
61
61
|
- name: Run unit tests (Node ${{ matrix.node-version }}, shard ${{ matrix.shard }}/2)
|
|
62
62
|
run: npm test -- --reporter=verbose --shard=${{ matrix.shard }}/2
|
|
63
63
|
|
|
64
|
+
# ── Bun compatibility check ──
|
|
65
|
+
bun-test:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v6
|
|
69
|
+
|
|
70
|
+
- uses: oven-sh/setup-bun@v2
|
|
71
|
+
with:
|
|
72
|
+
bun-version: 1.3.5
|
|
73
|
+
|
|
74
|
+
- uses: actions/setup-node@v6
|
|
75
|
+
with:
|
|
76
|
+
node-version: '22'
|
|
77
|
+
cache: 'npm'
|
|
78
|
+
|
|
79
|
+
- name: Install dependencies
|
|
80
|
+
run: npm ci
|
|
81
|
+
|
|
82
|
+
- name: Run unit tests under Bun
|
|
83
|
+
run: bun vitest run --project unit --reporter=verbose
|
|
84
|
+
|
|
64
85
|
adapter-test:
|
|
65
86
|
runs-on: ${{ matrix.os }}
|
|
66
87
|
strategy:
|
|
@@ -125,4 +146,3 @@ jobs:
|
|
|
125
146
|
env:
|
|
126
147
|
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
|
127
148
|
timeout-minutes: 15
|
|
128
|
-
|
package/README.md
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
> **Make any website, Electron App, or Local Tool your CLI.**
|
|
4
4
|
> Zero risk · Reuse Chrome login · AI-powered discovery · Universal CLI Hub
|
|
5
5
|
|
|
6
|
-
[中文文档](./README.zh-CN.md)
|
|
7
|
-
|
|
6
|
+
[](./README.zh-CN.md)
|
|
8
7
|
[](https://www.npmjs.com/package/@jackwener/opencli)
|
|
9
8
|
[](https://nodejs.org)
|
|
10
9
|
[](./LICENSE)
|
|
@@ -50,11 +49,31 @@ There are many great browser automation tools. Here's when opencli is the right
|
|
|
50
49
|
|
|
51
50
|
## Prerequisites
|
|
52
51
|
|
|
53
|
-
- **Node.js**: >= 20.0.0
|
|
52
|
+
- **Node.js**: >= 20.0.0 (or **Bun** >= 1.0 — see [Runtime Support](#runtime-support) below)
|
|
54
53
|
- **Chrome** running **and logged into the target site** (e.g. bilibili.com, zhihu.com, xiaohongshu.com).
|
|
55
54
|
|
|
56
55
|
> **⚠️ 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.
|
|
57
56
|
|
|
57
|
+
### Runtime Support
|
|
58
|
+
|
|
59
|
+
OpenCLI works with both **Node.js** (≥ 20) and **Bun** (≥ 1.0). All commands and adapters are runtime-agnostic.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Development with Bun (faster startup)
|
|
63
|
+
npm run dev:bun
|
|
64
|
+
|
|
65
|
+
# Run the built CLI with Bun
|
|
66
|
+
npm run start:bun
|
|
67
|
+
|
|
68
|
+
# Run unit tests under Bun
|
|
69
|
+
npm run test:bun
|
|
70
|
+
|
|
71
|
+
# Run E2E tests with Bun as the runtime
|
|
72
|
+
OPENCLI_TEST_RUNTIME=bun npm run test:e2e
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Use `opencli doctor` to check your current runtime — it displays the active engine (e.g. `node v22.13.0` or `bun 1.1.42`).
|
|
76
|
+
|
|
58
77
|
OpenCLI connects to your browser through a lightweight **Browser Bridge** Chrome Extension + micro-daemon (zero config, auto-start).
|
|
59
78
|
|
|
60
79
|
### Browser Bridge Extension Setup
|
|
@@ -132,7 +151,7 @@ Run `opencli list` for the live registry.
|
|
|
132
151
|
| **v2ex** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | Public / Browser |
|
|
133
152
|
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` `earnings-date` `fund-holdings` `fund-snapshot` | Browser |
|
|
134
153
|
| **antigravity** | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` | Desktop |
|
|
135
|
-
| **chatgpt** | `status` `new` `send` `read` `ask` | Desktop |
|
|
154
|
+
| **chatgpt** | `status` `new` `send` `read` `ask` `model` | Desktop |
|
|
136
155
|
| **xiaohongshu** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | Browser |
|
|
137
156
|
| **apple-podcasts** | `search` `episodes` `top` | Public |
|
|
138
157
|
| **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | Public |
|
|
@@ -147,6 +166,7 @@ Run `opencli list` for the live registry.
|
|
|
147
166
|
| **devto** | `top` `tag` `user` | Public |
|
|
148
167
|
| **dictionary** | `search` `synonyms` `examples` | Public |
|
|
149
168
|
| **arxiv** | `search` `paper` | Public |
|
|
169
|
+
| **paperreview** | `submit` `review` `feedback` | Public |
|
|
150
170
|
| **wikipedia** | `search` `summary` `random` `trending` | Public |
|
|
151
171
|
| **hackernews** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | Public |
|
|
152
172
|
| **jd** | `item` | Browser |
|
|
@@ -164,13 +184,16 @@ Run `opencli list` for the live registry.
|
|
|
164
184
|
| **jike** | `feed` `search` `create` `like` `comment` `repost` `notifications` `post` `topic` `user` | Browser |
|
|
165
185
|
| **jimeng** | `generate` `history` | Browser |
|
|
166
186
|
| **yollomi** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | Browser |
|
|
167
|
-
| **linux-do** | `
|
|
187
|
+
| **linux-do** | `feed` `categories` `tags` `search` `topic` `user-topics` `user-posts` | Browser |
|
|
168
188
|
| **stackoverflow** | `hot` `search` `bounties` `unanswered` | Public |
|
|
169
189
|
| **steam** | `top-sellers` | Public |
|
|
170
190
|
| **weread** | `shelf` `search` `book` `highlights` `notes` `notebooks` `ranking` | Browser |
|
|
171
|
-
| **douban** | `search` `top250` `subject` `marks` `reviews` `movie-hot` `book-hot` | Browser |
|
|
191
|
+
| **douban** | `search` `top250` `subject` `photos` `download` `marks` `reviews` `movie-hot` `book-hot` | Browser |
|
|
172
192
|
| **facebook** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | Browser |
|
|
173
193
|
| **google** | `news` `search` `suggest` `trends` | Public |
|
|
194
|
+
| **36kr** | `news` `hot` `search` `article` | Public / Browser |
|
|
195
|
+
| **imdb** | `search` `title` `top` `trending` `person` `reviews` | Public |
|
|
196
|
+
| **producthunt** | `posts` `today` `hot` `browse` | Public / Browser |
|
|
174
197
|
| **instagram** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | Browser |
|
|
175
198
|
| **lobsters** | `hot` `newest` `active` `tag` | Public |
|
|
176
199
|
| **medium** | `feed` `search` `user` | Browser |
|
|
@@ -206,6 +229,8 @@ opencli register mycli
|
|
|
206
229
|
|
|
207
230
|
Each desktop adapter has its own detailed documentation with commands reference, setup guide, and examples:
|
|
208
231
|
|
|
232
|
+
If you want to add support for a new Electron desktop app, start with [docs/guide/electron-app-cli.md](./docs/guide/electron-app-cli.md) and the deeper [Electron guide](./docs/advanced/electron.md).
|
|
233
|
+
|
|
209
234
|
| App | Description | Doc |
|
|
210
235
|
|-----|-------------|-----|
|
|
211
236
|
| **Cursor** | Control Cursor IDE — Composer, chat, code extraction | [Doc](./docs/adapters/desktop/cursor.md) |
|
|
@@ -228,6 +253,7 @@ OpenCLI supports downloading images, videos, and articles from supported platfor
|
|
|
228
253
|
| **xiaohongshu** | Images, Videos | Downloads all media from a note |
|
|
229
254
|
| **bilibili** | Videos | Requires `yt-dlp` installed |
|
|
230
255
|
| **twitter** | Images, Videos | Downloads from user media tab or single tweet |
|
|
256
|
+
| **douban** | Images | Downloads poster / still image lists from movie subjects |
|
|
231
257
|
| **pixiv** | Images | Downloads original-quality illustrations, supports multi-page works |
|
|
232
258
|
| **zhihu** | Articles (Markdown) | Exports articles with optional image download |
|
|
233
259
|
| **weixin** | Articles (Markdown) | Exports WeChat Official Account articles |
|
|
@@ -259,6 +285,9 @@ opencli twitter download elonmusk --limit 20 --output ./twitter
|
|
|
259
285
|
# Download single tweet media
|
|
260
286
|
opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
|
|
261
287
|
|
|
288
|
+
# Download Douban posters / stills
|
|
289
|
+
opencli douban download 30382501 --output ./douban
|
|
290
|
+
|
|
262
291
|
# Export Zhihu article to Markdown
|
|
263
292
|
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
|
|
264
293
|
|
package/README.zh-CN.md
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
> **把任何网站、本地工具、Electron 应用变成能够让 AI 调用的命令行!**
|
|
4
4
|
> 零风控 · 复用 Chrome 登录 · AI 自动发现接口 · 全能 CLI 枢纽
|
|
5
5
|
|
|
6
|
-
[English](./README.md)
|
|
7
|
-
|
|
6
|
+
[](./README.md)
|
|
8
7
|
[](https://www.npmjs.com/package/@jackwener/opencli)
|
|
9
8
|
[](https://nodejs.org)
|
|
10
9
|
[](./LICENSE)
|
|
@@ -134,7 +133,7 @@ npm install -g @jackwener/opencli@latest
|
|
|
134
133
|
| **v2ex** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | 公开 / 浏览器 |
|
|
135
134
|
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` `earnings-date` `fund-holdings` `fund-snapshot` | 浏览器 |
|
|
136
135
|
| **antigravity** | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` | 桌面端 |
|
|
137
|
-
| **chatgpt** | `status` `new` `send` `read` `ask` | 桌面端 |
|
|
136
|
+
| **chatgpt** | `status` `new` `send` `read` `ask` `model` | 桌面端 |
|
|
138
137
|
| **xiaohongshu** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | 浏览器 |
|
|
139
138
|
| **apple-podcasts** | `search` `episodes` `top` | 公开 |
|
|
140
139
|
| **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 公开 |
|
|
@@ -149,6 +148,7 @@ npm install -g @jackwener/opencli@latest
|
|
|
149
148
|
| **devto** | `top` `tag` `user` | 公开 |
|
|
150
149
|
| **dictionary** | `search` `synonyms` `examples` | 公开 |
|
|
151
150
|
| **arxiv** | `search` `paper` | 公开 |
|
|
151
|
+
| **paperreview** | `submit` `review` `feedback` | 公开 |
|
|
152
152
|
| **wikipedia** | `search` `summary` `random` `trending` | 公开 |
|
|
153
153
|
| **hackernews** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 公共 API |
|
|
154
154
|
| **jd** | `item` | 浏览器 |
|
|
@@ -166,13 +166,16 @@ npm install -g @jackwener/opencli@latest
|
|
|
166
166
|
| **jike** | `feed` `search` `create` `like` `comment` `repost` `notifications` `post` `topic` `user` | 浏览器 |
|
|
167
167
|
| **jimeng** | `generate` `history` | 浏览器 |
|
|
168
168
|
| **yollomi** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | 浏览器 |
|
|
169
|
-
| **linux-do** | `
|
|
169
|
+
| **linux-do** | `feed` `categories` `tags` `search` `topic` `user-topics` `user-posts` | 浏览器 |
|
|
170
170
|
| **stackoverflow** | `hot` `search` `bounties` `unanswered` | 公开 |
|
|
171
171
|
| **steam** | `top-sellers` | 公开 |
|
|
172
172
|
| **weread** | `shelf` `search` `book` `highlights` `notes` `notebooks` `ranking` | 浏览器 |
|
|
173
|
-
| **douban** | `search` `top250` `subject` `marks` `reviews` `movie-hot` `book-hot` | 浏览器 |
|
|
173
|
+
| **douban** | `search` `top250` `subject` `photos` `download` `marks` `reviews` `movie-hot` `book-hot` | 浏览器 |
|
|
174
174
|
| **facebook** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | 浏览器 |
|
|
175
175
|
| **google** | `news` `search` `suggest` `trends` | 公开 |
|
|
176
|
+
| **36kr** | `news` `hot` `search` `article` | 公开 / 浏览器 |
|
|
177
|
+
| **imdb** | `search` `title` `top` `trending` `person` `reviews` | 公开 |
|
|
178
|
+
| **producthunt** | `posts` `today` `hot` `browse` | 公开 / 浏览器 |
|
|
176
179
|
| **instagram** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | 浏览器 |
|
|
177
180
|
| **lobsters** | `hot` `newest` `active` `tag` | 公开 |
|
|
178
181
|
| **medium** | `feed` `search` `user` | 浏览器 |
|
|
@@ -233,6 +236,7 @@ OpenCLI 支持从各平台下载图片、视频和文章。
|
|
|
233
236
|
| **Pixiv** | 图片 | 下载原始画质插画,支持多页作品 |
|
|
234
237
|
| **知乎** | 文章(Markdown) | 导出文章,可选下载图片到本地 |
|
|
235
238
|
| **微信公众号** | 文章(Markdown) | 导出微信公众号文章为 Markdown |
|
|
239
|
+
| **豆瓣** | 图片 | 下载电影条目的海报 / 剧照图片 |
|
|
236
240
|
|
|
237
241
|
### 前置依赖
|
|
238
242
|
|
|
@@ -261,6 +265,9 @@ opencli twitter download elonmusk --limit 20 --output ./twitter
|
|
|
261
265
|
# 下载单条推文的媒体
|
|
262
266
|
opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
|
|
263
267
|
|
|
268
|
+
# 下载豆瓣电影海报 / 剧照
|
|
269
|
+
opencli douban download 30382501 --output ./douban
|
|
270
|
+
|
|
264
271
|
# 导出知乎文章为 Markdown
|
|
265
272
|
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
|
|
266
273
|
|
package/SKILL.md
CHANGED
|
@@ -286,6 +286,8 @@ opencli chaoxing exams # 考试列表
|
|
|
286
286
|
opencli douban search "三体" # 搜索 (query positional)
|
|
287
287
|
opencli douban top250 # 豆瓣 Top 250
|
|
288
288
|
opencli douban subject 1234567 # 条目详情 (id positional)
|
|
289
|
+
opencli douban photos 30382501 # 图片列表 / 直链(默认海报)
|
|
290
|
+
opencli douban download 30382501 # 下载海报 / 剧照
|
|
289
291
|
opencli douban marks --limit 10 # 我的标记
|
|
290
292
|
opencli douban reviews --limit 10 # 短评
|
|
291
293
|
|
package/dist/browser/cdp.d.ts
CHANGED
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
* - Shared DOM helper methods extracted to reduce duplication with Page (P1 #5)
|
|
9
9
|
*/
|
|
10
10
|
import type { IPage } from '../types.js';
|
|
11
|
+
import type { IBrowserFactory } from '../runtime.js';
|
|
11
12
|
export interface CDPTarget {
|
|
12
13
|
type?: string;
|
|
13
14
|
url?: string;
|
|
14
15
|
title?: string;
|
|
15
16
|
webSocketDebuggerUrl?: string;
|
|
16
17
|
}
|
|
17
|
-
export declare class CDPBridge {
|
|
18
|
+
export declare class CDPBridge implements IBrowserFactory {
|
|
18
19
|
private _ws;
|
|
19
20
|
private _idCounter;
|
|
20
21
|
private _pending;
|
package/dist/browser/cdp.js
CHANGED
|
@@ -135,6 +135,7 @@ export class CDPBridge {
|
|
|
135
135
|
class CDPPage {
|
|
136
136
|
bridge;
|
|
137
137
|
_pageEnabled = false;
|
|
138
|
+
_lastUrl = null;
|
|
138
139
|
constructor(bridge) {
|
|
139
140
|
this.bridge = bridge;
|
|
140
141
|
}
|
|
@@ -146,6 +147,7 @@ class CDPPage {
|
|
|
146
147
|
const loadPromise = this.bridge.waitForEvent('Page.loadEventFired', 30_000).catch(() => { });
|
|
147
148
|
await this.bridge.send('Page.navigate', { url });
|
|
148
149
|
await loadPromise;
|
|
150
|
+
this._lastUrl = url;
|
|
149
151
|
if (options?.waitUntil !== 'none') {
|
|
150
152
|
const maxMs = options?.settleMs ?? 1000;
|
|
151
153
|
await this.evaluate(waitForDomStableJs(maxMs, Math.min(500, maxMs)));
|
|
@@ -251,6 +253,9 @@ class CDPPage {
|
|
|
251
253
|
async consoleMessages(_level) {
|
|
252
254
|
return [];
|
|
253
255
|
}
|
|
256
|
+
async getCurrentUrl() {
|
|
257
|
+
return this._lastUrl;
|
|
258
|
+
}
|
|
254
259
|
async installInterceptor(pattern) {
|
|
255
260
|
const { generateInterceptorJs } = await import('../interceptor.js');
|
|
256
261
|
await this.evaluate(generateInterceptorJs(JSON.stringify(pattern), {
|
|
@@ -9,7 +9,10 @@ export { isDaemonRunning };
|
|
|
9
9
|
/**
|
|
10
10
|
* Check daemon status and return connection info.
|
|
11
11
|
*/
|
|
12
|
-
export declare function checkDaemonStatus(
|
|
12
|
+
export declare function checkDaemonStatus(opts?: {
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}): Promise<{
|
|
13
15
|
running: boolean;
|
|
14
16
|
extensionConnected: boolean;
|
|
17
|
+
extensionVersion?: string;
|
|
15
18
|
}>;
|
package/dist/browser/discover.js
CHANGED
|
@@ -10,14 +10,18 @@ export { isDaemonRunning };
|
|
|
10
10
|
/**
|
|
11
11
|
* Check daemon status and return connection info.
|
|
12
12
|
*/
|
|
13
|
-
export async function checkDaemonStatus() {
|
|
13
|
+
export async function checkDaemonStatus(opts) {
|
|
14
14
|
try {
|
|
15
15
|
const port = parseInt(process.env.OPENCLI_DAEMON_PORT ?? String(DEFAULT_DAEMON_PORT), 10);
|
|
16
|
+
const controller = new AbortController();
|
|
17
|
+
const timer = setTimeout(() => controller.abort(), opts?.timeout ?? 2000);
|
|
16
18
|
const res = await fetch(`http://127.0.0.1:${port}/status`, {
|
|
17
19
|
headers: { 'X-OpenCLI': '1' },
|
|
20
|
+
signal: controller.signal,
|
|
18
21
|
});
|
|
19
22
|
const data = await res.json();
|
|
20
|
-
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
return { running: true, extensionConnected: data.extensionConnected, extensionVersion: data.extensionVersion };
|
|
21
25
|
}
|
|
22
26
|
catch {
|
|
23
27
|
return { running: false, extensionConnected: false };
|
package/dist/browser/errors.d.ts
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
* Simplified — no more token/extension/CDP classification.
|
|
5
5
|
* The daemon architecture has a single failure mode: daemon not reachable or extension not connected.
|
|
6
6
|
*/
|
|
7
|
-
import { BrowserConnectError } from '../errors.js';
|
|
8
|
-
export type ConnectFailureKind =
|
|
7
|
+
import { BrowserConnectError, type BrowserConnectKind } from '../errors.js';
|
|
8
|
+
export type ConnectFailureKind = BrowserConnectKind;
|
|
9
9
|
export declare function formatBrowserConnectError(kind: ConnectFailureKind, detail?: string): BrowserConnectError;
|
package/dist/browser/errors.js
CHANGED
|
@@ -9,20 +9,12 @@ import { DEFAULT_DAEMON_PORT } from '../constants.js';
|
|
|
9
9
|
export function formatBrowserConnectError(kind, detail) {
|
|
10
10
|
switch (kind) {
|
|
11
11
|
case 'daemon-not-running':
|
|
12
|
-
return new BrowserConnectError('Cannot connect to opencli daemon.' +
|
|
13
|
-
(detail ? `\n\n${detail}` : ''), 'The daemon should start automatically. If it doesn\'t, try:\n' +
|
|
14
|
-
' node dist/daemon.js\n' +
|
|
15
|
-
`Make sure port ${DEFAULT_DAEMON_PORT} is available.`);
|
|
12
|
+
return new BrowserConnectError('Cannot connect to opencli daemon.' + (detail ? `\n\n${detail}` : ''), `The daemon should auto-start. If it keeps failing, make sure port ${DEFAULT_DAEMON_PORT} is available.`, kind);
|
|
16
13
|
case 'extension-not-connected':
|
|
17
|
-
return new BrowserConnectError('
|
|
18
|
-
(detail ? `\n\n${detail}` : ''), 'Please install the extension:\n' +
|
|
19
|
-
' 1. Download from GitHub Releases\n' +
|
|
20
|
-
' 2. Open chrome://extensions/ → Enable Developer Mode\n' +
|
|
21
|
-
' 3. Click "Load unpacked" → select the extension folder\n' +
|
|
22
|
-
' 4. Make sure Chrome is running');
|
|
14
|
+
return new BrowserConnectError('Browser Bridge extension is not connected.' + (detail ? `\n\n${detail}` : ''), 'Install the extension from GitHub Releases, then reload.', kind);
|
|
23
15
|
case 'command-failed':
|
|
24
|
-
return new BrowserConnectError(`Browser command failed: ${detail ?? 'unknown error'}
|
|
16
|
+
return new BrowserConnectError(`Browser command failed: ${detail ?? 'unknown error'}`, undefined, kind);
|
|
25
17
|
default:
|
|
26
|
-
return new BrowserConnectError(detail ?? 'Failed to connect to browser');
|
|
18
|
+
return new BrowserConnectError(detail ?? 'Failed to connect to browser', undefined, kind);
|
|
27
19
|
}
|
|
28
20
|
}
|
package/dist/browser/mcp.d.ts
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* Browser session manager — auto-spawns daemon and provides IPage.
|
|
3
3
|
*/
|
|
4
4
|
import type { IPage } from '../types.js';
|
|
5
|
+
import type { IBrowserFactory } from '../runtime.js';
|
|
5
6
|
export type BrowserBridgeState = 'idle' | 'connecting' | 'connected' | 'closing' | 'closed';
|
|
6
7
|
/**
|
|
7
8
|
* Browser factory: manages daemon lifecycle and provides IPage instances.
|
|
8
9
|
*/
|
|
9
|
-
export declare class BrowserBridge {
|
|
10
|
+
export declare class BrowserBridge implements IBrowserFactory {
|
|
10
11
|
private _state;
|
|
11
12
|
private _page;
|
|
12
13
|
private _daemonProc;
|
package/dist/browser/page.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export declare class Page implements IPage {
|
|
|
18
18
|
constructor(workspace?: string);
|
|
19
19
|
/** Active tab ID, set after navigate and used in all subsequent commands */
|
|
20
20
|
private _tabId;
|
|
21
|
+
/** Last navigated URL, tracked in-memory to avoid extra round-trips */
|
|
22
|
+
private _lastUrl;
|
|
21
23
|
/** Helper: spread workspace into command params */
|
|
22
24
|
private _wsOpt;
|
|
23
25
|
/** Helper: spread workspace + tabId into command params */
|
|
@@ -26,6 +28,7 @@ export declare class Page implements IPage {
|
|
|
26
28
|
waitUntil?: 'load' | 'none';
|
|
27
29
|
settleMs?: number;
|
|
28
30
|
}): Promise<void>;
|
|
31
|
+
getCurrentUrl(): Promise<string | null>;
|
|
29
32
|
/** Close the automation window in the extension */
|
|
30
33
|
closeWindow(): Promise<void>;
|
|
31
34
|
evaluate(js: string): Promise<unknown>;
|
package/dist/browser/page.js
CHANGED
|
@@ -26,6 +26,8 @@ export class Page {
|
|
|
26
26
|
}
|
|
27
27
|
/** Active tab ID, set after navigate and used in all subsequent commands */
|
|
28
28
|
_tabId;
|
|
29
|
+
/** Last navigated URL, tracked in-memory to avoid extra round-trips */
|
|
30
|
+
_lastUrl = null;
|
|
29
31
|
/** Helper: spread workspace into command params */
|
|
30
32
|
_wsOpt() {
|
|
31
33
|
return { workspace: this.workspace };
|
|
@@ -42,10 +44,11 @@ export class Page {
|
|
|
42
44
|
url,
|
|
43
45
|
...this._cmdOpts(),
|
|
44
46
|
});
|
|
45
|
-
// Remember the tabId for subsequent
|
|
47
|
+
// Remember the tabId and URL for subsequent calls
|
|
46
48
|
if (result?.tabId) {
|
|
47
49
|
this._tabId = result.tabId;
|
|
48
50
|
}
|
|
51
|
+
this._lastUrl = url;
|
|
49
52
|
// Inject stealth anti-detection patches (guard flag prevents double-injection).
|
|
50
53
|
try {
|
|
51
54
|
await sendCommand('exec', {
|
|
@@ -66,6 +69,9 @@ export class Page {
|
|
|
66
69
|
});
|
|
67
70
|
}
|
|
68
71
|
}
|
|
72
|
+
async getCurrentUrl() {
|
|
73
|
+
return this._lastUrl;
|
|
74
|
+
}
|
|
69
75
|
/** Close the automation window in the extension */
|
|
70
76
|
async closeWindow() {
|
|
71
77
|
try {
|
|
@@ -163,6 +169,23 @@ export class Page {
|
|
|
163
169
|
}
|
|
164
170
|
async wait(options) {
|
|
165
171
|
if (typeof options === 'number') {
|
|
172
|
+
if (options >= 1) {
|
|
173
|
+
// For waits >= 1s, use DOM-stable check: return early when the page
|
|
174
|
+
// stops mutating, with the original wait time as the hard cap.
|
|
175
|
+
// This turns e.g. `page.wait(5)` from a fixed 5s sleep into
|
|
176
|
+
// "wait until DOM is stable, max 5s" — often completing in <1s.
|
|
177
|
+
try {
|
|
178
|
+
const maxMs = options * 1000;
|
|
179
|
+
await sendCommand('exec', {
|
|
180
|
+
code: waitForDomStableJs(maxMs, Math.min(500, maxMs)),
|
|
181
|
+
...this._cmdOpts(),
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// Fallback: fixed sleep (e.g. if page has no DOM yet)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
166
189
|
await new Promise(resolve => setTimeout(resolve, options * 1000));
|
|
167
190
|
return;
|
|
168
191
|
}
|
package/dist/build-manifest.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export interface ManifestEntry {
|
|
|
27
27
|
columns?: string[];
|
|
28
28
|
pipeline?: Record<string, unknown>[];
|
|
29
29
|
timeout?: number;
|
|
30
|
+
deprecated?: boolean | string;
|
|
31
|
+
replacedBy?: string;
|
|
30
32
|
/** 'yaml' or 'ts' — determines how executeCommand loads the handler */
|
|
31
33
|
type: 'yaml' | 'ts';
|
|
32
34
|
/** Relative path from clis/ dir, e.g. 'bilibili/hot.yaml' or 'bilibili/search.js' */
|
package/dist/build-manifest.js
CHANGED
|
@@ -16,6 +16,7 @@ import { getErrorMessage } from './errors.js';
|
|
|
16
16
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
const CLIS_DIR = path.resolve(__dirname, 'clis');
|
|
18
18
|
const OUTPUT = path.resolve(__dirname, '..', 'dist', 'cli-manifest.json');
|
|
19
|
+
import { parseYamlArgs } from './yaml-schema.js';
|
|
19
20
|
import { isRecord } from './utils.js';
|
|
20
21
|
function extractBalancedBlock(source, startIndex, openChar, closeChar) {
|
|
21
22
|
let depth = 0;
|
|
@@ -126,20 +127,7 @@ function scanYaml(filePath, site) {
|
|
|
126
127
|
const strategyStr = cliDef.strategy ?? (cliDef.browser === false ? 'public' : 'cookie');
|
|
127
128
|
const strategy = strategyStr.toUpperCase();
|
|
128
129
|
const browser = cliDef.browser ?? (strategy !== 'PUBLIC');
|
|
129
|
-
const args =
|
|
130
|
-
if (cliDef.args && typeof cliDef.args === 'object') {
|
|
131
|
-
for (const [argName, argDef] of Object.entries(cliDef.args)) {
|
|
132
|
-
args.push({
|
|
133
|
-
name: argName,
|
|
134
|
-
type: argDef?.type ?? 'str',
|
|
135
|
-
default: argDef?.default,
|
|
136
|
-
required: argDef?.required ?? false,
|
|
137
|
-
positional: argDef?.positional === true || undefined,
|
|
138
|
-
help: argDef?.description ?? argDef?.help ?? '',
|
|
139
|
-
choices: argDef?.choices,
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
}
|
|
130
|
+
const args = parseYamlArgs(cliDef.args);
|
|
143
131
|
return {
|
|
144
132
|
site: cliDef.site ?? site,
|
|
145
133
|
name: cliDef.name ?? path.basename(filePath, path.extname(filePath)),
|
|
@@ -151,6 +139,8 @@ function scanYaml(filePath, site) {
|
|
|
151
139
|
columns: cliDef.columns,
|
|
152
140
|
pipeline: cliDef.pipeline,
|
|
153
141
|
timeout: cliDef.timeout,
|
|
142
|
+
deprecated: cliDef.deprecated,
|
|
143
|
+
replacedBy: cliDef.replacedBy,
|
|
154
144
|
type: 'yaml',
|
|
155
145
|
navigateBefore: cliDef.navigateBefore,
|
|
156
146
|
};
|
|
@@ -218,6 +208,18 @@ export function scanTs(filePath, site) {
|
|
|
218
208
|
if (navStringMatch)
|
|
219
209
|
entry.navigateBefore = navStringMatch[1];
|
|
220
210
|
}
|
|
211
|
+
const deprecatedBoolMatch = src.match(/deprecated\s*:\s*(true|false)/);
|
|
212
|
+
if (deprecatedBoolMatch) {
|
|
213
|
+
entry.deprecated = deprecatedBoolMatch[1] === 'true';
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
const deprecatedStringMatch = src.match(/deprecated\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
217
|
+
if (deprecatedStringMatch)
|
|
218
|
+
entry.deprecated = deprecatedStringMatch[1];
|
|
219
|
+
}
|
|
220
|
+
const replacedByMatch = src.match(/replacedBy\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
221
|
+
if (replacedByMatch)
|
|
222
|
+
entry.replacedBy = replacedByMatch[1];
|
|
221
223
|
return entry;
|
|
222
224
|
}
|
|
223
225
|
catch (err) {
|
|
@@ -283,6 +285,29 @@ function main() {
|
|
|
283
285
|
const yamlCount = manifest.filter(e => e.type === 'yaml').length;
|
|
284
286
|
const tsCount = manifest.filter(e => e.type === 'ts').length;
|
|
285
287
|
console.log(`✅ Manifest compiled: ${manifest.length} entries (${yamlCount} YAML, ${tsCount} TS) → ${OUTPUT}`);
|
|
288
|
+
// Restore executable permissions on bin entries.
|
|
289
|
+
// tsc does not preserve the +x bit, so after a clean rebuild the CLI
|
|
290
|
+
// entry-point loses its executable permission, causing "Permission denied".
|
|
291
|
+
// See: https://github.com/jackwener/opencli/issues/446
|
|
292
|
+
if (process.platform !== 'win32') {
|
|
293
|
+
const pkgPath = path.resolve(__dirname, '..', 'package.json');
|
|
294
|
+
try {
|
|
295
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
296
|
+
const bins = typeof pkg.bin === 'string'
|
|
297
|
+
? { [pkg.name ?? 'cli']: pkg.bin }
|
|
298
|
+
: pkg.bin ?? {};
|
|
299
|
+
for (const binPath of Object.values(bins)) {
|
|
300
|
+
const abs = path.resolve(__dirname, '..', binPath);
|
|
301
|
+
if (fs.existsSync(abs)) {
|
|
302
|
+
fs.chmodSync(abs, 0o755);
|
|
303
|
+
console.log(`✅ Restored executable permission: ${binPath}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
// Best-effort; never break the build for a permission fix.
|
|
309
|
+
}
|
|
310
|
+
}
|
|
286
311
|
}
|
|
287
312
|
const entrypoint = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : null;
|
|
288
313
|
if (entrypoint === import.meta.url) {
|
|
@@ -125,4 +125,25 @@ describe('manifest helper rules', () => {
|
|
|
125
125
|
modulePath: 'xueqiu/fund-holdings.js',
|
|
126
126
|
});
|
|
127
127
|
});
|
|
128
|
+
it('captures deprecated metadata for TS adapters', () => {
|
|
129
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
|
|
130
|
+
tempDirs.push(dir);
|
|
131
|
+
const file = path.join(dir, 'legacy.ts');
|
|
132
|
+
fs.writeFileSync(file, `
|
|
133
|
+
import { cli } from '../../registry.js';
|
|
134
|
+
cli({
|
|
135
|
+
site: 'demo',
|
|
136
|
+
name: 'legacy',
|
|
137
|
+
description: 'legacy command',
|
|
138
|
+
deprecated: 'legacy is deprecated',
|
|
139
|
+
replacedBy: 'opencli demo new',
|
|
140
|
+
});
|
|
141
|
+
`);
|
|
142
|
+
expect(scanTs(file, 'demo')).toMatchObject({
|
|
143
|
+
site: 'demo',
|
|
144
|
+
name: 'legacy',
|
|
145
|
+
deprecated: 'legacy is deprecated',
|
|
146
|
+
replacedBy: 'opencli demo new',
|
|
147
|
+
});
|
|
148
|
+
});
|
|
128
149
|
});
|