@jackwener/opencli 1.5.6 → 1.5.8

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.
Files changed (338) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +4 -2
  3. package/README.zh-CN.md +4 -1
  4. package/SKILL.md +879 -0
  5. package/dist/browser/cdp.d.ts +1 -0
  6. package/dist/browser/cdp.js +30 -27
  7. package/dist/browser/daemon-client.d.ts +7 -1
  8. package/dist/browser/daemon-client.js +3 -0
  9. package/dist/browser/dom-helpers.js +1 -0
  10. package/dist/browser/dom-helpers.test.js +14 -1
  11. package/dist/browser/mcp.js +18 -13
  12. package/dist/browser/page.js +22 -2
  13. package/dist/browser/page.test.d.ts +1 -0
  14. package/dist/browser/page.test.js +44 -0
  15. package/dist/browser/stealth.js +198 -0
  16. package/dist/browser/stealth.test.d.ts +1 -0
  17. package/dist/browser/stealth.test.js +134 -0
  18. package/dist/browser.test.js +1 -1
  19. package/dist/build-manifest.d.ts +1 -0
  20. package/dist/build-manifest.js +5 -1
  21. package/dist/build-manifest.test.js +2 -0
  22. package/dist/cli-manifest.json +544 -137
  23. package/dist/cli.js +20 -3
  24. package/dist/clis/antigravity/serve.d.ts +1 -1
  25. package/dist/clis/antigravity/serve.js +5 -8
  26. package/dist/clis/bilibili/subtitle.js +4 -0
  27. package/dist/clis/bilibili/subtitle.test.d.ts +1 -0
  28. package/dist/clis/bilibili/subtitle.test.js +48 -0
  29. package/dist/clis/chatwise/ask.js +0 -2
  30. package/dist/clis/chatwise/export.js +0 -2
  31. package/dist/clis/chatwise/history.js +0 -2
  32. package/dist/clis/chatwise/model.js +0 -2
  33. package/dist/clis/chatwise/new.js +1 -2
  34. package/dist/clis/chatwise/read.js +0 -2
  35. package/dist/clis/chatwise/screenshot.js +1 -2
  36. package/dist/clis/chatwise/send.js +0 -2
  37. package/dist/clis/chatwise/status.js +1 -2
  38. package/dist/clis/ctrip/search.d.ts +13 -0
  39. package/dist/clis/ctrip/search.js +73 -48
  40. package/dist/clis/ctrip/search.test.d.ts +1 -0
  41. package/dist/clis/ctrip/search.test.js +64 -0
  42. package/dist/clis/douyin/_shared/sts2.js +8 -2
  43. package/dist/clis/douyin/_shared/sts2.test.d.ts +1 -0
  44. package/dist/clis/douyin/_shared/sts2.test.js +27 -0
  45. package/dist/clis/douyin/activities.js +4 -2
  46. package/dist/clis/douyin/activities.test.js +34 -1
  47. package/dist/clis/douyin/collections.js +1 -1
  48. package/dist/clis/douyin/collections.test.js +24 -2
  49. package/dist/clis/douyin/draft.d.ts +8 -11
  50. package/dist/clis/douyin/draft.js +302 -185
  51. package/dist/clis/douyin/draft.test.d.ts +1 -1
  52. package/dist/clis/douyin/draft.test.js +357 -2
  53. package/dist/clis/douyin/hashtag.js +9 -2
  54. package/dist/clis/douyin/hashtag.test.js +35 -2
  55. package/dist/clis/douyin/profile.js +1 -1
  56. package/dist/clis/douyin/profile.test.js +36 -1
  57. package/dist/clis/douyin/videos.js +22 -5
  58. package/dist/clis/douyin/videos.test.js +45 -2
  59. package/dist/clis/facebook/search.test.d.ts +5 -0
  60. package/dist/clis/facebook/search.test.js +60 -0
  61. package/dist/clis/facebook/search.yaml +4 -3
  62. package/dist/clis/instagram/download.d.ts +16 -0
  63. package/dist/clis/instagram/download.js +225 -0
  64. package/dist/clis/instagram/download.test.d.ts +1 -0
  65. package/dist/clis/instagram/download.test.js +118 -0
  66. package/dist/clis/notebooklm/bind-current.d.ts +1 -0
  67. package/dist/clis/notebooklm/bind-current.js +29 -0
  68. package/dist/clis/notebooklm/bind-current.test.d.ts +1 -0
  69. package/dist/clis/notebooklm/bind-current.test.js +35 -0
  70. package/dist/clis/notebooklm/binding.test.d.ts +1 -0
  71. package/dist/clis/notebooklm/binding.test.js +44 -0
  72. package/dist/clis/notebooklm/compat.test.d.ts +3 -0
  73. package/dist/clis/notebooklm/compat.test.js +16 -0
  74. package/dist/clis/notebooklm/current.d.ts +1 -0
  75. package/dist/clis/notebooklm/current.js +28 -0
  76. package/dist/clis/notebooklm/get.d.ts +1 -0
  77. package/dist/clis/notebooklm/get.js +37 -0
  78. package/dist/clis/notebooklm/history.d.ts +1 -0
  79. package/dist/clis/notebooklm/history.js +25 -0
  80. package/dist/clis/notebooklm/history.test.d.ts +1 -0
  81. package/dist/clis/notebooklm/history.test.js +58 -0
  82. package/dist/clis/notebooklm/list.d.ts +1 -0
  83. package/dist/clis/notebooklm/list.js +35 -0
  84. package/dist/clis/notebooklm/note-list.d.ts +1 -0
  85. package/dist/clis/notebooklm/note-list.js +28 -0
  86. package/dist/clis/notebooklm/note-list.test.d.ts +1 -0
  87. package/dist/clis/notebooklm/note-list.test.js +56 -0
  88. package/dist/clis/notebooklm/notes-get.d.ts +1 -0
  89. package/dist/clis/notebooklm/notes-get.js +47 -0
  90. package/dist/clis/notebooklm/notes-get.test.d.ts +1 -0
  91. package/dist/clis/notebooklm/notes-get.test.js +72 -0
  92. package/dist/clis/notebooklm/rpc.d.ts +36 -0
  93. package/dist/clis/notebooklm/rpc.js +189 -0
  94. package/dist/clis/notebooklm/rpc.test.d.ts +1 -0
  95. package/dist/clis/notebooklm/rpc.test.js +105 -0
  96. package/dist/clis/notebooklm/shared.d.ts +87 -0
  97. package/dist/clis/notebooklm/shared.js +3 -0
  98. package/dist/clis/notebooklm/source-fulltext.d.ts +1 -0
  99. package/dist/clis/notebooklm/source-fulltext.js +44 -0
  100. package/dist/clis/notebooklm/source-fulltext.test.d.ts +1 -0
  101. package/dist/clis/notebooklm/source-fulltext.test.js +106 -0
  102. package/dist/clis/notebooklm/source-get.d.ts +1 -0
  103. package/dist/clis/notebooklm/source-get.js +40 -0
  104. package/dist/clis/notebooklm/source-get.test.d.ts +1 -0
  105. package/dist/clis/notebooklm/source-get.test.js +84 -0
  106. package/dist/clis/notebooklm/source-guide.d.ts +1 -0
  107. package/dist/clis/notebooklm/source-guide.js +44 -0
  108. package/dist/clis/notebooklm/source-guide.test.d.ts +1 -0
  109. package/dist/clis/notebooklm/source-guide.test.js +104 -0
  110. package/dist/clis/notebooklm/source-list.d.ts +1 -0
  111. package/dist/clis/notebooklm/source-list.js +30 -0
  112. package/dist/clis/notebooklm/status.d.ts +1 -0
  113. package/dist/clis/notebooklm/status.js +31 -0
  114. package/dist/clis/notebooklm/summary.d.ts +1 -0
  115. package/dist/clis/notebooklm/summary.js +30 -0
  116. package/dist/clis/notebooklm/summary.test.d.ts +1 -0
  117. package/dist/clis/notebooklm/summary.test.js +78 -0
  118. package/dist/clis/notebooklm/utils.d.ts +37 -0
  119. package/dist/clis/notebooklm/utils.js +739 -0
  120. package/dist/clis/notebooklm/utils.test.d.ts +1 -0
  121. package/dist/clis/notebooklm/utils.test.js +390 -0
  122. package/dist/clis/substack/utils.d.ts +4 -0
  123. package/dist/clis/substack/utils.js +8 -2
  124. package/dist/clis/substack/utils.test.d.ts +1 -0
  125. package/dist/clis/substack/utils.test.js +46 -0
  126. package/dist/clis/v2ex/hot.yaml +4 -1
  127. package/dist/clis/v2ex/latest.yaml +4 -1
  128. package/dist/clis/v2ex/topic.yaml +6 -1
  129. package/dist/clis/weixin/download.d.ts +9 -0
  130. package/dist/clis/weixin/download.js +76 -6
  131. package/dist/clis/weread/book.js +108 -2
  132. package/dist/clis/weread/commands.test.js +262 -152
  133. package/dist/clis/weread/utils.d.ts +10 -0
  134. package/dist/clis/weread/utils.js +27 -7
  135. package/dist/clis/xiaohongshu/comments.d.ts +3 -0
  136. package/dist/clis/xiaohongshu/comments.js +76 -17
  137. package/dist/clis/xiaohongshu/comments.test.js +70 -9
  138. package/dist/clis/xiaohongshu/download.d.ts +4 -1
  139. package/dist/clis/xiaohongshu/download.js +83 -22
  140. package/dist/clis/xiaohongshu/download.test.d.ts +1 -0
  141. package/dist/clis/xiaohongshu/download.test.js +75 -0
  142. package/dist/clis/xiaohongshu/note-helpers.d.ts +12 -0
  143. package/dist/clis/xiaohongshu/note-helpers.js +23 -0
  144. package/dist/clis/xiaohongshu/note.d.ts +7 -0
  145. package/dist/clis/xiaohongshu/note.js +76 -0
  146. package/dist/clis/xiaohongshu/note.test.d.ts +1 -0
  147. package/dist/clis/xiaohongshu/note.test.js +136 -0
  148. package/dist/clis/xiaohongshu/search.js +9 -0
  149. package/dist/clis/xiaohongshu/search.test.js +10 -4
  150. package/dist/clis/youtube/search.js +57 -17
  151. package/dist/clis/zhihu/question.js +19 -17
  152. package/dist/clis/zhihu/question.test.d.ts +1 -0
  153. package/dist/clis/zhihu/question.test.js +54 -0
  154. package/dist/commanderAdapter.js +9 -0
  155. package/dist/commanderAdapter.test.js +25 -0
  156. package/dist/commands/daemon.d.ts +9 -0
  157. package/dist/commands/daemon.js +124 -0
  158. package/dist/commands/daemon.test.d.ts +1 -0
  159. package/dist/commands/daemon.test.js +185 -0
  160. package/dist/completion.js +3 -1
  161. package/dist/constants.d.ts +2 -0
  162. package/dist/constants.js +2 -0
  163. package/dist/daemon.d.ts +1 -1
  164. package/dist/daemon.js +25 -14
  165. package/dist/daemon.test.d.ts +1 -0
  166. package/dist/daemon.test.js +65 -0
  167. package/dist/discovery.d.ts +9 -0
  168. package/dist/discovery.js +47 -2
  169. package/dist/electron-apps.d.ts +29 -0
  170. package/dist/electron-apps.js +65 -0
  171. package/dist/electron-apps.test.d.ts +1 -0
  172. package/dist/electron-apps.test.js +43 -0
  173. package/dist/engine.test.js +41 -9
  174. package/dist/execution.js +20 -16
  175. package/dist/extension-manifest-regression.test.js +1 -0
  176. package/dist/idle-manager.d.ts +19 -0
  177. package/dist/idle-manager.js +54 -0
  178. package/dist/launcher.d.ts +36 -0
  179. package/dist/launcher.js +152 -0
  180. package/dist/launcher.test.d.ts +1 -0
  181. package/dist/launcher.test.js +57 -0
  182. package/dist/main.js +3 -3
  183. package/dist/registry.d.ts +1 -0
  184. package/dist/registry.js +31 -3
  185. package/dist/registry.test.js +13 -0
  186. package/dist/runtime.d.ts +5 -3
  187. package/dist/runtime.js +12 -5
  188. package/dist/serialization.d.ts +1 -0
  189. package/dist/serialization.js +3 -0
  190. package/dist/serialization.test.js +17 -1
  191. package/dist/tui.d.ts +7 -0
  192. package/dist/tui.js +52 -0
  193. package/dist/tui.test.d.ts +1 -0
  194. package/dist/tui.test.js +19 -0
  195. package/dist/weixin-download.test.js +14 -0
  196. package/docs/.vitepress/config.mts +1 -0
  197. package/docs/adapters/browser/notebooklm.md +69 -0
  198. package/docs/adapters/browser/xiaohongshu.md +19 -10
  199. package/docs/adapters/index.md +67 -66
  200. package/docs/guide/browser-bridge.md +12 -0
  201. package/docs/guide/troubleshooting.md +9 -4
  202. package/docs/superpowers/plans/2026-03-31-daemon-lifecycle-redesign.md +857 -0
  203. package/docs/superpowers/specs/2026-03-31-daemon-lifecycle-redesign.md +208 -0
  204. package/docs/zh/guide/browser-bridge.md +12 -0
  205. package/extension/dist/background.js +250 -11
  206. package/extension/manifest.json +2 -1
  207. package/extension/src/background.test.ts +202 -2
  208. package/extension/src/background.ts +175 -10
  209. package/extension/src/cdp.test.ts +75 -0
  210. package/extension/src/cdp.ts +89 -3
  211. package/extension/src/protocol.ts +7 -5
  212. package/package.json +1 -1
  213. package/src/browser/cdp.ts +24 -17
  214. package/src/browser/daemon-client.ts +7 -1
  215. package/src/browser/dom-helpers.test.ts +15 -1
  216. package/src/browser/dom-helpers.ts +1 -0
  217. package/src/browser/mcp.ts +18 -13
  218. package/src/browser/page.test.ts +58 -0
  219. package/src/browser/page.ts +18 -2
  220. package/src/browser/stealth.test.ts +153 -0
  221. package/src/browser/stealth.ts +198 -0
  222. package/src/browser.test.ts +1 -1
  223. package/src/build-manifest.test.ts +2 -0
  224. package/src/build-manifest.ts +6 -1
  225. package/src/cli.ts +21 -3
  226. package/src/clis/antigravity/SKILL.md +3 -12
  227. package/src/clis/antigravity/serve.ts +5 -10
  228. package/src/clis/bilibili/subtitle.test.ts +60 -0
  229. package/src/clis/bilibili/subtitle.ts +4 -0
  230. package/src/clis/chatwise/ask.ts +0 -2
  231. package/src/clis/chatwise/export.ts +0 -2
  232. package/src/clis/chatwise/history.ts +0 -2
  233. package/src/clis/chatwise/model.ts +0 -2
  234. package/src/clis/chatwise/new.ts +1 -2
  235. package/src/clis/chatwise/read.ts +0 -2
  236. package/src/clis/chatwise/screenshot.ts +1 -2
  237. package/src/clis/chatwise/send.ts +0 -2
  238. package/src/clis/chatwise/status.ts +1 -2
  239. package/src/clis/ctrip/search.test.ts +73 -0
  240. package/src/clis/ctrip/search.ts +97 -47
  241. package/src/clis/douyin/_shared/sts2.test.ts +31 -0
  242. package/src/clis/douyin/_shared/sts2.ts +11 -3
  243. package/src/clis/douyin/activities.test.ts +41 -1
  244. package/src/clis/douyin/activities.ts +12 -3
  245. package/src/clis/douyin/collections.test.ts +35 -2
  246. package/src/clis/douyin/collections.ts +1 -1
  247. package/src/clis/douyin/draft.test.ts +444 -2
  248. package/src/clis/douyin/draft.ts +382 -218
  249. package/src/clis/douyin/hashtag.test.ts +42 -2
  250. package/src/clis/douyin/hashtag.ts +11 -3
  251. package/src/clis/douyin/profile.test.ts +43 -1
  252. package/src/clis/douyin/profile.ts +9 -2
  253. package/src/clis/douyin/videos.test.ts +52 -2
  254. package/src/clis/douyin/videos.ts +49 -15
  255. package/src/clis/facebook/search.test.ts +70 -0
  256. package/src/clis/facebook/search.yaml +4 -3
  257. package/src/clis/instagram/download.test.ts +159 -0
  258. package/src/clis/instagram/download.ts +286 -0
  259. package/src/clis/notebooklm/bind-current.test.ts +43 -0
  260. package/src/clis/notebooklm/bind-current.ts +36 -0
  261. package/src/clis/notebooklm/binding.test.ts +53 -0
  262. package/src/clis/notebooklm/compat.test.ts +19 -0
  263. package/src/clis/notebooklm/current.ts +38 -0
  264. package/src/clis/notebooklm/get.ts +53 -0
  265. package/src/clis/notebooklm/history.test.ts +70 -0
  266. package/src/clis/notebooklm/history.ts +36 -0
  267. package/src/clis/notebooklm/list.ts +40 -0
  268. package/src/clis/notebooklm/note-list.test.ts +64 -0
  269. package/src/clis/notebooklm/note-list.ts +42 -0
  270. package/src/clis/notebooklm/notes-get.test.ts +88 -0
  271. package/src/clis/notebooklm/notes-get.ts +67 -0
  272. package/src/clis/notebooklm/rpc.test.ts +126 -0
  273. package/src/clis/notebooklm/rpc.ts +286 -0
  274. package/src/clis/notebooklm/shared.ts +98 -0
  275. package/src/clis/notebooklm/source-fulltext.test.ts +123 -0
  276. package/src/clis/notebooklm/source-fulltext.ts +69 -0
  277. package/src/clis/notebooklm/source-get.test.ts +100 -0
  278. package/src/clis/notebooklm/source-get.ts +60 -0
  279. package/src/clis/notebooklm/source-guide.test.ts +121 -0
  280. package/src/clis/notebooklm/source-guide.ts +69 -0
  281. package/src/clis/notebooklm/source-list.ts +45 -0
  282. package/src/clis/notebooklm/status.ts +34 -0
  283. package/src/clis/notebooklm/summary.test.ts +94 -0
  284. package/src/clis/notebooklm/summary.ts +45 -0
  285. package/src/clis/notebooklm/utils.test.ts +446 -0
  286. package/src/clis/notebooklm/utils.ts +893 -0
  287. package/src/clis/substack/utils.test.ts +54 -0
  288. package/src/clis/substack/utils.ts +10 -2
  289. package/src/clis/v2ex/hot.yaml +4 -1
  290. package/src/clis/v2ex/latest.yaml +4 -1
  291. package/src/clis/v2ex/topic.yaml +6 -1
  292. package/src/clis/weixin/download.ts +95 -6
  293. package/src/clis/weread/book.ts +142 -2
  294. package/src/clis/weread/commands.test.ts +314 -154
  295. package/src/clis/weread/utils.ts +33 -4
  296. package/src/clis/xiaohongshu/comments.test.ts +85 -9
  297. package/src/clis/xiaohongshu/comments.ts +76 -17
  298. package/src/clis/xiaohongshu/download.test.ts +96 -0
  299. package/src/clis/xiaohongshu/download.ts +83 -22
  300. package/src/clis/xiaohongshu/note-helpers.ts +25 -0
  301. package/src/clis/xiaohongshu/note.test.ts +164 -0
  302. package/src/clis/xiaohongshu/note.ts +86 -0
  303. package/src/clis/xiaohongshu/search.test.ts +11 -4
  304. package/src/clis/xiaohongshu/search.ts +13 -0
  305. package/src/clis/youtube/search.ts +57 -17
  306. package/src/clis/zhihu/question.test.ts +71 -0
  307. package/src/clis/zhihu/question.ts +27 -15
  308. package/src/commanderAdapter.test.ts +30 -0
  309. package/src/commanderAdapter.ts +7 -0
  310. package/src/commands/daemon.test.ts +238 -0
  311. package/src/commands/daemon.ts +135 -0
  312. package/src/completion.ts +2 -1
  313. package/src/constants.ts +3 -0
  314. package/src/daemon.test.ts +88 -0
  315. package/src/daemon.ts +26 -14
  316. package/src/discovery.ts +52 -2
  317. package/src/electron-apps.test.ts +50 -0
  318. package/src/electron-apps.ts +89 -0
  319. package/src/engine.test.ts +45 -9
  320. package/src/execution.ts +24 -19
  321. package/src/extension-manifest-regression.test.ts +1 -0
  322. package/src/idle-manager.ts +60 -0
  323. package/src/launcher.test.ts +67 -0
  324. package/src/launcher.ts +185 -0
  325. package/src/main.ts +3 -2
  326. package/src/registry.test.ts +15 -0
  327. package/src/registry.ts +32 -3
  328. package/src/runtime.ts +13 -7
  329. package/src/serialization.test.ts +19 -1
  330. package/src/serialization.ts +2 -0
  331. package/src/tui.test.ts +23 -0
  332. package/src/tui.ts +65 -0
  333. package/src/weixin-download.test.ts +27 -0
  334. package/tests/e2e/browser-public-extended.test.ts +6 -2
  335. package/chatwise-opencli.ps1 +0 -82
  336. package/dist/clis/chatwise/shared.d.ts +0 -2
  337. package/dist/clis/chatwise/shared.js +0 -6
  338. package/src/clis/chatwise/shared.ts +0 -8
