@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
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { getRegistry } from '../../registry.js';
|
|
3
|
+
import './topic.js';
|
|
4
|
+
describe('zsxq topic command', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.restoreAllMocks();
|
|
7
|
+
});
|
|
8
|
+
it('maps topic detail 404 responses to NOT_FOUND before fetching comments', async () => {
|
|
9
|
+
const command = getRegistry().get('zsxq/topic');
|
|
10
|
+
expect(command?.func).toBeTypeOf('function');
|
|
11
|
+
const mockPage = {
|
|
12
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
13
|
+
evaluate: vi.fn()
|
|
14
|
+
.mockResolvedValueOnce(true)
|
|
15
|
+
.mockResolvedValueOnce({
|
|
16
|
+
ok: true,
|
|
17
|
+
status: 404,
|
|
18
|
+
url: 'https://api.zsxq.com/v2/topics/404',
|
|
19
|
+
data: null,
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
await expect(command.func(mockPage, { id: '404', comment_limit: 20 })).rejects.toMatchObject({
|
|
23
|
+
code: 'NOT_FOUND',
|
|
24
|
+
message: 'Topic 404 not found',
|
|
25
|
+
});
|
|
26
|
+
expect(mockPage.goto).toHaveBeenCalledWith('https://wx.zsxq.com');
|
|
27
|
+
expect(mockPage.evaluate).toHaveBeenCalledTimes(2);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { getActiveGroupId, ensureZsxqAuth, ensureZsxqPage, fetchFirstJson, getTopicsFromResponse, toTopicRow, } from './utils.js';
|
|
3
|
+
cli({
|
|
4
|
+
site: 'zsxq',
|
|
5
|
+
name: 'topics',
|
|
6
|
+
description: '获取当前星球的话题列表',
|
|
7
|
+
domain: 'wx.zsxq.com',
|
|
8
|
+
strategy: Strategy.COOKIE,
|
|
9
|
+
browser: true,
|
|
10
|
+
args: [
|
|
11
|
+
{ name: 'limit', type: 'int', default: 20, help: 'Number of topics to return' },
|
|
12
|
+
{ name: 'group_id', help: 'Optional group id; defaults to the active group in Chrome' },
|
|
13
|
+
],
|
|
14
|
+
columns: ['topic_id', 'type', 'author', 'title', 'comments', 'likes', 'time', 'url'],
|
|
15
|
+
func: async (page, kwargs) => {
|
|
16
|
+
await ensureZsxqPage(page);
|
|
17
|
+
await ensureZsxqAuth(page);
|
|
18
|
+
const limit = Math.max(1, Number(kwargs.limit) || 20);
|
|
19
|
+
const groupId = String(kwargs.group_id || await getActiveGroupId(page));
|
|
20
|
+
const { data } = await fetchFirstJson(page, [
|
|
21
|
+
`https://api.zsxq.com/v2/groups/${groupId}/topics?scope=all&count=${limit}`,
|
|
22
|
+
]);
|
|
23
|
+
return getTopicsFromResponse(data).slice(0, limit).map(toTopicRow);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './topics.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { getRegistry } from '../../registry.js';
|
|
3
|
+
import './topics.js';
|
|
4
|
+
describe('zsxq topics command', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.restoreAllMocks();
|
|
7
|
+
});
|
|
8
|
+
it('requires an explicit group_id when there is no active group context', async () => {
|
|
9
|
+
const command = getRegistry().get('zsxq/topics');
|
|
10
|
+
expect(command?.func).toBeTypeOf('function');
|
|
11
|
+
const mockPage = {
|
|
12
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
13
|
+
evaluate: vi.fn()
|
|
14
|
+
.mockResolvedValueOnce(true)
|
|
15
|
+
.mockResolvedValueOnce(null),
|
|
16
|
+
};
|
|
17
|
+
await expect(command.func(mockPage, { limit: 20 })).rejects.toMatchObject({
|
|
18
|
+
code: 'ARGUMENT',
|
|
19
|
+
message: 'Cannot determine active group_id',
|
|
20
|
+
});
|
|
21
|
+
expect(mockPage.goto).toHaveBeenCalledWith('https://wx.zsxq.com');
|
|
22
|
+
expect(mockPage.evaluate).toHaveBeenCalledTimes(2);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { IPage } from '../../types.js';
|
|
2
|
+
export interface ZsxqUser {
|
|
3
|
+
user_id?: number;
|
|
4
|
+
name?: string;
|
|
5
|
+
avatar_url?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ZsxqGroup {
|
|
8
|
+
group_id?: number;
|
|
9
|
+
name?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
background_url?: string;
|
|
12
|
+
owner?: ZsxqUser;
|
|
13
|
+
statistics?: {
|
|
14
|
+
topics_count?: number;
|
|
15
|
+
answers_count?: number;
|
|
16
|
+
comments_count?: number;
|
|
17
|
+
likes_count?: number;
|
|
18
|
+
subscriptions_count?: number;
|
|
19
|
+
};
|
|
20
|
+
category?: {
|
|
21
|
+
title?: string;
|
|
22
|
+
};
|
|
23
|
+
user_specific?: {
|
|
24
|
+
join_time?: string;
|
|
25
|
+
validity?: {
|
|
26
|
+
end_time?: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface ZsxqComment {
|
|
31
|
+
comment_id?: number;
|
|
32
|
+
create_time?: string;
|
|
33
|
+
text?: string;
|
|
34
|
+
owner?: ZsxqUser;
|
|
35
|
+
likes_count?: number;
|
|
36
|
+
rewards_count?: number;
|
|
37
|
+
repliee?: ZsxqUser;
|
|
38
|
+
}
|
|
39
|
+
export interface ZsxqTopic {
|
|
40
|
+
topic_id?: number;
|
|
41
|
+
create_time?: string;
|
|
42
|
+
comments_count?: number;
|
|
43
|
+
likes_count?: number;
|
|
44
|
+
readers_count?: number;
|
|
45
|
+
reading_count?: number;
|
|
46
|
+
rewards_count?: number;
|
|
47
|
+
title?: string;
|
|
48
|
+
type?: string;
|
|
49
|
+
group?: ZsxqGroup;
|
|
50
|
+
owner?: ZsxqUser;
|
|
51
|
+
user_specific?: Record<string, unknown>;
|
|
52
|
+
talk?: {
|
|
53
|
+
owner?: ZsxqUser;
|
|
54
|
+
text?: string;
|
|
55
|
+
};
|
|
56
|
+
question?: {
|
|
57
|
+
owner?: ZsxqUser;
|
|
58
|
+
text?: string;
|
|
59
|
+
};
|
|
60
|
+
answer?: {
|
|
61
|
+
owner?: ZsxqUser;
|
|
62
|
+
text?: string;
|
|
63
|
+
};
|
|
64
|
+
task?: {
|
|
65
|
+
owner?: ZsxqUser;
|
|
66
|
+
text?: string;
|
|
67
|
+
};
|
|
68
|
+
solution?: {
|
|
69
|
+
owner?: ZsxqUser;
|
|
70
|
+
text?: string;
|
|
71
|
+
};
|
|
72
|
+
show_comments?: ZsxqComment[];
|
|
73
|
+
comments?: ZsxqComment[];
|
|
74
|
+
}
|
|
75
|
+
export interface BrowserFetchResult {
|
|
76
|
+
ok: boolean;
|
|
77
|
+
url?: string;
|
|
78
|
+
status?: number;
|
|
79
|
+
error?: string;
|
|
80
|
+
data?: unknown;
|
|
81
|
+
}
|
|
82
|
+
export declare function ensureZsxqPage(page: IPage): Promise<void>;
|
|
83
|
+
export declare function ensureZsxqAuth(page: IPage): Promise<void>;
|
|
84
|
+
export declare function getCookieValue(page: IPage, name: string): Promise<string | undefined>;
|
|
85
|
+
export declare function getActiveGroupId(page: IPage): Promise<string>;
|
|
86
|
+
export declare function browserJsonRequest(page: IPage, path: string): Promise<BrowserFetchResult>;
|
|
87
|
+
export declare function fetchFirstJson(page: IPage, paths: string[]): Promise<BrowserFetchResult>;
|
|
88
|
+
export declare function unwrapRespData<T>(payload: unknown): T;
|
|
89
|
+
export declare function getTopicsFromResponse(payload: unknown): ZsxqTopic[];
|
|
90
|
+
export declare function getCommentsFromResponse(payload: unknown): ZsxqComment[];
|
|
91
|
+
export declare function getGroupsFromResponse(payload: unknown): ZsxqGroup[];
|
|
92
|
+
export declare function getTopicFromResponse(payload: unknown): ZsxqTopic | null;
|
|
93
|
+
export declare function getTopicAuthor(topic: ZsxqTopic): string;
|
|
94
|
+
export declare function getTopicText(topic: ZsxqTopic): string;
|
|
95
|
+
export declare function getTopicUrl(topicId: number | string | undefined): string;
|
|
96
|
+
export declare function summarizeComments(comments: ZsxqComment[], limit?: number): string;
|
|
97
|
+
export declare function toTopicRow(topic: ZsxqTopic): Record<string, unknown>;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { ArgumentError, AuthRequiredError, CliError } from '../../errors.js';
|
|
2
|
+
const SITE_DOMAIN = 'wx.zsxq.com';
|
|
3
|
+
const SITE_URL = 'https://wx.zsxq.com';
|
|
4
|
+
function asRecord(value) {
|
|
5
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
6
|
+
? value
|
|
7
|
+
: null;
|
|
8
|
+
}
|
|
9
|
+
function pickArray(...values) {
|
|
10
|
+
for (const value of values) {
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
export async function ensureZsxqPage(page) {
|
|
18
|
+
await page.goto(SITE_URL);
|
|
19
|
+
}
|
|
20
|
+
export async function ensureZsxqAuth(page) {
|
|
21
|
+
// zsxq uses httpOnly cookies that may be on different subdomains.
|
|
22
|
+
// Verify auth by attempting a lightweight API call instead of checking cookies.
|
|
23
|
+
try {
|
|
24
|
+
const result = await page.evaluate(`
|
|
25
|
+
(async () => {
|
|
26
|
+
try {
|
|
27
|
+
const r = await new Promise((resolve, reject) => {
|
|
28
|
+
const xhr = new XMLHttpRequest();
|
|
29
|
+
xhr.open('GET', 'https://api.zsxq.com/v2/groups', true);
|
|
30
|
+
xhr.withCredentials = true;
|
|
31
|
+
xhr.setRequestHeader('accept', 'application/json');
|
|
32
|
+
xhr.onload = () => {
|
|
33
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
34
|
+
try { resolve(JSON.parse(xhr.responseText)); }
|
|
35
|
+
catch { resolve(null); }
|
|
36
|
+
} else { resolve(null); }
|
|
37
|
+
};
|
|
38
|
+
xhr.onerror = () => resolve(null);
|
|
39
|
+
xhr.send();
|
|
40
|
+
});
|
|
41
|
+
return r !== null;
|
|
42
|
+
} catch { return false; }
|
|
43
|
+
})()
|
|
44
|
+
`);
|
|
45
|
+
if (!result) {
|
|
46
|
+
throw new AuthRequiredError('zsxq.com');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (err instanceof AuthRequiredError)
|
|
51
|
+
throw err;
|
|
52
|
+
throw new AuthRequiredError('zsxq.com');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function getCookieValue(page, name) {
|
|
56
|
+
const cookies = await page.getCookies({ domain: SITE_DOMAIN });
|
|
57
|
+
return cookies.find(cookie => cookie.name === name)?.value;
|
|
58
|
+
}
|
|
59
|
+
export async function getActiveGroupId(page) {
|
|
60
|
+
const groupId = await page.evaluate(`
|
|
61
|
+
(() => {
|
|
62
|
+
const target = localStorage.getItem('target_group');
|
|
63
|
+
if (target) {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(target);
|
|
66
|
+
if (parsed.group_id) return String(parsed.group_id);
|
|
67
|
+
} catch {}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
})()
|
|
71
|
+
`);
|
|
72
|
+
if (groupId)
|
|
73
|
+
return groupId;
|
|
74
|
+
throw new ArgumentError('Cannot determine active group_id', 'Pass --group_id <id> or open the target 知识星球 page in Chrome first');
|
|
75
|
+
}
|
|
76
|
+
export async function browserJsonRequest(page, path) {
|
|
77
|
+
return await page.evaluate(`
|
|
78
|
+
(async () => {
|
|
79
|
+
const path = ${JSON.stringify(path)};
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
return await new Promise((resolve) => {
|
|
83
|
+
const xhr = new XMLHttpRequest();
|
|
84
|
+
xhr.open('GET', path, true);
|
|
85
|
+
xhr.withCredentials = true;
|
|
86
|
+
xhr.setRequestHeader('accept', 'application/json, text/plain, */*');
|
|
87
|
+
xhr.onload = () => {
|
|
88
|
+
let parsed = null;
|
|
89
|
+
if (xhr.responseText) {
|
|
90
|
+
try { parsed = JSON.parse(xhr.responseText); }
|
|
91
|
+
catch {}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
resolve({
|
|
95
|
+
ok: xhr.status >= 200 && xhr.status < 300,
|
|
96
|
+
url: path,
|
|
97
|
+
status: xhr.status,
|
|
98
|
+
data: parsed,
|
|
99
|
+
error: xhr.status >= 200 && xhr.status < 300 ? undefined : 'HTTP ' + xhr.status,
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
xhr.onerror = () => resolve({
|
|
103
|
+
ok: false,
|
|
104
|
+
url: path,
|
|
105
|
+
error: 'Network error',
|
|
106
|
+
});
|
|
107
|
+
xhr.send();
|
|
108
|
+
});
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return {
|
|
111
|
+
ok: false,
|
|
112
|
+
url: path,
|
|
113
|
+
error: error instanceof Error ? error.message : String(error),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
})()
|
|
117
|
+
`);
|
|
118
|
+
}
|
|
119
|
+
export async function fetchFirstJson(page, paths) {
|
|
120
|
+
let lastFailure = null;
|
|
121
|
+
for (const path of paths) {
|
|
122
|
+
const result = await browserJsonRequest(page, path);
|
|
123
|
+
if (result.ok) {
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
lastFailure = result;
|
|
127
|
+
}
|
|
128
|
+
if (!lastFailure) {
|
|
129
|
+
throw new CliError('FETCH_ERROR', 'No candidate endpoint returned JSON', `Checked endpoints: ${paths.join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
throw new CliError('FETCH_ERROR', lastFailure.error || 'Failed to fetch ZSXQ API', `Checked endpoints: ${paths.join(', ')}`);
|
|
132
|
+
}
|
|
133
|
+
export function unwrapRespData(payload) {
|
|
134
|
+
const record = asRecord(payload);
|
|
135
|
+
if (!record) {
|
|
136
|
+
throw new CliError('PARSE_ERROR', 'Invalid ZSXQ API response');
|
|
137
|
+
}
|
|
138
|
+
if (record.succeeded === false) {
|
|
139
|
+
const code = typeof record.code === 'number' ? String(record.code) : 'API_ERROR';
|
|
140
|
+
const message = typeof record.info === 'string'
|
|
141
|
+
? record.info
|
|
142
|
+
: typeof record.error === 'string'
|
|
143
|
+
? record.error
|
|
144
|
+
: 'ZSXQ API returned an error';
|
|
145
|
+
throw new CliError(code, message);
|
|
146
|
+
}
|
|
147
|
+
return (record.resp_data ?? record.data ?? payload);
|
|
148
|
+
}
|
|
149
|
+
export function getTopicsFromResponse(payload) {
|
|
150
|
+
const data = unwrapRespData(payload);
|
|
151
|
+
if (Array.isArray(data))
|
|
152
|
+
return data;
|
|
153
|
+
return pickArray(data.topics, data.list, data.records, data.items, data.search_result);
|
|
154
|
+
}
|
|
155
|
+
export function getCommentsFromResponse(payload) {
|
|
156
|
+
const data = unwrapRespData(payload);
|
|
157
|
+
if (Array.isArray(data))
|
|
158
|
+
return data;
|
|
159
|
+
return pickArray(data.comments, data.list, data.items);
|
|
160
|
+
}
|
|
161
|
+
export function getGroupsFromResponse(payload) {
|
|
162
|
+
const data = unwrapRespData(payload);
|
|
163
|
+
if (Array.isArray(data))
|
|
164
|
+
return data;
|
|
165
|
+
return pickArray(data.groups, data.list, data.items);
|
|
166
|
+
}
|
|
167
|
+
export function getTopicFromResponse(payload) {
|
|
168
|
+
const data = unwrapRespData(payload);
|
|
169
|
+
if (Array.isArray(data))
|
|
170
|
+
return data[0] ?? null;
|
|
171
|
+
if (typeof data.topic_id === 'number')
|
|
172
|
+
return data;
|
|
173
|
+
const record = asRecord(data);
|
|
174
|
+
if (!record)
|
|
175
|
+
return null;
|
|
176
|
+
const topic = record.topic;
|
|
177
|
+
return topic && typeof topic === 'object' ? topic : null;
|
|
178
|
+
}
|
|
179
|
+
export function getTopicAuthor(topic) {
|
|
180
|
+
return (topic.owner?.name ||
|
|
181
|
+
topic.talk?.owner?.name ||
|
|
182
|
+
topic.question?.owner?.name ||
|
|
183
|
+
topic.answer?.owner?.name ||
|
|
184
|
+
topic.task?.owner?.name ||
|
|
185
|
+
topic.solution?.owner?.name ||
|
|
186
|
+
'');
|
|
187
|
+
}
|
|
188
|
+
export function getTopicText(topic) {
|
|
189
|
+
const primary = [
|
|
190
|
+
topic.title,
|
|
191
|
+
topic.talk?.text,
|
|
192
|
+
topic.question?.text,
|
|
193
|
+
topic.answer?.text,
|
|
194
|
+
topic.task?.text,
|
|
195
|
+
topic.solution?.text,
|
|
196
|
+
].find(value => typeof value === 'string' && value.trim());
|
|
197
|
+
return (primary || '').replace(/\s+/g, ' ').trim();
|
|
198
|
+
}
|
|
199
|
+
export function getTopicUrl(topicId) {
|
|
200
|
+
return topicId ? `${SITE_URL}/topic/${topicId}` : SITE_URL;
|
|
201
|
+
}
|
|
202
|
+
export function summarizeComments(comments, limit = 3) {
|
|
203
|
+
return comments
|
|
204
|
+
.slice(0, limit)
|
|
205
|
+
.map((comment) => {
|
|
206
|
+
const author = comment.owner?.name || '匿名';
|
|
207
|
+
const target = comment.repliee?.name ? ` -> ${comment.repliee.name}` : '';
|
|
208
|
+
const text = (comment.text || '').replace(/\s+/g, ' ').trim();
|
|
209
|
+
return `${author}${target}: ${text}`;
|
|
210
|
+
})
|
|
211
|
+
.join(' | ');
|
|
212
|
+
}
|
|
213
|
+
export function toTopicRow(topic) {
|
|
214
|
+
const topicId = topic.topic_id ?? '';
|
|
215
|
+
const comments = pickArray(topic.show_comments, topic.comments);
|
|
216
|
+
return {
|
|
217
|
+
topic_id: topicId,
|
|
218
|
+
type: topic.type || '',
|
|
219
|
+
group: topic.group?.name || '',
|
|
220
|
+
author: getTopicAuthor(topic),
|
|
221
|
+
title: getTopicText(topic).slice(0, 120),
|
|
222
|
+
content: getTopicText(topic),
|
|
223
|
+
comments: topic.comments_count ?? comments.length ?? 0,
|
|
224
|
+
likes: topic.likes_count ?? 0,
|
|
225
|
+
readers: topic.readers_count ?? topic.reading_count ?? 0,
|
|
226
|
+
time: topic.create_time || '',
|
|
227
|
+
comment_preview: summarizeComments(comments),
|
|
228
|
+
url: getTopicUrl(topicId),
|
|
229
|
+
};
|
|
230
|
+
}
|
package/dist/commanderAdapter.js
CHANGED
|
@@ -17,7 +17,7 @@ import { executeCommand } from './execution.js';
|
|
|
17
17
|
import { CliError, EXIT_CODES, ERROR_ICONS, getErrorMessage, BrowserConnectError, AuthRequiredError, TimeoutError, SelectorError, EmptyResultError, ArgumentError, AdapterLoadError, CommandExecutionError, } from './errors.js';
|
|
18
18
|
import { checkDaemonStatus } from './browser/discover.js';
|
|
19
19
|
export function normalizeArgValue(argType, value, name) {
|
|
20
|
-
if (argType !== 'bool')
|
|
20
|
+
if (argType !== 'bool' && argType !== 'boolean')
|
|
21
21
|
return value;
|
|
22
22
|
if (typeof value === 'boolean')
|
|
23
23
|
return value;
|
|
@@ -38,6 +38,8 @@ export function registerCommandToProgram(siteCmd, cmd) {
|
|
|
38
38
|
return;
|
|
39
39
|
const deprecatedSuffix = cmd.deprecated ? ' [deprecated]' : '';
|
|
40
40
|
const subCmd = siteCmd.command(cmd.name).description(`${cmd.description}${deprecatedSuffix}`);
|
|
41
|
+
if (cmd.aliases?.length)
|
|
42
|
+
subCmd.aliases(cmd.aliases);
|
|
41
43
|
// Register positional args first, then named options
|
|
42
44
|
const positionalArgs = [];
|
|
43
45
|
for (const arg of cmd.args) {
|
|
@@ -91,6 +93,9 @@ export function registerCommandToProgram(siteCmd, cmd) {
|
|
|
91
93
|
console.error(chalk.yellow(`Deprecated: ${message}${replacement}`));
|
|
92
94
|
}
|
|
93
95
|
const result = await executeCommand(cmd, kwargs, verbose);
|
|
96
|
+
if (result === null || result === undefined) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
94
99
|
if (verbose && (!result || (Array.isArray(result) && result.length === 0))) {
|
|
95
100
|
console.error(chalk.yellow('[Verbose] Warning: Command returned an empty result.'));
|
|
96
101
|
}
|
|
@@ -274,7 +279,11 @@ async function renderError(err, cmdName, verbose) {
|
|
|
274
279
|
* Register all commands from the registry onto a Commander program.
|
|
275
280
|
*/
|
|
276
281
|
export function registerAllCommands(program, siteGroups) {
|
|
282
|
+
const seen = new Set();
|
|
277
283
|
for (const [, cmd] of getRegistry()) {
|
|
284
|
+
if (seen.has(cmd))
|
|
285
|
+
continue;
|
|
286
|
+
seen.add(cmd);
|
|
278
287
|
let siteCmd = siteGroups.get(cmd.site);
|
|
279
288
|
if (!siteCmd) {
|
|
280
289
|
siteCmd = program.command(cmd.site).description(`${cmd.site} commands`);
|
|
@@ -60,3 +60,67 @@ describe('commanderAdapter arg passing', () => {
|
|
|
60
60
|
expect(mockExecuteCommand).not.toHaveBeenCalled();
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
|
+
describe('commanderAdapter boolean alias support', () => {
|
|
64
|
+
const cmd = {
|
|
65
|
+
site: 'reddit',
|
|
66
|
+
name: 'save',
|
|
67
|
+
description: 'Save a post',
|
|
68
|
+
browser: false,
|
|
69
|
+
args: [
|
|
70
|
+
{ name: 'post-id', positional: true, required: true, help: 'Post ID' },
|
|
71
|
+
{ name: 'undo', type: 'boolean', default: false, help: 'Unsave instead of save' },
|
|
72
|
+
],
|
|
73
|
+
func: vi.fn(),
|
|
74
|
+
};
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
mockExecuteCommand.mockReset();
|
|
77
|
+
mockExecuteCommand.mockResolvedValue([]);
|
|
78
|
+
mockRenderOutput.mockReset();
|
|
79
|
+
delete process.env.OPENCLI_VERBOSE;
|
|
80
|
+
process.exitCode = undefined;
|
|
81
|
+
});
|
|
82
|
+
it('coerces default false for boolean args to a real boolean', async () => {
|
|
83
|
+
const program = new Command();
|
|
84
|
+
const siteCmd = program.command('reddit');
|
|
85
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
86
|
+
await program.parseAsync(['node', 'opencli', 'reddit', 'save', 't3_abc123']);
|
|
87
|
+
expect(mockExecuteCommand).toHaveBeenCalled();
|
|
88
|
+
const kwargs = mockExecuteCommand.mock.calls[0][1];
|
|
89
|
+
expect(kwargs['post-id']).toBe('t3_abc123');
|
|
90
|
+
expect(kwargs.undo).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
it('coerces explicit false for boolean args to a real boolean', async () => {
|
|
93
|
+
const program = new Command();
|
|
94
|
+
const siteCmd = program.command('reddit');
|
|
95
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
96
|
+
await program.parseAsync(['node', 'opencli', 'reddit', 'save', 't3_abc123', '--undo', 'false']);
|
|
97
|
+
expect(mockExecuteCommand).toHaveBeenCalled();
|
|
98
|
+
const kwargs = mockExecuteCommand.mock.calls[0][1];
|
|
99
|
+
expect(kwargs.undo).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('commanderAdapter command aliases', () => {
|
|
103
|
+
const cmd = {
|
|
104
|
+
site: 'notebooklm',
|
|
105
|
+
name: 'get',
|
|
106
|
+
aliases: ['metadata'],
|
|
107
|
+
description: 'Get notebook metadata',
|
|
108
|
+
browser: false,
|
|
109
|
+
args: [],
|
|
110
|
+
func: vi.fn(),
|
|
111
|
+
};
|
|
112
|
+
beforeEach(() => {
|
|
113
|
+
mockExecuteCommand.mockReset();
|
|
114
|
+
mockExecuteCommand.mockResolvedValue([]);
|
|
115
|
+
mockRenderOutput.mockReset();
|
|
116
|
+
delete process.env.OPENCLI_VERBOSE;
|
|
117
|
+
process.exitCode = undefined;
|
|
118
|
+
});
|
|
119
|
+
it('registers aliases with Commander so compatibility names execute the same command', async () => {
|
|
120
|
+
const program = new Command();
|
|
121
|
+
const siteCmd = program.command('notebooklm');
|
|
122
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
123
|
+
await program.parseAsync(['node', 'opencli', 'notebooklm', 'metadata']);
|
|
124
|
+
expect(mockExecuteCommand).toHaveBeenCalledWith(cmd, {}, false);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI commands for daemon lifecycle management:
|
|
3
|
+
* opencli daemon status — show daemon state
|
|
4
|
+
* opencli daemon stop — graceful shutdown
|
|
5
|
+
* opencli daemon restart — stop + respawn
|
|
6
|
+
*/
|
|
7
|
+
export declare function daemonStatus(): Promise<void>;
|
|
8
|
+
export declare function daemonStop(): Promise<void>;
|
|
9
|
+
export declare function daemonRestart(): Promise<void>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI commands for daemon lifecycle management:
|
|
3
|
+
* opencli daemon status — show daemon state
|
|
4
|
+
* opencli daemon stop — graceful shutdown
|
|
5
|
+
* opencli daemon restart — stop + respawn
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { DEFAULT_DAEMON_PORT } from '../constants.js';
|
|
9
|
+
const DAEMON_PORT = parseInt(process.env.OPENCLI_DAEMON_PORT ?? String(DEFAULT_DAEMON_PORT), 10);
|
|
10
|
+
const DAEMON_URL = `http://127.0.0.1:${DAEMON_PORT}`;
|
|
11
|
+
async function fetchStatus() {
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const timer = setTimeout(() => controller.abort(), 2000);
|
|
14
|
+
try {
|
|
15
|
+
const res = await fetch(`${DAEMON_URL}/status`, {
|
|
16
|
+
headers: { 'X-OpenCLI': '1' },
|
|
17
|
+
signal: controller.signal,
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok)
|
|
20
|
+
return null;
|
|
21
|
+
return await res.json();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function requestShutdown() {
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const timer = setTimeout(() => controller.abort(), 5000);
|
|
33
|
+
try {
|
|
34
|
+
const res = await fetch(`${DAEMON_URL}/shutdown`, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: { 'X-OpenCLI': '1' },
|
|
37
|
+
signal: controller.signal,
|
|
38
|
+
});
|
|
39
|
+
return res.ok;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function formatUptime(seconds) {
|
|
49
|
+
const h = Math.floor(seconds / 3600);
|
|
50
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
51
|
+
if (h > 0)
|
|
52
|
+
return `${h}h ${m}m`;
|
|
53
|
+
if (m > 0)
|
|
54
|
+
return `${m}m`;
|
|
55
|
+
return `${Math.floor(seconds)}s`;
|
|
56
|
+
}
|
|
57
|
+
function formatTimeSince(timestampMs) {
|
|
58
|
+
const seconds = (Date.now() - timestampMs) / 1000;
|
|
59
|
+
if (seconds < 60)
|
|
60
|
+
return `${Math.floor(seconds)}s ago`;
|
|
61
|
+
const m = Math.floor(seconds / 60);
|
|
62
|
+
if (m < 60)
|
|
63
|
+
return `${m} min ago`;
|
|
64
|
+
const h = Math.floor(m / 60);
|
|
65
|
+
return `${h}h ${m % 60}m ago`;
|
|
66
|
+
}
|
|
67
|
+
export async function daemonStatus() {
|
|
68
|
+
const status = await fetchStatus();
|
|
69
|
+
if (!status) {
|
|
70
|
+
console.log(`Daemon: ${chalk.dim('not running')}`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
console.log(`Daemon: ${chalk.green('running')} (PID ${status.pid})`);
|
|
74
|
+
console.log(`Uptime: ${formatUptime(status.uptime)}`);
|
|
75
|
+
console.log(`Extension: ${status.extensionConnected ? chalk.green('connected') : chalk.yellow('disconnected')}`);
|
|
76
|
+
console.log(`Last CLI request: ${formatTimeSince(status.lastCliRequestTime)}`);
|
|
77
|
+
console.log(`Memory: ${status.memoryMB} MB`);
|
|
78
|
+
console.log(`Port: ${status.port}`);
|
|
79
|
+
}
|
|
80
|
+
export async function daemonStop() {
|
|
81
|
+
const status = await fetchStatus();
|
|
82
|
+
if (!status) {
|
|
83
|
+
console.log(chalk.dim('Daemon is not running.'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const ok = await requestShutdown();
|
|
87
|
+
if (ok) {
|
|
88
|
+
console.log(chalk.green('Daemon stopped.'));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.error(chalk.red('Failed to stop daemon.'));
|
|
92
|
+
process.exitCode = 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export async function daemonRestart() {
|
|
96
|
+
const status = await fetchStatus();
|
|
97
|
+
if (status) {
|
|
98
|
+
const ok = await requestShutdown();
|
|
99
|
+
if (!ok) {
|
|
100
|
+
console.error(chalk.red('Failed to stop daemon.'));
|
|
101
|
+
process.exitCode = 1;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Wait for daemon to actually exit (poll until unreachable)
|
|
105
|
+
const deadline = Date.now() + 5000;
|
|
106
|
+
while (Date.now() < deadline) {
|
|
107
|
+
await new Promise(r => setTimeout(r, 200));
|
|
108
|
+
if (!(await fetchStatus()))
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Import BrowserBridge to spawn a new daemon
|
|
113
|
+
const { BrowserBridge } = await import('../browser/mcp.js');
|
|
114
|
+
const bridge = new BrowserBridge();
|
|
115
|
+
try {
|
|
116
|
+
console.log('Starting daemon...');
|
|
117
|
+
await bridge.connect({ timeout: 10 });
|
|
118
|
+
console.log(chalk.green('Daemon restarted.'));
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.error(chalk.red(`Failed to restart daemon: ${err instanceof Error ? err.message : err}`));
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|