@zenalexa/unicli 0.220.1 → 0.221.0

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 (471) hide show
  1. package/AGENTS.md +28 -6
  2. package/README.md +8 -8
  3. package/README.zh-CN.md +8 -8
  4. package/dist/adapters/bilibili/comments.js +66 -4
  5. package/dist/adapters/bilibili/comments.js.map +1 -1
  6. package/dist/adapters/bilibili/compat.js +2 -2
  7. package/dist/adapters/bilibili/compat.js.map +1 -1
  8. package/dist/adapters/bilibili/download.js +4 -4
  9. package/dist/adapters/bilibili/download.js.map +1 -1
  10. package/dist/adapters/bilibili/wbi.d.ts.map +1 -1
  11. package/dist/adapters/bilibili/wbi.js +3 -3
  12. package/dist/adapters/bilibili/wbi.js.map +1 -1
  13. package/dist/adapters/cipo/_shared.d.ts +21 -0
  14. package/dist/adapters/cipo/_shared.d.ts.map +1 -0
  15. package/dist/adapters/cipo/_shared.js +67 -0
  16. package/dist/adapters/cipo/_shared.js.map +1 -0
  17. package/dist/adapters/cipo/get.d.ts +19 -0
  18. package/dist/adapters/cipo/get.d.ts.map +1 -0
  19. package/dist/adapters/cipo/get.js +140 -0
  20. package/dist/adapters/cipo/get.js.map +1 -0
  21. package/dist/adapters/cipo/legal-status.d.ts +19 -0
  22. package/dist/adapters/cipo/legal-status.d.ts.map +1 -0
  23. package/dist/adapters/cipo/legal-status.js +111 -0
  24. package/dist/adapters/cipo/legal-status.js.map +1 -0
  25. package/dist/adapters/cipo/search.d.ts +20 -0
  26. package/dist/adapters/cipo/search.d.ts.map +1 -0
  27. package/dist/adapters/cipo/search.js +148 -0
  28. package/dist/adapters/cipo/search.js.map +1 -0
  29. package/dist/adapters/cnipa/_shared.d.ts +47 -0
  30. package/dist/adapters/cnipa/_shared.d.ts.map +1 -0
  31. package/dist/adapters/cnipa/_shared.js +97 -0
  32. package/dist/adapters/cnipa/_shared.js.map +1 -0
  33. package/dist/adapters/cnipa/get.d.ts +19 -0
  34. package/dist/adapters/cnipa/get.d.ts.map +1 -0
  35. package/dist/adapters/cnipa/get.js +149 -0
  36. package/dist/adapters/cnipa/get.js.map +1 -0
  37. package/dist/adapters/cnipa/legal-status.d.ts +19 -0
  38. package/dist/adapters/cnipa/legal-status.d.ts.map +1 -0
  39. package/dist/adapters/cnipa/legal-status.js +119 -0
  40. package/dist/adapters/cnipa/legal-status.js.map +1 -0
  41. package/dist/adapters/cnipa/search.d.ts +21 -0
  42. package/dist/adapters/cnipa/search.d.ts.map +1 -0
  43. package/dist/adapters/cnipa/search.js +170 -0
  44. package/dist/adapters/cnipa/search.js.map +1 -0
  45. package/dist/adapters/espacenet/_shared.d.ts +21 -0
  46. package/dist/adapters/espacenet/_shared.d.ts.map +1 -0
  47. package/dist/adapters/espacenet/_shared.js +67 -0
  48. package/dist/adapters/espacenet/_shared.js.map +1 -0
  49. package/dist/adapters/espacenet/family.d.ts +19 -0
  50. package/dist/adapters/espacenet/family.d.ts.map +1 -0
  51. package/dist/adapters/espacenet/family.js +118 -0
  52. package/dist/adapters/espacenet/family.js.map +1 -0
  53. package/dist/adapters/espacenet/get.d.ts +19 -0
  54. package/dist/adapters/espacenet/get.d.ts.map +1 -0
  55. package/dist/adapters/espacenet/get.js +130 -0
  56. package/dist/adapters/espacenet/get.js.map +1 -0
  57. package/dist/adapters/espacenet/legal-status.d.ts +19 -0
  58. package/dist/adapters/espacenet/legal-status.d.ts.map +1 -0
  59. package/dist/adapters/espacenet/legal-status.js +110 -0
  60. package/dist/adapters/espacenet/legal-status.js.map +1 -0
  61. package/dist/adapters/espacenet/search.d.ts +20 -0
  62. package/dist/adapters/espacenet/search.d.ts.map +1 -0
  63. package/dist/adapters/espacenet/search.js +165 -0
  64. package/dist/adapters/espacenet/search.js.map +1 -0
  65. package/dist/adapters/facebook/subtitles.d.ts +9 -0
  66. package/dist/adapters/facebook/subtitles.d.ts.map +1 -0
  67. package/dist/adapters/facebook/subtitles.js +42 -0
  68. package/dist/adapters/facebook/subtitles.js.map +1 -0
  69. package/dist/adapters/fips/_shared.d.ts +21 -0
  70. package/dist/adapters/fips/_shared.d.ts.map +1 -0
  71. package/dist/adapters/fips/_shared.js +77 -0
  72. package/dist/adapters/fips/_shared.js.map +1 -0
  73. package/dist/adapters/fips/get.d.ts +19 -0
  74. package/dist/adapters/fips/get.d.ts.map +1 -0
  75. package/dist/adapters/fips/get.js +139 -0
  76. package/dist/adapters/fips/get.js.map +1 -0
  77. package/dist/adapters/fips/search.d.ts +20 -0
  78. package/dist/adapters/fips/search.d.ts.map +1 -0
  79. package/dist/adapters/fips/search.js +148 -0
  80. package/dist/adapters/fips/search.js.map +1 -0
  81. package/dist/adapters/freepatentsonline-web/_shared.d.ts +72 -0
  82. package/dist/adapters/freepatentsonline-web/_shared.d.ts.map +1 -0
  83. package/dist/adapters/freepatentsonline-web/_shared.js +216 -0
  84. package/dist/adapters/freepatentsonline-web/_shared.js.map +1 -0
  85. package/dist/adapters/freepatentsonline-web/get.d.ts +21 -0
  86. package/dist/adapters/freepatentsonline-web/get.d.ts.map +1 -0
  87. package/dist/adapters/freepatentsonline-web/get.js +127 -0
  88. package/dist/adapters/freepatentsonline-web/get.js.map +1 -0
  89. package/dist/adapters/freepatentsonline-web/search.d.ts +22 -0
  90. package/dist/adapters/freepatentsonline-web/search.d.ts.map +1 -0
  91. package/dist/adapters/freepatentsonline-web/search.js +149 -0
  92. package/dist/adapters/freepatentsonline-web/search.js.map +1 -0
  93. package/dist/adapters/google-patents-web/_shared.d.ts +110 -0
  94. package/dist/adapters/google-patents-web/_shared.d.ts.map +1 -0
  95. package/dist/adapters/google-patents-web/_shared.js +164 -0
  96. package/dist/adapters/google-patents-web/_shared.js.map +1 -0
  97. package/dist/adapters/google-patents-web/get.d.ts +36 -0
  98. package/dist/adapters/google-patents-web/get.d.ts.map +1 -0
  99. package/dist/adapters/google-patents-web/get.js +187 -0
  100. package/dist/adapters/google-patents-web/get.js.map +1 -0
  101. package/dist/adapters/google-patents-web/search.d.ts +23 -0
  102. package/dist/adapters/google-patents-web/search.d.ts.map +1 -0
  103. package/dist/adapters/google-patents-web/search.js +169 -0
  104. package/dist/adapters/google-patents-web/search.js.map +1 -0
  105. package/dist/adapters/inpi-br/_shared.d.ts +21 -0
  106. package/dist/adapters/inpi-br/_shared.d.ts.map +1 -0
  107. package/dist/adapters/inpi-br/_shared.js +67 -0
  108. package/dist/adapters/inpi-br/_shared.js.map +1 -0
  109. package/dist/adapters/inpi-br/get.d.ts +19 -0
  110. package/dist/adapters/inpi-br/get.d.ts.map +1 -0
  111. package/dist/adapters/inpi-br/get.js +142 -0
  112. package/dist/adapters/inpi-br/get.js.map +1 -0
  113. package/dist/adapters/inpi-br/search.d.ts +20 -0
  114. package/dist/adapters/inpi-br/search.d.ts.map +1 -0
  115. package/dist/adapters/inpi-br/search.js +154 -0
  116. package/dist/adapters/inpi-br/search.js.map +1 -0
  117. package/dist/adapters/instagram/subtitles.d.ts +9 -0
  118. package/dist/adapters/instagram/subtitles.d.ts.map +1 -0
  119. package/dist/adapters/instagram/subtitles.js +42 -0
  120. package/dist/adapters/instagram/subtitles.js.map +1 -0
  121. package/dist/adapters/mastodon/statuses.d.ts +40 -0
  122. package/dist/adapters/mastodon/statuses.d.ts.map +1 -0
  123. package/dist/adapters/mastodon/statuses.js +153 -0
  124. package/dist/adapters/mastodon/statuses.js.map +1 -0
  125. package/dist/adapters/reddit/comments.d.ts +9 -0
  126. package/dist/adapters/reddit/comments.d.ts.map +1 -0
  127. package/dist/adapters/reddit/comments.js +124 -0
  128. package/dist/adapters/reddit/comments.js.map +1 -0
  129. package/dist/adapters/threads/post.d.ts +32 -0
  130. package/dist/adapters/threads/post.d.ts.map +1 -0
  131. package/dist/adapters/threads/post.js +287 -0
  132. package/dist/adapters/threads/post.js.map +1 -0
  133. package/dist/adapters/tiktok/subtitles.d.ts +9 -0
  134. package/dist/adapters/tiktok/subtitles.d.ts.map +1 -0
  135. package/dist/adapters/tiktok/subtitles.js +42 -0
  136. package/dist/adapters/tiktok/subtitles.js.map +1 -0
  137. package/dist/adapters/twitter/accept.js +2 -2
  138. package/dist/adapters/twitter/accept.js.map +1 -1
  139. package/dist/adapters/twitter/browser-fallback.d.ts +26 -0
  140. package/dist/adapters/twitter/browser-fallback.d.ts.map +1 -0
  141. package/dist/adapters/twitter/browser-fallback.js +93 -0
  142. package/dist/adapters/twitter/browser-fallback.js.map +1 -0
  143. package/dist/adapters/twitter/browser-state.d.ts +11 -0
  144. package/dist/adapters/twitter/browser-state.d.ts.map +1 -0
  145. package/dist/adapters/twitter/browser-state.js +46 -0
  146. package/dist/adapters/twitter/browser-state.js.map +1 -0
  147. package/dist/adapters/twitter/client.d.ts.map +1 -1
  148. package/dist/adapters/twitter/client.js +36 -13
  149. package/dist/adapters/twitter/client.js.map +1 -1
  150. package/dist/adapters/twitter/reply-dm.js +2 -2
  151. package/dist/adapters/twitter/reply-dm.js.map +1 -1
  152. package/dist/adapters/twitter/reply.js +1 -0
  153. package/dist/adapters/twitter/reply.js.map +1 -1
  154. package/dist/adapters/twitter/search.js +11 -18
  155. package/dist/adapters/twitter/search.js.map +1 -1
  156. package/dist/adapters/twitter/thread.d.ts +14 -0
  157. package/dist/adapters/twitter/thread.d.ts.map +1 -1
  158. package/dist/adapters/twitter/thread.js +28 -2
  159. package/dist/adapters/twitter/thread.js.map +1 -1
  160. package/dist/adapters/twitter/trending.js +13 -59
  161. package/dist/adapters/twitter/trending.js.map +1 -1
  162. package/dist/adapters/xiaohongshu/browser-state.d.ts +19 -0
  163. package/dist/adapters/xiaohongshu/browser-state.d.ts.map +1 -0
  164. package/dist/adapters/xiaohongshu/browser-state.js +67 -0
  165. package/dist/adapters/xiaohongshu/browser-state.js.map +1 -0
  166. package/dist/adapters/xiaohongshu/comments.js +28 -5
  167. package/dist/adapters/xiaohongshu/comments.js.map +1 -1
  168. package/dist/adapters/xiaohongshu/download.js +49 -11
  169. package/dist/adapters/xiaohongshu/download.js.map +1 -1
  170. package/dist/adapters/xiaohongshu/search.d.ts.map +1 -1
  171. package/dist/adapters/xiaohongshu/search.js +11 -5
  172. package/dist/adapters/xiaohongshu/search.js.map +1 -1
  173. package/dist/adapters/xiaohongshu/trending.d.ts +9 -0
  174. package/dist/adapters/xiaohongshu/trending.d.ts.map +1 -0
  175. package/dist/adapters/xiaohongshu/trending.js +94 -0
  176. package/dist/adapters/xiaohongshu/trending.js.map +1 -0
  177. package/dist/adapters/youtube/comments.d.ts +80 -0
  178. package/dist/adapters/youtube/comments.d.ts.map +1 -1
  179. package/dist/adapters/youtube/comments.js +108 -12
  180. package/dist/adapters/youtube/comments.js.map +1 -1
  181. package/dist/adapters/youtube/subtitles.d.ts +9 -0
  182. package/dist/adapters/youtube/subtitles.d.ts.map +1 -0
  183. package/dist/adapters/youtube/subtitles.js +42 -0
  184. package/dist/adapters/youtube/subtitles.js.map +1 -0
  185. package/dist/adapters/yt-dlp/subtitles.d.ts +9 -0
  186. package/dist/adapters/yt-dlp/subtitles.d.ts.map +1 -0
  187. package/dist/adapters/yt-dlp/subtitles.js +41 -0
  188. package/dist/adapters/yt-dlp/subtitles.js.map +1 -0
  189. package/dist/adapters/zhihu/answer-detail.d.ts +39 -0
  190. package/dist/adapters/zhihu/answer-detail.d.ts.map +1 -0
  191. package/dist/adapters/zhihu/answer-detail.js +204 -0
  192. package/dist/adapters/zhihu/answer-detail.js.map +1 -0
  193. package/dist/adapters/zhihu/comment.d.ts +9 -0
  194. package/dist/adapters/zhihu/comment.d.ts.map +1 -0
  195. package/dist/adapters/zhihu/comment.js +149 -0
  196. package/dist/adapters/zhihu/comment.js.map +1 -0
  197. package/dist/adapters/zhihu/recommend.d.ts +36 -0
  198. package/dist/adapters/zhihu/recommend.d.ts.map +1 -0
  199. package/dist/adapters/zhihu/recommend.js +151 -0
  200. package/dist/adapters/zhihu/recommend.js.map +1 -0
  201. package/dist/browser/bridge.d.ts.map +1 -1
  202. package/dist/browser/bridge.js +14 -3
  203. package/dist/browser/bridge.js.map +1 -1
  204. package/dist/browser/daemon-client.d.ts +6 -0
  205. package/dist/browser/daemon-client.d.ts.map +1 -1
  206. package/dist/browser/daemon-client.js +75 -15
  207. package/dist/browser/daemon-client.js.map +1 -1
  208. package/dist/browser/daemon.js +39 -15
  209. package/dist/browser/daemon.js.map +1 -1
  210. package/dist/browser/protocol.d.ts +1 -0
  211. package/dist/browser/protocol.d.ts.map +1 -1
  212. package/dist/browser/protocol.js +1 -0
  213. package/dist/browser/protocol.js.map +1 -1
  214. package/dist/cli.d.ts.map +1 -1
  215. package/dist/cli.js +6 -0
  216. package/dist/cli.js.map +1 -1
  217. package/dist/commands/approvals.d.ts.map +1 -1
  218. package/dist/commands/approvals.js +1 -37
  219. package/dist/commands/approvals.js.map +1 -1
  220. package/dist/commands/browser/index.d.ts.map +1 -1
  221. package/dist/commands/browser/index.js +7 -2
  222. package/dist/commands/browser/index.js.map +1 -1
  223. package/dist/commands/daemon.d.ts.map +1 -1
  224. package/dist/commands/daemon.js +7 -3
  225. package/dist/commands/daemon.js.map +1 -1
  226. package/dist/commands/dispatch.d.ts.map +1 -1
  227. package/dist/commands/dispatch.js +27 -3
  228. package/dist/commands/dispatch.js.map +1 -1
  229. package/dist/commands/patent-doctor.d.ts +48 -0
  230. package/dist/commands/patent-doctor.d.ts.map +1 -0
  231. package/dist/commands/patent-doctor.js +109 -0
  232. package/dist/commands/patent-doctor.js.map +1 -0
  233. package/dist/commands/patent.d.ts +78 -0
  234. package/dist/commands/patent.d.ts.map +1 -0
  235. package/dist/commands/patent.js +919 -0
  236. package/dist/commands/patent.js.map +1 -0
  237. package/dist/commands/social.d.ts +19 -0
  238. package/dist/commands/social.d.ts.map +1 -0
  239. package/dist/commands/social.js +236 -0
  240. package/dist/commands/social.js.map +1 -0
  241. package/dist/core/registry.d.ts +1 -1
  242. package/dist/core/registry.d.ts.map +1 -1
  243. package/dist/core/registry.js +11 -2
  244. package/dist/core/registry.js.map +1 -1
  245. package/dist/discovery/loader.d.ts.map +1 -1
  246. package/dist/discovery/loader.js +4 -0
  247. package/dist/discovery/loader.js.map +1 -1
  248. package/dist/engine/approval-presenter.d.ts +10 -0
  249. package/dist/engine/approval-presenter.d.ts.map +1 -0
  250. package/dist/engine/approval-presenter.js +45 -0
  251. package/dist/engine/approval-presenter.js.map +1 -0
  252. package/dist/engine/approval-store.d.ts +4 -0
  253. package/dist/engine/approval-store.d.ts.map +1 -1
  254. package/dist/engine/approval-store.js +85 -11
  255. package/dist/engine/approval-store.js.map +1 -1
  256. package/dist/engine/auth/oauth2-cc.d.ts +67 -0
  257. package/dist/engine/auth/oauth2-cc.d.ts.map +1 -0
  258. package/dist/engine/auth/oauth2-cc.js +120 -0
  259. package/dist/engine/auth/oauth2-cc.js.map +1 -0
  260. package/dist/engine/cookies.d.ts +10 -0
  261. package/dist/engine/cookies.d.ts.map +1 -1
  262. package/dist/engine/cookies.js +64 -0
  263. package/dist/engine/cookies.js.map +1 -1
  264. package/dist/engine/download.d.ts +5 -0
  265. package/dist/engine/download.d.ts.map +1 -1
  266. package/dist/engine/download.js +11 -4
  267. package/dist/engine/download.js.map +1 -1
  268. package/dist/engine/executor.d.ts +1 -0
  269. package/dist/engine/executor.d.ts.map +1 -1
  270. package/dist/engine/executor.js +25 -0
  271. package/dist/engine/executor.js.map +1 -1
  272. package/dist/engine/framework.d.ts +5 -5
  273. package/dist/engine/framework.js +5 -5
  274. package/dist/engine/harden.d.ts +1 -1
  275. package/dist/engine/harden.js +1 -1
  276. package/dist/engine/kernel/stages.d.ts.map +1 -1
  277. package/dist/engine/kernel/stages.js +2 -1
  278. package/dist/engine/kernel/stages.js.map +1 -1
  279. package/dist/engine/normalizer/patent-envelope.d.ts +61 -0
  280. package/dist/engine/normalizer/patent-envelope.d.ts.map +1 -0
  281. package/dist/engine/normalizer/patent-envelope.js +132 -0
  282. package/dist/engine/normalizer/patent-envelope.js.map +1 -0
  283. package/dist/engine/research.d.ts +5 -7
  284. package/dist/engine/research.d.ts.map +1 -1
  285. package/dist/engine/research.js +6 -9
  286. package/dist/engine/research.js.map +1 -1
  287. package/dist/engine/steps/browser-helpers.d.ts +2 -2
  288. package/dist/engine/steps/browser-helpers.d.ts.map +1 -1
  289. package/dist/engine/steps/browser-helpers.js +39 -16
  290. package/dist/engine/steps/browser-helpers.js.map +1 -1
  291. package/dist/engine/steps/download.d.ts +1 -0
  292. package/dist/engine/steps/download.d.ts.map +1 -1
  293. package/dist/engine/steps/download.js +3 -1
  294. package/dist/engine/steps/download.js.map +1 -1
  295. package/dist/engine/steps/index.d.ts +2 -0
  296. package/dist/engine/steps/index.d.ts.map +1 -1
  297. package/dist/engine/steps/index.js +2 -0
  298. package/dist/engine/steps/index.js.map +1 -1
  299. package/dist/engine/steps/oauth2-token.d.ts +41 -0
  300. package/dist/engine/steps/oauth2-token.d.ts.map +1 -0
  301. package/dist/engine/steps/oauth2-token.js +115 -0
  302. package/dist/engine/steps/oauth2-token.js.map +1 -0
  303. package/dist/engine/steps/select-xml.d.ts +34 -0
  304. package/dist/engine/steps/select-xml.d.ts.map +1 -0
  305. package/dist/engine/steps/select-xml.js +222 -0
  306. package/dist/engine/steps/select-xml.js.map +1 -0
  307. package/dist/engine/template.d.ts.map +1 -1
  308. package/dist/engine/template.js +7 -0
  309. package/dist/engine/template.js.map +1 -1
  310. package/dist/engine/transport/mcp-browser.d.ts +128 -0
  311. package/dist/engine/transport/mcp-browser.d.ts.map +1 -0
  312. package/dist/engine/transport/mcp-browser.js +120 -0
  313. package/dist/engine/transport/mcp-browser.js.map +1 -0
  314. package/dist/fast-path/handlers/approvals.d.ts +11 -0
  315. package/dist/fast-path/handlers/approvals.d.ts.map +1 -0
  316. package/dist/fast-path/handlers/approvals.js +136 -0
  317. package/dist/fast-path/handlers/approvals.js.map +1 -0
  318. package/dist/fast-path/manifest.d.ts +1 -0
  319. package/dist/fast-path/manifest.d.ts.map +1 -1
  320. package/dist/fast-path/manifest.js.map +1 -1
  321. package/dist/fast-path.d.ts.map +1 -1
  322. package/dist/fast-path.js +3 -0
  323. package/dist/fast-path.js.map +1 -1
  324. package/dist/index.d.ts +23 -0
  325. package/dist/index.d.ts.map +1 -0
  326. package/dist/index.js +38 -0
  327. package/dist/index.js.map +1 -0
  328. package/dist/manifest-compact.txt +3 -3
  329. package/dist/manifest-search.json +1 -1
  330. package/dist/manifest.json +2239 -176
  331. package/dist/output/auth-guidance.d.ts +14 -0
  332. package/dist/output/auth-guidance.d.ts.map +1 -0
  333. package/dist/output/auth-guidance.js +50 -0
  334. package/dist/output/auth-guidance.js.map +1 -0
  335. package/dist/output/error-map.d.ts +1 -1
  336. package/dist/output/error-map.d.ts.map +1 -1
  337. package/dist/output/error-map.js +28 -4
  338. package/dist/output/error-map.js.map +1 -1
  339. package/dist/output/next-actions.d.ts.map +1 -1
  340. package/dist/output/next-actions.js +19 -3
  341. package/dist/output/next-actions.js.map +1 -1
  342. package/dist/registry.d.ts +18 -1
  343. package/dist/registry.d.ts.map +1 -1
  344. package/dist/registry.js +5 -0
  345. package/dist/registry.js.map +1 -1
  346. package/dist/social/browser-errors.d.ts +13 -0
  347. package/dist/social/browser-errors.d.ts.map +1 -0
  348. package/dist/social/browser-errors.js +36 -0
  349. package/dist/social/browser-errors.js.map +1 -0
  350. package/dist/social/capabilities.d.ts +29 -0
  351. package/dist/social/capabilities.d.ts.map +1 -0
  352. package/dist/social/capabilities.js +448 -0
  353. package/dist/social/capabilities.js.map +1 -0
  354. package/dist/social/comments.d.ts +26 -0
  355. package/dist/social/comments.d.ts.map +1 -0
  356. package/dist/social/comments.js +97 -0
  357. package/dist/social/comments.js.map +1 -0
  358. package/dist/social/video-text.d.ts +27 -0
  359. package/dist/social/video-text.d.ts.map +1 -0
  360. package/dist/social/video-text.js +140 -0
  361. package/dist/social/video-text.js.map +1 -0
  362. package/dist/types/patent.d.ts +160 -0
  363. package/dist/types/patent.d.ts.map +1 -0
  364. package/dist/types/patent.js +16 -0
  365. package/dist/types/patent.js.map +1 -0
  366. package/dist/types.d.ts +12 -0
  367. package/dist/types.d.ts.map +1 -1
  368. package/dist/types.js.map +1 -1
  369. package/package.json +9 -4
  370. package/server.json +3 -3
  371. package/skills/unicli/SKILL.md +1 -1
  372. package/skills/unicli-claude-code/SKILL.md +1 -1
  373. package/skills/unicli-hermes/SKILL.md +1 -1
  374. package/src/adapters/bilibili/comments-tree.test.ts +41 -0
  375. package/src/adapters/bilibili/comments.ts +78 -4
  376. package/src/adapters/bilibili/compat.ts +5 -2
  377. package/src/adapters/bilibili/download.ts +7 -4
  378. package/src/adapters/bilibili/wbi.ts +6 -3
  379. package/src/adapters/brave/search.yaml +53 -0
  380. package/src/adapters/cipo/_shared.ts +98 -0
  381. package/src/adapters/cipo/get.ts +188 -0
  382. package/src/adapters/cipo/legal-status.ts +148 -0
  383. package/src/adapters/cipo/search.ts +195 -0
  384. package/src/adapters/cnipa/_shared.ts +138 -0
  385. package/src/adapters/cnipa/get.ts +199 -0
  386. package/src/adapters/cnipa/legal-status.ts +162 -0
  387. package/src/adapters/cnipa/search.ts +229 -0
  388. package/src/adapters/dpma/get.yaml +67 -0
  389. package/src/adapters/dpma/search.yaml +77 -0
  390. package/src/adapters/duckduckgo/search.yaml +54 -0
  391. package/src/adapters/duckduckgo/suggest.yaml +52 -0
  392. package/src/adapters/epo/family.yaml +69 -0
  393. package/src/adapters/epo/get.yaml +74 -0
  394. package/src/adapters/epo/legal-status.yaml +63 -0
  395. package/src/adapters/epo/search.yaml +84 -0
  396. package/src/adapters/espacenet/_shared.ts +98 -0
  397. package/src/adapters/espacenet/family.ts +161 -0
  398. package/src/adapters/espacenet/get.ts +185 -0
  399. package/src/adapters/espacenet/legal-status.ts +151 -0
  400. package/src/adapters/espacenet/search.ts +229 -0
  401. package/src/adapters/facebook/subtitles.ts +44 -0
  402. package/src/adapters/fips/_shared.ts +109 -0
  403. package/src/adapters/fips/get.ts +186 -0
  404. package/src/adapters/fips/search.ts +195 -0
  405. package/src/adapters/freepatentsonline-web/_shared.ts +273 -0
  406. package/src/adapters/freepatentsonline-web/get.ts +144 -0
  407. package/src/adapters/freepatentsonline-web/search.ts +170 -0
  408. package/src/adapters/google-patents-bq/prior-art.yaml +80 -0
  409. package/src/adapters/google-patents-bq/search.yaml +97 -0
  410. package/src/adapters/google-patents-web/_shared.ts +242 -0
  411. package/src/adapters/google-patents-web/get.ts +224 -0
  412. package/src/adapters/google-patents-web/search.ts +196 -0
  413. package/src/adapters/inpi-br/_shared.ts +98 -0
  414. package/src/adapters/inpi-br/get.ts +193 -0
  415. package/src/adapters/inpi-br/search.ts +206 -0
  416. package/src/adapters/inpi-fr/get.yaml +62 -0
  417. package/src/adapters/inpi-fr/search.yaml +74 -0
  418. package/src/adapters/instagram/subtitles.ts +44 -0
  419. package/src/adapters/ipaustralia/get.yaml +67 -0
  420. package/src/adapters/ipaustralia/search.yaml +74 -0
  421. package/src/adapters/jpo/get.yaml +63 -0
  422. package/src/adapters/jpo/search.yaml +76 -0
  423. package/src/adapters/kipris/get.yaml +69 -0
  424. package/src/adapters/kipris/legal-status.yaml +58 -0
  425. package/src/adapters/kipris/search.yaml +79 -0
  426. package/src/adapters/lens/get.yaml +64 -0
  427. package/src/adapters/lens/search.yaml +82 -0
  428. package/src/adapters/mastodon/statuses.test.ts +82 -0
  429. package/src/adapters/mastodon/statuses.ts +208 -0
  430. package/src/adapters/patsnap/get.yaml +65 -0
  431. package/src/adapters/patsnap/search.yaml +77 -0
  432. package/src/adapters/pqai/prior-art.yaml +59 -0
  433. package/src/adapters/pqai/search.yaml +60 -0
  434. package/src/adapters/reddit/comments-tree.test.ts +79 -0
  435. package/src/adapters/reddit/comments.ts +159 -0
  436. package/src/adapters/threads/post.test.ts +64 -0
  437. package/src/adapters/threads/post.ts +366 -0
  438. package/src/adapters/threads/user.yaml +73 -0
  439. package/src/adapters/tiktok/subtitles.ts +44 -0
  440. package/src/adapters/twitter/accept.ts +5 -2
  441. package/src/adapters/twitter/browser-fallback.ts +138 -0
  442. package/src/adapters/twitter/browser-state.ts +74 -0
  443. package/src/adapters/twitter/client.ts +51 -21
  444. package/src/adapters/twitter/reply-dm.ts +5 -2
  445. package/src/adapters/twitter/reply.ts +1 -0
  446. package/src/adapters/twitter/search.ts +12 -38
  447. package/src/adapters/twitter/thread.test.ts +43 -0
  448. package/src/adapters/twitter/thread.ts +44 -2
  449. package/src/adapters/twitter/trending.ts +14 -95
  450. package/src/adapters/ukipo/info.yaml +43 -0
  451. package/src/adapters/uspto/get.yaml +67 -0
  452. package/src/adapters/uspto/legal-status.yaml +58 -0
  453. package/src/adapters/uspto/search.yaml +88 -0
  454. package/src/adapters/wipo-patentscope/info.yaml +43 -0
  455. package/src/adapters/xiaohongshu/browser-state.ts +95 -0
  456. package/src/adapters/xiaohongshu/comments.ts +29 -6
  457. package/src/adapters/xiaohongshu/download.ts +60 -11
  458. package/src/adapters/xiaohongshu/search.ts +18 -6
  459. package/src/adapters/xiaohongshu/trending.ts +112 -0
  460. package/src/adapters/yahoo/search.yaml +52 -0
  461. package/src/adapters/youtube/comments-microformat.test.ts +35 -0
  462. package/src/adapters/youtube/comments-tree.test.ts +74 -0
  463. package/src/adapters/youtube/comments.ts +166 -12
  464. package/src/adapters/youtube/subtitles.ts +44 -0
  465. package/src/adapters/yt-dlp/subtitles.ts +43 -0
  466. package/src/adapters/zhihu/answer-detail.test.ts +83 -0
  467. package/src/adapters/zhihu/answer-detail.ts +275 -0
  468. package/src/adapters/zhihu/comment-tree.test.ts +57 -0
  469. package/src/adapters/zhihu/comment.ts +186 -0
  470. package/src/adapters/zhihu/recommend.test.ts +65 -0
  471. package/src/adapters/zhihu/recommend.ts +207 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @owner TikTok subtitle adapter.