@@ -0,0 +1,34 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import type { IPage } from '../../types.js';
3
+ import { NOTEBOOKLM_DOMAIN, NOTEBOOKLM_HOME_URL, NOTEBOOKLM_SITE } from './shared.js';
4
+ import { ensureNotebooklmNotebookBinding, getNotebooklmPageState } from './utils.js';
5
+
6
+ cli({
7
+ site: NOTEBOOKLM_SITE,
8
+ name: 'status',
9
+ description: 'Check NotebookLM page availability and login state in the current Chrome session',
10
+ domain: NOTEBOOKLM_DOMAIN,
11
+ strategy: Strategy.COOKIE,
12
+ browser: true,
13
+ navigateBefore: false,
14
+ args: [],
15
+ columns: ['status', 'login', 'page', 'url', 'title', 'notebooks'],
16
+ func: async (page: IPage) => {
17
+ await ensureNotebooklmNotebookBinding(page);
18
+ const currentUrl = await page.getCurrentUrl?.().catch(() => null);
19
+ if (!currentUrl || !currentUrl.includes(NOTEBOOKLM_DOMAIN)) {
20
+ await page.goto(NOTEBOOKLM_HOME_URL);
21
+ await page.wait(2);
22
+ }
23
+
24
+ const state = await getNotebooklmPageState(page);
25
+ return [{
26
+ status: state.hostname === NOTEBOOKLM_DOMAIN ? 'Connected' : 'Unavailable',
27
+ login: state.loginRequired ? 'Required' : 'OK',
28
+ page: state.kind,
29
+ url: state.url,
30
+ title: state.title,
31
+ notebooks: state.notebookCount,
32
+ }];
33
+ },
34
+ });
@@ -0,0 +1,94 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ const {
4
+ mockReadNotebooklmSummaryFromPage,
5
+ mockGetNotebooklmSummaryViaRpc,
6
+ mockGetNotebooklmPageState,
7
+ mockRequireNotebooklmSession,
8
+ } = vi.hoisted(() => ({
9
+ mockReadNotebooklmSummaryFromPage: vi.fn(),
10
+ mockGetNotebooklmSummaryViaRpc: vi.fn(),
11
+ mockGetNotebooklmPageState: vi.fn(),
12
+ mockRequireNotebooklmSession: vi.fn(),
13
+ }));
14
+
15
+ vi.mock('./utils.js', async () => {
16
+ const actual = await vi.importActual<typeof import('./utils.js')>('./utils.js');
17
+ return {
18
+ ...actual,
19
+ readNotebooklmSummaryFromPage: mockReadNotebooklmSummaryFromPage,
20
+ getNotebooklmSummaryViaRpc: mockGetNotebooklmSummaryViaRpc,
21
+ getNotebooklmPageState: mockGetNotebooklmPageState,
22
+ requireNotebooklmSession: mockRequireNotebooklmSession,
23
+ };
24
+ });
25
+
26
+ import { getRegistry } from '../../registry.js';
27
+ import './summary.js';
28
+
29
+ describe('notebooklm summary', () => {
30
+ const command = getRegistry().get('notebooklm/summary');
31
+
32
+ beforeEach(() => {
33
+ mockReadNotebooklmSummaryFromPage.mockReset();
34
+ mockGetNotebooklmSummaryViaRpc.mockReset();
35
+ mockGetNotebooklmPageState.mockReset();
36
+ mockRequireNotebooklmSession.mockReset();
37
+ mockRequireNotebooklmSession.mockResolvedValue(undefined);
38
+ mockGetNotebooklmPageState.mockResolvedValue({
39
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
40
+ title: 'Browser Automation',
41
+ hostname: 'notebooklm.google.com',
42
+ kind: 'notebook',
43
+ notebookId: 'nb-demo',
44
+ loginRequired: false,
45
+ notebookCount: 1,
46
+ });
47
+ });
48
+
49
+ it('returns the current notebook summary from the visible page first', async () => {
50
+ mockReadNotebooklmSummaryFromPage.mockResolvedValue({
51
+ notebook_id: 'nb-demo',
52
+ title: 'Browser Automation',
53
+ summary: 'A concise notebook summary.',
54
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
55
+ source: 'summary-dom',
56
+ });
57
+
58
+ const result = await command!.func!({} as any, {});
59
+
60
+ expect(result).toEqual([
61
+ {
62
+ notebook_id: 'nb-demo',
63
+ title: 'Browser Automation',
64
+ summary: 'A concise notebook summary.',
65
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
66
+ source: 'summary-dom',
67
+ },
68
+ ]);
69
+ expect(mockGetNotebooklmSummaryViaRpc).not.toHaveBeenCalled();
70
+ });
71
+
72
+ it('falls back to rpc summary extraction when no visible summary block is found', async () => {
73
+ mockReadNotebooklmSummaryFromPage.mockResolvedValue(null);
74
+ mockGetNotebooklmSummaryViaRpc.mockResolvedValue({
75
+ notebook_id: 'nb-demo',
76
+ title: 'Browser Automation',
77
+ summary: 'Summary recovered from rpc.',
78
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
79
+ source: 'rpc',
80
+ });
81
+
82
+ const result = await command!.func!({} as any, {});
83
+
84
+ expect(result).toEqual([
85
+ {
86
+ notebook_id: 'nb-demo',
87
+ title: 'Browser Automation',
88
+ summary: 'Summary recovered from rpc.',
89
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
90
+ source: 'rpc',
91
+ },
92
+ ]);
93
+ });
94
+ });
@@ -0,0 +1,45 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import type { IPage } from '../../types.js';
3
+ import { EmptyResultError } from '../../errors.js';
4
+ import { NOTEBOOKLM_DOMAIN, NOTEBOOKLM_SITE } from './shared.js';
5
+ import {
6
+ ensureNotebooklmNotebookBinding,
7
+ getNotebooklmPageState,
8
+ getNotebooklmSummaryViaRpc,
9
+ readNotebooklmSummaryFromPage,
10
+ requireNotebooklmSession,
11
+ } from './utils.js';
12
+
13
+ cli({
14
+ site: NOTEBOOKLM_SITE,
15
+ name: 'summary',
16
+ description: 'Get the summary block from the currently opened NotebookLM notebook',
17
+ domain: NOTEBOOKLM_DOMAIN,
18
+ strategy: Strategy.COOKIE,
19
+ browser: true,
20
+ navigateBefore: false,
21
+ args: [],
22
+ columns: ['title', 'summary', 'source', 'url'],
23
+ func: async (page: IPage) => {
24
+ await ensureNotebooklmNotebookBinding(page);
25
+ await requireNotebooklmSession(page);
26
+ const state = await getNotebooklmPageState(page);
27
+ if (state.kind !== 'notebook') {
28
+ throw new EmptyResultError(
29
+ 'opencli notebooklm summary',
30
+ 'Open a specific NotebookLM notebook tab first, then retry.',
31
+ );
32
+ }
33
+
34
+ const domSummary = await readNotebooklmSummaryFromPage(page);
35
+ if (domSummary) return [domSummary];
36
+
37
+ const rpcSummary = await getNotebooklmSummaryViaRpc(page).catch(() => null);
38
+ if (rpcSummary) return [rpcSummary];
39
+
40
+ throw new EmptyResultError(
41
+ 'opencli notebooklm summary',
42
+ 'NotebookLM summary was not found on the current page.',
43
+ );
44
+ },
45
+ });
@@ -0,0 +1,446 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ buildNotebooklmRpcBody,
4
+ classifyNotebooklmPage,
5
+ extractNotebooklmHistoryPreview,
6
+ extractNotebooklmRpcResult,
7
+ getNotebooklmPageState,
8
+ normalizeNotebooklmTitle,
9
+ parseNotebooklmHistoryThreadIdsResult,
10
+ parseNotebooklmIdFromUrl,
11
+ parseNotebooklmListResult,
12
+ parseNotebooklmNoteListRawRows,
13
+ parseNotebooklmNotebookDetailResult,
14
+ parseNotebooklmSourceFulltextResult,
15
+ parseNotebooklmSourceGuideResult,
16
+ parseNotebooklmSourceListResult,
17
+ } from './utils.js';
18
+
19
+ describe('notebooklm utils', () => {
20
+ it('parses notebook id from a notebook url', () => {
21
+ expect(parseNotebooklmIdFromUrl('https://notebooklm.google.com/notebook/abc-123')).toBe('abc-123');
22
+ });
23
+
24
+ it('returns empty string when notebook id is absent', () => {
25
+ expect(parseNotebooklmIdFromUrl('https://notebooklm.google.com/')).toBe('');
26
+ });
27
+
28
+ it('classifies notebook pages correctly', () => {
29
+ expect(classifyNotebooklmPage('https://notebooklm.google.com/notebook/demo-id')).toBe('notebook');
30
+ expect(classifyNotebooklmPage('https://notebooklm.google.com/')).toBe('home');
31
+ expect(classifyNotebooklmPage('https://example.com/notebook/demo-id')).toBe('unknown');
32
+ });
33
+
34
+ it('normalizes notebook titles', () => {
35
+ expect(normalizeNotebooklmTitle(' Demo Notebook ')).toBe('Demo Notebook');
36
+ expect(normalizeNotebooklmTitle('', 'Untitled')).toBe('Untitled');
37
+ });
38
+
39
+ it('builds the notebooklm rpc request body with csrf token', () => {
40
+ const body = buildNotebooklmRpcBody('wXbhsf', [null, 1, null, [2]], 'csrf123');
41
+ expect(body).toContain('f.req=');
42
+ expect(body).toContain('at=csrf123');
43
+ expect(body.endsWith('&')).toBe(true);
44
+ expect(decodeURIComponent(body)).toContain('"[null,1,null,[2]]"');
45
+ });
46
+
47
+ it('extracts notebooklm rpc payload from chunked batchexecute response', () => {
48
+ const raw = ')]}\'\n107\n[["wrb.fr","wXbhsf","[[[\\"Notebook One\\",null,\\"nb1\\",null,null,[null,false,null,null,null,[1704067200]]]]]"]]';
49
+ const result = extractNotebooklmRpcResult(raw, 'wXbhsf');
50
+ expect(Array.isArray(result)).toBe(true);
51
+ expect((result as unknown[])[0]).toBeDefined();
52
+ });
53
+
54
+ it('parses notebook rows from notebooklm rpc payload', () => {
55
+ const rows = parseNotebooklmListResult([
56
+ [
57
+ ['Notebook One', null, 'nb1', null, null, [null, false, null, null, null, [1704067200]]],
58
+ ],
59
+ ]);
60
+
61
+ expect(rows).toEqual([
62
+ {
63
+ id: 'nb1',
64
+ title: 'Notebook One',
65
+ url: 'https://notebooklm.google.com/notebook/nb1',
66
+ source: 'rpc',
67
+ is_owner: true,
68
+ created_at: '2024-01-01T00:00:00.000Z',
69
+ },
70
+ ]);
71
+ });
72
+
73
+ it('parses notebook metadata from notebook detail rpc payload', () => {
74
+ const notebook = parseNotebooklmNotebookDetailResult([
75
+ 'Browser Automation',
76
+ [
77
+ [
78
+ [['src1']],
79
+ 'Pasted text',
80
+ [null, 359, [1774872183, 855096000], ['doc1', [1774872183, 356519000]], 8, null, 1, null, null, null, null, null, null, null, [1774872185, 395271000]],
81
+ [null, 2],
82
+ ],
83
+ ],
84
+ 'nb-demo',
85
+ '🕸️',
86
+ null,
87
+ [1, false, true, null, null, [1774889558, 348721000], 1, false, [1774872161, 361922000], null, null, null, false, true, 1, false, null, true, 1],
88
+ ]);
89
+
90
+ expect(notebook).toEqual({
91
+ id: 'nb-demo',
92
+ title: 'Browser Automation',
93
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
94
+ source: 'rpc',
95
+ emoji: '🕸️',
96
+ source_count: 1,
97
+ is_owner: true,
98
+ created_at: '2026-03-30T12:02:41.361Z',
99
+ updated_at: '2026-03-30T16:52:38.348Z',
100
+ });
101
+ });
102
+
103
+ it('parses notebook metadata when detail rpc wraps the payload in a singleton envelope', () => {
104
+ const notebook = parseNotebooklmNotebookDetailResult([
105
+ [
106
+ 'Browser Automation',
107
+ [
108
+ [
109
+ [['src1']],
110
+ 'Pasted text',
111
+ [null, 359, [1774872183, 855096000], ['doc1', [1774872183, 356519000]], 8, null, 1, null, null, null, null, null, null, null, [1774872185, 395271000]],
112
+ [null, 2],
113
+ ],
114
+ ],
115
+ 'nb-demo',
116
+ '🕸️',
117
+ null,
118
+ [1, false, true, null, null, [1774889558, 348721000], 1, false, [1774872161, 361922000], null, null, null, false, true, 1, false, null, true, 1],
119
+ ],
120
+ ]);
121
+
122
+ expect(notebook).toEqual({
123
+ id: 'nb-demo',
124
+ title: 'Browser Automation',
125
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
126
+ source: 'rpc',
127
+ emoji: '🕸️',
128
+ source_count: 1,
129
+ is_owner: true,
130
+ created_at: '2026-03-30T12:02:41.361Z',
131
+ updated_at: '2026-03-30T16:52:38.348Z',
132
+ });
133
+ });
134
+
135
+ it('parses sources from notebook detail rpc payload', () => {
136
+ const rows = parseNotebooklmSourceListResult([
137
+ 'Browser Automation',
138
+ [
139
+ [
140
+ [['src1']],
141
+ 'Pasted text',
142
+ [null, 359, [1774872183, 855096000], ['doc1', [1774872183, 356519000]], 8, null, 1, null, null, null, null, null, null, null, [1774872185, 395271000]],
143
+ [null, 2],
144
+ ],
145
+ ],
146
+ 'nb-demo',
147
+ '🕸️',
148
+ null,
149
+ [1, false, true, null, null, [1774889558, 348721000], 1, false, [1774872161, 361922000], null, null, null, false, true, 1, false, null, true, 1],
150
+ ]);
151
+
152
+ expect(rows).toEqual([
153
+ {
154
+ id: 'src1',
155
+ notebook_id: 'nb-demo',
156
+ title: 'Pasted text',
157
+ type: 'pasted-text',
158
+ type_code: 8,
159
+ size: 359,
160
+ created_at: '2026-03-30T12:03:03.855Z',
161
+ updated_at: '2026-03-30T12:03:05.395Z',
162
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
163
+ source: 'rpc',
164
+ },
165
+ ]);
166
+ });
167
+
168
+ it('parses sources when detail rpc wraps the payload in a singleton envelope', () => {
169
+ const rows = parseNotebooklmSourceListResult([
170
+ [
171
+ 'Browser Automation',
172
+ [
173
+ [
174
+ [['src1']],
175
+ 'Pasted text',
176
+ [null, 359, [1774872183, 855096000], ['doc1', [1774872183, 356519000]], 8, null, 1, null, null, null, null, null, null, null, [1774872185, 395271000]],
177
+ [null, 2],
178
+ ],
179
+ ],
180
+ 'nb-demo',
181
+ '🕸️',
182
+ null,
183
+ [1, false, true, null, null, [1774889558, 348721000], 1, false, [1774872161, 361922000], null, null, null, false, true, 1, false, null, true, 1],
184
+ ],
185
+ ]);
186
+
187
+ expect(rows).toEqual([
188
+ {
189
+ id: 'src1',
190
+ notebook_id: 'nb-demo',
191
+ title: 'Pasted text',
192
+ type: 'pasted-text',
193
+ type_code: 8,
194
+ size: 359,
195
+ created_at: '2026-03-30T12:03:03.855Z',
196
+ updated_at: '2026-03-30T12:03:05.395Z',
197
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
198
+ source: 'rpc',
199
+ },
200
+ ]);
201
+ });
202
+
203
+ it('parses sources when the source id container is only wrapped once', () => {
204
+ const rows = parseNotebooklmSourceListResult([
205
+ [
206
+ 'Browser Automation',
207
+ [
208
+ [
209
+ ['src-live'],
210
+ 'Pasted text',
211
+ [null, 359, [1774872183, 855096000], ['doc1', [1774872183, 356519000]], 8, null, 1, null, null, null, null, null, null, null, [1774872185, 395271000]],
212
+ [null, 2],
213
+ ],
214
+ ],
215
+ 'nb-demo',
216
+ '🕸️',
217
+ null,
218
+ [1, false, true, null, null, [1774889558, 348721000], 1, false, [1774872161, 361922000], null, null, null, false, true, 1, false, null, true, 1],
219
+ ],
220
+ ]);
221
+
222
+ expect(rows).toEqual([
223
+ {
224
+ id: 'src-live',
225
+ notebook_id: 'nb-demo',
226
+ title: 'Pasted text',
227
+ type: 'pasted-text',
228
+ type_code: 8,
229
+ size: 359,
230
+ created_at: '2026-03-30T12:03:03.855Z',
231
+ updated_at: '2026-03-30T12:03:05.395Z',
232
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
233
+ source: 'rpc',
234
+ },
235
+ ]);
236
+ });
237
+
238
+ it('parses source type from metadata slot instead of the stale entry[3] envelope', () => {
239
+ const rows = parseNotebooklmSourceListResult([
240
+ [
241
+ 'Browser Automation',
242
+ [
243
+ [
244
+ ['src-pdf'],
245
+ 'Manual.pdf',
246
+ [null, 18940, [1774872183, 855096000], ['doc1', [1774872183, 356519000]], 3, null, 1, null, null, null, null, null, null, null, [1774872185, 395271000]],
247
+ [null, 2],
248
+ ],
249
+ [
250
+ ['src-web'],
251
+ 'Example Site',
252
+ [null, 131, [1774872183, 855096000], ['doc2', [1774872183, 356519000]], 5, ['https://example.com'], 1, null, null, null, null, null, null, null, [1774872185, 395271000]],
253
+ [null, 2],
254
+ ],
255
+ [
256
+ ['src-yt'],
257
+ 'Video Source',
258
+ [null, 11958, [1774872183, 855096000], ['doc3', [1774872183, 356519000]], 9, ['https://youtu.be/demo', 'demo', 'Uploader'], 1, null, null, null, null, null, null, null, [1774872185, 395271000]],
259
+ [null, 2],
260
+ ],
261
+ ],
262
+ 'nb-demo',
263
+ '🕸️',
264
+ null,
265
+ [1, false, true, null, null, [1774889558, 348721000], 1, false, [1774872161, 361922000], null, null, null, false, true, 1, false, null, true, 1],
266
+ ],
267
+ ]);
268
+
269
+ expect(rows).toEqual([
270
+ expect.objectContaining({
271
+ id: 'src-pdf',
272
+ type: 'pdf',
273
+ type_code: 3,
274
+ }),
275
+ expect.objectContaining({
276
+ id: 'src-web',
277
+ type: 'web',
278
+ type_code: 5,
279
+ }),
280
+ expect.objectContaining({
281
+ id: 'src-yt',
282
+ type: 'youtube',
283
+ type_code: 9,
284
+ }),
285
+ ]);
286
+ });
287
+
288
+ it('parses notebook history thread ids from hPTbtc payload', () => {
289
+ const threadIds = parseNotebooklmHistoryThreadIdsResult([
290
+ [[['28e0f2cb-4591-45a3-a661-7653666f7c78']]],
291
+ ]);
292
+
293
+ expect(threadIds).toEqual(['28e0f2cb-4591-45a3-a661-7653666f7c78']);
294
+ });
295
+
296
+ it('extracts a notebook history preview from khqZz payload', () => {
297
+ const preview = extractNotebooklmHistoryPreview([
298
+ [
299
+ ['28e0f2cb-4591-45a3-a661-7653666f7c78'],
300
+ [null, 'Summarize this notebook'],
301
+ ],
302
+ ]);
303
+
304
+ expect(preview).toBe('Summarize this notebook');
305
+ });
306
+
307
+ it('parses notebook notes from studio note rows', () => {
308
+ const rows = parseNotebooklmNoteListRawRows(
309
+ [
310
+ {
311
+ title: '新建笔记',
312
+ text: 'sticky_note_2 新建笔记 6 分钟前 more_vert',
313
+ },
314
+ ],
315
+ 'nb-demo',
316
+ 'https://notebooklm.google.com/notebook/nb-demo',
317
+ );
318
+
319
+ expect(rows).toEqual([
320
+ {
321
+ notebook_id: 'nb-demo',
322
+ title: '新建笔记',
323
+ created_at: '6 分钟前',
324
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
325
+ source: 'studio-list',
326
+ },
327
+ ]);
328
+ });
329
+
330
+ it('parses source fulltext from hizoJc payload', () => {
331
+ const row = parseNotebooklmSourceFulltextResult(
332
+ [
333
+ [
334
+ [['src-1']],
335
+ '粘贴的文字',
336
+ [null, 359, [1774872183, 855096000], null, 8, null, 1, ['https://example.com/source']],
337
+ [null, 2],
338
+ ],
339
+ null,
340
+ null,
341
+ [
342
+ [
343
+ [
344
+ [0, 5, [[[0, 5, ['第一段']]]]],
345
+ [5, 10, [[[5, 10, ['第二段']]]]],
346
+ ],
347
+ ],
348
+ ],
349
+ ],
350
+ 'nb-demo',
351
+ 'https://notebooklm.google.com/notebook/nb-demo',
352
+ );
353
+
354
+ expect(row).toEqual({
355
+ source_id: 'src-1',
356
+ notebook_id: 'nb-demo',
357
+ title: '粘贴的文字',
358
+ kind: 'pasted-text',
359
+ content: '第一段\n第二段',
360
+ char_count: 7,
361
+ url: 'https://example.com/source',
362
+ source: 'rpc',
363
+ });
364
+ });
365
+
366
+ it('parses source guide from tr032e payloads with either null or source-id envelope in slot 0', () => {
367
+ const source = {
368
+ id: 'src-yt',
369
+ notebook_id: 'nb-demo',
370
+ title: 'Video Source',
371
+ type: 'youtube',
372
+ };
373
+
374
+ expect(parseNotebooklmSourceGuideResult([
375
+ [
376
+ [
377
+ null,
378
+ ['Guide summary'],
379
+ [['AI', 'agents']],
380
+ [],
381
+ ],
382
+ ],
383
+ ], source)).toEqual({
384
+ source_id: 'src-yt',
385
+ notebook_id: 'nb-demo',
386
+ title: 'Video Source',
387
+ type: 'youtube',
388
+ summary: 'Guide summary',
389
+ keywords: ['AI', 'agents'],
390
+ source: 'rpc',
391
+ });
392
+
393
+ expect(parseNotebooklmSourceGuideResult([
394
+ [
395
+ [
396
+ [['src-yt']],
397
+ ['Guide summary'],
398
+ [['AI', 'agents']],
399
+ [],
400
+ ],
401
+ ],
402
+ ], source)).toEqual({
403
+ source_id: 'src-yt',
404
+ notebook_id: 'nb-demo',
405
+ title: 'Video Source',
406
+ type: 'youtube',
407
+ summary: 'Guide summary',
408
+ keywords: ['AI', 'agents'],
409
+ source: 'rpc',
410
+ });
411
+ });
412
+
413
+ it('prefers real NotebookLM page tokens over login text heuristics', async () => {
414
+ let call = 0;
415
+ const page = {
416
+ evaluate: async () => {
417
+ call += 1;
418
+ if (call === 1) {
419
+ return {
420
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
421
+ title: 'Demo Notebook - NotebookLM',
422
+ hostname: 'notebooklm.google.com',
423
+ kind: 'notebook',
424
+ notebookId: 'nb-demo',
425
+ loginRequired: true,
426
+ notebookCount: 0,
427
+ };
428
+ }
429
+ return {
430
+ html: '<html>"SNlM0e":"csrf-123","FdrFJe":"sess-456"</html>',
431
+ sourcePath: '/notebook/nb-demo',
432
+ };
433
+ },
434
+ };
435
+
436
+ await expect(getNotebooklmPageState(page as any)).resolves.toEqual({
437
+ url: 'https://notebooklm.google.com/notebook/nb-demo',
438
+ title: 'Demo Notebook - NotebookLM',
439
+ hostname: 'notebooklm.google.com',
440
+ kind: 'notebook',
441
+ notebookId: 'nb-demo',
442
+ loginRequired: false,
443
+ notebookCount: 0,
444
+ });
445
+ });
446
+ });