@jackwener/opencli 1.7.12 → 1.7.14
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 +8 -7
- package/README.zh-CN.md +9 -8
- package/cli-manifest.json +12128 -6665
- package/clis/1point3acres/digest.js +35 -0
- package/clis/1point3acres/forum.js +51 -0
- package/clis/1point3acres/forums.js +44 -0
- package/clis/1point3acres/hot.js +35 -0
- package/clis/1point3acres/latest.js +35 -0
- package/clis/1point3acres/notifications.js +64 -0
- package/clis/1point3acres/search.js +71 -0
- package/clis/1point3acres/thread.js +117 -0
- package/clis/1point3acres/user.js +77 -0
- package/clis/1point3acres/utils.js +247 -0
- package/clis/_shared/desktop-commands.js +4 -0
- package/clis/aibase/news.js +110 -0
- package/clis/aibase/news.test.js +59 -0
- package/clis/amazon/discussion.test.js +1 -28
- package/clis/antigravity/watch.js +3 -2
- package/clis/arxiv/author.js +44 -0
- package/clis/baidu-scholar/search.js +0 -1
- package/clis/bbc/topic.js +57 -0
- package/clis/bbc/utils.js +79 -0
- package/clis/chaoxing/assignments.js +1 -1
- package/clis/chaoxing/exams.js +1 -1
- package/clis/chatgpt/ask.js +57 -0
- package/clis/chatgpt/commands.test.js +45 -0
- package/clis/chatgpt/detail.js +46 -0
- package/clis/chatgpt/history.js +39 -0
- package/clis/chatgpt/image.js +12 -11
- package/clis/chatgpt/image.test.js +23 -0
- package/clis/chatgpt/new.js +25 -0
- package/clis/chatgpt/read.js +43 -0
- package/clis/chatgpt/send.js +46 -0
- package/clis/chatgpt/status.js +29 -0
- package/clis/chatgpt/utils.js +294 -4
- package/clis/chatgpt/utils.test.js +13 -0
- package/clis/chatgpt-app/ask.js +6 -3
- package/clis/chatwise/ask.js +16 -43
- package/clis/chatwise/composer.test.js +186 -0
- package/clis/chatwise/send.js +2 -24
- package/clis/chatwise/utils.js +143 -0
- package/clis/claude/ask.js +1 -1
- package/clis/claude/detail.js +1 -0
- package/clis/claude/history.js +1 -0
- package/clis/claude/new.js +1 -0
- package/clis/claude/read.js +1 -0
- package/clis/claude/send.js +1 -0
- package/clis/claude/status.js +1 -0
- package/clis/codex/ask.js +15 -9
- package/clis/codex/history.js +16 -33
- package/clis/codex/projects.js +28 -0
- package/clis/codex/read.js +10 -4
- package/clis/codex/send.js +10 -3
- package/clis/codex/sidebar.js +356 -0
- package/clis/codex/sidebar.test.js +329 -0
- package/clis/coingecko/categories.js +75 -0
- package/clis/coingecko/coin.js +107 -0
- package/clis/coingecko/coingecko.test.js +109 -0
- package/clis/coingecko/derivatives.js +84 -0
- package/clis/coingecko/exchanges.js +74 -0
- package/clis/coingecko/global.js +71 -0
- package/clis/coingecko/top.js +64 -0
- package/clis/coingecko/trending.js +55 -0
- package/clis/coupang/add-to-cart.js +21 -13
- package/clis/coupang/coupang.test.js +159 -0
- package/clis/coupang/product.js +257 -0
- package/clis/coupang/search.js +38 -16
- package/clis/coupang/utils.js +55 -1
- package/clis/crates/crate.js +62 -0
- package/clis/crates/search.js +44 -0
- package/clis/crates/utils.js +72 -0
- package/clis/ctrip/ctrip.test.js +234 -0
- package/clis/ctrip/hotel-suggest.js +45 -0
- package/clis/ctrip/search.js +22 -68
- package/clis/ctrip/utils.js +175 -0
- package/clis/cursor/ask.js +6 -3
- package/clis/dblp/author.js +133 -0
- package/clis/dblp/venue.js +64 -0
- package/clis/deepseek/ask.js +12 -7
- package/clis/deepseek/ask.test.js +13 -13
- package/clis/deepseek/detail.js +38 -0
- package/clis/deepseek/detail.test.js +81 -0
- package/clis/deepseek/history.js +1 -0
- package/clis/deepseek/new.js +1 -0
- package/clis/deepseek/read.js +1 -0
- package/clis/deepseek/send.js +140 -0
- package/clis/deepseek/send.test.js +107 -0
- package/clis/deepseek/status.js +1 -0
- package/clis/deepseek/utils.js +66 -0
- package/clis/deepseek/utils.test.js +107 -1
- package/clis/defillama/defillama.test.js +99 -0
- package/clis/defillama/protocol.js +84 -0
- package/clis/defillama/protocols.js +55 -0
- package/clis/defillama/utils.js +99 -0
- package/clis/devto/latest.js +74 -0
- package/clis/dockerhub/image.js +52 -0
- package/clis/dockerhub/search.js +47 -0
- package/clis/dockerhub/utils.js +100 -0
- package/clis/doubao/ask.js +7 -3
- package/clis/doubao/detail.js +1 -0
- package/clis/doubao/history.js +1 -0
- package/clis/doubao/meeting-summary.js +1 -0
- package/clis/doubao/meeting-transcript.js +1 -0
- package/clis/doubao/new.js +1 -0
- package/clis/doubao/read.js +1 -0
- package/clis/doubao/send.js +1 -0
- package/clis/doubao/status.js +1 -0
- package/clis/douyin/draft.test.js +1 -30
- package/clis/endoflife/endoflife.test.js +51 -0
- package/clis/endoflife/product.js +55 -0
- package/clis/endoflife/utils.js +89 -0
- package/clis/facebook/__fixtures__/notifications-page.html +13 -0
- package/clis/facebook/notifications.js +326 -30
- package/clis/facebook/notifications.test.js +458 -0
- package/clis/flathub/app.js +71 -0
- package/clis/flathub/flathub.test.js +90 -0
- package/clis/flathub/search.js +80 -0
- package/clis/flathub/utils.js +114 -0
- package/clis/gemini/ask.js +7 -3
- package/clis/gemini/ask.test.js +2 -2
- package/clis/gemini/deep-research-result.js +6 -2
- package/clis/gemini/deep-research-result.test.js +15 -14
- package/clis/gemini/deep-research.js +8 -4
- package/clis/gemini/deep-research.test.js +15 -18
- package/clis/gemini/image.js +7 -2
- package/clis/gemini/new.js +1 -0
- package/clis/gemini/utils.js +0 -4
- package/clis/google-scholar/cite.js +0 -1
- package/clis/google-scholar/profile.js +0 -1
- package/clis/google-scholar/search.js +0 -1
- package/clis/goproxy/goproxy.test.js +103 -0
- package/clis/goproxy/module.js +47 -0
- package/clis/goproxy/utils.js +165 -0
- package/clis/goproxy/versions.js +59 -0
- package/clis/gov-law/recent.js +0 -1
- package/clis/gov-law/search.js +0 -1
- package/clis/gov-policy/__fixtures__/recent.html +16 -0
- package/clis/gov-policy/__fixtures__/search.html +41 -0
- package/clis/gov-policy/gov-policy.test.js +224 -0
- package/clis/gov-policy/recent.js +66 -24
- package/clis/gov-policy/search.js +65 -23
- package/clis/gov-policy/utils.js +54 -0
- package/clis/grok/ask.js +49 -265
- package/clis/grok/ask.test.js +21 -46
- package/clis/grok/detail.js +60 -0
- package/clis/grok/history.js +48 -0
- package/clis/grok/{image.ts → image.js} +56 -70
- package/clis/grok/image.test.ts +20 -0
- package/clis/grok/new.js +20 -0
- package/clis/grok/read.js +39 -0
- package/clis/grok/send.js +50 -0
- package/clis/grok/status.js +41 -0
- package/clis/grok/utils.js +326 -0
- package/clis/grok/utils.test.js +103 -0
- package/clis/hf/datasets.js +88 -0
- package/clis/hf/hf.test.js +16 -0
- package/clis/hf/models.js +91 -0
- package/clis/hf/paper.js +79 -0
- package/clis/hf/spaces.js +101 -0
- package/clis/hf/top.js +1 -0
- package/clis/homebrew/cask.js +39 -0
- package/clis/homebrew/formula.js +41 -0
- package/clis/homebrew/popular.js +54 -0
- package/clis/homebrew/utils.js +100 -0
- package/clis/hupu/__fixtures__/hot-home.html +64 -0
- package/clis/hupu/detail.js +0 -1
- package/clis/hupu/hot.js +156 -35
- package/clis/hupu/hot.test.js +224 -0
- package/clis/hupu/search.js +0 -1
- package/clis/instagram/note.js +1 -1
- package/clis/instagram/note.test.js +1 -29
- package/clis/instagram/post.js +1 -1
- package/clis/instagram/post.test.js +1 -1
- package/clis/instagram/reel.js +1 -1
- package/clis/instagram/story.js +1 -1
- package/clis/instagram/story.test.js +1 -34
- package/clis/jd/commands.test.js +1 -24
- package/clis/lichess/lichess.test.js +85 -0
- package/clis/lichess/top.js +46 -0
- package/clis/lichess/user.js +91 -0
- package/clis/lichess/utils.js +97 -0
- package/clis/linkedin/search.js +107 -10
- package/clis/linkedin/search.test.js +222 -0
- package/clis/linux-do/feed.js +2 -5
- package/clis/linux-do/feed.test.js +35 -0
- package/clis/lobsters/domain.js +92 -0
- package/clis/maven/artifact.js +49 -0
- package/clis/maven/search.js +51 -0
- package/clis/maven/utils.js +110 -0
- package/clis/mdn/search.js +97 -0
- package/clis/medium/tag.js +135 -0
- package/clis/npm/downloads.js +59 -0
- package/clis/npm/package.js +70 -0
- package/clis/npm/search.js +49 -0
- package/clis/npm/utils.js +76 -0
- package/clis/nuget/nuget.test.js +111 -0
- package/clis/nuget/package.js +101 -0
- package/clis/nuget/search.js +69 -0
- package/clis/nuget/utils.js +87 -0
- package/clis/nvd/cve.js +121 -0
- package/clis/oeis/oeis.test.js +88 -0
- package/clis/oeis/search.js +63 -0
- package/clis/oeis/sequence.js +71 -0
- package/clis/oeis/utils.js +88 -0
- package/clis/openalex/search.js +69 -0
- package/clis/openalex/utils.js +160 -0
- package/clis/openalex/work.js +65 -0
- package/clis/openfda/drug-label.js +74 -0
- package/clis/openfda/food-recall.js +65 -0
- package/clis/openfda/openfda.test.js +114 -0
- package/clis/openfda/utils.js +67 -0
- package/clis/osv/osv.test.js +97 -0
- package/clis/osv/query.js +72 -0
- package/clis/osv/utils.js +169 -0
- package/clis/osv/vulnerability.js +54 -0
- package/clis/packagist/package.js +49 -0
- package/clis/packagist/search.js +43 -0
- package/clis/packagist/utils.js +113 -0
- package/clis/paperreview/feedback.js +1 -1
- package/clis/paperreview/review.js +1 -1
- package/clis/paperreview/submit.js +1 -1
- package/clis/pixiv/download.test.js +1 -1
- package/clis/pixiv/illusts.test.js +1 -1
- package/clis/pixiv/search.test.js +1 -1
- package/clis/pubmed/article.js +50 -0
- package/clis/pubmed/author.js +64 -0
- package/clis/pubmed/citations.js +36 -0
- package/clis/pubmed/pubmed.test.js +276 -0
- package/clis/pubmed/related.js +45 -0
- package/clis/pubmed/search.js +75 -0
- package/clis/pubmed/utils.js +309 -0
- package/clis/pypi/downloads.js +66 -0
- package/clis/pypi/package.js +79 -0
- package/clis/pypi/utils.js +55 -0
- package/clis/quark/mv.js +1 -1
- package/clis/quark/save.js +1 -1
- package/clis/qwen/ask.js +85 -0
- package/clis/qwen/detail.js +62 -0
- package/clis/qwen/history.js +61 -0
- package/clis/qwen/image.js +179 -0
- package/clis/qwen/new.js +23 -0
- package/clis/qwen/read.js +41 -0
- package/clis/qwen/send.js +55 -0
- package/clis/qwen/status.js +37 -0
- package/clis/qwen/utils.js +409 -0
- package/clis/qwen/utils.test.js +45 -0
- package/clis/rest-countries/country.js +65 -0
- package/clis/rest-countries/region.js +64 -0
- package/clis/rest-countries/rest-countries.test.js +83 -0
- package/clis/rest-countries/utils.js +126 -0
- package/clis/reuters/article-detail.js +53 -0
- package/clis/reuters/reuters.test.js +299 -0
- package/clis/reuters/search.js +45 -34
- package/clis/reuters/utils.js +159 -0
- package/clis/rfc/rfc.js +52 -0
- package/clis/rfc/rfc.test.js +74 -0
- package/clis/rfc/utils.js +72 -0
- package/clis/rubygems/gem.js +42 -0
- package/clis/rubygems/search.js +47 -0
- package/clis/rubygems/utils.js +86 -0
- package/clis/stackoverflow/related.js +66 -0
- package/clis/stackoverflow/stackoverflow.test.js +58 -0
- package/clis/stackoverflow/tag.js +60 -0
- package/clis/stackoverflow/user.js +50 -0
- package/clis/stackoverflow/utils.js +118 -0
- package/clis/steam/app.js +67 -0
- package/clis/steam/search.js +58 -0
- package/clis/steam/steam.test.js +46 -0
- package/clis/steam/utils.js +107 -0
- package/clis/taobao/commands.test.js +1 -24
- package/clis/test-utils.js +61 -0
- package/clis/tieba/hot.js +0 -1
- package/clis/tiktok/comment.js +128 -41
- package/clis/tiktok/creator-videos.js +270 -0
- package/clis/tiktok/creator-videos.test.js +113 -0
- package/clis/tiktok/explore.js +137 -29
- package/clis/tiktok/follow.js +115 -33
- package/clis/tiktok/following.js +157 -36
- package/clis/tiktok/friends.js +139 -37
- package/clis/tiktok/live.js +137 -41
- package/clis/tiktok/notifications.js +141 -38
- package/clis/tiktok/refactor.test.js +389 -0
- package/clis/tiktok/unfollow.js +124 -38
- package/clis/tiktok/user.js +203 -29
- package/clis/tiktok/utils.js +505 -0
- package/clis/tiktok/write-refactor.test.js +370 -0
- package/clis/toutiao/articles.js +36 -62
- package/clis/toutiao/hot.js +63 -0
- package/clis/toutiao/toutiao.test.js +378 -0
- package/clis/toutiao/utils.js +161 -0
- package/clis/tvmaze/search.js +61 -0
- package/clis/tvmaze/show.js +60 -0
- package/clis/tvmaze/tvmaze.test.js +93 -0
- package/clis/tvmaze/utils.js +110 -0
- package/clis/twitter/accept.js +1 -1
- package/clis/twitter/followers.js +134 -69
- package/clis/twitter/quote.js +139 -0
- package/clis/twitter/quote.test.js +106 -0
- package/clis/twitter/reply-dm.js +1 -1
- package/clis/twitter/reply.test.js +1 -29
- package/clis/twitter/retweet.js +99 -0
- package/clis/twitter/retweet.test.js +69 -0
- package/clis/twitter/shared.js +38 -0
- package/clis/twitter/shared.test.js +28 -1
- package/clis/twitter/unlike.js +87 -0
- package/clis/twitter/unlike.test.js +72 -0
- package/clis/twitter/unretweet.js +99 -0
- package/clis/twitter/unretweet.test.js +69 -0
- package/clis/uisdc/news.js +105 -0
- package/clis/uisdc/news.test.js +66 -0
- package/clis/wanfang/search.js +0 -1
- package/clis/web/read.js +47 -17
- package/clis/web/read.test.js +101 -1
- package/clis/weixin/create-draft.js +1 -1
- package/clis/weixin/drafts.js +1 -1
- package/clis/weixin/drafts.test.js +5 -1
- package/clis/weixin/search.js +157 -0
- package/clis/weixin/search.test.js +227 -0
- package/clis/wikidata/entity.js +60 -0
- package/clis/wikidata/search.js +50 -0
- package/clis/wikidata/utils.js +117 -0
- package/clis/wikidata/wikidata.test.js +83 -0
- package/clis/wikipedia/page.js +95 -0
- package/clis/wttr/current.js +63 -0
- package/clis/wttr/forecast.js +71 -0
- package/clis/wttr/utils.js +50 -0
- package/clis/wttr/wttr.test.js +84 -0
- package/clis/xianyu/chat.js +16 -4
- package/clis/xianyu/chat.test.js +64 -0
- package/clis/xianyu/publish.js +485 -0
- package/clis/xianyu/publish.test.js +220 -0
- package/clis/xiaoe/catalog.js +105 -40
- package/clis/xiaoe/content.js +164 -29
- package/clis/xiaoe/courses.js +86 -29
- package/clis/xiaoe/xiaoe.test.js +486 -0
- package/clis/xiaohongshu/creator-notes-summary.js +1 -1
- package/clis/xiaohongshu/publish.js +16 -3
- package/clis/xiaohongshu/publish.test.js +46 -1
- package/clis/youtube/transcript.js +13 -19
- package/clis/youtube/transcript.test.js +17 -0
- package/clis/yuanbao/ask.js +17 -66
- package/clis/yuanbao/ask.test.js +5 -5
- package/clis/yuanbao/detail.js +65 -0
- package/clis/yuanbao/history.js +51 -0
- package/clis/yuanbao/new.js +1 -0
- package/clis/yuanbao/read.js +38 -0
- package/clis/yuanbao/send.js +57 -0
- package/clis/yuanbao/shared.js +297 -5
- package/clis/yuanbao/shared.test.js +80 -0
- package/clis/yuanbao/status.js +44 -0
- package/clis/zlibrary/commands.test.js +1 -11
- package/dist/src/browser/base-page.d.ts +9 -0
- package/dist/src/browser/base-page.js +44 -1
- package/dist/src/browser/base-page.test.js +66 -0
- package/dist/src/browser/bridge.js +47 -45
- package/dist/src/browser/cdp.d.ts +1 -0
- package/dist/src/browser/cdp.js +51 -9
- package/dist/src/browser/daemon-client.d.ts +4 -0
- package/dist/src/browser/errors.js +1 -1
- package/dist/src/browser/page.d.ts +1 -1
- package/dist/src/browser/page.js +3 -1
- package/dist/src/browser/page.test.js +29 -0
- package/dist/src/browser/target-errors.d.ts +2 -1
- package/dist/src/browser/target-errors.js +1 -0
- package/dist/src/browser/target-resolver.d.ts +25 -0
- package/dist/src/browser/target-resolver.js +43 -0
- package/dist/src/browser.test.js +18 -0
- package/dist/src/build-manifest.js +9 -4
- package/dist/src/build-manifest.test.js +2 -8
- package/dist/src/capabilityRouting.d.ts +16 -1
- package/dist/src/capabilityRouting.js +24 -1
- package/dist/src/capabilityRouting.test.js +19 -1
- package/dist/src/cli.js +76 -11
- package/dist/src/cli.test.js +241 -1
- package/dist/src/commanderAdapter.js +23 -9
- package/dist/src/commanderAdapter.test.js +0 -1
- package/dist/src/discovery.js +2 -5
- package/dist/src/errors.js +1 -1
- package/dist/src/execution.d.ts +1 -1
- package/dist/src/execution.js +111 -27
- package/dist/src/execution.test.js +326 -17
- package/dist/src/help.d.ts +27 -2
- package/dist/src/help.js +196 -23
- package/dist/src/help.test.d.ts +1 -0
- package/dist/src/help.test.js +54 -0
- package/dist/src/main.js +14 -1
- package/dist/src/manifest-types.d.ts +5 -3
- package/dist/src/pipeline/executor.js +1 -1
- package/dist/src/pipeline/executor.test.js +8 -0
- package/dist/src/pipeline/registry.d.ts +9 -0
- package/dist/src/pipeline/registry.js +13 -1
- package/dist/src/pipeline/steps/browser.d.ts +1 -0
- package/dist/src/pipeline/steps/browser.js +10 -0
- package/dist/src/pipeline/steps/download.test.js +1 -0
- package/dist/src/registry-api.d.ts +1 -1
- package/dist/src/registry.d.ts +12 -11
- package/dist/src/registry.js +16 -6
- package/dist/src/registry.test.js +2 -2
- package/dist/src/runtime.d.ts +2 -1
- package/dist/src/runtime.js +1 -1
- package/dist/src/serialization.d.ts +2 -2
- package/dist/src/serialization.js +4 -6
- package/dist/src/serialization.test.js +17 -0
- package/dist/src/types.d.ts +17 -0
- package/dist/src/validate.js +15 -11
- package/dist/src/validate.test.d.ts +9 -0
- package/dist/src/validate.test.js +90 -0
- package/package.json +1 -1
- package/scripts/fetch-adapters.js +1 -1
- package/scripts/typed-error-lint-baseline.json +5 -77
- package/clis/ctrip/search.test.js +0 -64
- package/clis/gov-policy/commands.test.js +0 -27
- package/clis/linux-do/category.js +0 -37
- package/clis/linux-do/hot.js +0 -26
- package/clis/linux-do/latest.js +0 -19
- package/clis/pixiv/test-utils.js +0 -23
- package/clis/toutiao/articles.test.js +0 -30
- package/dist/src/analysis.d.ts +0 -40
- package/dist/src/analysis.js +0 -172
package/dist/src/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import * as fs from 'node:fs';
|
|
|
8
8
|
import * as os from 'node:os';
|
|
9
9
|
import * as path from 'node:path';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import { Command } from 'commander';
|
|
11
|
+
import { Command, InvalidArgumentError } from 'commander';
|
|
12
12
|
import { styleText } from 'node:util';
|
|
13
13
|
import { findPackageRoot, getBuiltEntryCandidates } from './package-paths.js';
|
|
14
14
|
import { fullName, getRegistry, strategyLabel } from './registry.js';
|
|
@@ -18,7 +18,7 @@ import { PKG_VERSION } from './version.js';
|
|
|
18
18
|
import { printCompletionScript } from './completion.js';
|
|
19
19
|
import { loadExternalClis, executeExternalCli, installExternalCli, registerExternalCli, isBinaryInstalled } from './external.js';
|
|
20
20
|
import { registerAllCommands } from './commanderAdapter.js';
|
|
21
|
-
import { formatRootAdapterHelpText, installStructuredHelp, rootHelpData } from './help.js';
|
|
21
|
+
import { classifyAdapter, formatRootAdapterHelpText, installStructuredHelp, rootHelpData } from './help.js';
|
|
22
22
|
import { EXIT_CODES, getErrorMessage, BrowserConnectError } from './errors.js';
|
|
23
23
|
import { TargetError } from './browser/target-errors.js';
|
|
24
24
|
import { resolveTargetJs, getTextResolvedJs, getValueResolvedJs, getAttributesResolvedJs, selectResolvedJs, isAutocompleteResolvedJs } from './browser/target-resolver.js';
|
|
@@ -316,7 +316,9 @@ async function resolveStoredBrowserTarget(page, scope = DEFAULT_BROWSER_WORKSPAC
|
|
|
316
316
|
async function getBrowserPage(targetPage, workspace = DEFAULT_BROWSER_WORKSPACE, contextId) {
|
|
317
317
|
const { BrowserBridge } = await import('./browser/index.js');
|
|
318
318
|
const bridge = new BrowserBridge();
|
|
319
|
-
|
|
319
|
+
// Idle timeout: how long the browser workspace lease stays alive between commands
|
|
320
|
+
// (controls when the automation tab is released). Not the per-command runtime timeout.
|
|
321
|
+
const envTimeout = process.env.OPENCLI_BROWSER_IDLE_TIMEOUT;
|
|
320
322
|
const idleTimeout = envTimeout ? parseInt(envTimeout, 10) : undefined;
|
|
321
323
|
const page = await bridge.connect({
|
|
322
324
|
timeout: 30,
|
|
@@ -389,6 +391,16 @@ function parsePositiveIntOption(val, label, fallback) {
|
|
|
389
391
|
}
|
|
390
392
|
return parsed;
|
|
391
393
|
}
|
|
394
|
+
function parseScreenshotDim(val, label) {
|
|
395
|
+
if (!/^\d+$/.test(val)) {
|
|
396
|
+
throw new InvalidArgumentError(`--${label} must be a positive integer (got "${val}")`);
|
|
397
|
+
}
|
|
398
|
+
const parsed = parseInt(val, 10);
|
|
399
|
+
if (parsed <= 0) {
|
|
400
|
+
throw new InvalidArgumentError(`--${label} must be a positive integer (got "${val}")`);
|
|
401
|
+
}
|
|
402
|
+
return parsed;
|
|
403
|
+
}
|
|
392
404
|
function applyVerbose(opts) {
|
|
393
405
|
if (opts.verbose)
|
|
394
406
|
process.env.OPENCLI_VERBOSE = '1';
|
|
@@ -888,14 +900,22 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
888
900
|
console.log(JSON.stringify(frames, null, 2));
|
|
889
901
|
}));
|
|
890
902
|
addBrowserTabOption(browser.command('screenshot').argument('[path]', 'Save to file (base64 if omitted)'))
|
|
903
|
+
.option('--full-page', 'Capture the full scrollable page, not just the viewport', false)
|
|
904
|
+
.option('--width <n>', 'Override viewport width in CSS pixels for this screenshot only', (v) => parseScreenshotDim(v, 'width'))
|
|
905
|
+
.option('--height <n>', 'Override viewport height in CSS pixels for this screenshot only (ignored with --full-page)', (v) => parseScreenshotDim(v, 'height'))
|
|
891
906
|
.description('Take screenshot')
|
|
892
|
-
.action(browserAction(async (page, path) => {
|
|
907
|
+
.action(browserAction(async (page, path, opts) => {
|
|
908
|
+
const shotOpts = {
|
|
909
|
+
fullPage: opts.fullPage === true,
|
|
910
|
+
width: opts.width,
|
|
911
|
+
height: opts.height,
|
|
912
|
+
};
|
|
893
913
|
if (path) {
|
|
894
|
-
await page.screenshot({ path });
|
|
914
|
+
await page.screenshot({ ...shotOpts, path });
|
|
895
915
|
console.log(`Screenshot saved to: ${path}`);
|
|
896
916
|
}
|
|
897
917
|
else {
|
|
898
|
-
console.log(await page.screenshot({ format: 'png' }));
|
|
918
|
+
console.log(await page.screenshot({ ...shotOpts, format: 'png' }));
|
|
899
919
|
}
|
|
900
920
|
}));
|
|
901
921
|
addBrowserTabOption(browser.command('console'))
|
|
@@ -1313,6 +1333,33 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
1313
1333
|
autocomplete: !!isAutocomplete,
|
|
1314
1334
|
}, null, 2));
|
|
1315
1335
|
}));
|
|
1336
|
+
addBrowserTabOption(browser.command('fill')
|
|
1337
|
+
.argument('<target>', 'Numeric ref (from browser state / find) or CSS selector')
|
|
1338
|
+
.argument('<text>', 'Text to set exactly')
|
|
1339
|
+
.option('--nth <n>', 'When <target> is a multi-match CSS selector, pick the nth match (0-based)')
|
|
1340
|
+
.description('Set input/textarea/contenteditable text exactly and verify the value — JSON envelope {filled, verified, text, actual}'))
|
|
1341
|
+
.action(browserAction(async (page, target, text, opts) => {
|
|
1342
|
+
const parsed = nthToResolveOpts(opts?.nth);
|
|
1343
|
+
if ('error' in parsed) {
|
|
1344
|
+
console.log(JSON.stringify({ error: { code: 'usage_error', message: parsed.error } }, null, 2));
|
|
1345
|
+
process.exitCode = EXIT_CODES.USAGE_ERROR;
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
const result = await page.fillText(String(target), String(text), parsed.opts);
|
|
1349
|
+
if (!result.verified)
|
|
1350
|
+
process.exitCode = EXIT_CODES.GENERIC_ERROR;
|
|
1351
|
+
console.log(JSON.stringify({
|
|
1352
|
+
filled: result.filled,
|
|
1353
|
+
verified: result.verified,
|
|
1354
|
+
target: String(target),
|
|
1355
|
+
text: String(text),
|
|
1356
|
+
actual: result.actual,
|
|
1357
|
+
length: result.length,
|
|
1358
|
+
matches_n: result.matches_n,
|
|
1359
|
+
match_level: result.match_level,
|
|
1360
|
+
...(result.mode ? { mode: result.mode } : {}),
|
|
1361
|
+
}, null, 2));
|
|
1362
|
+
}));
|
|
1316
1363
|
addBrowserTabOption(browser.command('select')
|
|
1317
1364
|
.argument('<target>', 'Numeric ref (from browser state / find) or CSS selector of a <select> element')
|
|
1318
1365
|
.argument('<option>', 'Option text (or value) to select')
|
|
@@ -2036,10 +2083,10 @@ cli({
|
|
|
2036
2083
|
}
|
|
2037
2084
|
});
|
|
2038
2085
|
// ── Session ──
|
|
2039
|
-
browser.command('close').description('
|
|
2086
|
+
browser.command('close').description('Release the current automation tab lease')
|
|
2040
2087
|
.action(browserAction(async (page) => {
|
|
2041
2088
|
await page.closeWindow?.();
|
|
2042
|
-
console.log('Automation
|
|
2089
|
+
console.log('Automation tab lease released');
|
|
2043
2090
|
}));
|
|
2044
2091
|
// ── Built-in: doctor / completion ──────────────────────────────────────────
|
|
2045
2092
|
program
|
|
@@ -2539,11 +2586,29 @@ cli({
|
|
|
2539
2586
|
siteGroups.set('antigravity', antigravityCmd);
|
|
2540
2587
|
const siteNames = registerAllCommands(program, siteGroups);
|
|
2541
2588
|
applyRootSubcommandSummaries(program);
|
|
2542
|
-
|
|
2589
|
+
// ── Help-text grouping: External CLIs / App adapters / Site adapters ──
|
|
2590
|
+
// Classification derives from each adapter's `domain` field — see classifyAdapter.
|
|
2591
|
+
// External CLIs are taken from the externalClis registry (passthrough binaries).
|
|
2592
|
+
const externalNames = externalClis.map(ext => ext.name);
|
|
2593
|
+
const siteDomains = new Map();
|
|
2594
|
+
for (const [, cmd] of getRegistry()) {
|
|
2595
|
+
if (!siteDomains.has(cmd.site))
|
|
2596
|
+
siteDomains.set(cmd.site, cmd.domain);
|
|
2597
|
+
}
|
|
2598
|
+
const apps = [];
|
|
2599
|
+
const sites = [];
|
|
2600
|
+
for (const site of siteNames) {
|
|
2601
|
+
if (classifyAdapter(siteDomains.get(site)) === 'app')
|
|
2602
|
+
apps.push(site);
|
|
2603
|
+
else
|
|
2604
|
+
sites.push(site);
|
|
2605
|
+
}
|
|
2606
|
+
const adapterGroups = { external: externalNames, apps, sites };
|
|
2607
|
+
const adapterNameSet = new Set([...externalNames, ...siteNames]);
|
|
2543
2608
|
program.configureHelp({
|
|
2544
|
-
visibleCommands: (command) => command.commands.filter(child => command !== program || !
|
|
2609
|
+
visibleCommands: (command) => command.commands.filter(child => command !== program || !adapterNameSet.has(child.name())),
|
|
2545
2610
|
});
|
|
2546
|
-
installStructuredHelp(program, () => rootHelpData(program,
|
|
2611
|
+
installStructuredHelp(program, () => rootHelpData(program, adapterGroups), () => formatRootAdapterHelpText(adapterGroups));
|
|
2547
2612
|
// ── Unknown command fallback ──────────────────────────────────────────────
|
|
2548
2613
|
// Security: do NOT auto-discover and register arbitrary system binaries.
|
|
2549
2614
|
// Only explicitly registered external CLIs are allowed.
|
package/dist/src/cli.test.js
CHANGED
|
@@ -96,6 +96,90 @@ describe('createProgram root help descriptions', () => {
|
|
|
96
96
|
registry.set(key, value);
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
|
+
it('groups adapters into App / Site buckets by domain field', () => {
|
|
100
|
+
const registry = getRegistry();
|
|
101
|
+
const snapshot = new Map(registry);
|
|
102
|
+
registry.clear();
|
|
103
|
+
try {
|
|
104
|
+
cli({
|
|
105
|
+
site: 'bilibili',
|
|
106
|
+
name: 'hot',
|
|
107
|
+
access: 'read',
|
|
108
|
+
description: 'Bilibili hot videos',
|
|
109
|
+
domain: 'www.bilibili.com',
|
|
110
|
+
strategy: Strategy.PUBLIC,
|
|
111
|
+
browser: false,
|
|
112
|
+
});
|
|
113
|
+
cli({
|
|
114
|
+
site: 'chatwise',
|
|
115
|
+
name: 'ask',
|
|
116
|
+
access: 'write',
|
|
117
|
+
description: 'Ask Chatwise desktop app',
|
|
118
|
+
domain: 'localhost',
|
|
119
|
+
strategy: Strategy.UI,
|
|
120
|
+
browser: true,
|
|
121
|
+
});
|
|
122
|
+
const program = createProgram('', '');
|
|
123
|
+
const help = program.helpInformation();
|
|
124
|
+
// Two separate sections, each with own count
|
|
125
|
+
expect(help).toContain('App adapters (1):');
|
|
126
|
+
expect(help).toMatch(/App adapters \(1\):\n {2}chatwise/);
|
|
127
|
+
expect(help).toContain('Site adapters (1):');
|
|
128
|
+
expect(help).toMatch(/Site adapters \(1\):\n {2}bilibili/);
|
|
129
|
+
// App adapters appear before Site adapters (External CLIs are absent here)
|
|
130
|
+
expect(help.indexOf('App adapters')).toBeLessThan(help.indexOf('Site adapters'));
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
registry.clear();
|
|
134
|
+
for (const [key, value] of snapshot)
|
|
135
|
+
registry.set(key, value);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
it('exposes external_clis / app_adapters / site_adapters in structured help', () => {
|
|
139
|
+
const registry = getRegistry();
|
|
140
|
+
const snapshot = new Map(registry);
|
|
141
|
+
const argv = process.argv;
|
|
142
|
+
registry.clear();
|
|
143
|
+
try {
|
|
144
|
+
cli({
|
|
145
|
+
site: 'bilibili',
|
|
146
|
+
name: 'hot',
|
|
147
|
+
access: 'read',
|
|
148
|
+
description: 'Bilibili hot videos',
|
|
149
|
+
domain: 'www.bilibili.com',
|
|
150
|
+
strategy: Strategy.PUBLIC,
|
|
151
|
+
browser: false,
|
|
152
|
+
});
|
|
153
|
+
cli({
|
|
154
|
+
site: 'chatwise',
|
|
155
|
+
name: 'ask',
|
|
156
|
+
access: 'write',
|
|
157
|
+
description: 'Ask Chatwise desktop app',
|
|
158
|
+
domain: 'localhost',
|
|
159
|
+
strategy: Strategy.UI,
|
|
160
|
+
browser: true,
|
|
161
|
+
});
|
|
162
|
+
const program = createProgram('', '');
|
|
163
|
+
process.argv = ['node', 'opencli', '--help', '-f', 'yaml'];
|
|
164
|
+
const data = yaml.load(program.helpInformation());
|
|
165
|
+
expect(data.app_adapters.count).toBe(1);
|
|
166
|
+
expect(data.app_adapters.apps).toEqual(['chatwise']);
|
|
167
|
+
expect(data.site_adapters.count).toBe(1);
|
|
168
|
+
expect(data.site_adapters.sites).toEqual(['bilibili']);
|
|
169
|
+
expect(data.external_clis.count).toBeGreaterThanOrEqual(0);
|
|
170
|
+
expect(Array.isArray(data.external_clis.clis)).toBe(true);
|
|
171
|
+
// Adapters must NOT leak into the core commands list
|
|
172
|
+
const commandNames = data.commands.map((cmd) => cmd.name);
|
|
173
|
+
expect(commandNames).not.toContain('bilibili');
|
|
174
|
+
expect(commandNames).not.toContain('chatwise');
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
process.argv = argv;
|
|
178
|
+
registry.clear();
|
|
179
|
+
for (const [key, value] of snapshot)
|
|
180
|
+
registry.set(key, value);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
99
183
|
it('renders root structured help with built-ins and site adapter names', () => {
|
|
100
184
|
const registry = getRegistry();
|
|
101
185
|
const snapshot = new Map(registry);
|
|
@@ -139,6 +223,7 @@ describe('createProgram root help descriptions', () => {
|
|
|
139
223
|
strategy: Strategy.PUBLIC,
|
|
140
224
|
browser: false,
|
|
141
225
|
args: [{ name: 'limit', type: 'int', default: 20, help: 'Number of videos' }],
|
|
226
|
+
columns: ['title', 'url'],
|
|
142
227
|
});
|
|
143
228
|
const program = createProgram('', '');
|
|
144
229
|
const site = program.commands.find(cmd => cmd.name() === 'bilibili');
|
|
@@ -151,10 +236,99 @@ describe('createProgram root help descriptions', () => {
|
|
|
151
236
|
name: 'hot',
|
|
152
237
|
access: 'read',
|
|
153
238
|
description: 'Bilibili hot videos',
|
|
239
|
+
browser: false,
|
|
154
240
|
example: 'opencli bilibili hot -f yaml',
|
|
155
|
-
|
|
241
|
+
command_options: [{ name: 'limit', type: 'int', default: 20 }],
|
|
242
|
+
columns: ['title', 'url'],
|
|
156
243
|
},
|
|
157
244
|
]);
|
|
245
|
+
expect(data.commands[0]).not.toHaveProperty('args');
|
|
246
|
+
}
|
|
247
|
+
finally {
|
|
248
|
+
process.argv = argv;
|
|
249
|
+
registry.clear();
|
|
250
|
+
for (const [key, value] of snapshot)
|
|
251
|
+
registry.set(key, value);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
it('renders per-site text help without per-command common option noise', () => {
|
|
255
|
+
const registry = getRegistry();
|
|
256
|
+
const snapshot = new Map(registry);
|
|
257
|
+
registry.clear();
|
|
258
|
+
try {
|
|
259
|
+
cli({
|
|
260
|
+
site: 'bilibili',
|
|
261
|
+
name: 'hot',
|
|
262
|
+
access: 'read',
|
|
263
|
+
description: 'Bilibili hot videos',
|
|
264
|
+
strategy: Strategy.PUBLIC,
|
|
265
|
+
browser: false,
|
|
266
|
+
args: [{ name: 'limit', type: 'int', default: 20, help: 'Number of videos' }],
|
|
267
|
+
});
|
|
268
|
+
cli({
|
|
269
|
+
site: 'bilibili',
|
|
270
|
+
name: 'video',
|
|
271
|
+
access: 'read',
|
|
272
|
+
description: 'Read one video',
|
|
273
|
+
domain: 'www.bilibili.com',
|
|
274
|
+
strategy: Strategy.PUBLIC,
|
|
275
|
+
browser: true,
|
|
276
|
+
args: [{ name: 'bvid', positional: true, required: true, help: 'Video id' }],
|
|
277
|
+
});
|
|
278
|
+
const program = createProgram('', '');
|
|
279
|
+
const site = program.commands.find(cmd => cmd.name() === 'bilibili');
|
|
280
|
+
expect(site).toBeTruthy();
|
|
281
|
+
const help = site.helpInformation();
|
|
282
|
+
expect(help).toContain('hot [options] [read] Bilibili hot videos');
|
|
283
|
+
expect(help).toContain('video <bvid> [read] Read one video');
|
|
284
|
+
expect(help).toContain('hot [options]');
|
|
285
|
+
expect(help).not.toContain('video <bvid> [options]');
|
|
286
|
+
expect(help).not.toContain('\nOptions:');
|
|
287
|
+
expect(help).toContain('Common options:');
|
|
288
|
+
expect(help).toContain('-f, --format <fmt>');
|
|
289
|
+
expect(help).toContain('--trace <mode>');
|
|
290
|
+
expect(help).toContain('get all command args/options in one structured response');
|
|
291
|
+
}
|
|
292
|
+
finally {
|
|
293
|
+
registry.clear();
|
|
294
|
+
for (const [key, value] of snapshot)
|
|
295
|
+
registry.set(key, value);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
it('separates command args from common options in structured help', () => {
|
|
299
|
+
const registry = getRegistry();
|
|
300
|
+
const snapshot = new Map(registry);
|
|
301
|
+
const argv = process.argv;
|
|
302
|
+
registry.clear();
|
|
303
|
+
try {
|
|
304
|
+
cli({
|
|
305
|
+
site: 'bilibili',
|
|
306
|
+
name: 'video',
|
|
307
|
+
access: 'read',
|
|
308
|
+
description: 'Read one video',
|
|
309
|
+
strategy: Strategy.PUBLIC,
|
|
310
|
+
domain: 'www.bilibili.com',
|
|
311
|
+
browser: true,
|
|
312
|
+
args: [
|
|
313
|
+
{ name: 'bvid', positional: true, required: true, help: 'Video id' },
|
|
314
|
+
{ name: 'with-comments', type: 'boolean', default: false, help: 'Include comments' },
|
|
315
|
+
],
|
|
316
|
+
columns: ['title', 'url'],
|
|
317
|
+
});
|
|
318
|
+
const program = createProgram('', '');
|
|
319
|
+
const site = program.commands.find(cmd => cmd.name() === 'bilibili');
|
|
320
|
+
const command = site.commands.find(cmd => cmd.name() === 'video');
|
|
321
|
+
expect(command).toBeTruthy();
|
|
322
|
+
process.argv = ['node', 'opencli', 'bilibili', 'video', '--help', '-f', 'yaml'];
|
|
323
|
+
const data = yaml.load(command.helpInformation());
|
|
324
|
+
expect(data.usage).toBe('opencli bilibili video <bvid> [options]');
|
|
325
|
+
expect(data.browser).toBe(true);
|
|
326
|
+
expect(data.domain).toBe('www.bilibili.com');
|
|
327
|
+
expect(data.positionals).toMatchObject([{ name: 'bvid', positional: true, required: true }]);
|
|
328
|
+
expect(data.command_options).toMatchObject([{ name: 'with-comments', default: false }]);
|
|
329
|
+
expect(data.common_options.map((option) => option.name)).toEqual(['format', 'trace', 'verbose', 'help']);
|
|
330
|
+
expect(data.columns).toEqual(['title', 'url']);
|
|
331
|
+
expect(data).not.toHaveProperty('args');
|
|
158
332
|
}
|
|
159
333
|
finally {
|
|
160
334
|
process.argv = argv;
|
|
@@ -1624,6 +1798,16 @@ describe('browser click/type commands', () => {
|
|
|
1624
1798
|
evaluate: vi.fn().mockResolvedValue(false),
|
|
1625
1799
|
click: vi.fn().mockResolvedValue({ matches_n: 1, match_level: 'exact' }),
|
|
1626
1800
|
typeText: vi.fn().mockResolvedValue({ matches_n: 1, match_level: 'exact' }),
|
|
1801
|
+
fillText: vi.fn().mockResolvedValue({
|
|
1802
|
+
filled: true,
|
|
1803
|
+
verified: true,
|
|
1804
|
+
expected: '',
|
|
1805
|
+
actual: '',
|
|
1806
|
+
length: 0,
|
|
1807
|
+
matches_n: 1,
|
|
1808
|
+
match_level: 'exact',
|
|
1809
|
+
mode: 'input',
|
|
1810
|
+
}),
|
|
1627
1811
|
wait: vi.fn().mockResolvedValue(undefined),
|
|
1628
1812
|
}));
|
|
1629
1813
|
it('emits {clicked, target, matches_n, match_level} on success', async () => {
|
|
@@ -1721,6 +1905,62 @@ describe('browser click/type commands', () => {
|
|
|
1721
1905
|
expect(browserState.page.click).toHaveBeenCalledWith('.field', { nth: 3 });
|
|
1722
1906
|
expect(browserState.page.typeText).toHaveBeenCalledWith('.field', 'x', { nth: 3 });
|
|
1723
1907
|
});
|
|
1908
|
+
it('fill: delegates exact raw text to page.fillText and emits verification details', async () => {
|
|
1909
|
+
browserState.page.fillText.mockResolvedValueOnce({
|
|
1910
|
+
filled: true,
|
|
1911
|
+
verified: true,
|
|
1912
|
+
expected: 'line1\\n/ / raw',
|
|
1913
|
+
actual: 'line1\\n/ / raw',
|
|
1914
|
+
length: 14,
|
|
1915
|
+
matches_n: 1,
|
|
1916
|
+
match_level: 'exact',
|
|
1917
|
+
mode: 'textarea',
|
|
1918
|
+
});
|
|
1919
|
+
const program = createProgram('', '');
|
|
1920
|
+
await program.parseAsync(['node', 'opencli', 'browser', 'fill', '#msg', 'line1\\n/ / raw']);
|
|
1921
|
+
expect(browserState.page.fillText).toHaveBeenCalledWith('#msg', 'line1\\n/ / raw', {});
|
|
1922
|
+
expect(lastJsonLog()).toEqual({
|
|
1923
|
+
filled: true,
|
|
1924
|
+
verified: true,
|
|
1925
|
+
target: '#msg',
|
|
1926
|
+
text: 'line1\\n/ / raw',
|
|
1927
|
+
actual: 'line1\\n/ / raw',
|
|
1928
|
+
length: 14,
|
|
1929
|
+
matches_n: 1,
|
|
1930
|
+
match_level: 'exact',
|
|
1931
|
+
mode: 'textarea',
|
|
1932
|
+
});
|
|
1933
|
+
expect(process.exitCode).toBeUndefined();
|
|
1934
|
+
});
|
|
1935
|
+
it('fill: sets a non-zero exit code when verification fails', async () => {
|
|
1936
|
+
browserState.page.fillText.mockResolvedValueOnce({
|
|
1937
|
+
filled: true,
|
|
1938
|
+
verified: false,
|
|
1939
|
+
expected: 'expected',
|
|
1940
|
+
actual: 'actual',
|
|
1941
|
+
length: 6,
|
|
1942
|
+
matches_n: 1,
|
|
1943
|
+
match_level: 'exact',
|
|
1944
|
+
});
|
|
1945
|
+
const program = createProgram('', '');
|
|
1946
|
+
await program.parseAsync(['node', 'opencli', 'browser', 'fill', '#msg', 'expected']);
|
|
1947
|
+
expect(lastJsonLog()).toEqual({
|
|
1948
|
+
filled: true,
|
|
1949
|
+
verified: false,
|
|
1950
|
+
target: '#msg',
|
|
1951
|
+
text: 'expected',
|
|
1952
|
+
actual: 'actual',
|
|
1953
|
+
length: 6,
|
|
1954
|
+
matches_n: 1,
|
|
1955
|
+
match_level: 'exact',
|
|
1956
|
+
});
|
|
1957
|
+
expect(process.exitCode).toBeDefined();
|
|
1958
|
+
});
|
|
1959
|
+
it('fill: forwards --nth to page.fillText', async () => {
|
|
1960
|
+
const program = createProgram('', '');
|
|
1961
|
+
await program.parseAsync(['node', 'opencli', 'browser', 'fill', '.field', 'x', '--nth', '2']);
|
|
1962
|
+
expect(browserState.page.fillText).toHaveBeenCalledWith('.field', 'x', { nth: 2 });
|
|
1963
|
+
});
|
|
1724
1964
|
});
|
|
1725
1965
|
describe('browser select command', () => {
|
|
1726
1966
|
const { lastJsonLog } = installSelectorFirstTestHarness('select', () => ({
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
import { log } from './logger.js';
|
|
13
13
|
import yaml from 'js-yaml';
|
|
14
14
|
import { fullName, getRegistry } from './registry.js';
|
|
15
|
-
import { formatRegistryHelpText } from './serialization.js';
|
|
16
15
|
import { render as renderOutput } from './output.js';
|
|
17
16
|
import { executeCommand, prepareCommandArgs } from './execution.js';
|
|
18
|
-
import { commandHelpData, formatSiteCommandDescription,
|
|
17
|
+
import { commandHelpData, formatCommandHelpText, formatCommandListTerm, formatSiteCommandDescription, formatSiteHelpText, getRequestedHelpFormat, renderStructuredHelp, siteHelpData, } from './help.js';
|
|
19
18
|
import { CliError, EXIT_CODES, toEnvelope, } from './errors.js';
|
|
20
19
|
/**
|
|
21
20
|
* Register a single CliCommand as a Commander subcommand.
|
|
@@ -49,7 +48,16 @@ export function registerCommandToProgram(siteCmd, cmd) {
|
|
|
49
48
|
.option('-f, --format <fmt>', 'Output format: table, plain, json, yaml, md, csv', 'table')
|
|
50
49
|
.option('--trace <mode>', 'Trace capture: off, on, retain-on-failure', 'off')
|
|
51
50
|
.option('-v, --verbose', 'Debug output', false);
|
|
52
|
-
|
|
51
|
+
const originalHelpInformation = subCmd.helpInformation.bind(subCmd);
|
|
52
|
+
subCmd.helpInformation = ((contextOptions) => {
|
|
53
|
+
const format = getRequestedHelpFormat();
|
|
54
|
+
if (format)
|
|
55
|
+
return renderStructuredHelp(commandHelpData(cmd), format);
|
|
56
|
+
// Keep a fallback reference so future Commander upgrades still initialize
|
|
57
|
+
// internal help state before we render the cleaner grouped command help.
|
|
58
|
+
void originalHelpInformation(contextOptions);
|
|
59
|
+
return formatCommandHelpText(cmd);
|
|
60
|
+
});
|
|
53
61
|
subCmd.action(async (...actionArgs) => {
|
|
54
62
|
const actionOpts = actionArgs[positionalArgs.length] ?? {};
|
|
55
63
|
const optionsRecord = typeof actionOpts === 'object' && actionOpts !== null ? actionOpts : {};
|
|
@@ -89,11 +97,6 @@ export function registerCommandToProgram(siteCmd, cmd) {
|
|
|
89
97
|
const formatExplicit = subCmd.getOptionValueSource('format') === 'cli';
|
|
90
98
|
if (verbose)
|
|
91
99
|
process.env.OPENCLI_VERBOSE = '1';
|
|
92
|
-
if (cmd.deprecated) {
|
|
93
|
-
const message = typeof cmd.deprecated === 'string' ? cmd.deprecated : `${fullName(cmd)} is deprecated.`;
|
|
94
|
-
const replacement = cmd.replacedBy ? ` Use ${cmd.replacedBy} instead.` : '';
|
|
95
|
-
log.warn(`Deprecated: ${message}${replacement}`);
|
|
96
|
-
}
|
|
97
100
|
const globals = typeof subCmd.optsWithGlobals === 'function' ? subCmd.optsWithGlobals() : {};
|
|
98
101
|
const result = await executeCommand(cmd, kwargs, verbose, {
|
|
99
102
|
prepared: true,
|
|
@@ -179,7 +182,18 @@ export function registerAllCommands(program, siteGroups) {
|
|
|
179
182
|
for (const cmd of commands) {
|
|
180
183
|
registerCommandToProgram(siteCmd, cmd);
|
|
181
184
|
}
|
|
182
|
-
|
|
185
|
+
const commandTerms = new Map(commands.map(cmd => [cmd.name, formatCommandListTerm(cmd)]));
|
|
186
|
+
siteCmd.configureHelp({
|
|
187
|
+
subcommandTerm: command => commandTerms.get(command.name()) ?? command.name(),
|
|
188
|
+
});
|
|
189
|
+
const originalSiteHelpInformation = siteCmd.helpInformation.bind(siteCmd);
|
|
190
|
+
siteCmd.helpInformation = ((contextOptions) => {
|
|
191
|
+
const format = getRequestedHelpFormat();
|
|
192
|
+
if (format)
|
|
193
|
+
return renderStructuredHelp(siteHelpData(site, commands), format);
|
|
194
|
+
void originalSiteHelpInformation(contextOptions);
|
|
195
|
+
return formatSiteHelpText(site, commands);
|
|
196
|
+
});
|
|
183
197
|
}
|
|
184
198
|
return [...commandsBySite.keys()].sort((a, b) => a.localeCompare(b));
|
|
185
199
|
}
|
|
@@ -279,7 +279,6 @@ describe('commanderAdapter error envelope output', () => {
|
|
|
279
279
|
expect(output).toContain('xsec_token');
|
|
280
280
|
expect(output).toContain('--trace=retain-on-failure');
|
|
281
281
|
expect(output).toContain('opencli xiaohongshu note --trace retain-on-failure');
|
|
282
|
-
expect(output).not.toContain('OPENCLI_DIAGNOSTIC');
|
|
283
282
|
stderrSpy.mockRestore();
|
|
284
283
|
});
|
|
285
284
|
it('outputs YAML error envelope for selector errors', async () => {
|
package/dist/src/discovery.js
CHANGED
|
@@ -131,12 +131,11 @@ async function loadFromManifest(manifestPath, clisDir) {
|
|
|
131
131
|
browser: entry.browser,
|
|
132
132
|
args: entry.args ?? [],
|
|
133
133
|
columns: entry.columns,
|
|
134
|
+
defaultFormat: entry.defaultFormat,
|
|
134
135
|
pipeline: entry.pipeline,
|
|
135
|
-
timeoutSeconds: entry.timeout,
|
|
136
136
|
source: entry.sourceFile ? path.resolve(clisDir, entry.sourceFile) : modulePath,
|
|
137
|
-
deprecated: entry.deprecated,
|
|
138
|
-
replacedBy: entry.replacedBy,
|
|
139
137
|
navigateBefore: entry.navigateBefore,
|
|
138
|
+
browserSession: entry.browserSession,
|
|
140
139
|
_lazy: true,
|
|
141
140
|
_modulePath: modulePath,
|
|
142
141
|
};
|
|
@@ -170,7 +169,6 @@ async function discoverClisFromFs(dir) {
|
|
|
170
169
|
await Promise.all(files.map(async (file) => {
|
|
171
170
|
const filePath = path.join(siteDir, file);
|
|
172
171
|
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
|
|
173
|
-
log.warn(`Ignoring YAML adapter ${filePath} — YAML format is no longer supported. Convert to JavaScript using cli() from '@jackwener/opencli/registry'.`);
|
|
174
172
|
return;
|
|
175
173
|
}
|
|
176
174
|
if (file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts')) {
|
|
@@ -218,7 +216,6 @@ async function discoverPluginDir(dir, site) {
|
|
|
218
216
|
await Promise.all(files.map(async (file) => {
|
|
219
217
|
const filePath = path.join(dir, file);
|
|
220
218
|
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
|
|
221
|
-
log.warn(`Ignoring YAML plugin ${filePath} — YAML format is no longer supported. Convert to JavaScript using cli() from '@jackwener/opencli/registry'.`);
|
|
222
219
|
return;
|
|
223
220
|
}
|
|
224
221
|
if (file.endsWith('.js') && !file.endsWith('.d.js')) {
|
package/dist/src/errors.js
CHANGED
|
@@ -73,7 +73,7 @@ export class AuthRequiredError extends CliError {
|
|
|
73
73
|
}
|
|
74
74
|
export class TimeoutError extends CliError {
|
|
75
75
|
constructor(label, seconds, hint) {
|
|
76
|
-
super('TIMEOUT', `${label} timed out after ${seconds}s`, hint ?? 'Try again, or increase timeout with OPENCLI_BROWSER_COMMAND_TIMEOUT
|
|
76
|
+
super('TIMEOUT', `${label} timed out after ${seconds}s`, hint ?? 'Try again, or increase timeout with --timeout <seconds> (or OPENCLI_BROWSER_COMMAND_TIMEOUT for the global default)', EXIT_CODES.TEMPFAIL);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
export class ArgumentError extends CliError {
|
package/dist/src/execution.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This is the single entry point for executing any CLI command. It handles:
|
|
5
5
|
* 1. Argument validation and coercion
|
|
6
6
|
* 2. Browser session lifecycle (if needed)
|
|
7
|
-
* 3. Domain pre-navigation for cookie
|
|
7
|
+
* 3. Domain pre-navigation for cookie strategies
|
|
8
8
|
* 4. Timeout enforcement
|
|
9
9
|
* 5. Lazy-loading of TS modules from manifest
|
|
10
10
|
* 6. Lifecycle hooks (onBeforeExecute / onAfterExecute)
|