@zenalexa/unicli 0.209.0 → 0.211.2
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/AGENTS.md +75 -20
- package/README.md +832 -285
- package/dist/adapters/cursor/cursor.d.ts +2 -1
- package/dist/adapters/cursor/cursor.d.ts.map +1 -1
- package/dist/adapters/cursor/cursor.js +87 -1
- package/dist/adapters/cursor/cursor.js.map +1 -1
- package/dist/adapters/discord-app/discord-app.d.ts +1 -1
- package/dist/adapters/discord-app/discord-app.js +94 -1
- package/dist/adapters/discord-app/discord-app.js.map +1 -1
- package/dist/browser/bridge.d.ts +45 -0
- package/dist/browser/bridge.d.ts.map +1 -1
- package/dist/browser/bridge.js +94 -1
- package/dist/browser/bridge.js.map +1 -1
- package/dist/browser/cdp-client.d.ts +24 -1
- package/dist/browser/cdp-client.d.ts.map +1 -1
- package/dist/browser/cdp-client.js +56 -2
- package/dist/browser/cdp-client.js.map +1 -1
- package/dist/browser/launcher.d.ts +5 -0
- package/dist/browser/launcher.d.ts.map +1 -1
- package/dist/browser/launcher.js +7 -0
- package/dist/browser/launcher.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +72 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/agents.d.ts +14 -3
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +369 -140
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/browser.d.ts.map +1 -1
- package/dist/commands/browser.js +79 -1
- package/dist/commands/browser.js.map +1 -1
- package/dist/commands/ext.d.ts +11 -0
- package/dist/commands/ext.d.ts.map +1 -0
- package/dist/commands/ext.js +122 -0
- package/dist/commands/ext.js.map +1 -0
- package/dist/commands/mcp.d.ts +3 -4
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +47 -62
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/schema.d.ts +12 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +72 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/search.d.ts +12 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +47 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/skills.d.ts +2 -3
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +2 -3
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/status.d.ts +12 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +137 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/discovery/aliases.d.ts +31 -0
- package/dist/discovery/aliases.d.ts.map +1 -0
- package/dist/discovery/aliases.js +477 -0
- package/dist/discovery/aliases.js.map +1 -0
- package/dist/discovery/loader.d.ts.map +1 -1
- package/dist/discovery/loader.js +25 -0
- package/dist/discovery/loader.js.map +1 -1
- package/dist/discovery/search.d.ts +73 -0
- package/dist/discovery/search.d.ts.map +1 -0
- package/dist/discovery/search.js +355 -0
- package/dist/discovery/search.js.map +1 -0
- package/dist/engine/yaml-runner.d.ts +10 -0
- package/dist/engine/yaml-runner.d.ts.map +1 -1
- package/dist/engine/yaml-runner.js +51 -3
- package/dist/engine/yaml-runner.js.map +1 -1
- package/dist/hub/index.d.ts +37 -0
- package/dist/hub/index.d.ts.map +1 -0
- package/dist/hub/index.js +98 -0
- package/dist/hub/index.js.map +1 -0
- package/dist/hub/passthrough.d.ts +21 -0
- package/dist/hub/passthrough.d.ts.map +1 -0
- package/dist/hub/passthrough.js +71 -0
- package/dist/hub/passthrough.js.map +1 -0
- package/dist/manifest-compact.txt +15 -0
- package/dist/manifest-search.json +1 -0
- package/dist/manifest.json +2163 -268
- package/dist/mcp/oauth.d.ts +33 -0
- package/dist/mcp/oauth.d.ts.map +1 -0
- package/dist/mcp/oauth.js +220 -0
- package/dist/mcp/oauth.js.map +1 -0
- package/dist/mcp/schema.d.ts +65 -0
- package/dist/mcp/schema.d.ts.map +1 -0
- package/dist/mcp/schema.js +136 -0
- package/dist/mcp/schema.js.map +1 -0
- package/dist/mcp/server.d.ts +23 -10
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +350 -182
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/sse-transport.d.ts +34 -0
- package/dist/mcp/sse-transport.d.ts.map +1 -0
- package/dist/mcp/sse-transport.js +182 -0
- package/dist/mcp/sse-transport.js.map +1 -0
- package/dist/mcp/streamable-http.d.ts +64 -0
- package/dist/mcp/streamable-http.d.ts.map +1 -0
- package/dist/mcp/streamable-http.js +312 -0
- package/dist/mcp/streamable-http.js.map +1 -0
- package/dist/permissions/sensitive-paths.js +2 -2
- package/dist/permissions/sensitive-paths.js.map +1 -1
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +8 -7
- package/src/adapters/1688/_site.json +9 -0
- package/src/adapters/36kr/latest.yaml +36 -0
- package/src/adapters/arxiv/trending.yaml +37 -0
- package/src/adapters/baidu/hot.yaml +37 -0
- package/src/adapters/baidu/search.yaml +41 -0
- package/src/adapters/barchart/_site.json +10 -0
- package/src/adapters/bbc/technology.yaml +26 -0
- package/src/adapters/bbc/top.yaml +26 -0
- package/src/adapters/bbc/world.yaml +26 -0
- package/src/adapters/bilibili/coin.yaml +33 -0
- package/src/adapters/bilibili/later.yaml +31 -0
- package/src/adapters/bilibili/live.yaml +41 -0
- package/src/adapters/bilibili/trending.yaml +29 -0
- package/src/adapters/binance/hot.yaml +39 -0
- package/src/adapters/binance/kline.yaml +35 -0
- package/src/adapters/binance/ticker.yaml +33 -0
- package/src/adapters/bluesky/likes.yaml +35 -0
- package/src/adapters/bluesky/notifications.yaml +30 -0
- package/src/adapters/bluesky/post.yaml +25 -0
- package/src/adapters/chrome/bookmarks.yaml +26 -0
- package/src/adapters/chrome/tabs.yaml +25 -0
- package/src/adapters/cnki/search.yaml +40 -0
- package/src/adapters/cnn/technology.yaml +26 -0
- package/src/adapters/cocoapods/info.yaml +33 -0
- package/src/adapters/coinbase/prices.yaml +27 -0
- package/src/adapters/coinbase/rates.yaml +24 -0
- package/src/adapters/coupang/hot.yaml +29 -0
- package/src/adapters/crates-io/info.yaml +29 -0
- package/src/adapters/crates-io/versions.yaml +33 -0
- package/src/adapters/ctrip/hot.yaml +28 -0
- package/src/adapters/cursor/cursor.ts +91 -1
- package/src/adapters/dangdang/hot.yaml +38 -0
- package/src/adapters/dangdang/search.yaml +43 -0
- package/src/adapters/deepseek/chat.yaml +33 -0
- package/src/adapters/deepseek/models.yaml +18 -0
- package/src/adapters/devto/latest.yaml +29 -0
- package/src/adapters/devto/search.yaml +34 -0
- package/src/adapters/dianping/hot.yaml +43 -0
- package/src/adapters/dianping/search.yaml +48 -0
- package/src/adapters/discord-app/discord-app.ts +99 -1
- package/src/adapters/docker/networks.yaml +20 -0
- package/src/adapters/docker/volumes.yaml +19 -0
- package/src/adapters/docker-hub/info.yaml +26 -0
- package/src/adapters/docker-hub/tags.yaml +34 -0
- package/src/adapters/douban/group-hot.yaml +29 -0
- package/src/adapters/douban/new-movies.yaml +32 -0
- package/src/adapters/douban/tv-hot.yaml +32 -0
- package/src/adapters/doubao/ask.yaml +64 -0
- package/src/adapters/doubao/new.yaml +18 -0
- package/src/adapters/doubao/status.yaml +21 -0
- package/src/adapters/douyu/hot.yaml +34 -0
- package/src/adapters/douyu/search.yaml +42 -0
- package/src/adapters/eastmoney/fund.yaml +36 -0
- package/src/adapters/eastmoney/market.yaml +26 -0
- package/src/adapters/ele/hot.yaml +40 -0
- package/src/adapters/ele/search.yaml +47 -0
- package/src/adapters/exchangerate/list.yaml +25 -0
- package/src/adapters/facebook/marketplace.yaml +43 -0
- package/src/adapters/facebook/post.yaml +29 -0
- package/src/adapters/futu/hot.yaml +49 -0
- package/src/adapters/futu/quote.yaml +46 -0
- package/src/adapters/gitee/repos.yaml +34 -0
- package/src/adapters/github-trending/developers.yaml +33 -0
- package/src/adapters/github-trending/weekly.yaml +34 -0
- package/src/adapters/gitlab/projects.yaml +34 -0
- package/src/adapters/godot/scene-export.yaml +1 -1
- package/src/adapters/hackernews/comments.yaml +36 -0
- package/src/adapters/hackernews/item.yaml +28 -0
- package/src/adapters/hf/datasets.yaml +34 -0
- package/src/adapters/hf/spaces.yaml +34 -0
- package/src/adapters/homebrew/search.yaml +27 -0
- package/src/adapters/huggingface-papers/search.yaml +33 -0
- package/src/adapters/imdb/box-office.yaml +28 -0
- package/src/adapters/infoq/latest.yaml +26 -0
- package/src/adapters/instagram/activity.yaml +45 -0
- package/src/adapters/instagram/highlights.yaml +48 -0
- package/src/adapters/instagram/reels-trending.yaml +51 -0
- package/src/adapters/instagram/reels.yaml +34 -0
- package/src/adapters/instagram/stories.yaml +29 -0
- package/src/adapters/instagram/suggested.yaml +40 -0
- package/src/adapters/instagram/tags.yaml +52 -0
- package/src/adapters/itch-io/search.yaml +35 -0
- package/src/adapters/itch-io/top.yaml +31 -0
- package/src/adapters/ithome/hot.yaml +27 -0
- package/src/adapters/ithome/latest.yaml +26 -0
- package/src/adapters/jd/_site.json +9 -0
- package/src/adapters/jianyu/search.yaml +42 -0
- package/src/adapters/ke/ershoufang.yaml +75 -0
- package/src/adapters/ke/xiaoqu.yaml +73 -0
- package/src/adapters/kuaishou/hot.yaml +39 -0
- package/src/adapters/kuaishou/search.yaml +44 -0
- package/src/adapters/linkedin/_site.json +10 -0
- package/src/adapters/linkedin/jobs.yaml +47 -0
- package/src/adapters/lobsters/search.yaml +35 -0
- package/src/adapters/macos/active-app.yaml +16 -0
- package/src/adapters/macos/apps-list.yaml +14 -0
- package/src/adapters/macos/apps.yaml +31 -0
- package/src/adapters/macos/battery.yaml +26 -0
- package/src/adapters/macos/bluetooth.yaml +14 -0
- package/src/adapters/macos/brightness.yaml +22 -0
- package/src/adapters/macos/caffeinate.yaml +25 -0
- package/src/adapters/macos/calendar-create.yaml +81 -0
- package/src/adapters/macos/calendar-list.yaml +53 -0
- package/src/adapters/macos/calendar-today.yaml +30 -0
- package/src/adapters/macos/clipboard.yaml +23 -0
- package/src/adapters/macos/contacts-search.yaml +59 -0
- package/src/adapters/macos/dark-mode.yaml +19 -0
- package/src/adapters/macos/disk-info.yaml +31 -0
- package/src/adapters/macos/disk-usage.yaml +14 -0
- package/src/adapters/macos/do-not-disturb.yaml +21 -0
- package/src/adapters/macos/empty-trash.yaml +16 -0
- package/src/adapters/macos/finder-copy.yaml +40 -0
- package/src/adapters/macos/finder-move.yaml +40 -0
- package/src/adapters/macos/finder-new-folder.yaml +36 -0
- package/src/adapters/macos/finder-recent.yaml +24 -0
- package/src/adapters/macos/finder-selection.yaml +22 -0
- package/src/adapters/macos/finder-tags.yaml +40 -0
- package/src/adapters/macos/lock-screen.yaml +16 -0
- package/src/adapters/macos/mail-send.yaml +54 -0
- package/src/adapters/macos/mail-status.yaml +38 -0
- package/src/adapters/macos/messages-send.yaml +42 -0
- package/src/adapters/macos/music-control.yaml +43 -0
- package/src/adapters/macos/music-now.yaml +34 -0
- package/src/adapters/macos/notes-list.yaml +30 -0
- package/src/adapters/macos/notes-search.yaml +45 -0
- package/src/adapters/macos/notification.yaml +27 -0
- package/src/adapters/macos/notify.yaml +46 -0
- package/src/adapters/macos/open-app.yaml +21 -0
- package/src/adapters/macos/open.yaml +24 -0
- package/src/adapters/macos/photos-search.yaml +44 -0
- package/src/adapters/macos/processes.yaml +14 -0
- package/src/adapters/macos/reminder-create.yaml +75 -0
- package/src/adapters/macos/reminders-complete.yaml +33 -0
- package/src/adapters/macos/reminders-list.yaml +28 -0
- package/src/adapters/macos/safari-history.yaml +23 -0
- package/src/adapters/macos/safari-tabs.yaml +26 -0
- package/src/adapters/macos/safari-url.yaml +22 -0
- package/src/adapters/macos/say.yaml +36 -0
- package/src/adapters/macos/screen-lock.yaml +16 -0
- package/src/adapters/macos/screen-recording.yaml +32 -0
- package/src/adapters/macos/screenshot.yaml +34 -0
- package/src/adapters/macos/shortcuts-list.yaml +14 -0
- package/src/adapters/macos/shortcuts-run.yaml +28 -0
- package/src/adapters/macos/sleep.yaml +14 -0
- package/src/adapters/macos/spotlight.yaml +43 -0
- package/src/adapters/macos/system-info.yaml +32 -0
- package/src/adapters/macos/trash.yaml +32 -0
- package/src/adapters/macos/uptime.yaml +14 -0
- package/src/adapters/macos/volume.yaml +19 -0
- package/src/adapters/macos/wallpaper.yaml +33 -0
- package/src/adapters/macos/wifi-info.yaml +14 -0
- package/src/adapters/macos/wifi.yaml +63 -0
- package/src/adapters/maimai/search.yaml +62 -0
- package/src/adapters/maoyan/hot.yaml +33 -0
- package/src/adapters/maoyan/search.yaml +40 -0
- package/src/adapters/mastodon/timeline.yaml +33 -0
- package/src/adapters/mastodon/user.yaml +32 -0
- package/src/adapters/medium/article.yaml +22 -0
- package/src/adapters/medium/trending.yaml +30 -0
- package/src/adapters/meituan/hot.yaml +26 -0
- package/src/adapters/mubu/list.yaml +36 -0
- package/src/adapters/mubu/search.yaml +54 -0
- package/src/adapters/netease-music/playlist.yaml +34 -0
- package/src/adapters/netease-music/top.yaml +33 -0
- package/src/adapters/notion/databases.yaml +41 -0
- package/src/adapters/notion/pages.yaml +44 -0
- package/src/adapters/notion/search.yaml +41 -0
- package/src/adapters/npm/downloads.yaml +28 -0
- package/src/adapters/npm/info.yaml +25 -0
- package/src/adapters/npm/search.yaml +35 -0
- package/src/adapters/npm/versions.yaml +33 -0
- package/src/adapters/npm-trends/trending.yaml +17 -0
- package/src/adapters/nytimes/search.yaml +35 -0
- package/src/adapters/obsidian/daily.yaml +23 -0
- package/src/adapters/obsidian/open.yaml +26 -0
- package/src/adapters/obsidian/search.yaml +28 -0
- package/src/adapters/ollama/generate.yaml +33 -0
- package/src/adapters/ollama/models.yaml +29 -0
- package/src/adapters/ollama/ps.yaml +20 -0
- package/src/adapters/openrouter/search.yaml +31 -0
- package/src/adapters/perplexity/ask.yaml +33 -0
- package/src/adapters/pexels/curated.yaml +31 -0
- package/src/adapters/pinduoduo/search.yaml +36 -0
- package/src/adapters/producthunt/search.yaml +41 -0
- package/src/adapters/pypi/search.yaml +37 -0
- package/src/adapters/pypi/versions.yaml +31 -0
- package/src/adapters/quark/ls.yaml +39 -0
- package/src/adapters/quark/search.yaml +39 -0
- package/src/adapters/qweather/forecast.yaml +37 -0
- package/src/adapters/reddit/new.yaml +34 -0
- package/src/adapters/reddit/rising.yaml +33 -0
- package/src/adapters/reddit/top.yaml +40 -0
- package/src/adapters/reddit/trending.yaml +25 -0
- package/src/adapters/replicate/run.yaml +39 -0
- package/src/adapters/reuters/_site.json +9 -0
- package/src/adapters/reuters/latest.yaml +26 -0
- package/src/adapters/sinablog/_site.json +9 -0
- package/src/adapters/sinafinance/market.yaml +21 -0
- package/src/adapters/slack/messages.yaml +41 -0
- package/src/adapters/slack/post.yaml +32 -0
- package/src/adapters/slack/search.yaml +28 -0
- package/src/adapters/slack/send.yaml +35 -0
- package/src/adapters/slack/status.yaml +19 -0
- package/src/adapters/slack/users.yaml +37 -0
- package/src/adapters/slock/servers.yaml +32 -0
- package/src/adapters/smzdm/_site.json +9 -0
- package/src/adapters/spotify/now-playing.yaml +28 -0
- package/src/adapters/spotify/playlists.yaml +37 -0
- package/src/adapters/spotify/top-tracks.yaml +42 -0
- package/src/adapters/stackoverflow/question.yaml +34 -0
- package/src/adapters/stackoverflow/tags.yaml +30 -0
- package/src/adapters/steam/app-details.yaml +32 -0
- package/src/adapters/steam/new-releases.yaml +28 -0
- package/src/adapters/steam/search.yaml +36 -0
- package/src/adapters/steam/specials.yaml +29 -0
- package/src/adapters/steam/wishlist.yaml +36 -0
- package/src/adapters/substack/trending.yaml +28 -0
- package/src/adapters/taobao/hot.yaml +31 -0
- package/src/adapters/taobao/search.yaml +44 -0
- package/src/adapters/techcrunch/search.yaml +33 -0
- package/src/adapters/theverge/search.yaml +33 -0
- package/src/adapters/threads/hot.yaml +44 -0
- package/src/adapters/threads/search.yaml +47 -0
- package/src/adapters/tiktok/trending.yaml +28 -0
- package/src/adapters/toutiao/hot.yaml +31 -0
- package/src/adapters/toutiao/search.yaml +47 -0
- package/src/adapters/twitch/games.yaml +31 -0
- package/src/adapters/twitch/search.yaml +39 -0
- package/src/adapters/twitch/streams.yaml +38 -0
- package/src/adapters/twitter/lists.yaml +49 -0
- package/src/adapters/twitter/media.yaml +48 -0
- package/src/adapters/twitter/mentions.yaml +38 -0
- package/src/adapters/twitter/mute.yaml +42 -0
- package/src/adapters/twitter/pin.yaml +43 -0
- package/src/adapters/twitter/quotes.yaml +43 -0
- package/src/adapters/twitter/retweets.yaml +44 -0
- package/src/adapters/twitter/spaces.yaml +43 -0
- package/src/adapters/twitter/unmute.yaml +42 -0
- package/src/adapters/unsplash/random.yaml +27 -0
- package/src/adapters/v2ex/search.yaml +36 -0
- package/src/adapters/vscode/extensions.yaml +23 -0
- package/src/adapters/vscode/install-ext.yaml +26 -0
- package/src/adapters/vscode/open.yaml +25 -0
- package/src/adapters/wechat-channels/hot.yaml +39 -0
- package/src/adapters/wechat-channels/search.yaml +57 -0
- package/src/adapters/weibo/trending.yaml +28 -0
- package/src/adapters/weixin/_site.json +9 -0
- package/src/adapters/weixin/hot.yaml +22 -0
- package/src/adapters/wikipedia/today.yaml +25 -0
- package/src/adapters/xiaohongshu/follow.yaml +35 -0
- package/src/adapters/xiaohongshu/hashtag.yaml +44 -0
- package/src/adapters/xiaohongshu/hot.yaml +34 -0
- package/src/adapters/xiaohongshu/like.yaml +27 -0
- package/src/adapters/xiaohongshu/profile.yaml +39 -0
- package/src/adapters/xiaohongshu/save.yaml +27 -0
- package/src/adapters/xiaohongshu/suggest.yaml +34 -0
- package/src/adapters/xiaohongshu/trending.yaml +35 -0
- package/src/adapters/xiaohongshu/unfollow.yaml +40 -0
- package/src/adapters/xueqiu/market.yaml +25 -0
- package/src/adapters/yahoo-finance/quote.yaml +35 -0
- package/src/adapters/yahoo-finance/search.yaml +42 -0
- package/src/adapters/yahoo-finance/trending.yaml +30 -0
- package/src/adapters/youtube/playlist.yaml +43 -0
- package/src/adapters/youtube/shorts.yaml +42 -0
- package/src/adapters/youtube/trending.yaml +41 -0
- package/src/adapters/zhihu/answer.yaml +29 -0
- package/src/adapters/zhihu/answers.yaml +38 -0
- package/src/adapters/zhihu/article.yaml +27 -0
- package/src/adapters/zhihu/articles.yaml +38 -0
- package/src/adapters/zhihu/collections.yaml +37 -0
- package/src/adapters/zhihu/columns.yaml +38 -0
- package/src/adapters/zhihu/comment.yaml +42 -0
- package/src/adapters/zhihu/followers.yaml +37 -0
- package/src/adapters/zhihu/following.yaml +37 -0
- package/src/adapters/zhihu/pins.yaml +37 -0
- package/src/adapters/zhihu/topic.yaml +35 -0
- package/src/adapters/zhihu/topics.yaml +37 -0
- package/src/adapters/zhihu/trending.yaml +36 -0
- package/src/adapters/zhihu/user.yaml +30 -0
- package/src/adapters/zoom/join.yaml +24 -0
- package/src/adapters/zoom/start.yaml +17 -0
- package/src/hub/external-clis.yaml +343 -0
- package/src/hub/index.ts +128 -0
- package/src/hub/passthrough.ts +85 -0
- package/src/adapters/1688/manifest.yaml +0 -7
- package/src/adapters/barchart/manifest.yaml +0 -8
- package/src/adapters/jd/manifest.yaml +0 -7
- package/src/adapters/linkedin/manifest.yaml +0 -8
- package/src/adapters/reuters/manifest.yaml +0 -7
- package/src/adapters/sinablog/manifest.yaml +0 -7
- package/src/adapters/smzdm/manifest.yaml +0 -7
- package/src/adapters/weixin/manifest.yaml +0 -7
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BM25-based bilingual search engine for command discovery.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the naive `String.includes()` filter with a proper information
|
|
5
|
+
* retrieval algorithm. Designed for ~1000 commands across ~200 sites.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* 1. Build-time: `scripts/build-manifest.js` generates the inverted index
|
|
9
|
+
* and IDF values, shipped as `dist/manifest-search.json`.
|
|
10
|
+
* 2. Runtime: this module loads the index lazily on first search call,
|
|
11
|
+
* then scores queries using BM25 with bilingual keyword expansion.
|
|
12
|
+
*
|
|
13
|
+
* Performance: <10ms for 1000 documents on a cold index load. The inverted
|
|
14
|
+
* index is ~50KB — small enough to hold in memory permanently.
|
|
15
|
+
*/
|
|
16
|
+
export interface SearchResult {
|
|
17
|
+
site: string;
|
|
18
|
+
command: string;
|
|
19
|
+
description: string;
|
|
20
|
+
score: number;
|
|
21
|
+
usage: string;
|
|
22
|
+
category: string;
|
|
23
|
+
}
|
|
24
|
+
/** Serialized search index (generated at build time, loaded at runtime). */
|
|
25
|
+
export interface SearchIndex {
|
|
26
|
+
/** Mapping: term → list of document indices that contain this term */
|
|
27
|
+
postings: Record<string, number[]>;
|
|
28
|
+
/** IDF (Inverse Document Frequency) for each term */
|
|
29
|
+
idf: Record<string, number>;
|
|
30
|
+
/** All documents with their metadata */
|
|
31
|
+
documents: Array<{
|
|
32
|
+
id: string;
|
|
33
|
+
site: string;
|
|
34
|
+
command: string;
|
|
35
|
+
description: string;
|
|
36
|
+
terms: string[];
|
|
37
|
+
}>;
|
|
38
|
+
/** Average document length (term count) across the corpus */
|
|
39
|
+
avgDl: number;
|
|
40
|
+
/** Total document count */
|
|
41
|
+
N: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build a search index from a manifest object.
|
|
45
|
+
* Exported for use by the build script.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildIndex(manifest: {
|
|
48
|
+
sites: Record<string, {
|
|
49
|
+
commands: Array<{
|
|
50
|
+
name: string;
|
|
51
|
+
description: string;
|
|
52
|
+
}>;
|
|
53
|
+
}>;
|
|
54
|
+
}): SearchIndex;
|
|
55
|
+
/**
|
|
56
|
+
* Search all commands by natural language query.
|
|
57
|
+
*
|
|
58
|
+
* Algorithm:
|
|
59
|
+
* 1. Tokenize query (bilingual-aware)
|
|
60
|
+
* 2. Expand tokens via alias table
|
|
61
|
+
* 3. Compute BM25 base score for each candidate document
|
|
62
|
+
* 4. Apply boost signals (site match, command match, category match)
|
|
63
|
+
* 5. Return top-K results with usage examples
|
|
64
|
+
*
|
|
65
|
+
* @param query - Natural language query (Chinese or English)
|
|
66
|
+
* @param limit - Maximum results to return (default 5)
|
|
67
|
+
*/
|
|
68
|
+
export declare function search(query: string, limit?: number): SearchResult[];
|
|
69
|
+
/**
|
|
70
|
+
* Force-reload the search index (useful after index rebuild).
|
|
71
|
+
*/
|
|
72
|
+
export declare function invalidateCache(): void;
|
|
73
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/discovery/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAeH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAcD,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnC,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,wCAAwC;IACxC,SAAS,EAAE,KAAK,CAAC;QACf,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC,CAAC;IACH,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,CAAC,EAAE,MAAM,CAAC;CACX;AAkFD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE;IACnC,KAAK,EAAE,MAAM,CACX,MAAM,EACN;QAAE,QAAQ,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAC3D,CAAC;CACH,GAAG,WAAW,CAsDd;AAoID;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,YAAY,EAAE,CA0G/D;AASD;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BM25-based bilingual search engine for command discovery.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the naive `String.includes()` filter with a proper information
|
|
5
|
+
* retrieval algorithm. Designed for ~1000 commands across ~200 sites.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* 1. Build-time: `scripts/build-manifest.js` generates the inverted index
|
|
9
|
+
* and IDF values, shipped as `dist/manifest-search.json`.
|
|
10
|
+
* 2. Runtime: this module loads the index lazily on first search call,
|
|
11
|
+
* then scores queries using BM25 with bilingual keyword expansion.
|
|
12
|
+
*
|
|
13
|
+
* Performance: <10ms for 1000 documents on a cold index load. The inverted
|
|
14
|
+
* index is ~50KB — small enough to hold in memory permanently.
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
17
|
+
import { join, dirname } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { expandToken, tokenizeQuery, SITE_ALIASES, SITE_CATEGORIES, CATEGORY_ALIASES, } from "./aliases.js";
|
|
20
|
+
// ── BM25 Parameters ─────────────────────────────────────────────────────────
|
|
21
|
+
// Standard BM25 tuning. k1 controls term frequency saturation,
|
|
22
|
+
// b controls document length normalization.
|
|
23
|
+
const K1 = 1.2;
|
|
24
|
+
const B = 0.75;
|
|
25
|
+
// ── Hybrid Scoring ──────────────────────────────────────────────────────────
|
|
26
|
+
// StackOne benchmark (Feb 2026, 2700 test cases, 270 tools) found:
|
|
27
|
+
// Pure BM25 Top-1: 14%
|
|
28
|
+
// BM25+TF-IDF 20/80 blend Top-1: 21%
|
|
29
|
+
// Embedding Top-1: 38%
|
|
30
|
+
// We use the 20/80 blend as the base, with domain-specific boosts on top.
|
|
31
|
+
const ALPHA_BM25 = 0.2;
|
|
32
|
+
const ALPHA_TFIDF = 0.8;
|
|
33
|
+
// ── Score Boost Weights ─────────────────────────────────────────────────────
|
|
34
|
+
// Applied on top of the hybrid BM25+TF-IDF base score.
|
|
35
|
+
const BOOST_SITE_EXACT = 15.0; // Query token exactly matches site name
|
|
36
|
+
const BOOST_SITE_ALIAS = 12.0; // Query token's alias matches site name
|
|
37
|
+
const BOOST_CMD_EXACT = 8.0; // Query token exactly matches command name
|
|
38
|
+
const BOOST_CMD_PARTIAL = 3.0; // Query token is substring of command name
|
|
39
|
+
const BOOST_CATEGORY = 2.0; // Query token matches site's category
|
|
40
|
+
// ── Index Management ────────────────────────────────────────────────────────
|
|
41
|
+
let cachedIndex = null;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the path to the pre-built search index.
|
|
44
|
+
* Falls back to building one on-the-fly from the manifest if the
|
|
45
|
+
* pre-built index doesn't exist.
|
|
46
|
+
*/
|
|
47
|
+
function getIndexPath() {
|
|
48
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
49
|
+
return join(__dirname, "..", "..", "dist", "manifest-search.json");
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Load or build the search index. Called lazily on first search.
|
|
53
|
+
*/
|
|
54
|
+
function loadIndex() {
|
|
55
|
+
if (cachedIndex)
|
|
56
|
+
return cachedIndex;
|
|
57
|
+
const indexPath = getIndexPath();
|
|
58
|
+
if (existsSync(indexPath)) {
|
|
59
|
+
cachedIndex = JSON.parse(readFileSync(indexPath, "utf-8"));
|
|
60
|
+
return cachedIndex;
|
|
61
|
+
}
|
|
62
|
+
// Fallback: build index on-the-fly from manifest.json
|
|
63
|
+
cachedIndex = buildIndexFromManifest();
|
|
64
|
+
return cachedIndex;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build a search index from the manifest.json file.
|
|
68
|
+
* Used when the pre-built search index doesn't exist (dev mode).
|
|
69
|
+
*/
|
|
70
|
+
function buildIndexFromManifest() {
|
|
71
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
72
|
+
const manifestPath = join(__dirname, "..", "..", "dist", "manifest.json");
|
|
73
|
+
if (!existsSync(manifestPath)) {
|
|
74
|
+
// No manifest either — return empty index
|
|
75
|
+
return { postings: {}, idf: {}, documents: [], avgDl: 0, N: 0 };
|
|
76
|
+
}
|
|
77
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
78
|
+
return buildIndex(manifest);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Build a search index from a manifest object.
|
|
82
|
+
* Exported for use by the build script.
|
|
83
|
+
*/
|
|
84
|
+
export function buildIndex(manifest) {
|
|
85
|
+
const documents = [];
|
|
86
|
+
for (const [site, info] of Object.entries(manifest.sites)) {
|
|
87
|
+
for (const cmd of info.commands) {
|
|
88
|
+
const terms = tokenizeDocument(site, cmd.name, cmd.description ?? "");
|
|
89
|
+
documents.push({
|
|
90
|
+
id: `${site}/${cmd.name}`,
|
|
91
|
+
site,
|
|
92
|
+
command: cmd.name,
|
|
93
|
+
description: cmd.description ?? "",
|
|
94
|
+
terms,
|
|
95
|
+
termCount: terms.length,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const N = documents.length;
|
|
100
|
+
const avgDl = N > 0 ? documents.reduce((sum, d) => sum + d.termCount, 0) / N : 0;
|
|
101
|
+
// Build inverted index
|
|
102
|
+
const postings = {};
|
|
103
|
+
for (let i = 0; i < documents.length; i++) {
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
for (const term of documents[i].terms) {
|
|
106
|
+
if (seen.has(term))
|
|
107
|
+
continue;
|
|
108
|
+
seen.add(term);
|
|
109
|
+
if (!postings[term])
|
|
110
|
+
postings[term] = [];
|
|
111
|
+
postings[term].push(i);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Compute IDF for each term
|
|
115
|
+
const idf = {};
|
|
116
|
+
for (const [term, docs] of Object.entries(postings)) {
|
|
117
|
+
// BM25 IDF: log((N - df + 0.5) / (df + 0.5) + 1)
|
|
118
|
+
const df = docs.length;
|
|
119
|
+
idf[term] = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
postings,
|
|
123
|
+
idf,
|
|
124
|
+
documents: documents.map((d) => ({
|
|
125
|
+
id: d.id,
|
|
126
|
+
site: d.site,
|
|
127
|
+
command: d.command,
|
|
128
|
+
description: d.description,
|
|
129
|
+
terms: d.terms,
|
|
130
|
+
})),
|
|
131
|
+
avgDl,
|
|
132
|
+
N,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Tokenize a document (site + command + description) into search terms.
|
|
137
|
+
* Produces lowercase terms, splits on hyphens and underscores.
|
|
138
|
+
*/
|
|
139
|
+
function tokenizeDocument(site, command, description) {
|
|
140
|
+
const terms = [];
|
|
141
|
+
// Site name and its parts
|
|
142
|
+
const siteParts = site.toLowerCase().split(/[-_]/);
|
|
143
|
+
terms.push(site.toLowerCase(), ...siteParts);
|
|
144
|
+
// Command name and its parts
|
|
145
|
+
const cmdParts = command.toLowerCase().split(/[-_]/);
|
|
146
|
+
terms.push(command.toLowerCase(), ...cmdParts);
|
|
147
|
+
// Description words (lowercase, filter short words)
|
|
148
|
+
const descWords = description
|
|
149
|
+
.toLowerCase()
|
|
150
|
+
.replace(/[^a-z0-9\u4e00-\u9fff\s]/g, " ")
|
|
151
|
+
.split(/\s+/)
|
|
152
|
+
.filter((w) => w.length > 1);
|
|
153
|
+
terms.push(...descWords);
|
|
154
|
+
// Category as a term
|
|
155
|
+
const category = SITE_CATEGORIES.get(site);
|
|
156
|
+
if (category)
|
|
157
|
+
terms.push(category);
|
|
158
|
+
return terms;
|
|
159
|
+
}
|
|
160
|
+
// ── BM25 Scoring ────────────────────────────────────────────────────────────
|
|
161
|
+
/**
|
|
162
|
+
* Compute BM25 score for a single document given query terms.
|
|
163
|
+
*/
|
|
164
|
+
function bm25Score(docTerms, docLength, queryTerms, index) {
|
|
165
|
+
let score = 0;
|
|
166
|
+
// Count term frequencies in this document
|
|
167
|
+
const tf = new Map();
|
|
168
|
+
for (const term of docTerms) {
|
|
169
|
+
tf.set(term, (tf.get(term) ?? 0) + 1);
|
|
170
|
+
}
|
|
171
|
+
for (const qt of queryTerms) {
|
|
172
|
+
const termIdf = index.idf[qt];
|
|
173
|
+
if (termIdf === undefined)
|
|
174
|
+
continue; // term not in corpus
|
|
175
|
+
const termTf = tf.get(qt) ?? 0;
|
|
176
|
+
if (termTf === 0)
|
|
177
|
+
continue;
|
|
178
|
+
// BM25 TF component: (tf * (k1 + 1)) / (tf + k1 * (1 - b + b * dl/avgdl))
|
|
179
|
+
const numerator = termTf * (K1 + 1);
|
|
180
|
+
const denominator = termTf + K1 * (1 - B + B * (docLength / index.avgDl));
|
|
181
|
+
score += termIdf * (numerator / denominator);
|
|
182
|
+
}
|
|
183
|
+
return score;
|
|
184
|
+
}
|
|
185
|
+
// ── TF-IDF Cosine Similarity ────────────────────────────────────────────────
|
|
186
|
+
/**
|
|
187
|
+
* Compute TF-IDF cosine similarity between a query and a document.
|
|
188
|
+
*
|
|
189
|
+
* TF-IDF for a term t in document d:
|
|
190
|
+
* tf(t,d) = count(t in d) / |d|
|
|
191
|
+
* tfidf(t,d) = tf(t,d) * idf(t)
|
|
192
|
+
*
|
|
193
|
+
* Cosine similarity = dot(query_vec, doc_vec) / (|query_vec| * |doc_vec|)
|
|
194
|
+
*/
|
|
195
|
+
function tfidfCosine(docTerms, queryTerms, index) {
|
|
196
|
+
const docLen = docTerms.length;
|
|
197
|
+
if (docLen === 0)
|
|
198
|
+
return 0;
|
|
199
|
+
// Build full doc TF map
|
|
200
|
+
const docTf = new Map();
|
|
201
|
+
for (const term of docTerms) {
|
|
202
|
+
docTf.set(term, (docTf.get(term) ?? 0) + 1);
|
|
203
|
+
}
|
|
204
|
+
// Compute full document norm (all terms, not just query overlap)
|
|
205
|
+
let docNormSq = 0;
|
|
206
|
+
for (const [term, count] of docTf) {
|
|
207
|
+
const idfVal = index.idf[term];
|
|
208
|
+
if (idfVal === undefined)
|
|
209
|
+
continue;
|
|
210
|
+
const w = (count / docLen) * idfVal;
|
|
211
|
+
docNormSq += w * w;
|
|
212
|
+
}
|
|
213
|
+
// Compute query norm and dot product
|
|
214
|
+
let dotProduct = 0;
|
|
215
|
+
let queryNormSq = 0;
|
|
216
|
+
for (const qt of queryTerms) {
|
|
217
|
+
const idfVal = index.idf[qt];
|
|
218
|
+
if (idfVal === undefined)
|
|
219
|
+
continue;
|
|
220
|
+
// Query TF-IDF: binary tf (1) × idf
|
|
221
|
+
const queryWeight = idfVal;
|
|
222
|
+
queryNormSq += queryWeight * queryWeight;
|
|
223
|
+
// Doc TF-IDF: normalized tf × idf
|
|
224
|
+
const rawTf = docTf.get(qt) ?? 0;
|
|
225
|
+
if (rawTf === 0)
|
|
226
|
+
continue;
|
|
227
|
+
const docWeight = (rawTf / docLen) * idfVal;
|
|
228
|
+
dotProduct += queryWeight * docWeight;
|
|
229
|
+
}
|
|
230
|
+
const normProduct = Math.sqrt(queryNormSq) * Math.sqrt(docNormSq);
|
|
231
|
+
if (normProduct === 0)
|
|
232
|
+
return 0;
|
|
233
|
+
return dotProduct / normProduct;
|
|
234
|
+
}
|
|
235
|
+
// ── Main Search Function ────────────────────────────────────────────────────
|
|
236
|
+
/**
|
|
237
|
+
* Search all commands by natural language query.
|
|
238
|
+
*
|
|
239
|
+
* Algorithm:
|
|
240
|
+
* 1. Tokenize query (bilingual-aware)
|
|
241
|
+
* 2. Expand tokens via alias table
|
|
242
|
+
* 3. Compute BM25 base score for each candidate document
|
|
243
|
+
* 4. Apply boost signals (site match, command match, category match)
|
|
244
|
+
* 5. Return top-K results with usage examples
|
|
245
|
+
*
|
|
246
|
+
* @param query - Natural language query (Chinese or English)
|
|
247
|
+
* @param limit - Maximum results to return (default 5)
|
|
248
|
+
*/
|
|
249
|
+
export function search(query, limit = 5) {
|
|
250
|
+
const index = loadIndex();
|
|
251
|
+
if (index.N === 0)
|
|
252
|
+
return [];
|
|
253
|
+
// Step 1: Tokenize
|
|
254
|
+
const rawTokens = tokenizeQuery(query);
|
|
255
|
+
// Step 2: Expand via aliases
|
|
256
|
+
const expandedTerms = [];
|
|
257
|
+
const siteHints = []; // Directly matched site names
|
|
258
|
+
const categoryHints = []; // Matched categories
|
|
259
|
+
for (const token of rawTokens) {
|
|
260
|
+
const expanded = expandToken(token);
|
|
261
|
+
expandedTerms.push(...expanded);
|
|
262
|
+
// Check if this token resolves to a site name
|
|
263
|
+
const siteMatch = SITE_ALIASES.get(token) ?? SITE_ALIASES.get(token.toLowerCase());
|
|
264
|
+
if (siteMatch)
|
|
265
|
+
siteHints.push(siteMatch);
|
|
266
|
+
// Check if this token is directly a known site
|
|
267
|
+
if (index.documents.some((d) => d.site === token.toLowerCase())) {
|
|
268
|
+
siteHints.push(token.toLowerCase());
|
|
269
|
+
}
|
|
270
|
+
// Check category alias
|
|
271
|
+
const catMatch = CATEGORY_ALIASES.get(token) ?? CATEGORY_ALIASES.get(token.toLowerCase());
|
|
272
|
+
if (catMatch)
|
|
273
|
+
categoryHints.push(catMatch);
|
|
274
|
+
}
|
|
275
|
+
const queryTerms = [...new Set(expandedTerms.map((t) => t.toLowerCase()))];
|
|
276
|
+
// Step 3: Find candidate documents (union of posting lists)
|
|
277
|
+
const candidateSet = new Set();
|
|
278
|
+
for (const qt of queryTerms) {
|
|
279
|
+
const postings = index.postings[qt];
|
|
280
|
+
if (postings) {
|
|
281
|
+
for (const docIdx of postings) {
|
|
282
|
+
candidateSet.add(docIdx);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// If site hints exist, also add ALL commands for those sites
|
|
287
|
+
if (siteHints.length > 0) {
|
|
288
|
+
for (let i = 0; i < index.documents.length; i++) {
|
|
289
|
+
if (siteHints.includes(index.documents[i].site)) {
|
|
290
|
+
candidateSet.add(i);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (candidateSet.size === 0)
|
|
295
|
+
return [];
|
|
296
|
+
// Step 4: Score candidates using hybrid BM25 + TF-IDF
|
|
297
|
+
const scored = [];
|
|
298
|
+
for (const idx of candidateSet) {
|
|
299
|
+
const doc = index.documents[idx];
|
|
300
|
+
// Hybrid base: alpha-blend BM25 and TF-IDF cosine similarity.
|
|
301
|
+
// BM25 scores are unbounded; cosine is [0,1]. We scale cosine by the
|
|
302
|
+
// average BM25 score across candidates to keep the blend balanced.
|
|
303
|
+
const bm25 = bm25Score(doc.terms, doc.terms.length, queryTerms, index);
|
|
304
|
+
const tfidf = tfidfCosine(doc.terms, queryTerms, index);
|
|
305
|
+
let score = ALPHA_BM25 * bm25 + ALPHA_TFIDF * tfidf * 10;
|
|
306
|
+
// Boost: exact site name match
|
|
307
|
+
if (siteHints.includes(doc.site)) {
|
|
308
|
+
score += BOOST_SITE_EXACT;
|
|
309
|
+
}
|
|
310
|
+
// Boost: alias-resolved site match
|
|
311
|
+
for (const qt of queryTerms) {
|
|
312
|
+
if (qt === doc.site)
|
|
313
|
+
score += BOOST_SITE_ALIAS;
|
|
314
|
+
if (qt === doc.command)
|
|
315
|
+
score += BOOST_CMD_EXACT;
|
|
316
|
+
if (doc.command.includes(qt) && qt.length > 2)
|
|
317
|
+
score += BOOST_CMD_PARTIAL;
|
|
318
|
+
}
|
|
319
|
+
// Boost: category match
|
|
320
|
+
const docCategory = SITE_CATEGORIES.get(doc.site);
|
|
321
|
+
if (docCategory && categoryHints.includes(docCategory)) {
|
|
322
|
+
score += BOOST_CATEGORY;
|
|
323
|
+
}
|
|
324
|
+
if (score > 0)
|
|
325
|
+
scored.push({ idx, score });
|
|
326
|
+
}
|
|
327
|
+
// Step 5: Sort and return top-K
|
|
328
|
+
scored.sort((a, b) => b.score - a.score);
|
|
329
|
+
const topK = scored.slice(0, limit);
|
|
330
|
+
return topK.map(({ idx, score }) => {
|
|
331
|
+
const doc = index.documents[idx];
|
|
332
|
+
const category = SITE_CATEGORIES.get(doc.site) ?? "other";
|
|
333
|
+
return {
|
|
334
|
+
site: doc.site,
|
|
335
|
+
command: doc.command,
|
|
336
|
+
description: doc.description,
|
|
337
|
+
score: Math.round(score * 100) / 100,
|
|
338
|
+
usage: buildUsageExample(doc.site, doc.command),
|
|
339
|
+
category,
|
|
340
|
+
};
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Build a usage example string for a command.
|
|
345
|
+
*/
|
|
346
|
+
function buildUsageExample(site, command) {
|
|
347
|
+
return `unicli ${site} ${command}`;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Force-reload the search index (useful after index rebuild).
|
|
351
|
+
*/
|
|
352
|
+
export function invalidateCache() {
|
|
353
|
+
cachedIndex = null;
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/discovery/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -45,6 +45,10 @@ export declare class PipelineError extends Error {
|
|
|
45
45
|
statusCode?: number;
|
|
46
46
|
responsePreview?: string;
|
|
47
47
|
suggestion: string;
|
|
48
|
+
/** true for transient failures (timeout, 429, 5xx), false for permanent (404, auth, config) */
|
|
49
|
+
retryable?: boolean;
|
|
50
|
+
/** Fallback commands the agent can try when this command fails */
|
|
51
|
+
alternatives?: string[];
|
|
48
52
|
};
|
|
49
53
|
constructor(message: string, detail: {
|
|
50
54
|
step: number;
|
|
@@ -55,9 +59,15 @@ export declare class PipelineError extends Error {
|
|
|
55
59
|
statusCode?: number;
|
|
56
60
|
responsePreview?: string;
|
|
57
61
|
suggestion: string;
|
|
62
|
+
/** true for transient failures (timeout, 429, 5xx), false for permanent (404, auth, config) */
|
|
63
|
+
retryable?: boolean;
|
|
64
|
+
/** Fallback commands the agent can try when this command fails */
|
|
65
|
+
alternatives?: string[];
|
|
58
66
|
});
|
|
59
67
|
/** JSON output for AI agents — includes everything needed to self-repair */
|
|
60
68
|
toAgentJSON(adapterPath?: string): {
|
|
69
|
+
retryable: boolean;
|
|
70
|
+
alternatives: string[];
|
|
61
71
|
step: number;
|
|
62
72
|
action: string;
|
|
63
73
|
config: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml-runner.d.ts","sourceRoot":"","sources":["../../src/engine/yaml-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAkBH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAmBtD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,KAAK;aAGpB,MAAM,EAAE;QACtB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,EACL,YAAY,GACZ,eAAe,GACf,cAAc,GACd,aAAa,GACb,SAAS,GACT,kBAAkB,GAClB,kBAAkB,CAAC;QACvB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"yaml-runner.d.ts","sourceRoot":"","sources":["../../src/engine/yaml-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -49,6 +49,8 @@ export class PipelineError extends Error {
|
|
|
49
49
|
error: this.message,
|
|
50
50
|
adapter: adapterPath,
|
|
51
51
|
...this.detail,
|
|
52
|
+
retryable: this.detail.retryable ?? false,
|
|
53
|
+
alternatives: this.detail.alternatives ?? [],
|
|
52
54
|
};
|
|
53
55
|
}
|
|
54
56
|
}
|
|
@@ -170,6 +172,8 @@ export async function runPipeline(steps, args, base, options) {
|
|
|
170
172
|
config: { site: options.site, strategy: options.strategy },
|
|
171
173
|
errorType: "http_error",
|
|
172
174
|
suggestion: `Either start Chrome with "unicli browser start" and login to ${options.site}, or create cookie file at ~/.unicli/cookies/${options.site}.json`,
|
|
175
|
+
retryable: false,
|
|
176
|
+
alternatives: [`unicli auth setup ${options.site}`],
|
|
173
177
|
});
|
|
174
178
|
}
|
|
175
179
|
cookieHeader = formatCookieHeader(cookies);
|
|
@@ -353,12 +357,16 @@ export async function runPipeline(steps, args, base, options) {
|
|
|
353
357
|
}
|
|
354
358
|
if (err instanceof PipelineError)
|
|
355
359
|
throw err;
|
|
356
|
-
|
|
360
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
361
|
+
const isTransient = /timeout|ETIMEDOUT|ECONNREFUSED|ECONNRESET|socket hang up/i.test(errMsg);
|
|
362
|
+
throw new PipelineError(`Step ${i} (${action}) failed: ${errMsg}`, {
|
|
357
363
|
step: i,
|
|
358
364
|
action,
|
|
359
365
|
config,
|
|
360
|
-
errorType: "parse_error",
|
|
366
|
+
errorType: isTransient ? "timeout" : "parse_error",
|
|
361
367
|
suggestion: `Check the ${action} step at index ${i} in the adapter YAML. The expression or configuration may be invalid.`,
|
|
368
|
+
retryable: isTransient,
|
|
369
|
+
alternatives: [],
|
|
362
370
|
});
|
|
363
371
|
}
|
|
364
372
|
if (ctx.tempDir)
|
|
@@ -468,6 +476,8 @@ function assertionError(message, config, stepIndex) {
|
|
|
468
476
|
config,
|
|
469
477
|
errorType: "assertion_failed",
|
|
470
478
|
suggestion: "Check the assertion conditions in the adapter YAML. The page state may not match expectations.",
|
|
479
|
+
retryable: false,
|
|
480
|
+
alternatives: [],
|
|
471
481
|
});
|
|
472
482
|
}
|
|
473
483
|
async function stepFetch(ctx, config) {
|
|
@@ -619,6 +629,10 @@ async function fetchJson(url, config, cookieHeader) {
|
|
|
619
629
|
catch {
|
|
620
630
|
/* ignore */
|
|
621
631
|
}
|
|
632
|
+
const isRetryableStatus = resp.status === 429 ||
|
|
633
|
+
resp.status === 500 ||
|
|
634
|
+
resp.status === 502 ||
|
|
635
|
+
resp.status === 503;
|
|
622
636
|
throw new PipelineError(`HTTP ${resp.status} ${resp.statusText} from ${url}`, {
|
|
623
637
|
step: -1, // will be overwritten by caller
|
|
624
638
|
action: "fetch",
|
|
@@ -634,6 +648,10 @@ async function fetchJson(url, config, cookieHeader) {
|
|
|
634
648
|
: resp.status === 429
|
|
635
649
|
? "Rate limited. Add a delay between requests or reduce the limit parameter."
|
|
636
650
|
: `HTTP ${resp.status} error. Check if the API endpoint is still valid.`,
|
|
651
|
+
retryable: isRetryableStatus,
|
|
652
|
+
alternatives: resp.status === 401 || resp.status === 403
|
|
653
|
+
? ["unicli auth setup <site>"]
|
|
654
|
+
: [],
|
|
637
655
|
});
|
|
638
656
|
}
|
|
639
657
|
// Unreachable — loop always returns or throws — but satisfies TypeScript
|
|
@@ -649,6 +667,8 @@ function stepSelect(ctx, path, stepIndex) {
|
|
|
649
667
|
config: path,
|
|
650
668
|
errorType: "selector_miss",
|
|
651
669
|
suggestion: `The path "${resolved}" does not exist in the API response. Inspect the actual response JSON to find the correct path, then update the "select" step in the adapter YAML.`,
|
|
670
|
+
retryable: false,
|
|
671
|
+
alternatives: [],
|
|
652
672
|
});
|
|
653
673
|
}
|
|
654
674
|
return { ...ctx, data };
|
|
@@ -725,6 +745,13 @@ async function stepFetchText(ctx, config) {
|
|
|
725
745
|
url,
|
|
726
746
|
statusCode: resp.status,
|
|
727
747
|
suggestion: `Check if the URL is still valid: ${url}`,
|
|
748
|
+
retryable: resp.status === 429 ||
|
|
749
|
+
resp.status === 500 ||
|
|
750
|
+
resp.status === 502 ||
|
|
751
|
+
resp.status === 503,
|
|
752
|
+
alternatives: resp.status === 401 || resp.status === 403
|
|
753
|
+
? ["unicli auth setup <site>"]
|
|
754
|
+
: [],
|
|
728
755
|
});
|
|
729
756
|
}
|
|
730
757
|
const text = await resp.text();
|
|
@@ -837,6 +864,8 @@ async function stepExec(ctx, config) {
|
|
|
837
864
|
},
|
|
838
865
|
errorType: "assertion_failed",
|
|
839
866
|
suggestion: denial.hint,
|
|
867
|
+
retryable: false,
|
|
868
|
+
alternatives: [],
|
|
840
869
|
});
|
|
841
870
|
}
|
|
842
871
|
}
|
|
@@ -910,6 +939,8 @@ async function stepExec(ctx, config) {
|
|
|
910
939
|
config: { command: cmd, args: execArgs },
|
|
911
940
|
errorType: "parse_error",
|
|
912
941
|
suggestion: `Check that the command writes to "${outputFile}". Verify the path is correct.`,
|
|
942
|
+
retryable: false,
|
|
943
|
+
alternatives: [],
|
|
913
944
|
});
|
|
914
945
|
}
|
|
915
946
|
}
|
|
@@ -951,12 +982,15 @@ async function stepExec(ctx, config) {
|
|
|
951
982
|
if (err instanceof PipelineError)
|
|
952
983
|
throw err;
|
|
953
984
|
const msg = err instanceof Error ? err.message : String(err);
|
|
985
|
+
const isExecTransient = /timeout|ETIMEDOUT|ECONNREFUSED|ECONNRESET/i.test(msg);
|
|
954
986
|
throw new PipelineError(`exec "${cmd}" failed: ${msg}`, {
|
|
955
987
|
step: -1,
|
|
956
988
|
action: "exec",
|
|
957
989
|
config: { command: cmd, args: execArgs },
|
|
958
|
-
errorType: "parse_error",
|
|
990
|
+
errorType: isExecTransient ? "timeout" : "parse_error",
|
|
959
991
|
suggestion: `Check that "${cmd}" is installed and accessible. Run: which ${cmd}`,
|
|
992
|
+
retryable: isExecTransient,
|
|
993
|
+
alternatives: [],
|
|
960
994
|
});
|
|
961
995
|
}
|
|
962
996
|
}
|
|
@@ -1043,6 +1077,8 @@ async function stepIf(ctx, config, stepIndex, depth = 0) {
|
|
|
1043
1077
|
config,
|
|
1044
1078
|
errorType: "parse_error",
|
|
1045
1079
|
suggestion: "Reduce nesting depth of if/else steps. Maximum is 10 levels.",
|
|
1080
|
+
retryable: false,
|
|
1081
|
+
alternatives: [],
|
|
1046
1082
|
});
|
|
1047
1083
|
}
|
|
1048
1084
|
const conditionStr = typeof config.if === "string" ? config.if : String(config.if);
|
|
@@ -1069,6 +1105,8 @@ async function stepEach(ctx, config, stepIndex, depth) {
|
|
|
1069
1105
|
config,
|
|
1070
1106
|
errorType: "parse_error",
|
|
1071
1107
|
suggestion: "Reduce nesting depth of loop steps. Maximum is 10 levels.",
|
|
1108
|
+
retryable: false,
|
|
1109
|
+
alternatives: [],
|
|
1072
1110
|
});
|
|
1073
1111
|
}
|
|
1074
1112
|
const maxIterations = Math.max(config.max ?? 100, 1);
|
|
@@ -1111,6 +1149,8 @@ async function stepParallel(ctx, branches, merge, stepIndex, depth) {
|
|
|
1111
1149
|
config: branches,
|
|
1112
1150
|
errorType: "parse_error",
|
|
1113
1151
|
suggestion: "Reduce nesting depth of parallel steps. Maximum is 10 levels.",
|
|
1152
|
+
retryable: false,
|
|
1153
|
+
alternatives: [],
|
|
1114
1154
|
});
|
|
1115
1155
|
}
|
|
1116
1156
|
const results = await Promise.all(branches.map(async (branch) => {
|
|
@@ -1597,6 +1637,8 @@ async function stepClick(ctx, config) {
|
|
|
1597
1637
|
config,
|
|
1598
1638
|
errorType: "expression_error",
|
|
1599
1639
|
suggestion: 'Provide either a CSS selector string, {selector: "..."}, or {x: N, y: N} for coordinate click.',
|
|
1640
|
+
retryable: false,
|
|
1641
|
+
alternatives: [],
|
|
1600
1642
|
});
|
|
1601
1643
|
}
|
|
1602
1644
|
async function stepType(ctx, config) {
|
|
@@ -1675,6 +1717,8 @@ async function stepIntercept(ctx, config) {
|
|
|
1675
1717
|
config: { capture: capturePattern, trigger },
|
|
1676
1718
|
errorType: "timeout",
|
|
1677
1719
|
suggestion: `No network request matching "${capturePattern}" was observed. Verify the capture pattern matches the target API URL and that the trigger action causes the request.`,
|
|
1720
|
+
retryable: true,
|
|
1721
|
+
alternatives: [],
|
|
1678
1722
|
});
|
|
1679
1723
|
}
|
|
1680
1724
|
// Apply optional dot-path selector to captured data
|
|
@@ -1760,6 +1804,8 @@ async function stepTap(ctx, config) {
|
|
|
1760
1804
|
config,
|
|
1761
1805
|
errorType: "expression_error",
|
|
1762
1806
|
suggestion: "Store name must be a valid JavaScript identifier.",
|
|
1807
|
+
retryable: false,
|
|
1808
|
+
alternatives: [],
|
|
1763
1809
|
});
|
|
1764
1810
|
}
|
|
1765
1811
|
if (!/^[a-zA-Z_$][\w$]*$/.test(actionName)) {
|
|
@@ -1769,6 +1815,8 @@ async function stepTap(ctx, config) {
|
|
|
1769
1815
|
config,
|
|
1770
1816
|
errorType: "expression_error",
|
|
1771
1817
|
suggestion: "Action name must be a valid JavaScript identifier.",
|
|
1818
|
+
retryable: false,
|
|
1819
|
+
alternatives: [],
|
|
1772
1820
|
});
|
|
1773
1821
|
}
|
|
1774
1822
|
const framework = config.framework ?? "auto";
|