@jackwener/opencli 1.5.5 → 1.5.7
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/CHANGELOG.md +26 -0
- package/README.md +31 -4
- package/README.zh-CN.md +40 -5
- package/SKILL.md +1 -1
- package/dist/browser/cdp.d.ts +1 -0
- package/dist/browser/cdp.js +30 -27
- package/dist/browser/daemon-client.d.ts +11 -1
- package/dist/browser/daemon-client.js +3 -0
- package/dist/browser/dom-helpers.js +1 -0
- package/dist/browser/dom-helpers.test.js +14 -1
- package/dist/browser/mcp.js +18 -13
- package/dist/browser/page.d.ts +6 -0
- package/dist/browser/page.js +37 -2
- package/dist/browser/page.test.d.ts +1 -0
- package/dist/browser/page.test.js +44 -0
- package/dist/browser/stealth.js +198 -0
- package/dist/browser/stealth.test.d.ts +1 -0
- package/dist/browser/stealth.test.js +134 -0
- package/dist/browser.test.js +1 -1
- package/dist/build-manifest.d.ts +1 -0
- package/dist/build-manifest.js +5 -1
- package/dist/build-manifest.test.js +2 -0
- package/dist/cli-manifest.json +1821 -252
- package/dist/cli.js +20 -3
- package/dist/clis/antigravity/serve.d.ts +1 -1
- package/dist/clis/antigravity/serve.js +5 -8
- package/dist/clis/band/bands.d.ts +1 -0
- package/dist/clis/band/bands.js +72 -0
- package/dist/clis/band/mentions.d.ts +1 -0
- package/dist/clis/band/mentions.js +127 -0
- package/dist/clis/band/post.d.ts +1 -0
- package/dist/clis/band/post.js +175 -0
- package/dist/clis/band/posts.d.ts +1 -0
- package/dist/clis/band/posts.js +94 -0
- package/dist/clis/bilibili/subtitle.js +4 -0
- package/dist/clis/bilibili/subtitle.test.d.ts +1 -0
- package/dist/clis/bilibili/subtitle.test.js +48 -0
- package/dist/clis/chatwise/ask.js +0 -2
- package/dist/clis/chatwise/export.js +0 -2
- package/dist/clis/chatwise/history.js +0 -2
- package/dist/clis/chatwise/model.js +0 -2
- package/dist/clis/chatwise/new.js +1 -2
- package/dist/clis/chatwise/read.js +0 -2
- package/dist/clis/chatwise/screenshot.js +1 -2
- package/dist/clis/chatwise/send.js +0 -2
- package/dist/clis/chatwise/status.js +1 -2
- package/dist/clis/ctrip/search.d.ts +13 -0
- package/dist/clis/ctrip/search.js +73 -48
- package/dist/clis/ctrip/search.test.d.ts +1 -0
- package/dist/clis/ctrip/search.test.js +64 -0
- package/dist/clis/doubao/detail.d.ts +1 -0
- package/dist/clis/doubao/detail.js +33 -0
- package/dist/clis/doubao/detail.test.d.ts +1 -0
- package/dist/clis/doubao/detail.test.js +42 -0
- package/dist/clis/doubao/history.d.ts +1 -0
- package/dist/clis/doubao/history.js +28 -0
- package/dist/clis/doubao/history.test.d.ts +1 -0
- package/dist/clis/doubao/history.test.js +37 -0
- package/dist/clis/doubao/meeting-summary.d.ts +1 -0
- package/dist/clis/doubao/meeting-summary.js +39 -0
- package/dist/clis/doubao/meeting-transcript.d.ts +1 -0
- package/dist/clis/doubao/meeting-transcript.js +36 -0
- package/dist/clis/doubao/utils.d.ts +27 -0
- package/dist/clis/doubao/utils.js +317 -0
- package/dist/clis/doubao/utils.test.d.ts +1 -0
- package/dist/clis/doubao/utils.test.js +24 -0
- package/dist/clis/douyin/_shared/public-api.d.ts +33 -0
- package/dist/clis/douyin/_shared/public-api.js +29 -0
- package/dist/clis/douyin/_shared/sts2.js +8 -2
- package/dist/clis/douyin/_shared/sts2.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/sts2.test.js +27 -0
- package/dist/clis/douyin/activities.js +4 -2
- package/dist/clis/douyin/activities.test.js +34 -1
- package/dist/clis/douyin/collections.js +1 -1
- package/dist/clis/douyin/collections.test.js +24 -2
- package/dist/clis/douyin/draft.d.ts +8 -11
- package/dist/clis/douyin/draft.js +302 -185
- package/dist/clis/douyin/draft.test.d.ts +1 -1
- package/dist/clis/douyin/draft.test.js +357 -2
- package/dist/clis/douyin/hashtag.js +9 -2
- package/dist/clis/douyin/hashtag.test.js +35 -2
- package/dist/clis/douyin/profile.js +1 -1
- package/dist/clis/douyin/profile.test.js +36 -1
- package/dist/clis/douyin/user-videos.d.ts +5 -0
- package/dist/clis/douyin/user-videos.js +74 -0
- package/dist/clis/douyin/user-videos.test.d.ts +1 -0
- package/dist/clis/douyin/user-videos.test.js +108 -0
- package/dist/clis/douyin/videos.js +22 -5
- package/dist/clis/douyin/videos.test.js +45 -2
- package/dist/clis/facebook/search.test.d.ts +5 -0
- package/dist/clis/facebook/search.test.js +60 -0
- package/dist/clis/facebook/search.yaml +4 -3
- package/dist/clis/instagram/download.d.ts +16 -0
- package/dist/clis/instagram/download.js +225 -0
- package/dist/clis/instagram/download.test.d.ts +1 -0
- package/dist/clis/instagram/download.test.js +118 -0
- package/dist/clis/notebooklm/bind-current.d.ts +1 -0
- package/dist/clis/notebooklm/bind-current.js +29 -0
- package/dist/clis/notebooklm/bind-current.test.d.ts +1 -0
- package/dist/clis/notebooklm/bind-current.test.js +35 -0
- package/dist/clis/notebooklm/binding.test.d.ts +1 -0
- package/dist/clis/notebooklm/binding.test.js +44 -0
- package/dist/clis/notebooklm/compat.test.d.ts +3 -0
- package/dist/clis/notebooklm/compat.test.js +16 -0
- package/dist/clis/notebooklm/current.d.ts +1 -0
- package/dist/clis/notebooklm/current.js +28 -0
- package/dist/clis/notebooklm/get.d.ts +1 -0
- package/dist/clis/notebooklm/get.js +37 -0
- package/dist/clis/notebooklm/history.d.ts +1 -0
- package/dist/clis/notebooklm/history.js +25 -0
- package/dist/clis/notebooklm/history.test.d.ts +1 -0
- package/dist/clis/notebooklm/history.test.js +58 -0
- package/dist/clis/notebooklm/list.d.ts +1 -0
- package/dist/clis/notebooklm/list.js +35 -0
- package/dist/clis/notebooklm/note-list.d.ts +1 -0
- package/dist/clis/notebooklm/note-list.js +28 -0
- package/dist/clis/notebooklm/note-list.test.d.ts +1 -0
- package/dist/clis/notebooklm/note-list.test.js +56 -0
- package/dist/clis/notebooklm/notes-get.d.ts +1 -0
- package/dist/clis/notebooklm/notes-get.js +47 -0
- package/dist/clis/notebooklm/notes-get.test.d.ts +1 -0
- package/dist/clis/notebooklm/notes-get.test.js +72 -0
- package/dist/clis/notebooklm/rpc.d.ts +36 -0
- package/dist/clis/notebooklm/rpc.js +189 -0
- package/dist/clis/notebooklm/rpc.test.d.ts +1 -0
- package/dist/clis/notebooklm/rpc.test.js +105 -0
- package/dist/clis/notebooklm/shared.d.ts +87 -0
- package/dist/clis/notebooklm/shared.js +3 -0
- package/dist/clis/notebooklm/source-fulltext.d.ts +1 -0
- package/dist/clis/notebooklm/source-fulltext.js +44 -0
- package/dist/clis/notebooklm/source-fulltext.test.d.ts +1 -0
- package/dist/clis/notebooklm/source-fulltext.test.js +106 -0
- package/dist/clis/notebooklm/source-get.d.ts +1 -0
- package/dist/clis/notebooklm/source-get.js +40 -0
- package/dist/clis/notebooklm/source-get.test.d.ts +1 -0
- package/dist/clis/notebooklm/source-get.test.js +84 -0
- package/dist/clis/notebooklm/source-guide.d.ts +1 -0
- package/dist/clis/notebooklm/source-guide.js +44 -0
- package/dist/clis/notebooklm/source-guide.test.d.ts +1 -0
- package/dist/clis/notebooklm/source-guide.test.js +104 -0
- package/dist/clis/notebooklm/source-list.d.ts +1 -0
- package/dist/clis/notebooklm/source-list.js +30 -0
- package/dist/clis/notebooklm/status.d.ts +1 -0
- package/dist/clis/notebooklm/status.js +31 -0
- package/dist/clis/notebooklm/summary.d.ts +1 -0
- package/dist/clis/notebooklm/summary.js +30 -0
- package/dist/clis/notebooklm/summary.test.d.ts +1 -0
- package/dist/clis/notebooklm/summary.test.js +78 -0
- package/dist/clis/notebooklm/utils.d.ts +37 -0
- package/dist/clis/notebooklm/utils.js +739 -0
- package/dist/clis/notebooklm/utils.test.d.ts +1 -0
- package/dist/clis/notebooklm/utils.test.js +390 -0
- package/dist/clis/ones/common.d.ts +32 -0
- package/dist/clis/ones/common.js +144 -0
- package/dist/clis/ones/enrich-tasks.d.ts +5 -0
- package/dist/clis/ones/enrich-tasks.js +37 -0
- package/dist/clis/ones/login.d.ts +1 -0
- package/dist/clis/ones/login.js +80 -0
- package/dist/clis/ones/logout.d.ts +1 -0
- package/dist/clis/ones/logout.js +17 -0
- package/dist/clis/ones/me.d.ts +1 -0
- package/dist/clis/ones/me.js +30 -0
- package/dist/clis/ones/my-tasks.d.ts +1 -0
- package/dist/clis/ones/my-tasks.js +120 -0
- package/dist/clis/ones/resolve-labels.d.ts +10 -0
- package/dist/clis/ones/resolve-labels.js +64 -0
- package/dist/clis/ones/task-helpers.d.ts +29 -0
- package/dist/clis/ones/task-helpers.js +212 -0
- package/dist/clis/ones/task-helpers.test.d.ts +1 -0
- package/dist/clis/ones/task-helpers.test.js +12 -0
- package/dist/clis/ones/task.d.ts +1 -0
- package/dist/clis/ones/task.js +66 -0
- package/dist/clis/ones/tasks.d.ts +1 -0
- package/dist/clis/ones/tasks.js +79 -0
- package/dist/clis/ones/token-info.d.ts +1 -0
- package/dist/clis/ones/token-info.js +42 -0
- package/dist/clis/ones/worklog.d.ts +11 -0
- package/dist/clis/ones/worklog.js +267 -0
- package/dist/clis/ones/worklog.test.d.ts +1 -0
- package/dist/clis/ones/worklog.test.js +20 -0
- package/dist/clis/spotify/spotify.d.ts +1 -0
- package/dist/clis/spotify/spotify.js +316 -0
- package/dist/clis/spotify/utils.d.ts +21 -0
- package/dist/clis/spotify/utils.js +66 -0
- package/dist/clis/spotify/utils.test.d.ts +1 -0
- package/dist/clis/spotify/utils.test.js +67 -0
- package/dist/clis/substack/utils.d.ts +4 -0
- package/dist/clis/substack/utils.js +8 -2
- package/dist/clis/substack/utils.test.d.ts +1 -0
- package/dist/clis/substack/utils.test.js +46 -0
- package/dist/clis/tieba/commands.test.d.ts +4 -0
- package/dist/clis/tieba/commands.test.js +79 -0
- package/dist/clis/tieba/hot.d.ts +1 -0
- package/dist/clis/tieba/hot.js +48 -0
- package/dist/clis/tieba/posts.d.ts +1 -0
- package/dist/clis/tieba/posts.js +85 -0
- package/dist/clis/tieba/read.d.ts +1 -0
- package/dist/clis/tieba/read.js +140 -0
- package/dist/clis/tieba/search.d.ts +1 -0
- package/dist/clis/tieba/search.js +108 -0
- package/dist/clis/tieba/utils.d.ts +101 -0
- package/dist/clis/tieba/utils.js +240 -0
- package/dist/clis/tieba/utils.test.d.ts +1 -0
- package/dist/clis/tieba/utils.test.js +290 -0
- package/dist/clis/v2ex/hot.yaml +4 -1
- package/dist/clis/v2ex/latest.yaml +4 -1
- package/dist/clis/v2ex/topic.yaml +6 -1
- package/dist/clis/weixin/download.d.ts +9 -0
- package/dist/clis/weixin/download.js +76 -6
- package/dist/clis/weread/book.js +206 -13
- package/dist/clis/weread/commands.test.js +331 -0
- package/dist/clis/weread/private-api-regression.test.d.ts +1 -0
- package/dist/{weread-private-api-regression.test.js → clis/weread/private-api-regression.test.js} +92 -30
- package/dist/clis/weread/search-regression.test.d.ts +1 -0
- package/dist/clis/weread/search-regression.test.js +407 -0
- package/dist/clis/weread/search.js +143 -7
- package/dist/clis/weread/shelf.js +13 -95
- package/dist/clis/weread/utils.d.ts +56 -0
- package/dist/clis/weread/utils.js +234 -7
- package/dist/clis/weread/utils.test.js +71 -1
- package/dist/clis/xiaohongshu/comments.d.ts +3 -0
- package/dist/clis/xiaohongshu/comments.js +76 -17
- package/dist/clis/xiaohongshu/comments.test.js +70 -9
- package/dist/clis/xiaohongshu/download.d.ts +4 -1
- package/dist/clis/xiaohongshu/download.js +83 -22
- package/dist/clis/xiaohongshu/download.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/download.test.js +75 -0
- package/dist/clis/xiaohongshu/note-helpers.d.ts +12 -0
- package/dist/clis/xiaohongshu/note-helpers.js +23 -0
- package/dist/clis/xiaohongshu/note.d.ts +7 -0
- package/dist/clis/xiaohongshu/note.js +76 -0
- package/dist/clis/xiaohongshu/note.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/note.test.js +136 -0
- package/dist/clis/xiaohongshu/publish.d.ts +1 -1
- package/dist/clis/xiaohongshu/publish.js +78 -31
- package/dist/clis/xiaohongshu/publish.test.js +66 -1
- package/dist/clis/xiaohongshu/search.js +9 -0
- package/dist/clis/xiaohongshu/search.test.js +10 -4
- package/dist/clis/xiaohongshu/user-helpers.d.ts +1 -0
- package/dist/clis/xiaohongshu/user-helpers.js +2 -0
- package/dist/clis/xiaohongshu/user-helpers.test.js +18 -0
- package/dist/clis/xueqiu/comments.d.ts +118 -0
- package/dist/clis/xueqiu/comments.js +354 -0
- package/dist/clis/xueqiu/comments.test.d.ts +1 -0
- package/dist/clis/xueqiu/comments.test.js +696 -0
- package/dist/clis/youtube/search.js +57 -17
- package/dist/clis/youtube/transcript.js +2 -4
- package/dist/clis/youtube/utils.d.ts +9 -0
- package/dist/clis/youtube/utils.js +67 -3
- package/dist/clis/youtube/utils.test.d.ts +1 -0
- package/dist/clis/youtube/utils.test.js +37 -0
- package/dist/clis/youtube/video.js +16 -15
- package/dist/clis/zhihu/question.js +19 -17
- package/dist/clis/zhihu/question.test.d.ts +1 -0
- package/dist/clis/zhihu/question.test.js +54 -0
- package/dist/clis/zsxq/dynamics.d.ts +1 -0
- package/dist/clis/zsxq/dynamics.js +47 -0
- package/dist/clis/zsxq/groups.d.ts +1 -0
- package/dist/clis/zsxq/groups.js +32 -0
- package/dist/clis/zsxq/search.d.ts +1 -0
- package/dist/clis/zsxq/search.js +43 -0
- package/dist/clis/zsxq/search.test.d.ts +1 -0
- package/dist/clis/zsxq/search.test.js +24 -0
- package/dist/clis/zsxq/topic.d.ts +1 -0
- package/dist/clis/zsxq/topic.js +47 -0
- package/dist/clis/zsxq/topic.test.d.ts +1 -0
- package/dist/clis/zsxq/topic.test.js +29 -0
- package/dist/clis/zsxq/topics.d.ts +1 -0
- package/dist/clis/zsxq/topics.js +25 -0
- package/dist/clis/zsxq/topics.test.d.ts +1 -0
- package/dist/clis/zsxq/topics.test.js +24 -0
- package/dist/clis/zsxq/utils.d.ts +97 -0
- package/dist/clis/zsxq/utils.js +230 -0
- package/dist/commanderAdapter.js +10 -1
- package/dist/commanderAdapter.test.js +64 -0
- package/dist/commands/daemon.d.ts +9 -0
- package/dist/commands/daemon.js +124 -0
- package/dist/commands/daemon.test.d.ts +1 -0
- package/dist/commands/daemon.test.js +185 -0
- package/dist/completion.js +3 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/daemon.d.ts +1 -1
- package/dist/daemon.js +25 -14
- package/dist/daemon.test.d.ts +1 -0
- package/dist/daemon.test.js +65 -0
- package/dist/discovery.d.ts +9 -0
- package/dist/discovery.js +47 -2
- package/dist/electron-apps.d.ts +29 -0
- package/dist/electron-apps.js +65 -0
- package/dist/electron-apps.test.d.ts +1 -0
- package/dist/electron-apps.test.js +43 -0
- package/dist/engine.test.js +41 -9
- package/dist/execution.js +20 -16
- package/dist/external-clis.yaml +17 -0
- package/dist/idle-manager.d.ts +19 -0
- package/dist/idle-manager.js +54 -0
- package/dist/launcher.d.ts +36 -0
- package/dist/launcher.js +152 -0
- package/dist/launcher.test.d.ts +1 -0
- package/dist/launcher.test.js +57 -0
- package/dist/main.js +3 -3
- package/dist/registry.d.ts +1 -0
- package/dist/registry.js +31 -3
- package/dist/registry.test.js +13 -0
- package/dist/runtime.d.ts +5 -3
- package/dist/runtime.js +12 -5
- package/dist/serialization.d.ts +1 -0
- package/dist/serialization.js +3 -0
- package/dist/serialization.test.js +17 -1
- package/dist/tui.d.ts +7 -0
- package/dist/tui.js +52 -0
- package/dist/tui.test.d.ts +1 -0
- package/dist/tui.test.js +19 -0
- package/dist/types.d.ts +5 -0
- package/dist/weixin-download.test.js +14 -0
- package/docs/.vitepress/config.mts +4 -0
- package/docs/adapters/browser/band.md +63 -0
- package/docs/adapters/browser/notebooklm.md +69 -0
- package/docs/adapters/browser/ones.md +59 -0
- package/docs/adapters/browser/spotify.md +62 -0
- package/docs/adapters/browser/tieba.md +45 -0
- package/docs/adapters/browser/xiaohongshu.md +19 -10
- package/docs/adapters/browser/xueqiu.md +5 -0
- package/docs/adapters/browser/zsxq.md +49 -0
- package/docs/adapters/index.md +67 -63
- package/docs/adapters-doc/ones.md +32 -0
- package/docs/guide/browser-bridge.md +12 -0
- package/docs/guide/troubleshooting.md +9 -4
- package/docs/superpowers/plans/2026-03-31-daemon-lifecycle-redesign.md +857 -0
- package/docs/superpowers/specs/2026-03-31-daemon-lifecycle-redesign.md +208 -0
- package/docs/zh/guide/browser-bridge.md +12 -0
- package/extension/dist/background.js +794 -513
- package/extension/src/background.test.ts +202 -2
- package/extension/src/background.ts +189 -10
- package/extension/src/cdp.ts +54 -0
- package/extension/src/protocol.ts +11 -5
- package/package.json +1 -1
- package/scripts/postinstall.js +16 -0
- package/src/browser/cdp.ts +24 -17
- package/src/browser/daemon-client.ts +11 -1
- package/src/browser/dom-helpers.test.ts +15 -1
- package/src/browser/dom-helpers.ts +1 -0
- package/src/browser/mcp.ts +18 -13
- package/src/browser/page.test.ts +58 -0
- package/src/browser/page.ts +34 -2
- package/src/browser/stealth.test.ts +153 -0
- package/src/browser/stealth.ts +198 -0
- package/src/browser.test.ts +1 -1
- package/src/build-manifest.test.ts +2 -0
- package/src/build-manifest.ts +6 -1
- package/src/cli.ts +21 -3
- package/src/clis/antigravity/SKILL.md +3 -12
- package/src/clis/antigravity/serve.ts +5 -10
- package/src/clis/band/bands.ts +76 -0
- package/src/clis/band/mentions.ts +134 -0
- package/src/clis/band/post.ts +187 -0
- package/src/clis/band/posts.ts +106 -0
- package/src/clis/bilibili/subtitle.test.ts +60 -0
- package/src/clis/bilibili/subtitle.ts +4 -0
- package/src/clis/chatwise/ask.ts +0 -2
- package/src/clis/chatwise/export.ts +0 -2
- package/src/clis/chatwise/history.ts +0 -2
- package/src/clis/chatwise/model.ts +0 -2
- package/src/clis/chatwise/new.ts +1 -2
- package/src/clis/chatwise/read.ts +0 -2
- package/src/clis/chatwise/screenshot.ts +1 -2
- package/src/clis/chatwise/send.ts +0 -2
- package/src/clis/chatwise/status.ts +1 -2
- package/src/clis/ctrip/search.test.ts +73 -0
- package/src/clis/ctrip/search.ts +97 -47
- package/src/clis/doubao/detail.test.ts +53 -0
- package/src/clis/doubao/detail.ts +41 -0
- package/src/clis/doubao/history.test.ts +45 -0
- package/src/clis/doubao/history.ts +32 -0
- package/src/clis/doubao/meeting-summary.ts +53 -0
- package/src/clis/doubao/meeting-transcript.ts +48 -0
- package/src/clis/doubao/utils.test.ts +45 -0
- package/src/clis/doubao/utils.ts +371 -0
- package/src/clis/douyin/_shared/public-api.ts +84 -0
- package/src/clis/douyin/_shared/sts2.test.ts +31 -0
- package/src/clis/douyin/_shared/sts2.ts +11 -3
- package/src/clis/douyin/activities.test.ts +41 -1
- package/src/clis/douyin/activities.ts +12 -3
- package/src/clis/douyin/collections.test.ts +35 -2
- package/src/clis/douyin/collections.ts +1 -1
- package/src/clis/douyin/draft.test.ts +444 -2
- package/src/clis/douyin/draft.ts +382 -218
- package/src/clis/douyin/hashtag.test.ts +42 -2
- package/src/clis/douyin/hashtag.ts +11 -3
- package/src/clis/douyin/profile.test.ts +43 -1
- package/src/clis/douyin/profile.ts +9 -2
- package/src/clis/douyin/user-videos.test.ts +122 -0
- package/src/clis/douyin/user-videos.ts +101 -0
- package/src/clis/douyin/videos.test.ts +52 -2
- package/src/clis/douyin/videos.ts +49 -15
- package/src/clis/facebook/search.test.ts +70 -0
- package/src/clis/facebook/search.yaml +4 -3
- package/src/clis/instagram/download.test.ts +159 -0
- package/src/clis/instagram/download.ts +286 -0
- package/src/clis/notebooklm/bind-current.test.ts +43 -0
- package/src/clis/notebooklm/bind-current.ts +36 -0
- package/src/clis/notebooklm/binding.test.ts +53 -0
- package/src/clis/notebooklm/compat.test.ts +19 -0
- package/src/clis/notebooklm/current.ts +38 -0
- package/src/clis/notebooklm/get.ts +53 -0
- package/src/clis/notebooklm/history.test.ts +70 -0
- package/src/clis/notebooklm/history.ts +36 -0
- package/src/clis/notebooklm/list.ts +40 -0
- package/src/clis/notebooklm/note-list.test.ts +64 -0
- package/src/clis/notebooklm/note-list.ts +42 -0
- package/src/clis/notebooklm/notes-get.test.ts +88 -0
- package/src/clis/notebooklm/notes-get.ts +67 -0
- package/src/clis/notebooklm/rpc.test.ts +126 -0
- package/src/clis/notebooklm/rpc.ts +286 -0
- package/src/clis/notebooklm/shared.ts +98 -0
- package/src/clis/notebooklm/source-fulltext.test.ts +123 -0
- package/src/clis/notebooklm/source-fulltext.ts +69 -0
- package/src/clis/notebooklm/source-get.test.ts +100 -0
- package/src/clis/notebooklm/source-get.ts +60 -0
- package/src/clis/notebooklm/source-guide.test.ts +121 -0
- package/src/clis/notebooklm/source-guide.ts +69 -0
- package/src/clis/notebooklm/source-list.ts +45 -0
- package/src/clis/notebooklm/status.ts +34 -0
- package/src/clis/notebooklm/summary.test.ts +94 -0
- package/src/clis/notebooklm/summary.ts +45 -0
- package/src/clis/notebooklm/utils.test.ts +446 -0
- package/src/clis/notebooklm/utils.ts +893 -0
- package/src/clis/ones/common.ts +187 -0
- package/src/clis/ones/enrich-tasks.ts +47 -0
- package/src/clis/ones/login.ts +103 -0
- package/src/clis/ones/logout.ts +19 -0
- package/src/clis/ones/me.ts +34 -0
- package/src/clis/ones/my-tasks.ts +148 -0
- package/src/clis/ones/resolve-labels.ts +80 -0
- package/src/clis/ones/task-helpers.test.ts +14 -0
- package/src/clis/ones/task-helpers.ts +214 -0
- package/src/clis/ones/task.ts +79 -0
- package/src/clis/ones/tasks.ts +92 -0
- package/src/clis/ones/token-info.ts +46 -0
- package/src/clis/ones/worklog.test.ts +24 -0
- package/src/clis/ones/worklog.ts +306 -0
- package/src/clis/spotify/spotify.ts +328 -0
- package/src/clis/spotify/utils.test.ts +87 -0
- package/src/clis/spotify/utils.ts +92 -0
- package/src/clis/substack/utils.test.ts +54 -0
- package/src/clis/substack/utils.ts +10 -2
- package/src/clis/tieba/commands.test.ts +86 -0
- package/src/clis/tieba/hot.ts +52 -0
- package/src/clis/tieba/posts.ts +108 -0
- package/src/clis/tieba/read.ts +158 -0
- package/src/clis/tieba/search.ts +119 -0
- package/src/clis/tieba/utils.test.ts +322 -0
- package/src/clis/tieba/utils.ts +348 -0
- package/src/clis/v2ex/hot.yaml +4 -1
- package/src/clis/v2ex/latest.yaml +4 -1
- package/src/clis/v2ex/topic.yaml +6 -1
- package/src/clis/weixin/download.ts +95 -6
- package/src/clis/weread/book.ts +256 -13
- package/src/clis/weread/commands.test.ts +409 -0
- package/src/{weread-private-api-regression.test.ts → clis/weread/private-api-regression.test.ts} +108 -30
- package/src/clis/weread/search-regression.test.ts +440 -0
- package/src/clis/weread/search.ts +189 -9
- package/src/clis/weread/shelf.ts +20 -122
- package/src/clis/weread/utils.test.ts +81 -1
- package/src/clis/weread/utils.ts +293 -7
- package/src/clis/xiaohongshu/comments.test.ts +85 -9
- package/src/clis/xiaohongshu/comments.ts +76 -17
- package/src/clis/xiaohongshu/download.test.ts +96 -0
- package/src/clis/xiaohongshu/download.ts +83 -22
- package/src/clis/xiaohongshu/note-helpers.ts +25 -0
- package/src/clis/xiaohongshu/note.test.ts +164 -0
- package/src/clis/xiaohongshu/note.ts +86 -0
- package/src/clis/xiaohongshu/publish.test.ts +79 -1
- package/src/clis/xiaohongshu/publish.ts +84 -30
- package/src/clis/xiaohongshu/search.test.ts +11 -4
- package/src/clis/xiaohongshu/search.ts +13 -0
- package/src/clis/xiaohongshu/user-helpers.test.ts +23 -0
- package/src/clis/xiaohongshu/user-helpers.ts +4 -0
- package/src/clis/xueqiu/comments.test.ts +823 -0
- package/src/clis/xueqiu/comments.ts +461 -0
- package/src/clis/youtube/search.ts +57 -17
- package/src/clis/youtube/transcript.ts +2 -4
- package/src/clis/youtube/utils.test.ts +43 -0
- package/src/clis/youtube/utils.ts +69 -0
- package/src/clis/youtube/video.ts +16 -15
- package/src/clis/zhihu/question.test.ts +71 -0
- package/src/clis/zhihu/question.ts +27 -15
- package/src/clis/zsxq/dynamics.ts +60 -0
- package/src/clis/zsxq/groups.ts +41 -0
- package/src/clis/zsxq/search.test.ts +29 -0
- package/src/clis/zsxq/search.ts +54 -0
- package/src/clis/zsxq/topic.test.ts +34 -0
- package/src/clis/zsxq/topic.ts +68 -0
- package/src/clis/zsxq/topics.test.ts +29 -0
- package/src/clis/zsxq/topics.ts +36 -0
- package/src/clis/zsxq/utils.ts +351 -0
- package/src/commanderAdapter.test.ts +77 -0
- package/src/commanderAdapter.ts +8 -1
- package/src/commands/daemon.test.ts +238 -0
- package/src/commands/daemon.ts +135 -0
- package/src/completion.ts +2 -1
- package/src/constants.ts +3 -0
- package/src/daemon.test.ts +88 -0
- package/src/daemon.ts +26 -14
- package/src/discovery.ts +52 -2
- package/src/electron-apps.test.ts +50 -0
- package/src/electron-apps.ts +89 -0
- package/src/engine.test.ts +45 -9
- package/src/execution.ts +24 -19
- package/src/external-clis.yaml +17 -0
- package/src/idle-manager.ts +60 -0
- package/src/launcher.test.ts +67 -0
- package/src/launcher.ts +185 -0
- package/src/main.ts +3 -2
- package/src/registry.test.ts +15 -0
- package/src/registry.ts +32 -3
- package/src/runtime.ts +13 -7
- package/src/serialization.test.ts +19 -1
- package/src/serialization.ts +2 -0
- package/src/tui.test.ts +23 -0
- package/src/tui.ts +65 -0
- package/src/types.ts +5 -0
- package/src/weixin-download.test.ts +27 -0
- package/tests/e2e/band-auth.test.ts +20 -0
- package/tests/e2e/browser-auth-helpers.ts +18 -0
- package/tests/e2e/browser-auth.test.ts +35 -47
- package/tests/e2e/browser-public-extended.test.ts +6 -2
- package/tests/e2e/browser-public.test.ts +288 -0
- package/tests/e2e/management.test.ts +1 -1
- package/tests/e2e/plugin-management.test.ts +1 -1
- package/vitest.config.ts +1 -0
- package/chatwise-opencli.ps1 +0 -82
- package/dist/clis/chatwise/shared.d.ts +0 -2
- package/dist/clis/chatwise/shared.js +0 -6
- package/dist/weread-private-api-regression.test.d.ts +0 -1
- package/dist/weread-search-regression.test.d.ts +0 -1
- package/dist/weread-search-regression.test.js +0 -39
- package/src/clis/chatwise/shared.ts +0 -8
- package/src/weread-search-regression.test.ts +0 -44
package/src/execution.ts
CHANGED
|
@@ -20,6 +20,8 @@ import { getBrowserFactory, browserSession, runWithTimeout, DEFAULT_BROWSER_COMM
|
|
|
20
20
|
import { emitHook, type HookContext } from './hooks.js';
|
|
21
21
|
import { checkDaemonStatus } from './browser/discover.js';
|
|
22
22
|
import { log } from './logger.js';
|
|
23
|
+
import { isElectronApp } from './electron-apps.js';
|
|
24
|
+
import { resolveElectronEndpoint } from './launcher.js';
|
|
23
25
|
|
|
24
26
|
const _loadedModules = new Set<string>();
|
|
25
27
|
|
|
@@ -169,24 +171,29 @@ export async function executeCommand(
|
|
|
169
171
|
let result: unknown;
|
|
170
172
|
try {
|
|
171
173
|
if (shouldUseBrowserSession(cmd)) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
174
|
+
const electron = isElectronApp(cmd.site);
|
|
175
|
+
let cdpEndpoint: string | undefined;
|
|
176
|
+
|
|
177
|
+
if (electron) {
|
|
178
|
+
// Electron apps: auto-detect, prompt restart if needed, launch with CDP
|
|
179
|
+
cdpEndpoint = await resolveElectronEndpoint(cmd.site);
|
|
180
|
+
} else {
|
|
181
|
+
// Browser Bridge: fail-fast when daemon is up but extension is missing.
|
|
182
|
+
// 300ms timeout avoids a full 2s wait on cold-start.
|
|
183
|
+
const status = await checkDaemonStatus({ timeout: 300 });
|
|
184
|
+
if (status.running && !status.extensionConnected) {
|
|
185
|
+
throw new BrowserConnectError(
|
|
186
|
+
'Browser Bridge extension not connected',
|
|
187
|
+
'Install the Browser Bridge:\n' +
|
|
188
|
+
' 1. Download: https://github.com/jackwener/opencli/releases\n' +
|
|
189
|
+
' 2. chrome://extensions → Developer Mode → Load unpacked\n' +
|
|
190
|
+
' Then run: opencli doctor',
|
|
191
|
+
);
|
|
192
|
+
}
|
|
187
193
|
}
|
|
194
|
+
|
|
188
195
|
ensureRequiredEnv(cmd);
|
|
189
|
-
const BrowserFactory = getBrowserFactory();
|
|
196
|
+
const BrowserFactory = getBrowserFactory(cmd.site);
|
|
190
197
|
result = await browserSession(BrowserFactory, async (page) => {
|
|
191
198
|
const preNavUrl = resolvePreNav(cmd);
|
|
192
199
|
if (preNavUrl) {
|
|
@@ -195,8 +202,6 @@ export async function executeCommand(
|
|
|
195
202
|
if (debug) log.debug('[pre-nav] Already on target domain, skipping navigation');
|
|
196
203
|
} else {
|
|
197
204
|
try {
|
|
198
|
-
// goto() already includes smart DOM-settle detection (waitForDomStable).
|
|
199
|
-
// No additional fixed sleep needed.
|
|
200
205
|
await page.goto(preNavUrl);
|
|
201
206
|
} catch (err) {
|
|
202
207
|
if (debug) log.debug(`[pre-nav] Failed to navigate to ${preNavUrl}: ${err instanceof Error ? err.message : err}`);
|
|
@@ -207,7 +212,7 @@ export async function executeCommand(
|
|
|
207
212
|
timeout: cmd.timeoutSeconds ?? DEFAULT_BROWSER_COMMAND_TIMEOUT,
|
|
208
213
|
label: fullName(cmd),
|
|
209
214
|
});
|
|
210
|
-
}, { workspace: `site:${cmd.site}
|
|
215
|
+
}, { workspace: `site:${cmd.site}`, cdpEndpoint });
|
|
211
216
|
} else {
|
|
212
217
|
// Non-browser commands: apply timeout only when explicitly configured.
|
|
213
218
|
const timeout = cmd.timeoutSeconds;
|
package/src/external-clis.yaml
CHANGED
|
@@ -30,6 +30,23 @@
|
|
|
30
30
|
install:
|
|
31
31
|
default: "npm install -g @larksuite/cli"
|
|
32
32
|
|
|
33
|
+
- name: dws
|
|
34
|
+
binary: dws
|
|
35
|
+
description: "DingTalk Workspace CLI — messages, docs, calendar, contacts and more for humans and AI agents"
|
|
36
|
+
homepage: "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli"
|
|
37
|
+
tags: [dingtalk, collaboration, productivity, ai-agent]
|
|
38
|
+
install:
|
|
39
|
+
mac: "curl -fsSL https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/main/scripts/install.sh | sh"
|
|
40
|
+
linux: "curl -fsSL https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/main/scripts/install.sh | sh"
|
|
41
|
+
|
|
42
|
+
- name: wecom-cli
|
|
43
|
+
binary: wecom-cli
|
|
44
|
+
description: "WeCom/企业微信 CLI — contacts, todos, meetings, messages, calendar, docs and smart sheets for AI agents"
|
|
45
|
+
homepage: "https://github.com/WecomTeam/wecom-cli"
|
|
46
|
+
tags: [wecom, wechat-work, collaboration, productivity, ai-agent]
|
|
47
|
+
install:
|
|
48
|
+
default: "npm install -g @wecom/cli"
|
|
49
|
+
|
|
33
50
|
- name: vercel
|
|
34
51
|
binary: vercel
|
|
35
52
|
description: "Vercel CLI — deploy projects, manage domains, env vars, logs and serverless functions"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages daemon idle timeout with dual-condition logic:
|
|
3
|
+
* exits only when BOTH CLI is idle AND Extension is disconnected.
|
|
4
|
+
*/
|
|
5
|
+
export class IdleManager {
|
|
6
|
+
private _timer: ReturnType<typeof setTimeout> | null = null;
|
|
7
|
+
private _lastCliRequestTime = Date.now();
|
|
8
|
+
private _extensionConnected = false;
|
|
9
|
+
private _timeoutMs: number;
|
|
10
|
+
private _onExit: () => void;
|
|
11
|
+
|
|
12
|
+
constructor(timeoutMs: number, onExit: () => void) {
|
|
13
|
+
this._timeoutMs = timeoutMs;
|
|
14
|
+
this._onExit = onExit;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get lastCliRequestTime(): number {
|
|
18
|
+
return this._lastCliRequestTime;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Call when an HTTP request arrives from CLI */
|
|
22
|
+
onCliRequest(): void {
|
|
23
|
+
this._lastCliRequestTime = Date.now();
|
|
24
|
+
this._resetTimer();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Call when Extension WebSocket connects or disconnects */
|
|
28
|
+
setExtensionConnected(connected: boolean): void {
|
|
29
|
+
this._extensionConnected = connected;
|
|
30
|
+
if (connected) {
|
|
31
|
+
this._clearTimer();
|
|
32
|
+
} else {
|
|
33
|
+
this._resetTimer();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private _clearTimer(): void {
|
|
38
|
+
if (this._timer) {
|
|
39
|
+
clearTimeout(this._timer);
|
|
40
|
+
this._timer = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private _resetTimer(): void {
|
|
45
|
+
this._clearTimer();
|
|
46
|
+
|
|
47
|
+
if (this._timeoutMs <= 0) return;
|
|
48
|
+
if (this._extensionConnected) return;
|
|
49
|
+
|
|
50
|
+
const elapsed = Date.now() - this._lastCliRequestTime;
|
|
51
|
+
if (elapsed >= this._timeoutMs) {
|
|
52
|
+
this._onExit();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this._timer = setTimeout(() => {
|
|
57
|
+
this._onExit();
|
|
58
|
+
}, this._timeoutMs - elapsed);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { probeCDP, detectProcess, discoverAppPath } from './launcher.js';
|
|
3
|
+
|
|
4
|
+
vi.mock('node:child_process', () => ({
|
|
5
|
+
execFileSync: vi.fn(),
|
|
6
|
+
spawn: vi.fn(() => ({
|
|
7
|
+
unref: vi.fn(),
|
|
8
|
+
pid: 12345,
|
|
9
|
+
on: vi.fn(),
|
|
10
|
+
})),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const cp = vi.mocked(await import('node:child_process'));
|
|
14
|
+
|
|
15
|
+
describe('probeCDP', () => {
|
|
16
|
+
it('returns false when CDP endpoint is unreachable', async () => {
|
|
17
|
+
const result = await probeCDP(59999, 500);
|
|
18
|
+
expect(result).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('detectProcess', () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.restoreAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns false when pgrep finds no process', () => {
|
|
28
|
+
cp.execFileSync.mockImplementation(() => {
|
|
29
|
+
const err = new Error('exit 1') as Error & { status: number };
|
|
30
|
+
err.status = 1;
|
|
31
|
+
throw err;
|
|
32
|
+
});
|
|
33
|
+
const result = detectProcess('NonExistentApp');
|
|
34
|
+
expect(result).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns true when pgrep finds a process', () => {
|
|
38
|
+
cp.execFileSync.mockReturnValue('12345\n');
|
|
39
|
+
const result = detectProcess('Cursor');
|
|
40
|
+
expect(result).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('discoverAppPath', () => {
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
vi.restoreAllMocks();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it.skipIf(process.platform !== 'darwin')('returns path when osascript succeeds', () => {
|
|
50
|
+
cp.execFileSync.mockReturnValue('/Applications/Cursor.app/\n');
|
|
51
|
+
const result = discoverAppPath('Cursor');
|
|
52
|
+
expect(result).toBe('/Applications/Cursor.app');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it.skipIf(process.platform !== 'darwin')('returns null when osascript fails', () => {
|
|
56
|
+
cp.execFileSync.mockImplementation(() => {
|
|
57
|
+
throw new Error('app not found');
|
|
58
|
+
});
|
|
59
|
+
const result = discoverAppPath('NonExistent');
|
|
60
|
+
expect(result).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it.skipIf(process.platform === 'darwin')('returns null on non-darwin platform', () => {
|
|
64
|
+
const result = discoverAppPath('Cursor');
|
|
65
|
+
expect(result).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
});
|
package/src/launcher.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Electron app launcher — auto-detect, confirm, launch, and connect.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Probe CDP port → already running with debug? connect directly
|
|
6
|
+
* 2. Detect process → running without CDP? prompt to restart
|
|
7
|
+
* 3. Discover app path → not installed? error
|
|
8
|
+
* 4. Launch with --remote-debugging-port
|
|
9
|
+
* 5. Poll /json until ready
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
13
|
+
import { request as httpRequest } from 'node:http';
|
|
14
|
+
import type { ElectronAppEntry } from './electron-apps.js';
|
|
15
|
+
import { getElectronApp } from './electron-apps.js';
|
|
16
|
+
import { confirmPrompt } from './tui.js';
|
|
17
|
+
import { CommandExecutionError } from './errors.js';
|
|
18
|
+
import { log } from './logger.js';
|
|
19
|
+
|
|
20
|
+
const POLL_INTERVAL_MS = 500;
|
|
21
|
+
const POLL_TIMEOUT_MS = 15_000;
|
|
22
|
+
const PROBE_TIMEOUT_MS = 2_000;
|
|
23
|
+
const KILL_GRACE_MS = 3_000;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Probe whether a CDP endpoint is listening on the given port.
|
|
27
|
+
* Returns true if http://127.0.0.1:{port}/json responds successfully.
|
|
28
|
+
*/
|
|
29
|
+
export function probeCDP(port: number, timeoutMs: number = PROBE_TIMEOUT_MS): Promise<boolean> {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
const req = httpRequest(
|
|
32
|
+
{ hostname: '127.0.0.1', port, path: '/json', method: 'GET', timeout: timeoutMs },
|
|
33
|
+
(res) => {
|
|
34
|
+
res.resume();
|
|
35
|
+
resolve(res.statusCode !== undefined && res.statusCode >= 200 && res.statusCode < 300);
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
req.on('error', () => resolve(false));
|
|
39
|
+
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
40
|
+
req.end();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if a process with the given name is running.
|
|
46
|
+
* Uses pgrep on macOS/Linux.
|
|
47
|
+
*/
|
|
48
|
+
export function detectProcess(processName: string): boolean {
|
|
49
|
+
try {
|
|
50
|
+
execFileSync('pgrep', ['-x', processName], { encoding: 'utf-8', stdio: 'pipe' });
|
|
51
|
+
return true;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Kill a process by name. Sends SIGTERM first, then SIGKILL after grace period.
|
|
59
|
+
*/
|
|
60
|
+
export function killProcess(processName: string): void {
|
|
61
|
+
try {
|
|
62
|
+
execFileSync('pkill', ['-x', processName], { stdio: 'pipe' });
|
|
63
|
+
} catch {
|
|
64
|
+
// Process may have already exited
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const deadline = Date.now() + KILL_GRACE_MS;
|
|
68
|
+
while (Date.now() < deadline) {
|
|
69
|
+
if (!detectProcess(processName)) return;
|
|
70
|
+
execFileSync('sleep', ['0.2'], { stdio: 'pipe' });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
execFileSync('pkill', ['-9', '-x', processName], { stdio: 'pipe' });
|
|
75
|
+
} catch {
|
|
76
|
+
// Ignore
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Discover the app installation path on macOS.
|
|
82
|
+
* Uses osascript to resolve the app name to a POSIX path.
|
|
83
|
+
* Returns null if the app is not installed.
|
|
84
|
+
*/
|
|
85
|
+
export function discoverAppPath(displayName: string): string | null {
|
|
86
|
+
if (process.platform !== 'darwin') {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const result = execFileSync('osascript', [
|
|
92
|
+
'-e', `POSIX path of (path to application "${displayName}")`,
|
|
93
|
+
], { encoding: 'utf-8', stdio: 'pipe', timeout: 5_000 });
|
|
94
|
+
return result.trim().replace(/\/$/, '');
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function resolveExecutable(appPath: string, processName: string): string {
|
|
101
|
+
return `${appPath}/Contents/MacOS/${processName}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function pollForReady(port: number): Promise<void> {
|
|
105
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
106
|
+
while (Date.now() < deadline) {
|
|
107
|
+
if (await probeCDP(port, 1_000)) return;
|
|
108
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
109
|
+
}
|
|
110
|
+
throw new CommandExecutionError(
|
|
111
|
+
`App launched but CDP not available on port ${port} after ${POLL_TIMEOUT_MS / 1000}s`,
|
|
112
|
+
'The app may be slow to start. Try running the command again.',
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Main entry point: resolve an Electron app to a CDP endpoint URL.
|
|
118
|
+
*
|
|
119
|
+
* Returns the endpoint URL: http://127.0.0.1:{port}
|
|
120
|
+
*/
|
|
121
|
+
export async function resolveElectronEndpoint(site: string): Promise<string> {
|
|
122
|
+
const app = getElectronApp(site);
|
|
123
|
+
if (!app) {
|
|
124
|
+
throw new CommandExecutionError(
|
|
125
|
+
`No Electron app registered for site "${site}"`,
|
|
126
|
+
'Register the app in ~/.opencli/apps.yaml or check the site name.',
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const { port, processName, displayName } = app;
|
|
131
|
+
const label = displayName ?? processName;
|
|
132
|
+
const endpoint = `http://127.0.0.1:${port}`;
|
|
133
|
+
|
|
134
|
+
// Step 1: Already running with CDP?
|
|
135
|
+
log.debug(`[launcher] Probing CDP on port ${port}...`);
|
|
136
|
+
if (await probeCDP(port)) {
|
|
137
|
+
log.debug(`[launcher] CDP already available on port ${port}`);
|
|
138
|
+
return endpoint;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Step 2: Running without CDP?
|
|
142
|
+
const isRunning = detectProcess(processName);
|
|
143
|
+
if (isRunning) {
|
|
144
|
+
log.debug(`[launcher] ${label} is running but CDP not available`);
|
|
145
|
+
const confirmed = await confirmPrompt(
|
|
146
|
+
`${label} is running but CDP is not enabled. Restart with debug port?`,
|
|
147
|
+
true,
|
|
148
|
+
);
|
|
149
|
+
if (!confirmed) {
|
|
150
|
+
throw new CommandExecutionError(
|
|
151
|
+
`${label} needs to be restarted with CDP enabled.`,
|
|
152
|
+
`Manually restart: kill the app and relaunch with --remote-debugging-port=${port}`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
process.stderr.write(` Restarting ${label}...\n`);
|
|
156
|
+
killProcess(processName);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Step 3: Discover path
|
|
160
|
+
const appPath = discoverAppPath(label);
|
|
161
|
+
if (!appPath) {
|
|
162
|
+
throw new CommandExecutionError(
|
|
163
|
+
`Could not find ${label} on this machine.`,
|
|
164
|
+
`Install ${label} or register a custom path in ~/.opencli/apps.yaml`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Step 4: Launch
|
|
169
|
+
const executable = resolveExecutable(appPath, processName);
|
|
170
|
+
const args = [`--remote-debugging-port=${port}`, ...(app.extraArgs ?? [])];
|
|
171
|
+
log.debug(`[launcher] Launching: ${executable} ${args.join(' ')}`);
|
|
172
|
+
|
|
173
|
+
const child = spawn(executable, args, {
|
|
174
|
+
detached: true,
|
|
175
|
+
stdio: 'ignore',
|
|
176
|
+
});
|
|
177
|
+
child.unref();
|
|
178
|
+
|
|
179
|
+
// Step 5: Poll for readiness
|
|
180
|
+
process.stderr.write(` Waiting for ${label} on port ${port}...\n`);
|
|
181
|
+
await pollForReady(port);
|
|
182
|
+
process.stderr.write(` Connected to ${label} on port ${port}.\n`);
|
|
183
|
+
|
|
184
|
+
return endpoint;
|
|
185
|
+
}
|
package/src/main.ts
CHANGED
|
@@ -16,7 +16,7 @@ if (process.platform !== 'win32') {
|
|
|
16
16
|
import * as os from 'node:os';
|
|
17
17
|
import * as path from 'node:path';
|
|
18
18
|
import { fileURLToPath } from 'node:url';
|
|
19
|
-
import { discoverClis, discoverPlugins } from './discovery.js';
|
|
19
|
+
import { discoverClis, discoverPlugins, ensureUserCliCompatShims, USER_CLIS_DIR } from './discovery.js';
|
|
20
20
|
import { getCompletions } from './completion.js';
|
|
21
21
|
import { runCli } from './cli.js';
|
|
22
22
|
import { emitHook } from './hooks.js';
|
|
@@ -29,9 +29,10 @@ installNodeNetwork();
|
|
|
29
29
|
const __filename = fileURLToPath(import.meta.url);
|
|
30
30
|
const __dirname = path.dirname(__filename);
|
|
31
31
|
const BUILTIN_CLIS = path.resolve(__dirname, 'clis');
|
|
32
|
-
const USER_CLIS =
|
|
32
|
+
const USER_CLIS = USER_CLIS_DIR;
|
|
33
33
|
|
|
34
34
|
// Sequential: plugins must run after built-in discovery so they can override built-in commands.
|
|
35
|
+
await ensureUserCliCompatShims();
|
|
35
36
|
await discoverClis(BUILTIN_CLIS, USER_CLIS);
|
|
36
37
|
await discoverPlugins();
|
|
37
38
|
|
package/src/registry.test.ts
CHANGED
|
@@ -60,6 +60,21 @@ describe('cli() registration', () => {
|
|
|
60
60
|
const reg = getRegistry();
|
|
61
61
|
expect(reg.get('test-registry/overwrite')?.description).toBe('v2');
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
it('registers aliases as alternate registry keys for the same command', () => {
|
|
65
|
+
const cmd = cli({
|
|
66
|
+
site: 'test-registry',
|
|
67
|
+
name: 'canonical',
|
|
68
|
+
description: 'test aliases',
|
|
69
|
+
aliases: ['compat', 'legacy-name'],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const registry = getRegistry();
|
|
73
|
+
expect(cmd.aliases).toEqual(['compat', 'legacy-name']);
|
|
74
|
+
expect(registry.get('test-registry/canonical')).toBe(cmd);
|
|
75
|
+
expect(registry.get('test-registry/compat')).toBe(cmd);
|
|
76
|
+
expect(registry.get('test-registry/legacy-name')).toBe(cmd);
|
|
77
|
+
});
|
|
63
78
|
});
|
|
64
79
|
|
|
65
80
|
describe('fullName', () => {
|
package/src/registry.ts
CHANGED
|
@@ -33,6 +33,7 @@ export type CommandArgs = Record<string, any>;
|
|
|
33
33
|
export interface CliCommand {
|
|
34
34
|
site: string;
|
|
35
35
|
name: string;
|
|
36
|
+
aliases?: string[];
|
|
36
37
|
description: string;
|
|
37
38
|
domain?: string;
|
|
38
39
|
strategy?: Strategy;
|
|
@@ -85,9 +86,11 @@ const _registry: Map<string, CliCommand> =
|
|
|
85
86
|
export function cli(opts: CliOptions): CliCommand {
|
|
86
87
|
const strategy = opts.strategy ?? (opts.browser === false ? Strategy.PUBLIC : Strategy.COOKIE);
|
|
87
88
|
const browser = opts.browser ?? (strategy !== Strategy.PUBLIC);
|
|
89
|
+
const aliases = normalizeAliases(opts.aliases, opts.name);
|
|
88
90
|
const cmd: CliCommand = {
|
|
89
91
|
site: opts.site,
|
|
90
92
|
name: opts.name,
|
|
93
|
+
aliases,
|
|
91
94
|
description: opts.description ?? '',
|
|
92
95
|
domain: opts.domain,
|
|
93
96
|
strategy,
|
|
@@ -104,8 +107,7 @@ export function cli(opts: CliOptions): CliCommand {
|
|
|
104
107
|
navigateBefore: opts.navigateBefore,
|
|
105
108
|
};
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
_registry.set(key, cmd);
|
|
110
|
+
registerCommand(cmd);
|
|
109
111
|
return cmd;
|
|
110
112
|
}
|
|
111
113
|
|
|
@@ -122,5 +124,32 @@ export function strategyLabel(cmd: CliCommand): string {
|
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
export function registerCommand(cmd: CliCommand): void {
|
|
125
|
-
|
|
127
|
+
const canonicalKey = fullName(cmd);
|
|
128
|
+
const existing = _registry.get(canonicalKey);
|
|
129
|
+
if (existing) {
|
|
130
|
+
for (const [key, value] of _registry.entries()) {
|
|
131
|
+
if (value === existing && key !== canonicalKey) _registry.delete(key);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const aliases = normalizeAliases(cmd.aliases, cmd.name);
|
|
136
|
+
cmd.aliases = aliases.length > 0 ? aliases : undefined;
|
|
137
|
+
_registry.set(canonicalKey, cmd);
|
|
138
|
+
for (const alias of aliases) {
|
|
139
|
+
_registry.set(`${cmd.site}/${alias}`, cmd);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function normalizeAliases(aliases: string[] | undefined, commandName: string): string[] {
|
|
144
|
+
if (!Array.isArray(aliases) || aliases.length === 0) return [];
|
|
145
|
+
|
|
146
|
+
const seen = new Set<string>();
|
|
147
|
+
const normalized: string[] = [];
|
|
148
|
+
for (const alias of aliases) {
|
|
149
|
+
const value = typeof alias === 'string' ? alias.trim() : '';
|
|
150
|
+
if (!value || value === commandName || seen.has(value)) continue;
|
|
151
|
+
seen.add(value);
|
|
152
|
+
normalized.push(value);
|
|
153
|
+
}
|
|
154
|
+
return normalized;
|
|
126
155
|
}
|
package/src/runtime.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { BrowserBridge, CDPBridge } from './browser/index.js';
|
|
2
2
|
import type { IPage } from './types.js';
|
|
3
3
|
import { TimeoutError } from './errors.js';
|
|
4
|
+
import { isElectronApp } from './electron-apps.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* Returns the appropriate browser factory based on
|
|
7
|
-
* Uses CDPBridge
|
|
7
|
+
* Returns the appropriate browser factory based on site type.
|
|
8
|
+
* Uses CDPBridge for registered Electron apps, otherwise BrowserBridge.
|
|
8
9
|
*/
|
|
9
|
-
export function getBrowserFactory(): new () => IBrowserFactory {
|
|
10
|
-
|
|
10
|
+
export function getBrowserFactory(site?: string): new () => IBrowserFactory {
|
|
11
|
+
if (site && isElectronApp(site)) return CDPBridge;
|
|
12
|
+
return BrowserBridge;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
function parseEnvTimeout(envVar: string, fallback: number): number {
|
|
@@ -61,18 +63,22 @@ export function withTimeoutMs<T>(
|
|
|
61
63
|
|
|
62
64
|
/** Interface for browser factory (BrowserBridge or test mocks) */
|
|
63
65
|
export interface IBrowserFactory {
|
|
64
|
-
connect(opts?: { timeout?: number; workspace?: string }): Promise<IPage>;
|
|
66
|
+
connect(opts?: { timeout?: number; workspace?: string; cdpEndpoint?: string }): Promise<IPage>;
|
|
65
67
|
close(): Promise<void>;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
export async function browserSession<T>(
|
|
69
71
|
BrowserFactory: new () => IBrowserFactory,
|
|
70
72
|
fn: (page: IPage) => Promise<T>,
|
|
71
|
-
opts: { workspace?: string } = {},
|
|
73
|
+
opts: { workspace?: string; cdpEndpoint?: string } = {},
|
|
72
74
|
): Promise<T> {
|
|
73
75
|
const mcp = new BrowserFactory();
|
|
74
76
|
try {
|
|
75
|
-
const page = await mcp.connect({
|
|
77
|
+
const page = await mcp.connect({
|
|
78
|
+
timeout: DEFAULT_BROWSER_CONNECT_TIMEOUT,
|
|
79
|
+
workspace: opts.workspace,
|
|
80
|
+
cdpEndpoint: opts.cdpEndpoint,
|
|
81
|
+
});
|
|
76
82
|
return await fn(page);
|
|
77
83
|
} finally {
|
|
78
84
|
await mcp.close().catch(() => {});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import type { CliCommand } from './registry.js';
|
|
3
3
|
import { Strategy } from './registry.js';
|
|
4
|
-
import { formatRegistryHelpText } from './serialization.js';
|
|
4
|
+
import { formatRegistryHelpText, serializeCommand } from './serialization.js';
|
|
5
5
|
|
|
6
6
|
describe('formatRegistryHelpText', () => {
|
|
7
7
|
it('summarizes long choices lists so help text stays readable', () => {
|
|
@@ -23,4 +23,22 @@ describe('formatRegistryHelpText', () => {
|
|
|
23
23
|
|
|
24
24
|
expect(formatRegistryHelpText(cmd)).toContain('--field: all-fields, topic, title, author, ... (+3 more)');
|
|
25
25
|
});
|
|
26
|
+
|
|
27
|
+
it('includes aliases in structured serialization and help text', () => {
|
|
28
|
+
const cmd: CliCommand = {
|
|
29
|
+
site: 'demo',
|
|
30
|
+
name: 'get',
|
|
31
|
+
aliases: ['metadata'],
|
|
32
|
+
description: 'Demo command',
|
|
33
|
+
strategy: Strategy.COOKIE,
|
|
34
|
+
browser: true,
|
|
35
|
+
args: [],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
expect(serializeCommand(cmd)).toMatchObject({
|
|
39
|
+
command: 'demo/get',
|
|
40
|
+
aliases: ['metadata'],
|
|
41
|
+
});
|
|
42
|
+
expect(formatRegistryHelpText(cmd)).toContain('Aliases: metadata');
|
|
43
|
+
});
|
|
26
44
|
});
|
package/src/serialization.ts
CHANGED
|
@@ -39,6 +39,7 @@ export function serializeCommand(cmd: CliCommand) {
|
|
|
39
39
|
command: fullName(cmd),
|
|
40
40
|
site: cmd.site,
|
|
41
41
|
name: cmd.name,
|
|
42
|
+
aliases: cmd.aliases ?? [],
|
|
42
43
|
description: cmd.description,
|
|
43
44
|
strategy: strategyLabel(cmd),
|
|
44
45
|
browser: !!cmd.browser,
|
|
@@ -82,6 +83,7 @@ export function formatRegistryHelpText(cmd: CliCommand): string {
|
|
|
82
83
|
if (cmd.domain) meta.push(`Domain: ${cmd.domain}`);
|
|
83
84
|
if (cmd.deprecated) meta.push(`Deprecated: ${typeof cmd.deprecated === 'string' ? cmd.deprecated : 'yes'}`);
|
|
84
85
|
if (cmd.replacedBy) meta.push(`Use instead: ${cmd.replacedBy}`);
|
|
86
|
+
if (cmd.aliases?.length) meta.push(`Aliases: ${cmd.aliases.join(', ')}`);
|
|
85
87
|
lines.push(meta.join(' | '));
|
|
86
88
|
if (cmd.columns?.length) lines.push(`Output columns: ${cmd.columns.join(', ')}`);
|
|
87
89
|
return '\n' + lines.join('\n') + '\n';
|
package/src/tui.test.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { confirmPrompt } from './tui.js';
|
|
3
|
+
|
|
4
|
+
describe('confirmPrompt', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.restoreAllMocks();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('returns defaultYes when stdin is not TTY', async () => {
|
|
10
|
+
const result = await confirmPrompt('Restart?', true);
|
|
11
|
+
expect(result).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('returns false when defaultYes is false and non-TTY', async () => {
|
|
15
|
+
const result = await confirmPrompt('Restart?', false);
|
|
16
|
+
expect(result).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('defaults to true when defaultYes is omitted and non-TTY', async () => {
|
|
20
|
+
const result = await confirmPrompt('Restart?');
|
|
21
|
+
expect(result).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
});
|