@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,189 @@
|
|
|
1
|
+
import { AuthRequiredError, CliError } from '../../errors.js';
|
|
2
|
+
import { NOTEBOOKLM_DOMAIN } from './shared.js';
|
|
3
|
+
export function extractNotebooklmPageAuthFromHtml(html, sourcePath = '/', preferredTokens) {
|
|
4
|
+
const csrfMatch = html.match(/"SNlM0e":"([^"]+)"/);
|
|
5
|
+
const sessionMatch = html.match(/"FdrFJe":"([^"]+)"/);
|
|
6
|
+
const csrfToken = preferredTokens?.csrfToken?.trim() || (csrfMatch ? csrfMatch[1] : '');
|
|
7
|
+
const sessionId = preferredTokens?.sessionId?.trim() || (sessionMatch ? sessionMatch[1] : '');
|
|
8
|
+
if (!csrfToken || !sessionId) {
|
|
9
|
+
throw new CliError('NOTEBOOKLM_TOKENS', 'NotebookLM page tokens were not found in the current page HTML', 'Open the NotebookLM notebook page in Chrome, wait for it to finish loading, then retry with --verbose if it still fails.');
|
|
10
|
+
}
|
|
11
|
+
return { csrfToken, sessionId, sourcePath: sourcePath || '/' };
|
|
12
|
+
}
|
|
13
|
+
async function probeNotebooklmPageAuth(page) {
|
|
14
|
+
const raw = await page.evaluate(`(() => {
|
|
15
|
+
const wiz = window.WIZ_global_data || {};
|
|
16
|
+
const html = document.documentElement.innerHTML;
|
|
17
|
+
return {
|
|
18
|
+
html,
|
|
19
|
+
sourcePath: location.pathname || '/',
|
|
20
|
+
readyState: document.readyState || '',
|
|
21
|
+
csrfToken: typeof wiz.SNlM0e === 'string' ? wiz.SNlM0e : '',
|
|
22
|
+
sessionId: typeof wiz.FdrFJe === 'string' ? wiz.FdrFJe : '',
|
|
23
|
+
};
|
|
24
|
+
})()`);
|
|
25
|
+
return {
|
|
26
|
+
html: String(raw?.html ?? ''),
|
|
27
|
+
sourcePath: String(raw?.sourcePath ?? '/'),
|
|
28
|
+
readyState: String(raw?.readyState ?? ''),
|
|
29
|
+
csrfToken: String(raw?.csrfToken ?? ''),
|
|
30
|
+
sessionId: String(raw?.sessionId ?? ''),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export async function getNotebooklmPageAuth(page) {
|
|
34
|
+
let lastError;
|
|
35
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
36
|
+
const probe = await probeNotebooklmPageAuth(page);
|
|
37
|
+
try {
|
|
38
|
+
return extractNotebooklmPageAuthFromHtml(probe.html, probe.sourcePath, { csrfToken: probe.csrfToken, sessionId: probe.sessionId });
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
lastError = error;
|
|
42
|
+
if (attempt === 0 && typeof page.wait === 'function') {
|
|
43
|
+
await page.wait(0.5).catch(() => undefined);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
throw lastError;
|
|
49
|
+
}
|
|
50
|
+
export function buildNotebooklmRpcBody(rpcId, params, csrfToken) {
|
|
51
|
+
const rpcRequest = [[[rpcId, JSON.stringify(params), null, 'generic']]];
|
|
52
|
+
return `f.req=${encodeURIComponent(JSON.stringify(rpcRequest))}&at=${encodeURIComponent(csrfToken)}&`;
|
|
53
|
+
}
|
|
54
|
+
export function stripNotebooklmAntiXssi(rawBody) {
|
|
55
|
+
if (!rawBody.startsWith(")]}'"))
|
|
56
|
+
return rawBody;
|
|
57
|
+
return rawBody.replace(/^\)\]\}'\r?\n/, '');
|
|
58
|
+
}
|
|
59
|
+
export function parseNotebooklmChunkedResponse(rawBody) {
|
|
60
|
+
const cleaned = stripNotebooklmAntiXssi(rawBody).trim();
|
|
61
|
+
if (!cleaned)
|
|
62
|
+
return [];
|
|
63
|
+
const lines = cleaned.split('\n');
|
|
64
|
+
const chunks = [];
|
|
65
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
66
|
+
const line = lines[i].trim();
|
|
67
|
+
if (!line)
|
|
68
|
+
continue;
|
|
69
|
+
if (/^\d+$/.test(line)) {
|
|
70
|
+
const nextLine = lines[i + 1];
|
|
71
|
+
if (!nextLine)
|
|
72
|
+
continue;
|
|
73
|
+
try {
|
|
74
|
+
chunks.push(JSON.parse(nextLine));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Ignore malformed chunks and keep scanning.
|
|
78
|
+
}
|
|
79
|
+
i += 1;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (line.startsWith('[')) {
|
|
83
|
+
try {
|
|
84
|
+
chunks.push(JSON.parse(line));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Ignore malformed chunks and keep scanning.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return chunks;
|
|
92
|
+
}
|
|
93
|
+
export function extractNotebooklmRpcResult(rawBody, rpcId) {
|
|
94
|
+
const chunks = parseNotebooklmChunkedResponse(rawBody);
|
|
95
|
+
for (const chunk of chunks) {
|
|
96
|
+
if (!Array.isArray(chunk))
|
|
97
|
+
continue;
|
|
98
|
+
const items = Array.isArray(chunk[0]) ? chunk : [chunk];
|
|
99
|
+
for (const item of items) {
|
|
100
|
+
if (!Array.isArray(item) || item.length < 1)
|
|
101
|
+
continue;
|
|
102
|
+
if (item[0] === 'er') {
|
|
103
|
+
const errorCode = typeof item[2] === 'number'
|
|
104
|
+
? item[2]
|
|
105
|
+
: typeof item[5] === 'number'
|
|
106
|
+
? item[5]
|
|
107
|
+
: null;
|
|
108
|
+
if (errorCode === 401 || errorCode === 403) {
|
|
109
|
+
throw new AuthRequiredError(NOTEBOOKLM_DOMAIN, `NotebookLM RPC returned auth error (${errorCode})`);
|
|
110
|
+
}
|
|
111
|
+
throw new CliError('NOTEBOOKLM_RPC', `NotebookLM RPC failed${errorCode ? ` (code=${errorCode})` : ''}`, 'Retry from an already logged-in NotebookLM session, or inspect the raw response with debug logging.');
|
|
112
|
+
}
|
|
113
|
+
if (item[0] === 'wrb.fr' && item[1] === rpcId) {
|
|
114
|
+
const payload = item[2];
|
|
115
|
+
if (typeof payload === 'string') {
|
|
116
|
+
try {
|
|
117
|
+
return JSON.parse(payload);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return payload;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return payload;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
export async function fetchNotebooklmInPage(page, url, options = {}) {
|
|
130
|
+
const method = options.method ?? 'GET';
|
|
131
|
+
const headers = options.headers ?? {};
|
|
132
|
+
const body = options.body ?? '';
|
|
133
|
+
const raw = await page.evaluate(`(async () => {
|
|
134
|
+
const request = {
|
|
135
|
+
url: ${JSON.stringify(url)},
|
|
136
|
+
method: ${JSON.stringify(method)},
|
|
137
|
+
headers: ${JSON.stringify(headers)},
|
|
138
|
+
body: ${JSON.stringify(body)},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const response = await fetch(request.url, {
|
|
142
|
+
method: request.method,
|
|
143
|
+
headers: request.headers,
|
|
144
|
+
body: request.method === 'GET' ? undefined : request.body,
|
|
145
|
+
credentials: 'include',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
ok: response.ok,
|
|
150
|
+
status: response.status,
|
|
151
|
+
body: await response.text(),
|
|
152
|
+
finalUrl: response.url,
|
|
153
|
+
};
|
|
154
|
+
})()`);
|
|
155
|
+
return {
|
|
156
|
+
ok: Boolean(raw?.ok),
|
|
157
|
+
status: Number(raw?.status ?? 0),
|
|
158
|
+
body: String(raw?.body ?? ''),
|
|
159
|
+
finalUrl: String(raw?.finalUrl ?? url),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
export async function callNotebooklmRpc(page, rpcId, params, options = {}) {
|
|
163
|
+
const auth = await getNotebooklmPageAuth(page);
|
|
164
|
+
const requestBody = buildNotebooklmRpcBody(rpcId, params, auth.csrfToken);
|
|
165
|
+
const url = `https://${NOTEBOOKLM_DOMAIN}/_/LabsTailwindUi/data/batchexecute` +
|
|
166
|
+
`?rpcids=${rpcId}&source-path=${encodeURIComponent(auth.sourcePath)}` +
|
|
167
|
+
`&hl=${encodeURIComponent(options.hl ?? 'en')}` +
|
|
168
|
+
`&f.sid=${encodeURIComponent(auth.sessionId)}&rt=c`;
|
|
169
|
+
const response = await fetchNotebooklmInPage(page, url, {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
headers: {
|
|
172
|
+
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
|
173
|
+
},
|
|
174
|
+
body: requestBody,
|
|
175
|
+
});
|
|
176
|
+
if (response.status === 401 || response.status === 403) {
|
|
177
|
+
throw new AuthRequiredError(NOTEBOOKLM_DOMAIN, `NotebookLM RPC returned auth error (${response.status})`);
|
|
178
|
+
}
|
|
179
|
+
if (!response.ok) {
|
|
180
|
+
throw new CliError('NOTEBOOKLM_RPC', `NotebookLM RPC request failed with HTTP ${response.status}`, 'Retry from the NotebookLM home page in an already logged-in Chrome session.');
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
auth,
|
|
184
|
+
url,
|
|
185
|
+
requestBody,
|
|
186
|
+
response,
|
|
187
|
+
result: extractNotebooklmRpcResult(response.body, rpcId),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { AuthRequiredError } from '../../errors.js';
|
|
3
|
+
import { buildNotebooklmRpcBody, extractNotebooklmRpcResult, getNotebooklmPageAuth, parseNotebooklmChunkedResponse, } from './rpc.js';
|
|
4
|
+
describe('notebooklm rpc transport', () => {
|
|
5
|
+
it('extracts auth tokens from the page html via page evaluation', async () => {
|
|
6
|
+
const page = {
|
|
7
|
+
evaluate: vi.fn(async (script) => {
|
|
8
|
+
expect(script).toContain('document.documentElement.innerHTML');
|
|
9
|
+
return {
|
|
10
|
+
html: '<html>"SNlM0e":"csrf-123","FdrFJe":"sess-456"</html>',
|
|
11
|
+
sourcePath: '/',
|
|
12
|
+
};
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
await expect(getNotebooklmPageAuth(page)).resolves.toEqual({
|
|
16
|
+
csrfToken: 'csrf-123',
|
|
17
|
+
sessionId: 'sess-456',
|
|
18
|
+
sourcePath: '/',
|
|
19
|
+
});
|
|
20
|
+
expect(page.evaluate).toHaveBeenCalledTimes(1);
|
|
21
|
+
});
|
|
22
|
+
it('falls back to WIZ_global_data tokens when html regex data is missing', async () => {
|
|
23
|
+
const page = {
|
|
24
|
+
evaluate: vi.fn(async () => ({
|
|
25
|
+
html: '<html><body>NotebookLM</body></html>',
|
|
26
|
+
sourcePath: '/notebook/nb-demo',
|
|
27
|
+
readyState: 'complete',
|
|
28
|
+
csrfToken: 'csrf-wiz',
|
|
29
|
+
sessionId: 'sess-wiz',
|
|
30
|
+
})),
|
|
31
|
+
};
|
|
32
|
+
await expect(getNotebooklmPageAuth(page)).resolves.toEqual({
|
|
33
|
+
csrfToken: 'csrf-wiz',
|
|
34
|
+
sessionId: 'sess-wiz',
|
|
35
|
+
sourcePath: '/notebook/nb-demo',
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it('retries token extraction once when the first probe returns no tokens', async () => {
|
|
39
|
+
const page = {
|
|
40
|
+
evaluate: vi.fn()
|
|
41
|
+
.mockResolvedValueOnce({
|
|
42
|
+
html: '<html><body>Loading…</body></html>',
|
|
43
|
+
sourcePath: '/notebook/nb-demo',
|
|
44
|
+
readyState: 'interactive',
|
|
45
|
+
csrfToken: '',
|
|
46
|
+
sessionId: '',
|
|
47
|
+
})
|
|
48
|
+
.mockResolvedValueOnce({
|
|
49
|
+
html: '<html>"SNlM0e":"csrf-123","FdrFJe":"sess-456"</html>',
|
|
50
|
+
sourcePath: '/notebook/nb-demo',
|
|
51
|
+
readyState: 'complete',
|
|
52
|
+
csrfToken: '',
|
|
53
|
+
sessionId: '',
|
|
54
|
+
}),
|
|
55
|
+
wait: vi.fn(async () => undefined),
|
|
56
|
+
};
|
|
57
|
+
await expect(getNotebooklmPageAuth(page)).resolves.toEqual({
|
|
58
|
+
csrfToken: 'csrf-123',
|
|
59
|
+
sessionId: 'sess-456',
|
|
60
|
+
sourcePath: '/notebook/nb-demo',
|
|
61
|
+
});
|
|
62
|
+
expect(page.evaluate).toHaveBeenCalledTimes(2);
|
|
63
|
+
});
|
|
64
|
+
it('builds the rpc body with the expected notebooklm payload shape', () => {
|
|
65
|
+
const body = buildNotebooklmRpcBody('wXbhsf', [null, 1, null, [2]], 'csrf-123');
|
|
66
|
+
expect(body).toContain('f.req=');
|
|
67
|
+
expect(body).toContain('at=csrf-123');
|
|
68
|
+
expect(body.endsWith('&')).toBe(true);
|
|
69
|
+
expect(decodeURIComponent(body)).toContain('"[null,1,null,[2]]"');
|
|
70
|
+
});
|
|
71
|
+
it('parses chunked batchexecute responses into json chunks', () => {
|
|
72
|
+
const raw = `)]}'\n107\n[["wrb.fr","wXbhsf","[[[\\\"Notebook One\\\",null,\\\"nb1\\\",null,null,[null,false,null,null,null,[1704067200]]]]]"]]`;
|
|
73
|
+
const chunks = parseNotebooklmChunkedResponse(raw);
|
|
74
|
+
expect(chunks).toHaveLength(1);
|
|
75
|
+
expect(Array.isArray(chunks[0])).toBe(true);
|
|
76
|
+
expect(chunks[0]).toEqual([
|
|
77
|
+
[
|
|
78
|
+
'wrb.fr',
|
|
79
|
+
'wXbhsf',
|
|
80
|
+
'[[["Notebook One",null,"nb1",null,null,[null,false,null,null,null,[1704067200]]]]]',
|
|
81
|
+
],
|
|
82
|
+
]);
|
|
83
|
+
});
|
|
84
|
+
it('extracts the rpc payload from wrb.fr responses', () => {
|
|
85
|
+
const raw = `)]}'\n107\n[["wrb.fr","wXbhsf","[[[\\\"Notebook One\\\",null,\\\"nb1\\\",null,null,[null,false,null,null,null,[1704067200]]]]]"]]`;
|
|
86
|
+
const result = extractNotebooklmRpcResult(raw, 'wXbhsf');
|
|
87
|
+
expect(result).toEqual([
|
|
88
|
+
[
|
|
89
|
+
['Notebook One', null, 'nb1', null, null, [null, false, null, null, null, [1704067200]]],
|
|
90
|
+
],
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
it('classifies auth errors as AuthRequiredError', () => {
|
|
94
|
+
const raw = `)]}'\n25\n[["er",null,null,null,null,401,"generic"]]`;
|
|
95
|
+
expect(() => extractNotebooklmRpcResult(raw, 'wXbhsf')).toThrow(AuthRequiredError);
|
|
96
|
+
try {
|
|
97
|
+
extractNotebooklmRpcResult(raw, 'wXbhsf');
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
expect(error).toBeInstanceOf(AuthRequiredError);
|
|
101
|
+
expect(error.domain).toBe('notebooklm.google.com');
|
|
102
|
+
expect(error.code).toBe('AUTH_REQUIRED');
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export declare const NOTEBOOKLM_SITE = "notebooklm";
|
|
2
|
+
export declare const NOTEBOOKLM_DOMAIN = "notebooklm.google.com";
|
|
3
|
+
export declare const NOTEBOOKLM_HOME_URL = "https://notebooklm.google.com/";
|
|
4
|
+
export type NotebooklmPageKind = 'notebook' | 'home' | 'unknown';
|
|
5
|
+
export interface NotebooklmPageState {
|
|
6
|
+
url: string;
|
|
7
|
+
title: string;
|
|
8
|
+
hostname: string;
|
|
9
|
+
kind: NotebooklmPageKind;
|
|
10
|
+
notebookId: string;
|
|
11
|
+
loginRequired: boolean;
|
|
12
|
+
notebookCount: number;
|
|
13
|
+
}
|
|
14
|
+
export interface NotebooklmRow {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
url: string;
|
|
18
|
+
source: 'current-page' | 'home-links' | 'rpc';
|
|
19
|
+
is_owner?: boolean;
|
|
20
|
+
created_at?: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface NotebooklmSourceRow {
|
|
23
|
+
id: string;
|
|
24
|
+
notebook_id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
url: string;
|
|
27
|
+
source: 'current-page' | 'rpc';
|
|
28
|
+
type?: string | null;
|
|
29
|
+
type_code?: number | null;
|
|
30
|
+
size?: number | null;
|
|
31
|
+
created_at?: string | null;
|
|
32
|
+
updated_at?: string | null;
|
|
33
|
+
}
|
|
34
|
+
export interface NotebooklmSourceFulltextRow {
|
|
35
|
+
source_id: string;
|
|
36
|
+
notebook_id: string;
|
|
37
|
+
title: string;
|
|
38
|
+
kind?: string | null;
|
|
39
|
+
content: string;
|
|
40
|
+
char_count: number;
|
|
41
|
+
url?: string | null;
|
|
42
|
+
source: 'rpc';
|
|
43
|
+
}
|
|
44
|
+
export interface NotebooklmSourceGuideRow {
|
|
45
|
+
source_id: string;
|
|
46
|
+
notebook_id: string;
|
|
47
|
+
title: string;
|
|
48
|
+
type?: string | null;
|
|
49
|
+
summary: string;
|
|
50
|
+
keywords: string[];
|
|
51
|
+
source: 'rpc';
|
|
52
|
+
}
|
|
53
|
+
export interface NotebooklmNotebookDetailRow extends NotebooklmRow {
|
|
54
|
+
emoji?: string | null;
|
|
55
|
+
source_count?: number | null;
|
|
56
|
+
updated_at?: string | null;
|
|
57
|
+
}
|
|
58
|
+
export interface NotebooklmHistoryRow {
|
|
59
|
+
thread_id: string;
|
|
60
|
+
notebook_id: string;
|
|
61
|
+
item_count: number;
|
|
62
|
+
preview?: string | null;
|
|
63
|
+
url: string;
|
|
64
|
+
source: 'rpc';
|
|
65
|
+
}
|
|
66
|
+
export interface NotebooklmNoteRow {
|
|
67
|
+
notebook_id: string;
|
|
68
|
+
title: string;
|
|
69
|
+
created_at?: string | null;
|
|
70
|
+
url: string;
|
|
71
|
+
source: 'studio-list';
|
|
72
|
+
}
|
|
73
|
+
export interface NotebooklmSummaryRow {
|
|
74
|
+
notebook_id: string;
|
|
75
|
+
title: string;
|
|
76
|
+
summary: string;
|
|
77
|
+
url: string;
|
|
78
|
+
source: 'summary-dom' | 'rpc';
|
|
79
|
+
}
|
|
80
|
+
export interface NotebooklmNoteDetailRow {
|
|
81
|
+
notebook_id: string;
|
|
82
|
+
id?: string | null;
|
|
83
|
+
title: string;
|
|
84
|
+
content: string;
|
|
85
|
+
url: string;
|
|
86
|
+
source: 'studio-editor';
|
|
87
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { EmptyResultError } from '../../errors.js';
|
|
3
|
+
import { NOTEBOOKLM_DOMAIN, NOTEBOOKLM_SITE } from './shared.js';
|
|
4
|
+
import { ensureNotebooklmNotebookBinding, findNotebooklmSourceRow, getNotebooklmPageState, getNotebooklmSourceFulltextViaRpc, listNotebooklmSourcesFromPage, listNotebooklmSourcesViaRpc, requireNotebooklmSession, } from './utils.js';
|
|
5
|
+
cli({
|
|
6
|
+
site: NOTEBOOKLM_SITE,
|
|
7
|
+
name: 'source-fulltext',
|
|
8
|
+
description: 'Get the extracted fulltext for one source in the currently opened NotebookLM notebook',
|
|
9
|
+
domain: NOTEBOOKLM_DOMAIN,
|
|
10
|
+
strategy: Strategy.COOKIE,
|
|
11
|
+
browser: true,
|
|
12
|
+
navigateBefore: false,
|
|
13
|
+
args: [
|
|
14
|
+
{
|
|
15
|
+
name: 'source',
|
|
16
|
+
positional: true,
|
|
17
|
+
required: true,
|
|
18
|
+
help: 'Source id or title from the current notebook',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
columns: ['title', 'kind', 'char_count', 'url', 'source'],
|
|
22
|
+
func: async (page, kwargs) => {
|
|
23
|
+
await ensureNotebooklmNotebookBinding(page);
|
|
24
|
+
await requireNotebooklmSession(page);
|
|
25
|
+
const state = await getNotebooklmPageState(page);
|
|
26
|
+
if (state.kind !== 'notebook') {
|
|
27
|
+
throw new EmptyResultError('opencli notebooklm source-fulltext', 'Open a specific NotebookLM notebook tab first, then retry.');
|
|
28
|
+
}
|
|
29
|
+
const rpcRows = await listNotebooklmSourcesViaRpc(page).catch(() => []);
|
|
30
|
+
const rows = rpcRows.length > 0 ? rpcRows : await listNotebooklmSourcesFromPage(page);
|
|
31
|
+
if (rows.length === 0) {
|
|
32
|
+
throw new EmptyResultError('opencli notebooklm source-fulltext', 'No NotebookLM sources were found on the current page.');
|
|
33
|
+
}
|
|
34
|
+
const query = typeof kwargs.source === 'string' ? kwargs.source : String(kwargs.source ?? '');
|
|
35
|
+
const matched = findNotebooklmSourceRow(rows, query);
|
|
36
|
+
if (!matched) {
|
|
37
|
+
throw new EmptyResultError('opencli notebooklm source-fulltext', `Source "${query}" was not found in the current notebook.`);
|
|
38
|
+
}
|
|
39
|
+
const fulltext = await getNotebooklmSourceFulltextViaRpc(page, matched.id).catch(() => null);
|
|
40
|
+
if (fulltext)
|
|
41
|
+
return [fulltext];
|
|
42
|
+
throw new EmptyResultError('opencli notebooklm source-fulltext', `NotebookLM fulltext was not available for source "${matched.title}".`);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './source-fulltext.js';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
const { mockListNotebooklmSourcesViaRpc, mockListNotebooklmSourcesFromPage, mockGetNotebooklmSourceFulltextViaRpc, mockGetNotebooklmPageState, mockRequireNotebooklmSession, } = vi.hoisted(() => ({
|
|
3
|
+
mockListNotebooklmSourcesViaRpc: vi.fn(),
|
|
4
|
+
mockListNotebooklmSourcesFromPage: vi.fn(),
|
|
5
|
+
mockGetNotebooklmSourceFulltextViaRpc: vi.fn(),
|
|
6
|
+
mockGetNotebooklmPageState: vi.fn(),
|
|
7
|
+
mockRequireNotebooklmSession: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
vi.mock('./utils.js', async () => {
|
|
10
|
+
const actual = await vi.importActual('./utils.js');
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
listNotebooklmSourcesViaRpc: mockListNotebooklmSourcesViaRpc,
|
|
14
|
+
listNotebooklmSourcesFromPage: mockListNotebooklmSourcesFromPage,
|
|
15
|
+
getNotebooklmSourceFulltextViaRpc: mockGetNotebooklmSourceFulltextViaRpc,
|
|
16
|
+
getNotebooklmPageState: mockGetNotebooklmPageState,
|
|
17
|
+
requireNotebooklmSession: mockRequireNotebooklmSession,
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
import { getRegistry } from '../../registry.js';
|
|
21
|
+
import './source-fulltext.js';
|
|
22
|
+
describe('notebooklm source-fulltext', () => {
|
|
23
|
+
const command = getRegistry().get('notebooklm/source-fulltext');
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
mockListNotebooklmSourcesViaRpc.mockReset();
|
|
26
|
+
mockListNotebooklmSourcesFromPage.mockReset();
|
|
27
|
+
mockGetNotebooklmSourceFulltextViaRpc.mockReset();
|
|
28
|
+
mockGetNotebooklmPageState.mockReset();
|
|
29
|
+
mockRequireNotebooklmSession.mockReset();
|
|
30
|
+
mockRequireNotebooklmSession.mockResolvedValue(undefined);
|
|
31
|
+
mockGetNotebooklmPageState.mockResolvedValue({
|
|
32
|
+
url: 'https://notebooklm.google.com/notebook/nb-demo',
|
|
33
|
+
title: 'Browser Automation',
|
|
34
|
+
hostname: 'notebooklm.google.com',
|
|
35
|
+
kind: 'notebook',
|
|
36
|
+
notebookId: 'nb-demo',
|
|
37
|
+
loginRequired: false,
|
|
38
|
+
notebookCount: 1,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
it('returns fulltext for a source matched from rpc source rows', async () => {
|
|
42
|
+
mockListNotebooklmSourcesViaRpc.mockResolvedValue([
|
|
43
|
+
{
|
|
44
|
+
id: 'src-1',
|
|
45
|
+
notebook_id: 'nb-demo',
|
|
46
|
+
title: '粘贴的文字',
|
|
47
|
+
url: 'https://notebooklm.google.com/notebook/nb-demo',
|
|
48
|
+
source: 'rpc',
|
|
49
|
+
type: 'pasted-text',
|
|
50
|
+
},
|
|
51
|
+
]);
|
|
52
|
+
mockGetNotebooklmSourceFulltextViaRpc.mockResolvedValue({
|
|
53
|
+
source_id: 'src-1',
|
|
54
|
+
notebook_id: 'nb-demo',
|
|
55
|
+
title: '粘贴的文字',
|
|
56
|
+
kind: 'generated-text',
|
|
57
|
+
content: '第一段\n第二段',
|
|
58
|
+
char_count: 7,
|
|
59
|
+
url: 'https://example.com/source',
|
|
60
|
+
source: 'rpc',
|
|
61
|
+
});
|
|
62
|
+
const result = await command.func({}, { source: 'src-1' });
|
|
63
|
+
expect(result).toEqual([
|
|
64
|
+
{
|
|
65
|
+
source_id: 'src-1',
|
|
66
|
+
notebook_id: 'nb-demo',
|
|
67
|
+
title: '粘贴的文字',
|
|
68
|
+
kind: 'generated-text',
|
|
69
|
+
content: '第一段\n第二段',
|
|
70
|
+
char_count: 7,
|
|
71
|
+
url: 'https://example.com/source',
|
|
72
|
+
source: 'rpc',
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
it('matches by title from dom rows when rpc source list is unavailable', async () => {
|
|
77
|
+
mockListNotebooklmSourcesViaRpc.mockResolvedValue([]);
|
|
78
|
+
mockListNotebooklmSourcesFromPage.mockResolvedValue([
|
|
79
|
+
{
|
|
80
|
+
id: 'src-1',
|
|
81
|
+
notebook_id: 'nb-demo',
|
|
82
|
+
title: '粘贴的文字',
|
|
83
|
+
url: 'https://notebooklm.google.com/notebook/nb-demo',
|
|
84
|
+
source: 'current-page',
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
87
|
+
mockGetNotebooklmSourceFulltextViaRpc.mockResolvedValue({
|
|
88
|
+
source_id: 'src-1',
|
|
89
|
+
notebook_id: 'nb-demo',
|
|
90
|
+
title: '粘贴的文字',
|
|
91
|
+
kind: 'generated-text',
|
|
92
|
+
content: '第一段\n第二段',
|
|
93
|
+
char_count: 7,
|
|
94
|
+
url: 'https://example.com/source',
|
|
95
|
+
source: 'rpc',
|
|
96
|
+
});
|
|
97
|
+
const result = await command.func({}, { source: '粘贴的文字' });
|
|
98
|
+
expect(result).toEqual([
|
|
99
|
+
expect.objectContaining({
|
|
100
|
+
source_id: 'src-1',
|
|
101
|
+
title: '粘贴的文字',
|
|
102
|
+
content: '第一段\n第二段',
|
|
103
|
+
}),
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { EmptyResultError } from '../../errors.js';
|
|
3
|
+
import { NOTEBOOKLM_DOMAIN, NOTEBOOKLM_SITE } from './shared.js';
|
|
4
|
+
import { ensureNotebooklmNotebookBinding, findNotebooklmSourceRow, getNotebooklmPageState, listNotebooklmSourcesFromPage, listNotebooklmSourcesViaRpc, requireNotebooklmSession, } from './utils.js';
|
|
5
|
+
cli({
|
|
6
|
+
site: NOTEBOOKLM_SITE,
|
|
7
|
+
name: 'source-get',
|
|
8
|
+
description: 'Get one source from the currently opened NotebookLM notebook by id or title',
|
|
9
|
+
domain: NOTEBOOKLM_DOMAIN,
|
|
10
|
+
strategy: Strategy.COOKIE,
|
|
11
|
+
browser: true,
|
|
12
|
+
navigateBefore: false,
|
|
13
|
+
args: [
|
|
14
|
+
{
|
|
15
|
+
name: 'source',
|
|
16
|
+
positional: true,
|
|
17
|
+
required: true,
|
|
18
|
+
help: 'Source id or title from the current notebook',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
columns: ['title', 'id', 'type', 'size', 'created_at', 'updated_at', 'url', 'source'],
|
|
22
|
+
func: async (page, kwargs) => {
|
|
23
|
+
await ensureNotebooklmNotebookBinding(page);
|
|
24
|
+
await requireNotebooklmSession(page);
|
|
25
|
+
const state = await getNotebooklmPageState(page);
|
|
26
|
+
if (state.kind !== 'notebook') {
|
|
27
|
+
throw new EmptyResultError('opencli notebooklm source-get', 'Open a specific NotebookLM notebook tab first, then retry.');
|
|
28
|
+
}
|
|
29
|
+
const rpcRows = await listNotebooklmSourcesViaRpc(page).catch(() => []);
|
|
30
|
+
const rows = rpcRows.length > 0 ? rpcRows : await listNotebooklmSourcesFromPage(page);
|
|
31
|
+
if (rows.length === 0) {
|
|
32
|
+
throw new EmptyResultError('opencli notebooklm source-get', 'No NotebookLM sources were found on the current page.');
|
|
33
|
+
}
|
|
34
|
+
const query = typeof kwargs.source === 'string' ? kwargs.source : String(kwargs.source ?? '');
|
|
35
|
+
const matched = findNotebooklmSourceRow(rows, query);
|
|
36
|
+
if (matched)
|
|
37
|
+
return [matched];
|
|
38
|
+
throw new EmptyResultError('opencli notebooklm source-get', `Source "${query}" was not found in the current notebook.`);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './source-get.js';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
const { mockListNotebooklmSourcesViaRpc, mockListNotebooklmSourcesFromPage, mockGetNotebooklmPageState, mockRequireNotebooklmSession, } = vi.hoisted(() => ({
|
|
3
|
+
mockListNotebooklmSourcesViaRpc: vi.fn(),
|
|
4
|
+
mockListNotebooklmSourcesFromPage: vi.fn(),
|
|
5
|
+
mockGetNotebooklmPageState: vi.fn(),
|
|
6
|
+
mockRequireNotebooklmSession: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('./utils.js', async () => {
|
|
9
|
+
const actual = await vi.importActual('./utils.js');
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
getNotebooklmPageState: mockGetNotebooklmPageState,
|
|
13
|
+
listNotebooklmSourcesViaRpc: mockListNotebooklmSourcesViaRpc,
|
|
14
|
+
listNotebooklmSourcesFromPage: mockListNotebooklmSourcesFromPage,
|
|
15
|
+
requireNotebooklmSession: mockRequireNotebooklmSession,
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
import { getRegistry } from '../../registry.js';
|
|
19
|
+
import './source-get.js';
|
|
20
|
+
describe('notebooklm source-get', () => {
|
|
21
|
+
const command = getRegistry().get('notebooklm/source-get');
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
mockListNotebooklmSourcesViaRpc.mockReset();
|
|
24
|
+
mockListNotebooklmSourcesFromPage.mockReset();
|
|
25
|
+
mockGetNotebooklmPageState.mockReset();
|
|
26
|
+
mockRequireNotebooklmSession.mockReset();
|
|
27
|
+
mockRequireNotebooklmSession.mockResolvedValue(undefined);
|
|
28
|
+
mockGetNotebooklmPageState.mockResolvedValue({
|
|
29
|
+
url: 'https://notebooklm.google.com/notebook/nb-demo',
|
|
30
|
+
title: 'Browser Automation',
|
|
31
|
+
hostname: 'notebooklm.google.com',
|
|
32
|
+
kind: 'notebook',
|
|
33
|
+
notebookId: 'nb-demo',
|
|
34
|
+
loginRequired: false,
|
|
35
|
+
notebookCount: 1,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it('returns a source by exact id from rpc results', async () => {
|
|
39
|
+
mockListNotebooklmSourcesViaRpc.mockResolvedValue([
|
|
40
|
+
{
|
|
41
|
+
id: 'src-1',
|
|
42
|
+
notebook_id: 'nb-demo',
|
|
43
|
+
title: 'Release Notes',
|
|
44
|
+
url: 'https://notebooklm.google.com/notebook/nb-demo',
|
|
45
|
+
source: 'rpc',
|
|
46
|
+
type: 'web',
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
const result = await command.func({}, { source: 'src-1' });
|
|
50
|
+
expect(result).toEqual([
|
|
51
|
+
{
|
|
52
|
+
id: 'src-1',
|
|
53
|
+
notebook_id: 'nb-demo',
|
|
54
|
+
title: 'Release Notes',
|
|
55
|
+
url: 'https://notebooklm.google.com/notebook/nb-demo',
|
|
56
|
+
source: 'rpc',
|
|
57
|
+
type: 'web',
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
60
|
+
expect(mockListNotebooklmSourcesFromPage).not.toHaveBeenCalled();
|
|
61
|
+
});
|
|
62
|
+
it('falls back to page results and matches by title when rpc is empty', async () => {
|
|
63
|
+
mockListNotebooklmSourcesViaRpc.mockResolvedValue([]);
|
|
64
|
+
mockListNotebooklmSourcesFromPage.mockResolvedValue([
|
|
65
|
+
{
|
|
66
|
+
id: 'Meeting Notes',
|
|
67
|
+
notebook_id: 'nb-demo',
|
|
68
|
+
title: 'Meeting Notes',
|
|
69
|
+
url: 'https://notebooklm.google.com/notebook/nb-demo',
|
|
70
|
+
source: 'current-page',
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
const result = await command.func({}, { source: 'meeting notes' });
|
|
74
|
+
expect(result).toEqual([
|
|
75
|
+
{
|
|
76
|
+
id: 'Meeting Notes',
|
|
77
|
+
notebook_id: 'nb-demo',
|
|
78
|
+
title: 'Meeting Notes',
|
|
79
|
+
url: 'https://notebooklm.google.com/notebook/nb-demo',
|
|
80
|
+
source: 'current-page',
|
|
81
|
+
},
|
|
82
|
+
]);
|
|
83
|
+
});
|
|
84
|
+
});
|