@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
package/dist/registry.js CHANGED
@@ -13,9 +13,11 @@ const _registry = globalThis.__opencli_registry__ ??= new Map();
13
13
  export function cli(opts) {
14
14
  const strategy = opts.strategy ?? (opts.browser === false ? Strategy.PUBLIC : Strategy.COOKIE);
15
15
  const browser = opts.browser ?? (strategy !== Strategy.PUBLIC);
16
+ const aliases = normalizeAliases(opts.aliases, opts.name);
16
17
  const cmd = {
17
18
  site: opts.site,
18
19
  name: opts.name,
20
+ aliases,
19
21
  description: opts.description ?? '',
20
22
  domain: opts.domain,
21
23
  strategy,
@@ -31,8 +33,7 @@ export function cli(opts) {
31
33
  replacedBy: opts.replacedBy,
32
34
  navigateBefore: opts.navigateBefore,
33
35
  };
34
- const key = fullName(cmd);
35
- _registry.set(key, cmd);
36
+ registerCommand(cmd);
36
37
  return cmd;
37
38
  }
38
39
  export function getRegistry() {
@@ -45,5 +46,32 @@ export function strategyLabel(cmd) {
45
46
  return cmd.strategy ?? Strategy.PUBLIC;
46
47
  }
47
48
  export function registerCommand(cmd) {
48
- _registry.set(fullName(cmd), cmd);
49
+ const canonicalKey = fullName(cmd);
50
+ const existing = _registry.get(canonicalKey);
51
+ if (existing) {
52
+ for (const [key, value] of _registry.entries()) {
53
+ if (value === existing && key !== canonicalKey)
54
+ _registry.delete(key);
55
+ }
56
+ }
57
+ const aliases = normalizeAliases(cmd.aliases, cmd.name);
58
+ cmd.aliases = aliases.length > 0 ? aliases : undefined;
59
+ _registry.set(canonicalKey, cmd);
60
+ for (const alias of aliases) {
61
+ _registry.set(`${cmd.site}/${alias}`, cmd);
62
+ }
63
+ }
64
+ function normalizeAliases(aliases, commandName) {
65
+ if (!Array.isArray(aliases) || aliases.length === 0)
66
+ return [];
67
+ const seen = new Set();
68
+ const normalized = [];
69
+ for (const alias of aliases) {
70
+ const value = typeof alias === 'string' ? alias.trim() : '';
71
+ if (!value || value === commandName || seen.has(value))
72
+ continue;
73
+ seen.add(value);
74
+ normalized.push(value);
75
+ }
76
+ return normalized;
49
77
  }
@@ -49,6 +49,19 @@ describe('cli() registration', () => {
49
49
  const reg = getRegistry();
50
50
  expect(reg.get('test-registry/overwrite')?.description).toBe('v2');
51
51
  });
52
+ it('registers aliases as alternate registry keys for the same command', () => {
53
+ const cmd = cli({
54
+ site: 'test-registry',
55
+ name: 'canonical',
56
+ description: 'test aliases',
57
+ aliases: ['compat', 'legacy-name'],
58
+ });
59
+ const registry = getRegistry();
60
+ expect(cmd.aliases).toEqual(['compat', 'legacy-name']);
61
+ expect(registry.get('test-registry/canonical')).toBe(cmd);
62
+ expect(registry.get('test-registry/compat')).toBe(cmd);
63
+ expect(registry.get('test-registry/legacy-name')).toBe(cmd);
64
+ });
52
65
  });