3
+ * @does Extracts LLM-readable subtitle text from public TikTok video URLs.
4
+ * @needs `yt-dlp` on PATH and an accessible video subtitle track.
5
+ * @feeds Short-video search-to-transcript agent workflows.
6
+ * @breaks TikTok or yt-dlp extractor changes can alter subtitle availability.
7
+ */
8
+
9
+ import { cli, Strategy } from "../../registry.js";
10
+ import { runVideoSubtitleExtraction } from "../../social/video-text.js";
11
+
12
+ cli({
13
+ site: "tiktok",
14
+ name: "subtitles",
15
+ description:
16
+ "Extract LLM-readable subtitles from a TikTok video URL with optional browser-cookie reuse",
17
+ domain: "www.tiktok.com",
18
+ strategy: Strategy.PUBLIC,
19
+ args: [
20
+ {
21
+ name: "url",
22
+ type: "str",
23
+ required: true,
24
+ positional: true,
25
+ description: "TikTok video URL",
26
+ format: "uri",
27
+ },
28
+ {
29
+ name: "languages",
30
+ type: "str",
31
+ default: "zh-Hans,zh,en",
32
+ description: "Comma-separated subtitle languages",
33
+ },
34
+ {
35
+ name: "cookies-from-browser",
36
+ type: "str",
37
+ description: "Browser name for yt-dlp --cookies-from-browser",
38
+ },
39
+ ],
40
+ columns: ["language", "text", "path"],
41
+ socialCapabilities: ["read", "media", "subtitles"],
42
+ defaultFormat: "json",
43
+ func: async (_page, kwargs) => runVideoSubtitleExtraction(kwargs),
44
+ });
@@ -6,7 +6,10 @@
6
6
 
