@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
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const extensionDir = path.resolve(__dirname, '..');
|
|
7
|
+
const repoRoot = path.resolve(extensionDir, '..');
|
|
8
|
+
|
|
9
|
+
function parseArgs(argv) {
|
|
10
|
+
const args = { outDir: path.join(repoRoot, 'extension-package') };
|
|
11
|
+
for (let i = 0; i < argv.length; i++) {
|
|
12
|
+
const arg = argv[i];
|
|
13
|
+
if (arg === '--out' && argv[i + 1]) {
|
|
14
|
+
const outDir = argv[++i];
|
|
15
|
+
args.outDir = path.isAbsolute(outDir)
|
|
16
|
+
? outDir
|
|
17
|
+
: path.resolve(process.cwd(), outDir);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return args;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function exists(targetPath) {
|
|
24
|
+
try {
|
|
25
|
+
await fs.access(targetPath);
|
|
26
|
+
return true;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isLocalAsset(ref) {
|
|
33
|
+
return typeof ref === 'string'
|
|
34
|
+
&& ref.length > 0
|
|
35
|
+
&& !ref.startsWith('http://')
|
|
36
|
+
&& !ref.startsWith('https://')
|
|
37
|
+
&& !ref.startsWith('//')
|
|
38
|
+
&& !ref.startsWith('chrome://')
|
|
39
|
+
&& !ref.startsWith('chrome-extension://')
|
|
40
|
+
&& !ref.startsWith('data:')
|
|
41
|
+
&& !ref.startsWith('#');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function addLocalAsset(files, ref) {
|
|
45
|
+
if (isLocalAsset(ref)) files.add(ref);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function collectManifestEntrypoints(manifest) {
|
|
49
|
+
const files = new Set(['manifest.json']);
|
|
50
|
+
|
|
51
|
+
addLocalAsset(files, manifest.background?.service_worker);
|
|
52
|
+
addLocalAsset(files, manifest.action?.default_popup);
|
|
53
|
+
addLocalAsset(files, manifest.options_page);
|
|
54
|
+
addLocalAsset(files, manifest.devtools_page);
|
|
55
|
+
addLocalAsset(files, manifest.side_panel?.default_path);
|
|
56
|
+
|
|
57
|
+
for (const ref of Object.values(manifest.icons ?? {})) addLocalAsset(files, ref);
|
|
58
|
+
for (const ref of Object.values(manifest.action?.default_icon ?? {})) addLocalAsset(files, ref);
|
|
59
|
+
for (const contentScript of manifest.content_scripts ?? []) {
|
|
60
|
+
for (const jsFile of contentScript.js ?? []) addLocalAsset(files, jsFile);
|
|
61
|
+
for (const cssFile of contentScript.css ?? []) addLocalAsset(files, cssFile);
|
|
62
|
+
}
|
|
63
|
+
for (const page of manifest.sandbox?.pages ?? []) addLocalAsset(files, page);
|
|
64
|
+
for (const overridePage of Object.values(manifest.chrome_url_overrides ?? {})) addLocalAsset(files, overridePage);
|
|
65
|
+
for (const entry of manifest.web_accessible_resources ?? []) {
|
|
66
|
+
for (const resource of entry.resources ?? []) addLocalAsset(files, resource);
|
|
67
|
+
}
|
|
68
|
+
if (manifest.default_locale) files.add('_locales');
|
|
69
|
+
|
|
70
|
+
return [...files];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function collectHtmlDependencies(relativeHtmlPath, files, visited) {
|
|
74
|
+
if (visited.has(relativeHtmlPath)) return;
|
|
75
|
+
visited.add(relativeHtmlPath);
|
|
76
|
+
|
|
77
|
+
const htmlPath = path.join(extensionDir, relativeHtmlPath);
|
|
78
|
+
const html = await fs.readFile(htmlPath, 'utf8');
|
|
79
|
+
const attrRe = /\b(?:src|href)=["']([^"'#?]+(?:\?[^"']*)?)["']/gi;
|
|
80
|
+
|
|
81
|
+
for (const match of html.matchAll(attrRe)) {
|
|
82
|
+
const rawRef = match[1];
|
|
83
|
+
const cleanRef = rawRef.split('?')[0];
|
|
84
|
+
if (!isLocalAsset(cleanRef)) continue;
|
|
85
|
+
|
|
86
|
+
const resolvedRelativePath = cleanRef.startsWith('/')
|
|
87
|
+
? cleanRef.slice(1)
|
|
88
|
+
: path.posix.normalize(path.posix.join(path.posix.dirname(relativeHtmlPath), cleanRef));
|
|
89
|
+
|
|
90
|
+
addLocalAsset(files, resolvedRelativePath);
|
|
91
|
+
if (resolvedRelativePath.endsWith('.html')) {
|
|
92
|
+
await collectHtmlDependencies(resolvedRelativePath, files, visited);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function collectManifestAssets(manifest) {
|
|
98
|
+
const files = new Set(collectManifestEntrypoints(manifest));
|
|
99
|
+
const htmlPages = [];
|
|
100
|
+
|
|
101
|
+
if (manifest.action?.default_popup) {
|
|
102
|
+
htmlPages.push(manifest.action.default_popup);
|
|
103
|
+
}
|
|
104
|
+
if (manifest.options_page) htmlPages.push(manifest.options_page);
|
|
105
|
+
if (manifest.devtools_page) htmlPages.push(manifest.devtools_page);
|
|
106
|
+
if (manifest.side_panel?.default_path) htmlPages.push(manifest.side_panel.default_path);
|
|
107
|
+
for (const page of manifest.sandbox?.pages ?? []) htmlPages.push(page);
|
|
108
|
+
for (const overridePage of Object.values(manifest.chrome_url_overrides ?? {})) htmlPages.push(overridePage);
|
|
109
|
+
|
|
110
|
+
const visited = new Set();
|
|
111
|
+
for (const htmlPage of htmlPages) {
|
|
112
|
+
if (isLocalAsset(htmlPage)) {
|
|
113
|
+
await collectHtmlDependencies(htmlPage, files, visited);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return [...files];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function copyEntry(relativePath, outDir) {
|
|
121
|
+
const fromPath = path.join(extensionDir, relativePath);
|
|
122
|
+
const toPath = path.join(outDir, relativePath);
|
|
123
|
+
const stats = await fs.stat(fromPath);
|
|
124
|
+
|
|
125
|
+
if (stats.isDirectory()) {
|
|
126
|
+
await fs.cp(fromPath, toPath, { recursive: true });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
await fs.mkdir(path.dirname(toPath), { recursive: true });
|
|
131
|
+
await fs.copyFile(fromPath, toPath);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function findMissingEntries(baseDir, entries) {
|
|
135
|
+
const missingEntries = [];
|
|
136
|
+
for (const relativePath of entries) {
|
|
137
|
+
const absolutePath = path.join(baseDir, relativePath);
|
|
138
|
+
if (!(await exists(absolutePath))) {
|
|
139
|
+
missingEntries.push(relativePath);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return missingEntries;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function main() {
|
|
146
|
+
const { outDir } = parseArgs(process.argv.slice(2));
|
|
147
|
+
const manifestPath = path.join(extensionDir, 'manifest.json');
|
|
148
|
+
const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
|
|
149
|
+
|
|
150
|
+
const requiredEntries = await collectManifestAssets(manifest);
|
|
151
|
+
const missingEntries = await findMissingEntries(extensionDir, requiredEntries);
|
|
152
|
+
|
|
153
|
+
if (missingEntries.length > 0) {
|
|
154
|
+
console.error('Missing files referenced by the extension package:');
|
|
155
|
+
for (const missingEntry of missingEntries) console.error(`- ${missingEntry}`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
await fs.rm(outDir, { recursive: true, force: true });
|
|
160
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
161
|
+
|
|
162
|
+
for (const relativePath of requiredEntries) {
|
|
163
|
+
await copyEntry(relativePath, outDir);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Guard against regressions where manifest entry files (e.g. action.default_popup)
|
|
167
|
+
// are accidentally omitted from the packaged directory.
|
|
168
|
+
const packagedEntrypoints = collectManifestEntrypoints(manifest);
|
|
169
|
+
const missingPackagedEntrypoints = await findMissingEntries(outDir, packagedEntrypoints);
|
|
170
|
+
if (missingPackagedEntrypoints.length > 0) {
|
|
171
|
+
console.error('Packaged extension is missing files referenced by manifest.json:');
|
|
172
|
+
for (const missingEntry of missingPackagedEntrypoints) console.error(`- ${missingEntry}`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log(`Extension package prepared at ${path.relative(repoRoot, outDir) || outDir}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await main();
|
|
@@ -51,6 +51,8 @@ function connect(): void {
|
|
|
51
51
|
clearTimeout(reconnectTimer);
|
|
52
52
|
reconnectTimer = null;
|
|
53
53
|
}
|
|
54
|
+
// Send version so the daemon can report mismatches to the CLI
|
|
55
|
+
ws?.send(JSON.stringify({ type: 'hello', version: chrome.runtime.getManifest().version }));
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
ws.onmessage = async (event) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jackwener/opencli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -19,17 +19,20 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"dev": "tsx src/main.ts",
|
|
22
|
+
"dev:bun": "bun src/main.ts",
|
|
22
23
|
"build": "npm run clean-dist && tsc && npm run clean-yaml && npm run copy-yaml && npm run build-manifest",
|
|
23
24
|
"build-manifest": "node dist/build-manifest.js",
|
|
24
25
|
"clean-dist": "node scripts/clean-dist.cjs",
|
|
25
26
|
"clean-yaml": "node scripts/clean-yaml.cjs",
|
|
26
27
|
"copy-yaml": "node scripts/copy-yaml.cjs",
|
|
27
28
|
"start": "node dist/main.js",
|
|
29
|
+
"start:bun": "bun dist/main.js",
|
|
28
30
|
"postinstall": "node scripts/postinstall.js || true",
|
|
29
31
|
"typecheck": "tsc --noEmit",
|
|
30
32
|
"lint": "tsc --noEmit",
|
|
31
33
|
"prepublishOnly": "npm run build",
|
|
32
34
|
"test": "vitest run --project unit",
|
|
35
|
+
"test:bun": "bun vitest run --project unit",
|
|
33
36
|
"test:adapter": "vitest run --project adapter",
|
|
34
37
|
"test:all": "vitest run",
|
|
35
38
|
"test:e2e": "vitest run --project e2e",
|
package/scripts/postinstall.js
CHANGED
|
@@ -195,6 +195,16 @@ function main() {
|
|
|
195
195
|
console.error(`Warning: Could not install shell completion: ${err.message}`);
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
+
|
|
199
|
+
// ── Browser Bridge setup hint ───────────────────────────────────────
|
|
200
|
+
console.log('');
|
|
201
|
+
console.log(' \x1b[1mNext step — Browser Bridge setup\x1b[0m');
|
|
202
|
+
console.log(' Browser commands (bilibili, zhihu, twitter...) require the extension:');
|
|
203
|
+
console.log(' 1. Download: https://github.com/jackwener/opencli/releases');
|
|
204
|
+
console.log(' 2. Open chrome://extensions → enable Developer Mode → Load unpacked');
|
|
205
|
+
console.log('');
|
|
206
|
+
console.log(' Then run \x1b[36mopencli doctor\x1b[0m to verify.');
|
|
207
|
+
console.log('');
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
main();
|
package/src/browser/cdp.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { WebSocket, type RawData } from 'ws';
|
|
|
12
12
|
import { request as httpRequest } from 'node:http';
|
|
13
13
|
import { request as httpsRequest } from 'node:https';
|
|
14
14
|
import type { BrowserCookie, IPage, ScreenshotOptions, SnapshotOptions, WaitOptions } from '../types.js';
|
|
15
|
+
import type { IBrowserFactory } from '../runtime.js';
|
|
15
16
|
import { wrapForEval } from './utils.js';
|
|
16
17
|
import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
|
|
17
18
|
import { generateStealthJs } from './stealth.js';
|
|
@@ -47,7 +48,7 @@ interface RuntimeEvaluateResult {
|
|
|
47
48
|
|
|
48
49
|
const CDP_SEND_TIMEOUT = 30_000;
|
|
49
50
|
|
|
50
|
-
export class CDPBridge {
|
|
51
|
+
export class CDPBridge implements IBrowserFactory {
|
|
51
52
|
private _ws: WebSocket | null = null;
|
|
52
53
|
private _idCounter = 0;
|
|
53
54
|
private _pending = new Map<number, { resolve: (val: unknown) => void; reject: (err: Error) => void; timer: ReturnType<typeof setTimeout> }>();
|
|
@@ -172,6 +173,7 @@ export class CDPBridge {
|
|
|
172
173
|
|
|
173
174
|
class CDPPage implements IPage {
|
|
174
175
|
private _pageEnabled = false;
|
|
176
|
+
private _lastUrl: string | null = null;
|
|
175
177
|
constructor(private bridge: CDPBridge) {}
|
|
176
178
|
|
|
177
179
|
async goto(url: string, options?: { waitUntil?: 'load' | 'none'; settleMs?: number }): Promise<void> {
|
|
@@ -182,6 +184,7 @@ class CDPPage implements IPage {
|
|
|
182
184
|
const loadPromise = this.bridge.waitForEvent('Page.loadEventFired', 30_000).catch(() => {});
|
|
183
185
|
await this.bridge.send('Page.navigate', { url });
|
|
184
186
|
await loadPromise;
|
|
187
|
+
this._lastUrl = url;
|
|
185
188
|
if (options?.waitUntil !== 'none') {
|
|
186
189
|
const maxMs = options?.settleMs ?? 1000;
|
|
187
190
|
await this.evaluate(waitForDomStableJs(maxMs, Math.min(500, maxMs)));
|
|
@@ -306,6 +309,10 @@ class CDPPage implements IPage {
|
|
|
306
309
|
return [];
|
|
307
310
|
}
|
|
308
311
|
|
|
312
|
+
async getCurrentUrl(): Promise<string | null> {
|
|
313
|
+
return this._lastUrl;
|
|
314
|
+
}
|
|
315
|
+
|
|
309
316
|
async installInterceptor(pattern: string): Promise<void> {
|
|
310
317
|
const { generateInterceptorJs } = await import('../interceptor.js');
|
|
311
318
|
await this.evaluate(generateInterceptorJs(JSON.stringify(pattern), {
|
package/src/browser/discover.ts
CHANGED
|
@@ -13,17 +13,22 @@ export { isDaemonRunning };
|
|
|
13
13
|
/**
|
|
14
14
|
* Check daemon status and return connection info.
|
|
15
15
|
*/
|
|
16
|
-
export async function checkDaemonStatus(): Promise<{
|
|
16
|
+
export async function checkDaemonStatus(opts?: { timeout?: number }): Promise<{
|
|
17
17
|
running: boolean;
|
|
18
18
|
extensionConnected: boolean;
|
|
19
|
+
extensionVersion?: string;
|
|
19
20
|
}> {
|
|
20
21
|
try {
|
|
21
22
|
const port = parseInt(process.env.OPENCLI_DAEMON_PORT ?? String(DEFAULT_DAEMON_PORT), 10);
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timer = setTimeout(() => controller.abort(), opts?.timeout ?? 2000);
|
|
22
25
|
const res = await fetch(`http://127.0.0.1:${port}/status`, {
|
|
23
26
|
headers: { 'X-OpenCLI': '1' },
|
|
27
|
+
signal: controller.signal,
|
|
24
28
|
});
|
|
25
|
-
const data = await res.json() as { ok: boolean; extensionConnected: boolean };
|
|
26
|
-
|
|
29
|
+
const data = await res.json() as { ok: boolean; extensionConnected: boolean; extensionVersion?: string };
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
return { running: true, extensionConnected: data.extensionConnected, extensionVersion: data.extensionVersion };
|
|
27
32
|
} catch {
|
|
28
33
|
return { running: false, extensionConnected: false };
|
|
29
34
|
}
|
package/src/browser/errors.ts
CHANGED
|
@@ -5,38 +5,37 @@
|
|
|
5
5
|
* The daemon architecture has a single failure mode: daemon not reachable or extension not connected.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { BrowserConnectError } from '../errors.js';
|
|
8
|
+
import { BrowserConnectError, type BrowserConnectKind } from '../errors.js';
|
|
9
9
|
import { DEFAULT_DAEMON_PORT } from '../constants.js';
|
|
10
10
|
|
|
11
|
-
export
|
|
11
|
+
// Re-export so callers don't need to import from two places
|
|
12
|
+
export type ConnectFailureKind = BrowserConnectKind;
|
|
12
13
|
|
|
13
14
|
export function formatBrowserConnectError(kind: ConnectFailureKind, detail?: string): BrowserConnectError {
|
|
14
15
|
switch (kind) {
|
|
15
16
|
case 'daemon-not-running':
|
|
16
17
|
return new BrowserConnectError(
|
|
17
|
-
'Cannot connect to opencli daemon.' +
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
' node dist/daemon.js\n' +
|
|
21
|
-
`Make sure port ${DEFAULT_DAEMON_PORT} is available.`,
|
|
18
|
+
'Cannot connect to opencli daemon.' + (detail ? `\n\n${detail}` : ''),
|
|
19
|
+
`The daemon should auto-start. If it keeps failing, make sure port ${DEFAULT_DAEMON_PORT} is available.`,
|
|
20
|
+
kind,
|
|
22
21
|
);
|
|
23
22
|
case 'extension-not-connected':
|
|
24
23
|
return new BrowserConnectError(
|
|
25
|
-
'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
' 1. Download from GitHub Releases\n' +
|
|
29
|
-
' 2. Open chrome://extensions/ → Enable Developer Mode\n' +
|
|
30
|
-
' 3. Click "Load unpacked" → select the extension folder\n' +
|
|
31
|
-
' 4. Make sure Chrome is running',
|
|
24
|
+
'Browser Bridge extension is not connected.' + (detail ? `\n\n${detail}` : ''),
|
|
25
|
+
'Install the extension from GitHub Releases, then reload.',
|
|
26
|
+
kind,
|
|
32
27
|
);
|
|
33
28
|
case 'command-failed':
|
|
34
29
|
return new BrowserConnectError(
|
|
35
30
|
`Browser command failed: ${detail ?? 'unknown error'}`,
|
|
31
|
+
undefined,
|
|
32
|
+
kind,
|
|
36
33
|
);
|
|
37
34
|
default:
|
|
38
35
|
return new BrowserConnectError(
|
|
39
36
|
detail ?? 'Failed to connect to browser',
|
|
37
|
+
undefined,
|
|
38
|
+
kind,
|
|
40
39
|
);
|
|
41
40
|
}
|
|
42
41
|
}
|
package/src/browser/mcp.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
import * as path from 'node:path';
|
|
8
8
|
import * as fs from 'node:fs';
|
|
9
9
|
import type { IPage } from '../types.js';
|
|
10
|
+
import type { IBrowserFactory } from '../runtime.js';
|
|
10
11
|
import { Page } from './page.js';
|
|
11
12
|
import { isDaemonRunning, isExtensionConnected } from './daemon-client.js';
|
|
12
13
|
import { DEFAULT_DAEMON_PORT } from '../constants.js';
|
|
@@ -18,7 +19,7 @@ export type BrowserBridgeState = 'idle' | 'connecting' | 'connected' | 'closing'
|
|
|
18
19
|
/**
|
|
19
20
|
* Browser factory: manages daemon lifecycle and provides IPage instances.
|
|
20
21
|
*/
|
|
21
|
-
export class BrowserBridge {
|
|
22
|
+
export class BrowserBridge implements IBrowserFactory {
|
|
22
23
|
private _state: BrowserBridgeState = 'idle';
|
|
23
24
|
private _page: Page | null = null;
|
|
24
25
|
private _daemonProc: ChildProcess | null = null;
|
package/src/browser/page.ts
CHANGED
|
@@ -36,6 +36,8 @@ export class Page implements IPage {
|
|
|
36
36
|
|
|
37
37
|
/** Active tab ID, set after navigate and used in all subsequent commands */
|
|
38
38
|
private _tabId: number | undefined;
|
|
39
|
+
/** Last navigated URL, tracked in-memory to avoid extra round-trips */
|
|
40
|
+
private _lastUrl: string | null = null;
|
|
39
41
|
|
|
40
42
|
/** Helper: spread workspace into command params */
|
|
41
43
|
private _wsOpt(): { workspace: string } {
|
|
@@ -55,10 +57,11 @@ export class Page implements IPage {
|
|
|
55
57
|
url,
|
|
56
58
|
...this._cmdOpts(),
|
|
57
59
|
}) as { tabId?: number };
|
|
58
|
-
// Remember the tabId for subsequent
|
|
60
|
+
// Remember the tabId and URL for subsequent calls
|
|
59
61
|
if (result?.tabId) {
|
|
60
62
|
this._tabId = result.tabId;
|
|
61
63
|
}
|
|
64
|
+
this._lastUrl = url;
|
|
62
65
|
// Inject stealth anti-detection patches (guard flag prevents double-injection).
|
|
63
66
|
try {
|
|
64
67
|
await sendCommand('exec', {
|
|
@@ -79,6 +82,10 @@ export class Page implements IPage {
|
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
|
|
85
|
+
async getCurrentUrl(): Promise<string | null> {
|
|
86
|
+
return this._lastUrl;
|
|
87
|
+
}
|
|
88
|
+
|
|
82
89
|
/** Close the automation window in the extension */
|
|
83
90
|
async closeWindow(): Promise<void> {
|
|
84
91
|
try {
|
|
@@ -183,6 +190,22 @@ export class Page implements IPage {
|
|
|
183
190
|
|
|
184
191
|
async wait(options: number | WaitOptions): Promise<void> {
|
|
185
192
|
if (typeof options === 'number') {
|
|
193
|
+
if (options >= 1) {
|
|
194
|
+
// For waits >= 1s, use DOM-stable check: return early when the page
|
|
195
|
+
// stops mutating, with the original wait time as the hard cap.
|
|
196
|
+
// This turns e.g. `page.wait(5)` from a fixed 5s sleep into
|
|
197
|
+
// "wait until DOM is stable, max 5s" — often completing in <1s.
|
|
198
|
+
try {
|
|
199
|
+
const maxMs = options * 1000;
|
|
200
|
+
await sendCommand('exec', {
|
|
201
|
+
code: waitForDomStableJs(maxMs, Math.min(500, maxMs)),
|
|
202
|
+
...this._cmdOpts(),
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
} catch {
|
|
206
|
+
// Fallback: fixed sleep (e.g. if page has no DOM yet)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
186
209
|
await new Promise(resolve => setTimeout(resolve, options * 1000));
|
|
187
210
|
return;
|
|
188
211
|
}
|
|
@@ -143,4 +143,27 @@ describe('manifest helper rules', () => {
|
|
|
143
143
|
modulePath: 'xueqiu/fund-holdings.js',
|
|
144
144
|
});
|
|
145
145
|
});
|
|
146
|
+
|
|
147
|
+
it('captures deprecated metadata for TS adapters', () => {
|
|
148
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
|
|
149
|
+
tempDirs.push(dir);
|
|
150
|
+
const file = path.join(dir, 'legacy.ts');
|
|
151
|
+
fs.writeFileSync(file, `
|
|
152
|
+
import { cli } from '../../registry.js';
|
|
153
|
+
cli({
|
|
154
|
+
site: 'demo',
|
|
155
|
+
name: 'legacy',
|
|
156
|
+
description: 'legacy command',
|
|
157
|
+
deprecated: 'legacy is deprecated',
|
|
158
|
+
replacedBy: 'opencli demo new',
|
|
159
|
+
});
|
|
160
|
+
`);
|
|
161
|
+
|
|
162
|
+
expect(scanTs(file, 'demo')).toMatchObject({
|
|
163
|
+
site: 'demo',
|
|
164
|
+
name: 'legacy',
|
|
165
|
+
deprecated: 'legacy is deprecated',
|
|
166
|
+
replacedBy: 'opencli demo new',
|
|
167
|
+
});
|
|
168
|
+
});
|
|
146
169
|
});
|
package/src/build-manifest.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface ManifestEntry {
|
|
|
38
38
|
columns?: string[];
|
|
39
39
|
pipeline?: Record<string, unknown>[];
|
|
40
40
|
timeout?: number;
|
|
41
|
+
deprecated?: boolean | string;
|
|
42
|
+
replacedBy?: string;
|
|
41
43
|
/** 'yaml' or 'ts' — determines how executeCommand loads the handler */
|
|
42
44
|
type: 'yaml' | 'ts';
|
|
43
45
|
/** Relative path from clis/ dir, e.g. 'bilibili/hot.yaml' or 'bilibili/search.js' */
|
|
@@ -46,7 +48,7 @@ export interface ManifestEntry {
|
|
|
46
48
|
navigateBefore?: boolean | string;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
import type
|
|
51
|
+
import { type YamlCliDefinition, parseYamlArgs } from './yaml-schema.js';
|
|
50
52
|
|
|
51
53
|
import { isRecord } from './utils.js';
|
|
52
54
|
|
|
@@ -173,20 +175,7 @@ function scanYaml(filePath: string, site: string): ManifestEntry | null {
|
|
|
173
175
|
const strategy = strategyStr.toUpperCase();
|
|
174
176
|
const browser = cliDef.browser ?? (strategy !== 'PUBLIC');
|
|
175
177
|
|
|
176
|
-
const args
|
|
177
|
-
if (cliDef.args && typeof cliDef.args === 'object') {
|
|
178
|
-
for (const [argName, argDef] of Object.entries(cliDef.args)) {
|
|
179
|
-
args.push({
|
|
180
|
-
name: argName,
|
|
181
|
-
type: argDef?.type ?? 'str',
|
|
182
|
-
default: argDef?.default,
|
|
183
|
-
required: argDef?.required ?? false,
|
|
184
|
-
positional: argDef?.positional === true || undefined,
|
|
185
|
-
help: argDef?.description ?? argDef?.help ?? '',
|
|
186
|
-
choices: argDef?.choices,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}
|
|
178
|
+
const args = parseYamlArgs(cliDef.args);
|
|
190
179
|
|
|
191
180
|
return {
|
|
192
181
|
site: cliDef.site ?? site,
|
|
@@ -199,6 +188,8 @@ function scanYaml(filePath: string, site: string): ManifestEntry | null {
|
|
|
199
188
|
columns: cliDef.columns,
|
|
200
189
|
pipeline: cliDef.pipeline,
|
|
201
190
|
timeout: cliDef.timeout,
|
|
191
|
+
deprecated: (cliDef as Record<string, unknown>).deprecated as boolean | string | undefined,
|
|
192
|
+
replacedBy: (cliDef as Record<string, unknown>).replacedBy as string | undefined,
|
|
202
193
|
type: 'yaml',
|
|
203
194
|
navigateBefore: cliDef.navigateBefore,
|
|
204
195
|
};
|
|
@@ -269,6 +260,17 @@ export function scanTs(filePath: string, site: string): ManifestEntry | null {
|
|
|
269
260
|
if (navStringMatch) entry.navigateBefore = navStringMatch[1];
|
|
270
261
|
}
|
|
271
262
|
|
|
263
|
+
const deprecatedBoolMatch = src.match(/deprecated\s*:\s*(true|false)/);
|
|
264
|
+
if (deprecatedBoolMatch) {
|
|
265
|
+
entry.deprecated = deprecatedBoolMatch[1] === 'true';
|
|
266
|
+
} else {
|
|
267
|
+
const deprecatedStringMatch = src.match(/deprecated\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
268
|
+
if (deprecatedStringMatch) entry.deprecated = deprecatedStringMatch[1];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const replacedByMatch = src.match(/replacedBy\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
272
|
+
if (replacedByMatch) entry.replacedBy = replacedByMatch[1];
|
|
273
|
+
|
|
272
274
|
return entry;
|
|
273
275
|
} catch (err) {
|
|
274
276
|
// If parsing fails, log a warning (matching scanYaml behaviour) and skip the entry.
|
|
@@ -338,6 +340,29 @@ function main(): void {
|
|
|
338
340
|
const yamlCount = manifest.filter(e => e.type === 'yaml').length;
|
|
339
341
|
const tsCount = manifest.filter(e => e.type === 'ts').length;
|
|
340
342
|
console.log(`✅ Manifest compiled: ${manifest.length} entries (${yamlCount} YAML, ${tsCount} TS) → ${OUTPUT}`);
|
|
343
|
+
|
|
344
|
+
// Restore executable permissions on bin entries.
|
|
345
|
+
// tsc does not preserve the +x bit, so after a clean rebuild the CLI
|
|
346
|
+
// entry-point loses its executable permission, causing "Permission denied".
|
|
347
|
+
// See: https://github.com/jackwener/opencli/issues/446
|
|
348
|
+
if (process.platform !== 'win32') {
|
|
349
|
+
const pkgPath = path.resolve(__dirname, '..', 'package.json');
|
|
350
|
+
try {
|
|
351
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
352
|
+
const bins: Record<string, string> = typeof pkg.bin === 'string'
|
|
353
|
+
? { [pkg.name ?? 'cli']: pkg.bin }
|
|
354
|
+
: pkg.bin ?? {};
|
|
355
|
+
for (const binPath of Object.values(bins)) {
|
|
356
|
+
const abs = path.resolve(__dirname, '..', binPath);
|
|
357
|
+
if (fs.existsSync(abs)) {
|
|
358
|
+
fs.chmodSync(abs, 0o755);
|
|
359
|
+
console.log(`✅ Restored executable permission: ${binPath}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
} catch {
|
|
363
|
+
// Best-effort; never break the build for a permission fix.
|
|
364
|
+
}
|
|
365
|
+
}
|
|
341
366
|
}
|
|
342
367
|
|
|
343
368
|
const entrypoint = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : null;
|
package/src/capabilityRouting.ts
CHANGED