53
66
  describe('fullName', () => {
54
67
  it('returns site/name', () => {
package/dist/runtime.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type { IPage } from './types.js';
2
2
  /**
3
- * Returns the appropriate browser factory based on environment config.
4
- * Uses CDPBridge when OPENCLI_CDP_ENDPOINT is set, otherwise BrowserBridge.
3
+ * Returns the appropriate browser factory based on site type.
4
+ * Uses CDPBridge for registered Electron apps, otherwise BrowserBridge.
5
5
  */
6
- export declare function getBrowserFactory(): new () => IBrowserFactory;
6
+ export declare function getBrowserFactory(site?: string): new () => IBrowserFactory;
7
7
  export declare const DEFAULT_BROWSER_CONNECT_TIMEOUT: number;
8
8
  export declare const DEFAULT_BROWSER_COMMAND_TIMEOUT: number;
9
9
  export declare const DEFAULT_BROWSER_EXPLORE_TIMEOUT: number;
@@ -26,9 +26,11 @@ export interface IBrowserFactory {
26
26
  connect(opts?: {
27
27
  timeout?: number;
28
28
  workspace?: string;
29
+ cdpEndpoint?: string;
29
30
  }): Promise<IPage>;
30
31
  close(): Promise<void>;
31
32
  }
32
33
  export declare function browserSession<T>(BrowserFactory: new () => IBrowserFactory, fn: (page: IPage) => Promise<T>, opts?: {
33
34
  workspace?: string;
35
+ cdpEndpoint?: string;
34
36
  }): Promise<T>;
package/dist/runtime.js CHANGED
@@ -1,11 +1,14 @@
1
1
  import { BrowserBridge, CDPBridge } from './browser/index.js';
2
2
  import { TimeoutError } from './errors.js';
3
+ import { isElectronApp } from './electron-apps.js';
3
4
  /**
4
- * Returns the appropriate browser factory based on environment config.
5
- * Uses CDPBridge when OPENCLI_CDP_ENDPOINT is set, otherwise BrowserBridge.
5
+ * Returns the appropriate browser factory based on site type.
6
+ * Uses CDPBridge for registered Electron apps, otherwise BrowserBridge.
6
7
  */
7
- export function getBrowserFactory() {
8
- return process.env.OPENCLI_CDP_ENDPOINT ? CDPBridge : BrowserBridge;
8
+ export function getBrowserFactory(site) {
9
+ if (site && isElectronApp(site))
10
+ return CDPBridge;
11
+ return BrowserBridge;
9
12
  }
10
13
  function parseEnvTimeout(envVar, fallback) {
11
14
  const raw = process.env[envVar];
@@ -45,7 +48,11 @@ export function withTimeoutMs(promise, timeoutMs, makeError = 'Operation timed o
45
48
  export async function browserSession(BrowserFactory, fn, opts = {}) {
46
49
  const mcp = new BrowserFactory();
47
50
  try {
48
- const page = await mcp.connect({ timeout: DEFAULT_BROWSER_CONNECT_TIMEOUT, workspace: opts.workspace });
51
+ const page = await mcp.connect({
52
+ timeout: DEFAULT_BROWSER_CONNECT_TIMEOUT,
53
+ workspace: opts.workspace,
54
+ cdpEndpoint: opts.cdpEndpoint,
55
+ });
49
56
  return await fn(page);
50
57
  }
51
58
  finally {
@@ -21,6 +21,7 @@ export declare function serializeCommand(cmd: CliCommand): {
21
21
  command: string;
22
22
  site: string;
23
23
  name: string;
24
+ aliases: string[];
24
25
  description: string;
25
26
  strategy: string;
26
27
  browser: boolean;
@@ -23,6 +23,7 @@ export function serializeCommand(cmd) {
23
23
  command: fullName(cmd),
24
24
  site: cmd.site,
25
25
  name: cmd.name,
26
+ aliases: cmd.aliases ?? [],
26
27
  description: cmd.description,
27
28
  strategy: strategyLabel(cmd),
28
29
  browser: !!cmd.browser,
@@ -67,6 +68,8 @@ export function formatRegistryHelpText(cmd) {
67
68
  meta.push(`Deprecated: ${typeof cmd.deprecated === 'string' ? cmd.deprecated : 'yes'}`);
68
69
  if (cmd.replacedBy)
69
70
  meta.push(`Use instead: ${cmd.replacedBy}`);
71
+ if (cmd.aliases?.length)
72
+ meta.push(`Aliases: ${cmd.aliases.join(', ')}`);
70
73
  lines.push(meta.join(' | '));
71
74
  if (cmd.columns?.length)
72
75
  lines.push(`Output columns: ${cmd.columns.join(', ')}`);
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { Strategy } from './registry.js';
3
- import { formatRegistryHelpText } from './serialization.js';
3
+ import { formatRegistryHelpText, serializeCommand } from './serialization.js';
4
4
  describe('formatRegistryHelpText', () => {
5
5
  it('summarizes long choices lists so help text stays readable', () => {
6
6
  const cmd = {
@@ -20,4 +20,20 @@ describe('formatRegistryHelpText', () => {
20
20
  };
21
21
  expect(formatRegistryHelpText(cmd)).toContain('--field: all-fields, topic, title, author, ... (+3 more)');
22
22
  });
23
+ it('includes aliases in structured serialization and help text', () => {
24
+ const cmd = {
25
+ site: 'demo',
26
+ name: 'get',
27
+ aliases: ['metadata'],
28
+ description: 'Demo command',
29
+ strategy: Strategy.COOKIE,
30
+ browser: true,
31
+ args: [],
32
+ };
33
+ expect(serializeCommand(cmd)).toMatchObject({
34
+ command: 'demo/get',
35
+ aliases: ['metadata'],
36
+ });
37
+ expect(formatRegistryHelpText(cmd)).toContain('Aliases: metadata');
38
+ });
23
39
  });
package/dist/tui.d.ts CHANGED
@@ -20,3 +20,10 @@ export declare function checkboxPrompt(items: CheckboxItem[], opts?: {
20
20
  title?: string;
21
21
  hint?: string;
22
22
  }): Promise<string[]>;
23
+ /**
24
+ * Simple yes/no confirmation prompt.
25
+ *
26
+ * In non-TTY environments, returns `defaultYes` (defaults to true) without blocking.
27
+ * In TTY, waits for a single keypress: y/Enter → true, n/Esc/q → false.
28
+ */
29
+ export declare function confirmPrompt(message: string, defaultYes?: boolean): Promise<boolean>;
package/dist/tui.js CHANGED
@@ -138,3 +138,55 @@ export async function checkboxPrompt(items, opts = {}) {
138
138
  draw();
139
139
  });
140
140
  }
141
+ /**
142
+ * Simple yes/no confirmation prompt.
143
+ *
144
+ * In non-TTY environments, returns `defaultYes` (defaults to true) without blocking.
145
+ * In TTY, waits for a single keypress: y/Enter → true, n/Esc/q → false.
146
+ */
147
+ export async function confirmPrompt(message, defaultYes = true) {
148
+ const { stdin, stdout } = process;
149
+ if (!stdin.isTTY)
150
+ return defaultYes;
151
+ const hint = defaultYes ? '[Y/n]' : '[y/N]';
152
+ stdout.write(` ${message} ${chalk.dim(hint)} `);
153
+ return new Promise((resolve) => {
154
+ const wasRaw = stdin.isRaw;
155
+ stdin.setRawMode(true);
156
+ stdin.resume();
157
+ function cleanup() {
158
+ stdin.setRawMode(wasRaw ?? false);
159
+ stdin.pause();
160
+ stdin.removeListener('data', onData);
161
+ stdout.write('\n');
162
+ }
163
+ function onData(data) {
164
+ const key = data.toString();
165
+ // Ctrl+C
166
+ if (key === '\x03') {
167
+ cleanup();
168
+ process.exit(EXIT_CODES.INTERRUPTED);
169
+ }
170
+ // Enter — use default
171
+ if (key === '\r' || key === '\n') {
172
+ cleanup();
173
+ resolve(defaultYes);
174
+ return;
175
+ }
176
+ // y/Y — yes
177
+ if (key === 'y' || key === 'Y') {
178
+ cleanup();
179
+ resolve(true);
180
+ return;
181
+ }
182
+ // n/N/q/Esc — no
183
+ if (key === 'n' || key === 'N' || key === 'q' || key === '\x1b') {
184
+ cleanup();
185
+ resolve(false);
186
+ return;
187
+ }
188
+ // Ignore other keys
189
+ }
190
+ stdin.on('data', onData);
191
+ });
192
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { confirmPrompt } from './tui.js';
3
+ describe('confirmPrompt', () => {
4
+ beforeEach(() => {
5
+ vi.restoreAllMocks();
6
+ });
7
+ it('returns defaultYes when stdin is not TTY', async () => {
8
+ const result = await confirmPrompt('Restart?', true);
9
+ expect(result).toBe(true);
10
+ });
11
+ it('returns false when defaultYes is false and non-TTY', async () => {
12
+ const result = await confirmPrompt('Restart?', false);
13
+ expect(result).toBe(false);
14
+ });
15
+ it('defaults to true when defaultYes is omitted and non-TTY', async () => {
16
+ const result = await confirmPrompt('Restart?');
17
+ expect(result).toBe(true);
18
+ });
19
+ });
@@ -27,4 +27,18 @@ describe('weixin publish time extraction', () => {
27
27
  const extractInPage = eval(mod.buildExtractWechatPublishTimeJs());
28
28
  expect(extractInPage('2026年3月24日 22:38', 'var create_time = "1711291080";')).toBe('2026年3月24日 22:38');
29
29
  });
30
+ it('detects WeChat verification gate pages', async () => {
31
+ const mod = await loadModule();
32
+ expect(mod.detectWechatAccessIssue('环境异常 当前环境异常,完成验证后即可继续访问。 去验证', '<html><body><a id="js_verify">去验证</a></body></html>')).toBe('environment verification required');
33
+ });
34
+ it('browser access detector matches the server-side verifier', async () => {
35
+ const mod = await loadModule();
36
+ const detectInPage = eval(mod.buildDetectWechatAccessIssueJs());
37
+ expect(detectInPage('环境异常 当前环境异常,完成验证后即可继续访问。 去验证', '<html>secitptpage/verify.html<a id="js_verify">去验证</a></html>')).toBe('environment verification required');
38
+ });
39
+ it('picks the first non-empty WeChat metadata field', async () => {
40
+ const mod = await loadModule();
41
+ expect(mod.pickFirstWechatMetaText('', 'Name cleared', '数字生命卡兹克')).toBe('数字生命卡兹克');
42
+ expect(mod.pickFirstWechatMetaText('', ' 聊聊刚刚上线的PixVerse V6视频模型。 ')).toBe('聊聊刚刚上线的PixVerse V6视频模型。');
43
+ });
30
44
  });
@@ -72,6 +72,7 @@ export default defineConfig({
72
72
  { text: 'Band', link: '/adapters/browser/band' },
73
73
  { text: 'Chaoxing', link: '/adapters/browser/chaoxing' },
74
74
  { text: 'Grok', link: '/adapters/browser/grok' },
75
+ { text: 'NotebookLM', link: '/adapters/browser/notebooklm' },
75
76
  { text: 'WeRead', link: '/adapters/browser/weread' },
76
77
  { text: 'Douban', link: '/adapters/browser/douban' },
77
78
  { text: 'Sina Blog', link: '/adapters/browser/sinablog' },
@@ -0,0 +1,69 @@
1
+ # NotebookLM
2
+
3
+ **Mode**: 🔐 Browser Bridge · **Domain**: `notebooklm.google.com`
4
+
5
+ ## Commands
6
+
7
+ | Command | Description |
8
+ |---------|-------------|
9
+ | `opencli notebooklm status` | Check whether NotebookLM is reachable in the current Chrome session |
10
+ | `opencli notebooklm list` | List notebooks visible from the NotebookLM home page |
11
+ | `opencli notebooklm current` | Show metadata for the currently opened notebook tab |
12
+ | `opencli notebooklm get` | Get richer metadata for the current notebook |
13
+ | `opencli notebooklm source-list` | List sources in the current notebook |
14
+ | `opencli notebooklm source-get <source>` | Resolve one source in the current notebook by id or title |
15
+ | `opencli notebooklm source-fulltext <source>` | Fetch extracted source fulltext through NotebookLM RPC |
16
+ | `opencli notebooklm source-guide <source>` | Fetch guide summary and keywords for one source |
17
+ | `opencli notebooklm history` | List conversation history threads for the current notebook |
18
+ | `opencli notebooklm note-list` | List Studio notes visible in the current notebook |
19
+ | `opencli notebooklm notes-get <note>` | Read the currently visible Studio note by title |
20
+ | `opencli notebooklm bind-current` | Bind the current active NotebookLM tab into the `site:notebooklm` workspace |
21
+ | `opencli notebooklm summary` | Read the current notebook summary |
22
+
23
+ ## Compatibility Aliases
24
+
25
+ | Alias | Canonical command |
26
+ |-------|-------------------|
27
+ | `opencli notebooklm metadata` | `opencli notebooklm get` |
28
+ | `opencli notebooklm use` | `opencli notebooklm bind-current` |
29
+ | `opencli notebooklm notes-list` | `opencli notebooklm note-list` |
30
+
31
+ ## Positioning
32
+
33
+ This adapter reuses the existing OpenCLI Browser Bridge runtime:
34
+
35
+ - no custom NotebookLM extension
36
+ - no exported cookie replay
37
+ - requests and page state stay in the real Chrome session
38
+
39
+ The current milestone focuses on a stable NotebookLM read surface in desktop Chrome with an already logged-in Google account.
40
+
41
+ ## Usage Examples
42
+
43
+ ```bash
44
+ opencli notebooklm status
45
+ opencli notebooklm list -f json
46
+ opencli notebooklm current -f json
47
+ opencli notebooklm metadata -f json
48
+ opencli notebooklm source-list -f json
49
+ opencli notebooklm source-get "Quarterly report" -f json
50
+ opencli notebooklm source-guide "Quarterly report" -f json
51
+ opencli notebooklm source-fulltext "Quarterly report" -f json
52
+ opencli notebooklm history -f json
53
+ opencli notebooklm notes-list -f json
54
+ opencli notebooklm notes-get "Draft note" -f json
55
+ opencli notebooklm summary -f json
56
+ opencli notebooklm use -f json
57
+ ```
58
+
59
+ ## Prerequisites
60
+
61
+ - Chrome running and logged into Google / NotebookLM
62
+ - [Browser Bridge extension](/guide/browser-bridge) installed
63
+ - NotebookLM accessible in the current browser session
64
+
65
+ ## Notes
66
+
67
+ - Notebook-oriented commands assume you already have the target notebook open in Chrome, or that `opencli notebooklm use` can bind an existing notebook tab into `site:notebooklm`.
68
+ - `list`, `get`, `source-list`, `history`, `source-fulltext`, and `source-guide` prefer NotebookLM RPC paths and fall back only when the richer path is unavailable.
69
+ - `notes-get` currently reads note content only from the visible Studio note editor; if the note is listed but not open, open it in NotebookLM first and then retry.
@@ -7,15 +7,18 @@
7
7
  | Command | Description |
8
8
  |---------|-------------|
9
9
  | `opencli xiaohongshu search` | Search notes by keyword (returns title, author, likes, URL) |
10
- | `opencli xiaohongshu notifications` | |
11
- | `opencli xiaohongshu feed` | |
12
- | `opencli xiaohongshu user` | |
13
- | `opencli xiaohongshu download` | |
14
- | `opencli xiaohongshu creator-notes` | |
15
- | `opencli xiaohongshu creator-note-detail` | |
16
- | `opencli xiaohongshu creator-notes-summary` | |
17
- | `opencli xiaohongshu creator-profile` | |
18
- | `opencli xiaohongshu creator-stats` | |
10
+ | `opencli xiaohongshu note` | Read full note content (title, author, description, likes, collects, comments, tags) |
11
+ | `opencli xiaohongshu comments` | Read comments from a note (`--with-replies` for nested 楼中楼 replies) |
12
+ | `opencli xiaohongshu feed` | Home feed recommendations (via Pinia store interception) |
13
+ | `opencli xiaohongshu notifications` | User notifications (mentions, likes, connections) |
14
+ | `opencli xiaohongshu user` | Get public notes from a user profile |
15
+ | `opencli xiaohongshu download` | Download images and videos from a note |
16
+ | `opencli xiaohongshu publish` | Publish image-text notes (creator center UI automation) |
17
+ | `opencli xiaohongshu creator-notes` | Creator's note list with per-note metrics |
18
+ | `opencli xiaohongshu creator-note-detail` | Detailed analytics for a single creator note |
19
+ | `opencli xiaohongshu creator-notes-summary` | Combined note list + detail analytics summary |
20
+ | `opencli xiaohongshu creator-profile` | Creator account info (followers, growth level) |
21
+ | `opencli xiaohongshu creator-stats` | Creator data overview (views, likes, collects, trends) |
19
22
 
20
23
  ## Usage Examples
21
24
 
@@ -23,13 +26,19 @@
23
26
  # Search for notes
24
27
  opencli xiaohongshu search 美食 --limit 10
25
28
 
29
+ # Read a note's full content (pass URL from search results to preserve xsec_token)
30
+ opencli xiaohongshu note "https://www.xiaohongshu.com/search_result/<id>?xsec_token=..."
31
+
32
+ # Read comments with nested replies (楼中楼)
33
+ opencli xiaohongshu comments "https://www.xiaohongshu.com/search_result/<id>?xsec_token=..." --with-replies --limit 20
34
+
26
35
  # JSON output
27
36
  opencli xiaohongshu search 旅行 -f json
28
37
 
29
38
  # Other commands
30
39
  opencli xiaohongshu feed
31
40
  opencli xiaohongshu notifications
32
- opencli xiaohongshu download <url>
41
+ opencli xiaohongshu download <note-id or url>
33
42
  ```
34
43
 
35
44
  ## Prerequisites
@@ -6,79 +6,80 @@ Run `opencli list` for the live registry.
6
6
 
7
7
  | Site | Commands | Mode |
8
8
  |------|----------|------|
9
- | **[twitter](/adapters/browser/twitter)** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` `block` `unblock` `hide-reply` | 🔐 Browser |
10
- | **[reddit](/adapters/browser/reddit)** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 Browser |
11
- | **[tieba](/adapters/browser/tieba)** | `hot` `posts` `search` `read` | 🔐 Browser |
12
- | **[bilibili](/adapters/browser/bilibili)** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 🔐 Browser |
13
- | **[zhihu](/adapters/browser/zhihu)** | `hot` `search` `question` `download` | 🔐 Browser |
14
- | **[xiaohongshu](/adapters/browser/xiaohongshu)** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | 🔐 Browser |
15
- | **[xueqiu](/adapters/browser/xueqiu)** | `feed` `hot-stock` `hot` `search` `stock` `comments` `watchlist` `earnings-date` `fund-holdings` `fund-snapshot` | 🔐 Browser |
16
- | **[youtube](/adapters/browser/youtube)** | `search` `video` `transcript` | 🔐 Browser |
17
- | **[v2ex](/adapters/browser/v2ex)** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | 🌐 / 🔐 |
18
- | **[bloomberg](/adapters/browser/bloomberg)** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | 🌐 / 🔐 |
19
- | **[weibo](/adapters/browser/weibo)** | `hot` `search` | 🔐 Browser |
20
- | **[linkedin](/adapters/browser/linkedin)** | `search` `timeline` | 🔐 Browser |
21
- | **[coupang](/adapters/browser/coupang)** | `search` `add-to-cart` | 🔐 Browser |
22
- | **[boss](/adapters/browser/boss)** | `search` `detail` `recommend` `joblist` `greet` `batchgreet` `send` `chatlist` `chatmsg` `invite` `mark` `exchange` `resume` `stats` | 🔐 Browser |
23
- | **[ctrip](/adapters/browser/ctrip)** | `search` | 🔐 Browser |
24
- | **[reuters](/adapters/browser/reuters)** | `search` | 🔐 Browser |
25
- | **[smzdm](/adapters/browser/smzdm)** | `search` | 🔐 Browser |
26
- | **[jike](/adapters/browser/jike)** | `feed` `search` `post` `topic` `user` `create` `comment` `like` `repost` `notifications` | 🔐 Browser |
27
- | **[jimeng](/adapters/browser/jimeng)** | `generate` `history` | 🔐 Browser |
28
- | **[yollomi](/adapters/browser/yollomi)** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | 🔐 Browser |
29
- | **[linux-do](/adapters/browser/linux-do)** | `feed` `categories` `tags` `search` `topic` `user-topics` `user-posts` | 🔐 Browser |
30
- | **[chaoxing](/adapters/browser/chaoxing)** | `assignments` `exams` | 🔐 Browser |
31
- | **[grok](/adapters/browser/grok)** | `ask` | 🔐 Browser |
32
- | **[doubao](/adapters/browser/doubao)** | `status` `new` `send` `read` `ask` `history` `detail` `meeting-summary` `meeting-transcript` | 🔐 Browser |
33
- | **[weread](/adapters/browser/weread)** | `shelf` `search` `book` `ranking` `notebooks` `highlights` `notes` | 🔐 Browser |
34
- | **[douban](/adapters/browser/douban)** | `search` `top250` `subject` `photos` `download` `marks` `reviews` `movie-hot` `book-hot` | 🔐 Browser |
35
- | **[facebook](/adapters/browser/facebook)** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | 🔐 Browser |
36
- | **[imdb](/adapters/browser/imdb)** | `search` `title` `top` `trending` `person` `reviews` | 🌐 / 🔐 |
37
- | **[instagram](/adapters/browser/instagram)** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | 🔐 Browser |
38
- | **[medium](/adapters/browser/medium)** | `feed` `search` `user` | 🔐 Browser |
39
- | **[sinablog](/adapters/browser/sinablog)** | `hot` `search` `article` `user` | 🔐 Browser |
40
- | **[substack](/adapters/browser/substack)** | `feed` `search` `publication` | 🔐 Browser |
41
- | **[pixiv](/adapters/browser/pixiv)** | `ranking` `search` `user` `illusts` `detail` `download` | 🔐 Browser |
42
- | **[tiktok](/adapters/browser/tiktok)** | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` | 🔐 Browser |
43
- | **[google](/adapters/browser/google)** | `news` `search` `suggest` `trends` | 🌐 / 🔐 |
44
- | **[jd](/adapters/browser/jd)** | `item` | 🔐 Browser |
45
- | **[web](/adapters/browser/web)** | `read` | 🔐 Browser |
46
- | **[weixin](/adapters/browser/weixin)** | `download` | 🔐 Browser |
47
- | **[36kr](/adapters/browser/36kr)** | `news` `hot` `search` `article` | 🌐 / 🔐 |
48
- | **[producthunt](/adapters/browser/producthunt)** | `posts` `today` `hot` `browse` | 🌐 / 🔐 |
49
- | **[ones](/adapters/browser/ones)** | `login` `me` `token-info` `tasks` `my-tasks` `task` `worklog` `logout` | 🔐 Browser Bridge + `ONES_BASE_URL` |
9
+ | **[twitter](./browser/twitter)** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` `block` `unblock` `hide-reply` | 🔐 Browser |
10
+ | **[reddit](./browser/reddit)** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 Browser |
11
+ | **[tieba](./browser/tieba)** | `hot` `posts` `search` `read` | 🔐 Browser |
12
+ | **[bilibili](./browser/bilibili)** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 🔐 Browser |
13
+ | **[zhihu](./browser/zhihu)** | `hot` `search` `question` `download` | 🔐 Browser |
14
+ | **[xiaohongshu](./browser/xiaohongshu)** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | 🔐 Browser |
15
+ | **[xueqiu](./browser/xueqiu)** | `feed` `hot-stock` `hot` `search` `stock` `comments` `watchlist` `earnings-date` `fund-holdings` `fund-snapshot` | 🔐 Browser |
16
+ | **[youtube](./browser/youtube)** | `search` `video` `transcript` | 🔐 Browser |
17
+ | **[v2ex](./browser/v2ex)** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | 🌐 / 🔐 |
18
+ | **[bloomberg](./browser/bloomberg)** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | 🌐 / 🔐 |
19
+ | **[weibo](./browser/weibo)** | `hot` `search` | 🔐 Browser |
20
+ | **[linkedin](./browser/linkedin)** | `search` `timeline` | 🔐 Browser |
21
+ | **[coupang](./browser/coupang)** | `search` `add-to-cart` | 🔐 Browser |
22
+ | **[boss](./browser/boss)** | `search` `detail` `recommend` `joblist` `greet` `batchgreet` `send` `chatlist` `chatmsg` `invite` `mark` `exchange` `resume` `stats` | 🔐 Browser |
23
+ | **[ctrip](./browser/ctrip)** | `search` | 🔐 Browser |
24
+ | **[reuters](./browser/reuters)** | `search` | 🔐 Browser |
25
+ | **[smzdm](./browser/smzdm)** | `search` | 🔐 Browser |
26
+ | **[jike](./browser/jike)** | `feed` `search` `post` `topic` `user` `create` `comment` `like` `repost` `notifications` | 🔐 Browser |
27
+ | **[jimeng](./browser/jimeng)** | `generate` `history` | 🔐 Browser |
28
+ | **[yollomi](./browser/yollomi)** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | 🔐 Browser |
29
+ | **[linux-do](./browser/linux-do)** | `feed` `categories` `tags` `search` `topic` `user-topics` `user-posts` | 🔐 Browser |
30
+ | **[chaoxing](./browser/chaoxing)** | `assignments` `exams` | 🔐 Browser |
31
+ | **[grok](./browser/grok)** | `ask` | 🔐 Browser |
32
+ | **[notebooklm](./browser/notebooklm)** | `status` `list` `current` `get` `metadata` `bind-current` `use` `source-list` `source-get` `source-fulltext` `source-guide` `history` `note-list` `notes-list` `notes-get` `summary` | 🔐 Browser |
33
+ | **[doubao](./browser/doubao)** | `status` `new` `send` `read` `ask` `history` `detail` `meeting-summary` `meeting-transcript` | 🔐 Browser |
34
+ | **[weread](./browser/weread)** | `shelf` `search` `book` `ranking` `notebooks` `highlights` `notes` | 🔐 Browser |
35
+ | **[douban](./browser/douban)** | `search` `top250` `subject` `photos` `download` `marks` `reviews` `movie-hot` `book-hot` | 🔐 Browser |
36
+ | **[facebook](./browser/facebook)** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | 🔐 Browser |
37
+ | **[imdb](./browser/imdb)** | `search` `title` `top` `trending` `person` `reviews` | 🌐 / 🔐 |
38
+ | **[instagram](./browser/instagram)** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | 🔐 Browser |
39
+ | **[medium](./browser/medium)** | `feed` `search` `user` | 🔐 Browser |
40
+ | **[sinablog](./browser/sinablog)** | `hot` `search` `article` `user` | 🔐 Browser |
41
+ | **[substack](./browser/substack)** | `feed` `search` `publication` | 🔐 Browser |
42
+ | **[pixiv](./browser/pixiv)** | `ranking` `search` `user` `illusts` `detail` `download` | 🔐 Browser |
43
+ | **[tiktok](./browser/tiktok)** | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` | 🔐 Browser |
44
+ | **[google](./browser/google)** | `news` `search` `suggest` `trends` | 🌐 / 🔐 |
45
+ | **[jd](./browser/jd)** | `item` | 🔐 Browser |
46
+ | **[web](./browser/web)** | `read` | 🔐 Browser |
47
+ | **[weixin](./browser/weixin)** | `download` | 🔐 Browser |
48
+ | **[36kr](./browser/36kr)** | `news` `hot` `search` `article` | 🌐 / 🔐 |
49
+ | **[producthunt](./browser/producthunt)** | `posts` `today` `hot` `browse` | 🌐 / 🔐 |
50
+ | **[ones](./browser/ones)** | `login` `me` `token-info` `tasks` `my-tasks` `task` `worklog` `logout` | 🔐 Browser Bridge + `ONES_BASE_URL` |
50
51
 
51
52
  ## Public API Adapters
52
53
 
53
54
  | Site | Commands | Mode |
54
55
  |------|----------|------|
55
- | **[hackernews](/adapters/browser/hackernews)** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 🌐 Public |
56
- | **[bbc](/adapters/browser/bbc)** | `news` | 🌐 Public |
57
- | **[devto](/adapters/browser/devto)** | `top` `tag` `user` | 🌐 Public |
58
- | **[dictionary](/adapters/browser/dictionary)** | `search` `synonyms` `examples` | 🌐 Public |
59
- | **[apple-podcasts](/adapters/browser/apple-podcasts)** | `search` `episodes` `top` | 🌐 Public |
60
- | **[xiaoyuzhou](/adapters/browser/xiaoyuzhou)** | `podcast` `podcast-episodes` `episode` | 🌐 Public |
61
- | **[yahoo-finance](/adapters/browser/yahoo-finance)** | `quote` | 🌐 Public |
62
- | **[arxiv](/adapters/browser/arxiv)** | `search` `paper` | 🌐 Public |
63
- | **[paperreview](/adapters/browser/paperreview)** | `submit` `review` `feedback` | 🌐 Public |
64
- | **[barchart](/adapters/browser/barchart)** | `quote` `options` `greeks` `flow` | 🌐 Public |
65
- | **[hf](/adapters/browser/hf)** | `top` | 🌐 Public |
66
- | **[sinafinance](/adapters/browser/sinafinance)** | `news` | 🌐 Public |
67
- | **[spotify](/adapters/browser/spotify)** | `auth` `status` `play` `pause` `next` `prev` `volume` `search` `queue` `shuffle` `repeat` | 🔑 OAuth API |
68
- | **[stackoverflow](/adapters/browser/stackoverflow)** | `hot` `search` `bounties` `unanswered` | 🌐 Public |
69
- | **[wikipedia](/adapters/browser/wikipedia)** | `search` `summary` `random` `trending` | 🌐 Public |
70
- | **[lobsters](/adapters/browser/lobsters)** | `hot` `newest` `active` `tag` | 🌐 Public |
71
- | **[steam](/adapters/browser/steam)** | `top-sellers` | 🌐 Public |
56
+ | **[hackernews](./browser/hackernews)** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 🌐 Public |
57
+ | **[bbc](./browser/bbc)** | `news` | 🌐 Public |
58
+ | **[devto](./browser/devto)** | `top` `tag` `user` | 🌐 Public |
59
+ | **[dictionary](./browser/dictionary)** | `search` `synonyms` `examples` | 🌐 Public |
60
+ | **[apple-podcasts](./browser/apple-podcasts)** | `search` `episodes` `top` | 🌐 Public |
61
+ | **[xiaoyuzhou](./browser/xiaoyuzhou)** | `podcast` `podcast-episodes` `episode` | 🌐 Public |
62
+ | **[yahoo-finance](./browser/yahoo-finance)** | `quote` | 🌐 Public |
63
+ | **[arxiv](./browser/arxiv)** | `search` `paper` | 🌐 Public |
64
+ | **[paperreview](./browser/paperreview)** | `submit` `review` `feedback` | 🌐 Public |
65
+ | **[barchart](./browser/barchart)** | `quote` `options` `greeks` `flow` | 🌐 Public |
66
+ | **[hf](./browser/hf)** | `top` | 🌐 Public |
67
+ | **[sinafinance](./browser/sinafinance)** | `news` | 🌐 Public |
68
+ | **[spotify](./browser/spotify)** | `auth` `status` `play` `pause` `next` `prev` `volume` `search` `queue` `shuffle` `repeat` | 🔑 OAuth API |
69
+ | **[stackoverflow](./browser/stackoverflow)** | `hot` `search` `bounties` `unanswered` | 🌐 Public |
70
+ | **[wikipedia](./browser/wikipedia)** | `search` `summary` `random` `trending` | 🌐 Public |
71
+ | **[lobsters](./browser/lobsters)** | `hot` `newest` `active` `tag` | 🌐 Public |
72
+ | **[steam](./browser/steam)** | `top-sellers` | 🌐 Public |
72
73
 
73
74
  ## Desktop Adapters
74
75
 
75
76
  | App | Description | Commands |
76
77
  |-----|-------------|----------|
77
- | **[Cursor](/adapters/desktop/cursor)** | Control Cursor IDE | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` |
78
- | **[Codex](/adapters/desktop/codex)** | Drive OpenAI Codex CLI agent | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` |
79
- | **[Antigravity](/adapters/desktop/antigravity)** | Control Antigravity Ultra | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` |
80
- | **[ChatGPT](/adapters/desktop/chatgpt)** | Automate ChatGPT macOS app | `status` `new` `send` `read` `ask` |
81
- | **[ChatWise](/adapters/desktop/chatwise)** | Multi-LLM client | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` |
82
- | **[Notion](/adapters/desktop/notion)** | Search, read, write pages | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` |
83
- | **[Discord](/adapters/desktop/discord)** | Desktop messages & channels | `status` `send` `read` `channels` `servers` `search` `members` |
84
- | **[Doubao App](/adapters/desktop/doubao-app)** | Doubao AI desktop app via CDP | `status` `new` `send` `read` `ask` `screenshot` `dump` |
78
+ | **[Cursor](./desktop/cursor)** | Control Cursor IDE | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` |
79
+ | **[Codex](./desktop/codex)** | Drive OpenAI Codex CLI agent | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` |
80
+ | **[Antigravity](./desktop/antigravity)** | Control Antigravity Ultra | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` |
81
+ | **[ChatGPT](./desktop/chatgpt)** | Automate ChatGPT macOS app | `status` `new` `send` `read` `ask` |
82
+ | **[ChatWise](./desktop/chatwise)** | Multi-LLM client | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` |
83
+ | **[Notion](./desktop/notion)** | Search, read, write pages | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` |
84
+ | **[Discord](./desktop/discord)** | Desktop messages & channels | `status` `send` `read` `channels` `servers` `search` `members` |
85
+ | **[Doubao App](./desktop/doubao-app)** | Doubao AI desktop app via CDP | `status` `new` `send` `read` `ask` `screenshot` `dump` |
@@ -35,3 +35,15 @@ opencli doctor # Check extension + daemon connectivity
35
35
  ```
36
36
 
37
37
  The daemon manages the WebSocket connection between your CLI commands and the Chrome extension. The extension executes JavaScript in the context of web pages, with access to the logged-in session.
38
+
39
+ ## Daemon Lifecycle
40
+
41
+ The daemon auto-starts on first browser command and stays alive for **4 hours** by default. It exits only when both conditions are met: no CLI requests for the timeout period AND no Chrome extension connected.
42
+
43
+ ```bash
44
+ opencli daemon status # Check daemon state (PID, uptime, extension, memory)
45
+ opencli daemon stop # Graceful shutdown
46
+ opencli daemon restart # Stop + restart
47
+ ```
48
+
49
+ Override the timeout via the `OPENCLI_DAEMON_TIMEOUT` environment variable (milliseconds). Set to `0` to keep the daemon alive indefinitely.