7
7
  import { cli } from "../../registry.js";
8
8
  import { Strategy } from "../../types.js";
9
- import { loadCookies, formatCookieHeader } from "../../engine/cookies.js";
9
+ import {
10
+ loadCookiesWithCDP,
11
+ formatCookieHeader,
12
+ } from "../../engine/cookies.js";
10
13
  import { USER_AGENT } from "../../constants.js";
11
14
 
12
15
  const BEARER_TOKEN =
@@ -30,7 +33,7 @@ cli({
30
33
  func: async (_page, kwargs) => {
31
34
  const userId = String(kwargs.user_id);
32
35
 
33
- const cookies = loadCookies("twitter");
36
+ const cookies = await loadCookiesWithCDP("twitter", "x.com");
34
37
  if (!cookies) {
35
38
  throw new Error(
36
39
  'No cookies found for "twitter". Run: unicli auth setup twitter',
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @owner Twitter browser fallback extraction.
3
+ * @does Reads X/Twitter search and trend results from the logged-in web UI.
4
+ * @needs A user-owned Chrome session reachable by Uni-CLI's browser layer.
5
+ * @feeds twitter.search and twitter.trending.
6
+ * @breaks X/Twitter DOM changes can reduce parseable rows.
7
+ */
8
+
9
+ import type { IPage } from "../../types.js";
10
+ import { socialEmptyError } from "../../social/browser-errors.js";
11
+ import { assertTwitterReadable, gotoTwitterPage } from "./browser-state.js";
12
+
13
+ export interface TwitterTweetRow {
14
+ id: string;
15
+ author: string;
16
+ text: string;
17
+ likes: string;
18
+ retweets: string;
19
+ views: string;
20
+ url: string;
21
+ }
22
+
23
+ export interface TwitterTrendRow {
24
+ name: string;
25
+ tweet_count: string;
26
+ description: string;
27
+ url: string;
28
+ }
29
+
30
+ export async function browserSearchTweets(
31
+ page: IPage,
32
+ query: string,
33
+ limit: number,
34
+ ): Promise<TwitterTweetRow[]> {
35
+ await gotoTwitterPage(
36
+ page,
37
+ `https://x.com/search?q=${encodeURIComponent(query)}&src=typed_query&f=live`,
38
+ "search",
39
+ );
40
+ await page.autoScroll({ maxScrolls: 2, delay: 1000 });
41
+ await assertTwitterReadable(page, "search");
42
+
43
+ const raw = await page.evaluate(`
44
+ (() => {
45
+ const clean = (value) => (value || '').replace(/\\s+/g, ' ').trim();
46
+ const rows = [];
47
+ const seen = new Set();
48
+ for (const article of document.querySelectorAll('article[data-testid="tweet"]')) {
49
+ const status = article.querySelector('a[href*="/status/"]');
50
+ const href = status?.getAttribute('href') || '';
51
+ const id = (href.match(/\\/status\\/(\\d+)/) || [])[1] || '';
52
+ if (!id || seen.has(id)) continue;
53
+ seen.add(id);
54
+ const text = Array.from(article.querySelectorAll('[data-testid="tweetText"]'))
55
+ .map((el) => clean(el.textContent || ''))
56
+ .filter(Boolean)
57
+ .join('\\n');
58
+ if (!text) continue;
59
+ const userName = clean(article.querySelector('[data-testid="User-Name"]')?.textContent || '');
60
+ const author = userName.split('@')[0] || userName;
61
+ const metric = (name) => clean(article.querySelector('[data-testid="' + name + '"]')?.textContent || '');
62
+ rows.push({
63
+ id,
64
+ author,
65
+ text,
66
+ likes: metric('like'),
67
+ retweets: metric('retweet'),
68
+ views: clean(article.querySelector('a[href$="/analytics"]')?.textContent || ''),
69
+ url: 'https://x.com' + href.split('?')[0],
70
+ });
71
+ }
72
+ return rows;
73
+ })()
74
+ `);
75
+
76
+ const rows = Array.isArray(raw)
77
+ ? (raw as TwitterTweetRow[]).slice(0, limit)
78
+ : [];
79
+ if (rows.length > 0) return rows;
80
+ throw socialEmptyError(
81
+ "twitter",
82
+ "search",
83
+ `Twitter/X search loaded no parseable tweets for "${query}".`,
84
+ );
85
+ }
86
+
87
+ export async function browserTrendingTopics(
88
+ page: IPage,
89
+ limit: number,
90
+ ): Promise<TwitterTrendRow[]> {
91
+ await gotoTwitterPage(
92
+ page,
93
+ "https://x.com/explore/tabs/trending",
94
+ "trending",
95
+ );
96
+
97
+ const raw = await page.evaluate(`
98
+ (() => {
99
+ const clean = (value) => (value || '').replace(/\\s+/g, ' ').trim();
100
+ const rows = [];
101
+ const seen = new Set();
102
+ for (const link of document.querySelectorAll('a[href*="/search?q="]')) {
103
+ const href = link.getAttribute('href') || '';
104
+ let name = '';
105
+ try {
106
+ name = new URL(href, 'https://x.com').searchParams.get('q') || '';
107
+ } catch {}
108
+ const lines = clean(link.textContent || '')
109
+ .split(/(?=Trending|[0-9,.]+\\s+posts)/)
110
+ .map(clean)
111
+ .filter(Boolean);
112
+ if (!name) {
113
+ name = lines.find((line) => !/^Trending/i.test(line) && !/posts$/i.test(line)) || '';
114
+ }
115
+ if (!name || seen.has(name)) continue;
116
+ seen.add(name);
117
+ const meta = lines.find((line) => /posts$/i.test(line)) || '';
118
+ rows.push({
119
+ name,
120
+ tweet_count: meta,
121
+ description: lines.find((line) => /^Trending/i.test(line)) || '',
122
+ url: 'https://x.com' + href,
123
+ });
124
+ }
125
+ return rows;
126
+ })()
127
+ `);
128
+
129
+ const rows = Array.isArray(raw)
130
+ ? (raw as TwitterTrendRow[]).slice(0, limit)
131
+ : [];
132
+ if (rows.length > 0) return rows;
133
+ throw socialEmptyError(
134
+ "twitter",
135
+ "trending",
136
+ "Twitter/X explore loaded no parseable trend rows.",
137
+ );
138
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @owner Twitter browser adapters.
3
+ * @does Detects X/Twitter login and challenge pages in browser fallback flows.
4
+ * @needs Browser-backed IPage from Uni-CLI runtime.
5
+ * @feeds twitter.search and twitter.trending.
6
+ * @breaks X/Twitter copy or route changes can require updating page-state detection.
7
+ */
8
+
9
+ import type { IPage } from "../../types.js";
10
+ import {
11
+ socialAuthError,
12
+ socialChallengeError,
13
+ } from "../../social/browser-errors.js";
14
+
15
+ interface TwitterPageState {
16
+ url: string;
17
+ title: string;
18
+ text: string;
19
+ }
20
+
21
+ async function readTwitterPageState(page: IPage): Promise<TwitterPageState> {
22
+ const raw = await page.evaluate(`
23
+ (() => ({
24
+ url: window.location.href,
25
+ title: document.title || '',
26
+ text: (document.body?.innerText || '').replace(/\\s+/g, ' ').slice(0, 2000)
27
+ }))()
28
+ `);
29
+ const state = raw as Partial<TwitterPageState>;
30
+ return {
31
+ url: String(state.url ?? ""),
32
+ title: String(state.title ?? ""),
33
+ text: String(state.text ?? ""),
34
+ };
35
+ }
36
+
37
+ export async function gotoTwitterPage(
38
+ page: IPage,
39
+ url: string,
40
+ command: string,
41
+ ): Promise<void> {
42
+ try {
43
+ await page.goto(url, { settleMs: 2500 });
44
+ } catch (err) {
45
+ const message = err instanceof Error ? err.message : String(err);
46
+ if (!/net::ERR_ABORTED/i.test(message)) throw err;
47
+ }
48
+ await page.wait(2);
49
+ await assertTwitterReadable(page, command);
50
+ }
51
+
52
+ export async function assertTwitterReadable(
53
+ page: IPage,
54
+ command: string,
55
+ ): Promise<void> {
56
+ const state = await readTwitterPageState(page);
57
+ const haystack = `${state.url}\n${state.title}\n${state.text}`;
58
+ if (
59
+ /captcha|cloudflare|challenge|verify you are human|unusual traffic/i.test(
60
+ haystack,
61
+ )
62
+ ) {
63
+ throw socialChallengeError(
64
+ "twitter",
65
+ command,
66
+ `Twitter/X is showing a challenge page: ${state.title || state.url}`,
67
+ );
68
+ }
69
+ if (
70
+ /\/i\/flow\/login|Sign in to X|Log in to X|登录 X|登录后/i.test(haystack)
71
+ ) {
72
+ throw socialAuthError("twitter", command);
73
+ }
74
+ }
@@ -5,7 +5,10 @@
5
5
  * Requires ct0 (CSRF token) and auth_token cookies in ~/.unicli/cookies/twitter.json
6
6
  */
7
7
 
8
- import { loadCookies, formatCookieHeader } from "../../engine/cookies.js";
8
+ import {
9
+ loadCookiesWithCDP,
10
+ formatCookieHeader,
11
+ } from "../../engine/cookies.js";
9
12
  import { USER_AGENT } from "../../constants.js";
10
13
 
11
14
  // Public bearer token — same for all Twitter web clients, not a secret
@@ -14,6 +17,45 @@ const BEARER_TOKEN =
14
17
 
15
18
  const GRAPHQL_BASE = "https://x.com/i/api/graphql";
16
19
 
20
+ function throwTwitterApiError(
21
+ label: string,
22
+ status: number,
23
+ preview: string,
24
+ ): never {
25
+ const err = new Error(
26
+ `Twitter API error: HTTP ${status} on ${label}\n${preview.slice(0, 200)}`,
27
+ ) as Error & {
28
+ code?: string;
29
+ suggestion?: string;
30
+ retryable?: boolean;
31
+ alternatives?: string[];
32
+ };
33
+ err.code =
34
+ status === 401 || status === 403
35
+ ? "auth_required"
36
+ : status === 404
37
+ ? "upstream_error"
38
+ : status === 429
39
+ ? "rate_limited"
40
+ : status >= 500
41
+ ? "upstream_error"
42
+ : "api_error";
43
+ err.retryable = status === 429 || status >= 500;
44
+ err.suggestion =
45
+ status === 404
46
+ ? "Twitter/X changed or removed this web API operation. Run `unicli repair twitter <command>` or use a browser-backed command while the GraphQL operation is refreshed."
47
+ : status === 401 || status === 403
48
+ ? "Refresh X login state with `unicli --auth-retry twitter <command> --args-file <path.json>`, or open https://x.com in Chrome and sign in."
49
+ : status === 429
50
+ ? "Twitter/X rate-limited the request. Wait, reduce limit, then retry."
51
+ : "Twitter/X API returned an upstream error. Retry once; if it persists, run `unicli repair twitter <command>`.";
52
+ err.alternatives = [
53
+ "unicli auth import twitter --domain x.com",
54
+ "unicli browser open https://x.com",
55
+ ];
56
+ throw err;
57
+ }
58
+
17
59
  /** Standard Twitter GraphQL feature flags */
18
60
  export const FEATURES: Record<string, boolean> = {
19
61
  rweb_tipjar_consumption_enabled: true,
@@ -56,7 +98,7 @@ export async function twitterFetch(
56
98
  variables: Record<string, unknown>,
57
99
  features: Record<string, boolean> = FEATURES,
58
100
  ): Promise<unknown> {
59
- const cookies = loadCookies("twitter");
101
+ const cookies = await loadCookiesWithCDP("twitter", "x.com");
60
102
  if (!cookies) {
61
103
  throw new Error(
62
104
  'No cookies found for "twitter". Run: unicli auth setup twitter',
@@ -92,10 +134,7 @@ export async function twitterFetch(
92
134
 
93
135
  if (!resp.ok) {
94
136
  const preview = await resp.text().catch(() => "");
95
- throw new Error(
96
- `Twitter API error: HTTP ${resp.status} on ${endpoint}\n` +
97
- `${preview.slice(0, 200)}`,
98
- );
137
+ throwTwitterApiError(endpoint, resp.status, preview);
99
138
  }
100
139
 
101
140
  return resp.json();
@@ -110,7 +149,7 @@ export async function twitterPostFetch(
110
149
  variables: Record<string, unknown>,
111
150
  features: Record<string, boolean> = FEATURES,
112
151
  ): Promise<unknown> {
113
- const cookies = loadCookies("twitter");
152
+ const cookies = await loadCookiesWithCDP("twitter", "x.com");
114
153
  if (!cookies) {
115
154
  throw new Error(
116
155
  'No cookies found for "twitter". Run: unicli auth setup twitter',
@@ -143,10 +182,7 @@ export async function twitterPostFetch(
143
182
 
144
183
  if (!resp.ok) {
145
184
  const preview = await resp.text().catch(() => "");
146
- throw new Error(
147
- `Twitter API error: HTTP ${resp.status} on ${endpoint}\n` +
148
- `${preview.slice(0, 200)}`,
149
- );
185
+ throwTwitterApiError(endpoint, resp.status, preview);
150
186
  }
151
187
 
152
188
  return resp.json();
@@ -163,7 +199,7 @@ export async function twitterRestFetch(
163
199
  path: string,
164
200
  params: Record<string, string> = {},
165
201
  ): Promise<unknown> {
166
- const cookies = loadCookies("twitter");
202
+ const cookies = await loadCookiesWithCDP("twitter", "x.com");
167
203
  if (!cookies) {
168
204
  throw new Error(
169
205
  'No cookies found for "twitter". Run: unicli auth setup twitter',
@@ -195,10 +231,7 @@ export async function twitterRestFetch(
195
231
 
196
232
  if (!resp.ok) {
197
233
  const preview = await resp.text().catch(() => "");
198
- throw new Error(
199
- `Twitter REST API error: HTTP ${resp.status} on ${path}\n` +
200
- `${preview.slice(0, 200)}`,
201
- );
234
+ throwTwitterApiError(path, resp.status, preview);
202
235
  }
203
236
 
204
237
  return resp.json();
@@ -215,7 +248,7 @@ export async function twitterGuideFetch(
215
248
  url: string,
216
249
  params: Record<string, string> = {},
217
250
  ): Promise<unknown> {
218
- const cookies = loadCookies("twitter");
251
+ const cookies = await loadCookiesWithCDP("twitter", "x.com");
219
252
  if (!cookies) {
220
253
  throw new Error(
221
254
  'No cookies found for "twitter". Run: unicli auth setup twitter',
@@ -247,10 +280,7 @@ export async function twitterGuideFetch(
247
280
 
248
281
  if (!resp.ok) {
249
282
  const preview = await resp.text().catch(() => "");
250
- throw new Error(
251
- `Twitter Guide API error: HTTP ${resp.status}\n` +
252
- `${preview.slice(0, 200)}`,
253
- );
283
+ throwTwitterApiError("guide", resp.status, preview);
254
284
  }
255
285
 
256
286
  return resp.json();
@@ -6,7 +6,10 @@
6
6
 
7
7
  import { cli } from "../../registry.js";
8
8
  import { Strategy } from "../../types.js";
9
- import { loadCookies, formatCookieHeader } from "../../engine/cookies.js";
9
+ import {
10
+ loadCookiesWithCDP,
11
+ formatCookieHeader,
12
+ } from "../../engine/cookies.js";
10
13
  import { USER_AGENT } from "../../constants.js";
11
14
 
12
15
  const BEARER_TOKEN =
@@ -36,7 +39,7 @@ cli({
36
39
  const conversationId = String(kwargs.conversation_id);
37
40
  const text = kwargs.text as string;
38
41
 
39
- const cookies = loadCookies("twitter");
42
+ const cookies = await loadCookiesWithCDP("twitter", "x.com");
40
43
  if (!cookies) {
41
44
  throw new Error(
42
45
  'No cookies found for "twitter". Run: unicli auth setup twitter',
@@ -15,6 +15,7 @@ cli({
15
15
  description: "Reply to a tweet",
16
16
  domain: "x.com",
17
17
  strategy: Strategy.COOKIE,
18
+ socialCapabilities: ["write_comment"],
18
19
  args: [
19
20
  {
20
21
  name: "tweet_id",
@@ -4,14 +4,8 @@
4
4
 
5
5
  import { cli } from "../../registry.js";
6
6
  import { Strategy } from "../../types.js";
7
- import {
8
- twitterFetch,
9
- FEATURES,
10
- extractTweetsFromInstructions,
11
- } from "./client.js";
12
-
13
- const QUERY_ID = "nK1dw4oV3k4w5TdtcAdSww";
14
- const ENDPOINT = "SearchTimeline";
7
+ import { browserSearchTweets } from "./browser-fallback.js";
8
+ import type { IPage } from "../../types.js";
15
9
 
16
10
  cli({
17
11
  site: "twitter",
@@ -19,6 +13,8 @@ cli({
19
13
  description: "Search tweets by keyword",
20
14
  domain: "x.com",
21
15
  strategy: Strategy.COOKIE,
16
+ browser: true,
17
+ browserSession: "user",
22
18
  args: [
23
19
  {
24
20
  name: "query",
@@ -26,39 +22,17 @@ cli({
26
22
  positional: true,
27
23
  description: "Search query",
28
24
  },
25
+ {
26
+ name: "limit",
27
+ type: "int",
28
+ default: 20,
29
+ description: "Number of tweets",
30
+ },
29
31
  ],
30
32
  columns: ["id", "author", "text", "likes", "retweets", "views", "url"],
31
- func: async (_page, kwargs) => {
33
+ func: async (page, kwargs) => {
32
34
  const query = kwargs.query as string;
33
35
  const count = Math.min((kwargs.limit as number) ?? 20, 50);
34
-
35
- const variables = {
36
- rawQuery: query,
37
- count,
38
- querySource: "typed_query",
39
- product: "Latest",
40
- };
41
-
42
- const data = (await twitterFetch(
43
- ENDPOINT,
44
- QUERY_ID,
45
- variables,
46
- FEATURES,
47
- )) as Record<string, unknown>;
48
-
49
- // Navigate: data.search_by_raw_query.search_timeline.timeline.instructions
50
- const searchByRawQuery = data.data as Record<string, unknown> | undefined;
51
- const searchResult = searchByRawQuery?.search_by_raw_query as
52
- | Record<string, unknown>
53
- | undefined;
54
- const searchTimeline = searchResult?.search_timeline as
55
- | Record<string, unknown>
56
- | undefined;
57
- const timeline = searchTimeline?.timeline as
58
- | Record<string, unknown>
59
- | undefined;
60
- const instructions = (timeline?.instructions as unknown[]) ?? [];
61
-
62
- return extractTweetsFromInstructions(instructions);
36
+ return browserSearchTweets(page as IPage, query, count);
63
37
  },
64
38
  });
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { normalizeTwitterThreadRows } from "./thread.js";
4
+
5
+ describe("normalizeTwitterThreadRows", () => {
6
+ it("adds normalized comment hierarchy fields to thread rows", () => {
7
+ const rows = normalizeTwitterThreadRows("root", [
8
+ {
9
+ id: "root",
10
+ author: "Root",
11
+ text: "Root tweet",
12
+ likes: 10,
13
+ retweets: 2,
14
+ views: "100",
15
+ url: "https://x.com/i/status/root",
16
+ },
17
+ {
18
+ id: "reply-1",
19
+ author: "Reply",
20
+ text: "Reply tweet",
21
+ likes: 1,
22
+ retweets: 0,
23
+ views: "5",
24
+ url: "https://x.com/i/status/reply-1",
25
+ },
26
+ ]);
27
+
28
+ expect(rows).toEqual([
29
+ expect.objectContaining({
30
+ id: "root",
31
+ parent_id: "",
32
+ depth: 0,
33
+ path: "0001",
34
+ }),
35
+ expect.objectContaining({
36
+ id: "reply-1",
37
+ parent_id: "root",
38
+ depth: 1,
39
+ path: "0001.0001",
40
+ }),
41
+ ]);
42
+ });
43
+ });
@@ -13,12 +13,40 @@ import {
13
13
  const QUERY_ID = "B9_KmbkLhXt6jRwGjJrweg";
14
14
  const ENDPOINT = "TweetDetail";
15
15
 
16
+ interface TweetRow {
17
+ id: string;
18
+ author: string;
19
+ text: string;
20
+ likes: number;
21
+ retweets: number;
22
+ views: string;
23
+ url: string;
24
+ }
25
+
26
+ export function normalizeTwitterThreadRows(
27
+ tweetId: string,
28
+ tweets: TweetRow[],
29
+ ): Array<TweetRow & { parent_id: string; depth: number; path: string }> {
30
+ let replyRank = 0;
31
+ return tweets.map((tweet) => {
32
+ const isRoot = tweet.id === tweetId;
33
+ if (!isRoot) replyRank += 1;
34
+ return {
35
+ ...tweet,
36
+ parent_id: isRoot ? "" : tweetId,
37
+ depth: isRoot ? 0 : 1,
38
+ path: isRoot ? "0001" : `0001.${String(replyRank).padStart(4, "0")}`,
39
+ };
40
+ });
41
+ }
42
+
16
43
  cli({
17
44
  site: "twitter",
18
45
  name: "thread",
19
46
  description: "Get a tweet and its conversation thread",
20
47
  domain: "x.com",
21
48
  strategy: Strategy.COOKIE,
49
+ socialCapabilities: ["read", "comments", "comment_replies"],
22
50
  args: [
23
51
  {
24
52
  name: "tweet_id",
@@ -27,7 +55,18 @@ cli({
27
55
  description: "Tweet ID (numeric)",
28
56
  },
29
57
  ],
30
- columns: ["id", "author", "text", "likes", "retweets", "views", "url"],
58
+ columns: [
59
+ "id",
60
+ "parent_id",
61
+ "author",
62
+ "text",
63
+ "likes",
64
+ "retweets",
65
+ "views",
66
+ "url",
67
+ "depth",
68
+ "path",
69
+ ],
31
70
  func: async (_page, kwargs) => {
32
71
  const tweetId = String(kwargs.tweet_id);
33
72
 
@@ -56,6 +95,9 @@ cli({
56
95
  | undefined;
57
96
  const instructions = (conversation?.instructions as unknown[]) ?? [];
58
97
 
59
- return extractTweetsFromInstructions(instructions);
98
+ return normalizeTwitterThreadRows(
99
+ tweetId,
100
+ extractTweetsFromInstructions(instructions),
101
+ );
60
102
  },
61
103
  });