@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,919 @@
1
+ /**
2
+ * @owner src::commands::patent
3
+ * @does Top-level `unicli patent` meta-command — fans out search / get / family / citations / legal-status / prior-art / doctor across every registered patent adapter (discovered via the `patent.*` capability convention), normalises into PatentRecord, dedupes by family, and emits the standard agent envelope.
4
+ * @needs src/registry.ts, src/types/patent.ts, src/engine/normalizer/patent-envelope.ts, src/engine/kernel/execute.ts, src/output/formatter.ts
5
+ * @feeds src/cli.ts (registerPatentCommand wiring)
6
+ * @breaks emits PATENT_INVALID_NUMBER envelope on unknown publication-number prefix; PATENT_NOT_FOUND when every fan-out source returns empty; never falls back to a non-patent adapter (rule 02)
7
+ * @invariants --sources default = uspto,epo,jpo; --sources all = every registered adapter whose `capabilities[]` carries any `patent.*` tag; routing by publication-number prefix uses an explicit jurisdiction → adapter table — no fuzzy matching
8
+ * @side-effects spawns adapter pipelines via engine kernel; reads env for adapter-specific API keys; writes to stdout/stderr only
9
+ * @perf O(N · M) where N = sources, M = result rows per source — fan-out is sequential today; can move to parallel once the engine kernel proves reentrant under load
10
+ * @concurrency safe — Commander handlers run one at a time per process
11
+ * @test tests/unit/commands/patent.test.ts
12
+ * @stability experimental — wave-1 surface; field names locked, behaviour evolves with adapters
13
+ * @since 2026-05-18
14
+ *
15
+ * Capability convention introduced here:
16
+ *
17
+ * patent.search — adapter exposes a free-text + filter search
18
+ * patent.get — adapter retrieves one record by publication-number
19
+ * patent.family — adapter returns DOCDB / INPADOC family members
20
+ * patent.citations — adapter returns citing / cited records
21
+ * patent.legal-status — adapter resolves prosecution / grant status
22
+ * patent.fulltext — adapter delivers description + claims text
23
+ * patent.prior-art — adapter performs semantic prior-art retrieval
24
+ *
25
+ * Each patent-vertical YAML adapter must include the relevant tags in its
26
+ * `capabilities:` array so this meta-command discovers it without
27
+ * hard-coding a site list.
28
+ */
29
+ import { commandStrategy, getAllAdapters, resolveCommand, } from "../registry.js";
30
+ import { detectFormat, format } from "../output/formatter.js";
31
+ import { makeCtx } from "../output/envelope.js";
32
+ import { ExitCode, Strategy } from "../types.js";
33
+ import { buildInvocation, execute } from "../engine/kernel/execute.js";
34
+ import { resolveAdapterVerificationStatus, } from "./patent-doctor.js";
35
+ // ── Publication-number prefix table ─────────────────────────────────────
36
+ //
37
+ // ST.16 two-letter country codes → the canonical first-party adapter that
38
+ // resolves get/family for that jurisdiction. Espacenet (EPO) is the broker
39
+ // for `family` regardless of prefix, but for `get` we go to the home office
40
+ // when one is registered, then fall back to espacenet.
41
+ const JURISDICTION_ADAPTERS = {
42
+ US: "uspto",
43
+ EP: "epo",
44
+ WO: "epo", // PCT — EPO Espacenet brokers
45
+ JP: "jpo",
46
+ KR: "kipris",
47
+ CN: "cnipa",
48
+ DE: "dpma",
49
+ FR: "inpi-fr",
50
+ GB: "espacenet",
51
+ CA: "cipo",
52
+ AU: "ipaustralia",
53
+ BR: "inpi-br",
54
+ RU: "fips",
55
+ };
56
+ const FAMILY_BROKER = "epo";
57
+ const DEFAULT_SOURCES = ["uspto", "epo", "jpo"];
58
+ // Vertical capability tags (re-exported so adapters and tests can grep).
59
+ export const PATENT_CAPABILITIES = [
60
+ "patent.search",
61
+ "patent.get",
62
+ "patent.family",
63
+ "patent.citations",
64
+ "patent.legal-status",
65
+ "patent.fulltext",
66
+ "patent.prior-art",
67
+ ];
68
+ // ── Discovery helpers ───────────────────────────────────────────────────
69
+ function hasAnyPatentCapability(adapter) {
70
+ for (const command of Object.values(adapter.commands)) {
71
+ const caps = command.capabilities ?? [];
72
+ if (caps.some((cap) => cap.startsWith("patent.")))
73
+ return true;
74
+ }
75
+ return false;
76
+ }
77
+ /**
78
+ * Find every registered adapter whose command set carries any patent.* tag.
79
+ * Returns a stable alphabetical ordering so output is deterministic.
80
+ */
81
+ export function listPatentAdapters() {
82
+ return getAllAdapters()
83
+ .filter(hasAnyPatentCapability)
84
+ .sort((a, b) => a.name.localeCompare(b.name));
85
+ }
86
+ /**
87
+ * Filter the list of adapter names by a `--sources` string. `all` expands to
88
+ * every patent-capable adapter discovered in the registry.
89
+ */
90
+ export function resolveSources(sourcesArg, fallback = DEFAULT_SOURCES) {
91
+ if (!sourcesArg || sourcesArg.trim().length === 0) {
92
+ return [...fallback];
93
+ }
94
+ if (sourcesArg.trim() === "all") {
95
+ return listPatentAdapters().map((a) => a.name);
96
+ }
97
+ return sourcesArg
98
+ .split(",")
99
+ .map((s) => s.trim())
100
+ .filter((s) => s.length > 0);
101
+ }
102
+ /**
103
+ * Map a publication number to the adapter that should serve `get`/`family`
104
+ * for it. Returns `undefined` when the prefix is not recognised — callers
105
+ * surface PATENT_INVALID_NUMBER in that case.
106
+ */
107
+ export function routeByPublicationPrefix(publicationNumber) {
108
+ const match = /^([A-Z]{2})/.exec(publicationNumber.trim().toUpperCase());
109
+ if (!match)
110
+ return undefined;
111
+ return JURISDICTION_ADAPTERS[match[1]];
112
+ }
113
+ /**
114
+ * Find the first command on `adapter` that carries the requested patent.*
115
+ * capability tag. Adapters may name their command anything; the capability
116
+ * tag is what wires them up.
117
+ */
118
+ export function findCommandByCapability(adapter, capability) {
119
+ for (const [name, command] of Object.entries(adapter.commands)) {
120
+ const caps = command.capabilities ?? [];
121
+ if (caps.includes(capability))
122
+ return { name, command };
123
+ }
124
+ return undefined;
125
+ }
126
+ // ── Reciprocal-rank fusion ──────────────────────────────────────────────
127
+ /**
128
+ * Reciprocal-rank fusion across ranked result lists. The scoring follows
129
+ * Cormack/Clarke/Buettcher 2009 (`score = Σ 1 / (k + rank)` with k = 60).
130
+ * Records are keyed by `family_id` when present, falling back to
131
+ * canonical publication_number — that is the dedupe axis required by the
132
+ * patent vertical (different offices issue separate publication numbers
133
+ * for the same invention).
134
+ */
135
+ export function reciprocalRankFusion(rankedLists, options = {}) {
136
+ const k = options.k ?? 60;
137
+ const buckets = new Map();
138
+ let order = 0;
139
+ for (const list of rankedLists) {
140
+ for (let rank = 0; rank < list.length; rank++) {
141
+ const record = list[rank];
142
+ const key = record.family_id ?? record.publication_number;
143
+ const existing = buckets.get(key);
144
+ const increment = 1 / (k + rank + 1);
145
+ if (existing) {
146
+ existing.score += increment;
147
+ }
148
+ else {
149
+ buckets.set(key, { score: increment, record, firstSeen: order++ });
150
+ }
151
+ }
152
+ }
153
+ const fused = [...buckets.values()].sort((a, b) => b.score - a.score || a.firstSeen - b.firstSeen);
154
+ const top = options.topN ? fused.slice(0, options.topN) : fused;
155
+ return top.map((b) => b.record);
156
+ }
157
+ // ── Output formatting (search / get / prior-art) ───────────────────────
158
+ /**
159
+ * Column sets exposed to `--detailed`. The default set mirrors the wave-1
160
+ * surface (publication_number / title / publication_date / source_adapter);
161
+ * the detailed set adds inventors / assignees / classifications / dates
162
+ * and uses a per-record block in markdown rather than a 4-column table.
163
+ */
164
+ const DEFAULT_SEARCH_COLUMNS = [
165
+ "publication_number",
166
+ "title",
167
+ "publication_date",
168
+ "source_adapter",
169
+ ];
170
+ const DETAILED_SEARCH_COLUMNS = [
171
+ "publication_number",
172
+ "title",
173
+ "abstract",
174
+ "inventors",
175
+ "assignees",
176
+ "classifications",
177
+ "filing_date",
178
+ "publication_date",
179
+ "grant_date",
180
+ "priority_date",
181
+ "kind_code",
182
+ "legal_status",
183
+ "family_id",
184
+ "cited_by_count",
185
+ "cites_count",
186
+ "claims_count",
187
+ "relevance_score",
188
+ "source_adapter",
189
+ "source_url",
190
+ ];
191
+ /**
192
+ * Strip the raw field from records when `--include-raw` is not set.
193
+ * Preserves the input shape otherwise. Uses structuredClone to avoid
194
+ * mutating callers' arrays.
195
+ */
196
+ function stripRaw(records) {
197
+ return records.map((r) => {
198
+ if (r.raw === undefined)
199
+ return r;
200
+ const { raw: _raw, ...rest } = r;
201
+ void _raw;
202
+ return rest;
203
+ });
204
+ }
205
+ /**
206
+ * Serialise a single PatentRecord as one CSV row given a column list. The
207
+ * CSV serializer escapes commas, double-quotes, and newlines per RFC 4180.
208
+ * Array fields are JSON-serialized inside the cell so the cell remains a
209
+ * single column.
210
+ */
211
+ function csvCell(value) {
212
+ if (value === undefined || value === null)
213
+ return "";
214
+ const serialized = Array.isArray(value) || typeof value === "object"
215
+ ? JSON.stringify(value)
216
+ : String(value);
217
+ if (serialized.includes(",") ||
218
+ serialized.includes('"') ||
219
+ serialized.includes("\n") ||
220
+ serialized.includes("\r")) {
221
+ return `"${serialized.replace(/"/g, '""')}"`;
222
+ }
223
+ return serialized;
224
+ }
225
+ function renderCsv(records, columns) {
226
+ const header = columns.join(",");
227
+ const rows = records.map((r) => columns
228
+ .map((c) => csvCell(r[c]))
229
+ .join(","));
230
+ return [header, ...rows].join("\n");
231
+ }
232
+ function renderJsonl(records) {
233
+ return records.map((r) => JSON.stringify(r)).join("\n");
234
+ }
235
+ function renderDetailedMarkdown(records) {
236
+ // One block per record. Skips undefined fields so the output is dense.
237
+ // The block separator is a horizontal rule; that keeps copy-paste into
238
+ // an agent prompt deterministic.
239
+ const blocks = [];
240
+ for (const r of records) {
241
+ const lines = [];
242
+ lines.push(`### ${r.publication_number}`);
243
+ if (r.title)
244
+ lines.push(`**title**: ${r.title}`);
245
+ if (r.abstract)
246
+ lines.push(`**abstract**: ${r.abstract}`);
247
+ if (r.inventors && r.inventors.length > 0)
248
+ lines.push(`**inventors**: ${r.inventors.map((p) => p.name).join("; ")}`);
249
+ if (r.assignees && r.assignees.length > 0)
250
+ lines.push(`**assignees**: ${r.assignees.map((p) => p.name).join("; ")}`);
251
+ if (r.classifications && r.classifications.length > 0)
252
+ lines.push(`**classifications**: ${r.classifications
253
+ .map((c) => `${c.scheme.toUpperCase()}:${c.code}`)
254
+ .join(", ")}`);
255
+ if (r.filing_date)
256
+ lines.push(`**filing_date**: ${r.filing_date}`);
257
+ if (r.publication_date)
258
+ lines.push(`**publication_date**: ${r.publication_date}`);
259
+ if (r.grant_date)
260
+ lines.push(`**grant_date**: ${r.grant_date}`);
261
+ if (r.priority_date)
262
+ lines.push(`**priority_date**: ${r.priority_date}`);
263
+ if (r.kind_code)
264
+ lines.push(`**kind_code**: ${r.kind_code}`);
265
+ if (r.legal_status)
266
+ lines.push(`**legal_status**: ${r.legal_status}`);
267
+ if (r.family_id)
268
+ lines.push(`**family_id**: ${r.family_id}`);
269
+ if (r.cited_by_count !== undefined)
270
+ lines.push(`**cited_by_count**: ${r.cited_by_count}`);
271
+ if (r.cites_count !== undefined)
272
+ lines.push(`**cites_count**: ${r.cites_count}`);
273
+ if (r.claims_count !== undefined)
274
+ lines.push(`**claims_count**: ${r.claims_count}`);
275
+ if (r.relevance_score !== undefined)
276
+ lines.push(`**relevance_score**: ${r.relevance_score}`);
277
+ lines.push(`**source_adapter**: ${r.source_adapter}`);
278
+ if (r.source_url)
279
+ lines.push(`**source_url**: ${r.source_url}`);
280
+ blocks.push(lines.join("\n"));
281
+ }
282
+ return blocks.join("\n\n---\n\n");
283
+ }
284
+ /**
285
+ * Resolve the output payload for the patent search / get / prior-art
286
+ * commands honouring `--detailed`, `--include-raw`, and `--format jsonl`
287
+ * (alongside json / md / csv). Returns either a rendered string (when the
288
+ * format is a patent-vertical-specific output like jsonl / csv / detailed
289
+ * md) or `null` to signal the caller should fall through to the standard
290
+ * envelope formatter.
291
+ */
292
+ function renderPatentOutput(records, opts) {
293
+ const cleaned = opts.includeRaw ? records : stripRaw(records);
294
+ const fmt = (opts.format ?? "").toLowerCase();
295
+ if (fmt === "jsonl") {
296
+ return { output: renderJsonl(cleaned), standardFormatter: false };
297
+ }
298
+ if (fmt === "csv") {
299
+ const columns = opts.detailed
300
+ ? DETAILED_SEARCH_COLUMNS
301
+ : DEFAULT_SEARCH_COLUMNS;
302
+ return { output: renderCsv(cleaned, columns), standardFormatter: false };
303
+ }
304
+ if (opts.detailed && (fmt === "" || fmt === "md")) {
305
+ return {
306
+ output: renderDetailedMarkdown(cleaned),
307
+ standardFormatter: false,
308
+ };
309
+ }
310
+ return null;
311
+ }
312
+ // ── Pipeline-result coercion ────────────────────────────────────────────
313
+ /**
314
+ * Coerce a YAML-emitted value that is expected to be a JSON-serialized array
315
+ * (the `| json` filter result). Returns `undefined` when the value is not a
316
+ * non-empty array after parsing — never an empty array (that would surface as
317
+ * "the upstream said zero inventors" when it actually said "no field").
318
+ */
319
+ function parseJsonArray(value) {
320
+ if (Array.isArray(value)) {
321
+ return value.length > 0 ? value : undefined;
322
+ }
323
+ if (typeof value !== "string" || value.length === 0)
324
+ return undefined;
325
+ // Tolerate authors using `| json` on a missing field — the filter yields
326
+ // the literal string "undefined" in that case.
327
+ if (value === "undefined" || value === "null")
328
+ return undefined;
329
+ try {
330
+ const parsed = JSON.parse(value);
331
+ if (!Array.isArray(parsed) || parsed.length === 0)
332
+ return undefined;
333
+ return parsed;
334
+ }
335
+ catch {
336
+ return undefined;
337
+ }
338
+ }
339
+ /**
340
+ * Coerce a YAML-emitted scalar into a finite number. The pipeline template
341
+ * engine stringifies every map output, so `42` arrives as `"42"`. Returns
342
+ * `undefined` when the input is not a finite number — never a NaN, never
343
+ * a silent zero (that would manufacture a count that did not exist
344
+ * upstream).
345
+ */
346
+ function asFiniteNumber(value) {
347
+ if (typeof value === "number") {
348
+ return Number.isFinite(value) ? value : undefined;
349
+ }
350
+ if (typeof value === "string") {
351
+ if (value.length === 0)
352
+ return undefined;
353
+ const n = Number(value);
354
+ return Number.isFinite(n) ? n : undefined;
355
+ }
356
+ return undefined;
357
+ }
358
+ function coerceToPatentRecords(rows, source) {
359
+ if (!Array.isArray(rows))
360
+ return [];
361
+ const out = [];
362
+ for (const row of rows) {
363
+ if (!row || typeof row !== "object")
364
+ continue;
365
+ const r = row;
366
+ if (typeof r.publication_number !== "string")
367
+ continue;
368
+ if (r.publication_number.length === 0)
369
+ continue;
370
+ const record = {
371
+ publication_number: r.publication_number,
372
+ source_adapter: typeof r.source_adapter === "string" ? r.source_adapter : source,
373
+ retrieved_at: typeof r.retrieved_at === "string" && r.retrieved_at.length > 0
374
+ ? r.retrieved_at
375
+ : new Date().toISOString(),
376
+ };
377
+ if (typeof r.application_number === "string")
378
+ record.application_number = r.application_number;
379
+ if (typeof r.kind_code === "string")
380
+ record.kind_code = r.kind_code;
381
+ if (typeof r.title === "string")
382
+ record.title = r.title;
383
+ if (typeof r.abstract === "string")
384
+ record.abstract = r.abstract;
385
+ if (typeof r.snippet === "string")
386
+ record.snippet = r.snippet;
387
+ const inventors = parseJsonArray(r.inventors);
388
+ if (inventors)
389
+ record.inventors = inventors;
390
+ const assignees = parseJsonArray(r.assignees);
391
+ if (assignees)
392
+ record.assignees = assignees;
393
+ if (typeof r.filing_date === "string")
394
+ record.filing_date = r.filing_date;
395
+ if (typeof r.publication_date === "string")
396
+ record.publication_date = r.publication_date;
397
+ if (typeof r.grant_date === "string")
398
+ record.grant_date = r.grant_date;
399
+ if (typeof r.priority_date === "string")
400
+ record.priority_date = r.priority_date;
401
+ const classifications = parseJsonArray(r.classifications);
402
+ if (classifications)
403
+ record.classifications =
404
+ classifications;
405
+ if (typeof r.family_id === "string")
406
+ record.family_id = r.family_id;
407
+ const familyMembers = parseJsonArray(r.family_members);
408
+ if (familyMembers)
409
+ record.family_members = familyMembers;
410
+ if (typeof r.legal_status === "string")
411
+ record.legal_status = r.legal_status;
412
+ const claimsCount = asFiniteNumber(r.claims_count);
413
+ if (claimsCount !== undefined)
414
+ record.claims_count = claimsCount;
415
+ const citedByCount = asFiniteNumber(r.cited_by_count);
416
+ if (citedByCount !== undefined)
417
+ record.cited_by_count = citedByCount;
418
+ const citesCount = asFiniteNumber(r.cites_count);
419
+ if (citesCount !== undefined)
420
+ record.cites_count = citesCount;
421
+ if (typeof r.pdf_url === "string" && r.pdf_url.length > 0)
422
+ record.pdf_url = r.pdf_url;
423
+ const relevanceScore = asFiniteNumber(r.relevance_score);
424
+ if (relevanceScore !== undefined)
425
+ record.relevance_score = relevanceScore;
426
+ if (typeof r.source_url === "string")
427
+ record.source_url = r.source_url;
428
+ if (r.raw !== undefined)
429
+ record.raw = r.raw;
430
+ out.push(record);
431
+ }
432
+ return out;
433
+ }
434
+ async function runAdapterCommand(source, capability, args) {
435
+ const adapter = getAllAdapters().find((a) => a.name === source);
436
+ if (!adapter) {
437
+ return {
438
+ source,
439
+ records: [],
440
+ error: {
441
+ code: "adapter_not_found",
442
+ message: `unknown source: ${source}`,
443
+ },
444
+ };
445
+ }
446
+ const found = findCommandByCapability(adapter, capability);
447
+ if (!found) {
448
+ return {
449
+ source,
450
+ records: [],
451
+ error: {
452
+ code: "capability_unsupported",
453
+ message: `${source} does not expose ${capability}`,
454
+ },
455
+ };
456
+ }
457
+ const inv = buildInvocation("cli", source, found.name, { args, source: "internal" }, { approved: true });
458
+ if (!inv) {
459
+ return {
460
+ source,
461
+ records: [],
462
+ error: {
463
+ code: "build_invocation_failed",
464
+ message: `could not build invocation for ${source}.${found.name}`,
465
+ },
466
+ };
467
+ }
468
+ const result = await execute(inv);
469
+ if (result.error) {
470
+ return {
471
+ source,
472
+ records: [],
473
+ error: {
474
+ code: result.error.code ?? "execution_error",
475
+ message: result.error.message ?? "adapter pipeline failed",
476
+ },
477
+ };
478
+ }
479
+ return {
480
+ source,
481
+ records: coerceToPatentRecords(result.results, source),
482
+ };
483
+ }
484
+ async function runSearch(program, query, opts) {
485
+ const startedAt = Date.now();
486
+ // Resolve format precedence: explicit `--format` on the patent subcommand
487
+ // wins over the global `-f` (it can name `jsonl`, which the global formatter
488
+ // does not understand).
489
+ const requestedFormat = opts.format ?? program.opts().format;
490
+ const fmt = detectFormat(requestedFormat === "jsonl"
491
+ ? "json"
492
+ : requestedFormat);
493
+ const ctx = makeCtx("patent.search", startedAt);
494
+ const sources = resolveSources(opts.sources);
495
+ const limit = Number(opts.limit ?? "20");
496
+ const args = { query, limit };
497
+ if (opts.since)
498
+ args.date_from = `${opts.since}-01-01`;
499
+ if (opts.cpc)
500
+ args.cpc = opts.cpc;
501
+ const outcomes = [];
502
+ for (const source of sources) {
503
+ outcomes.push(await runAdapterCommand(source, "patent.search", args));
504
+ }
505
+ const lists = outcomes.map((o) => o.records);
506
+ const fused = reciprocalRankFusion(lists, { topN: limit });
507
+ const errors = outcomes.filter((o) => o.error);
508
+ ctx.duration_ms = Date.now() - startedAt;
509
+ ctx.surface = "web";
510
+ if (fused.length === 0) {
511
+ ctx.error = {
512
+ code: "PATENT_NOT_FOUND",
513
+ message: `no patent records returned for "${query}" across [${sources.join(", ")}]`,
514
+ suggestion: errors.length > 0
515
+ ? `Per-source errors: ${errors.map((e) => `${e.source}: ${e.error?.code}`).join("; ")}`
516
+ : "Try --sources all or relax filters (--since / --cpc).",
517
+ retryable: errors.some((e) => e.error?.code === "rate_limit"),
518
+ };
519
+ console.error(format(null, undefined, fmt, ctx));
520
+ process.exit(ExitCode.EMPTY_RESULT);
521
+ }
522
+ const patentOutput = renderPatentOutput(fused, {
523
+ detailed: opts.detailed,
524
+ format: requestedFormat,
525
+ includeRaw: opts.includeRaw,
526
+ });
527
+ if (patentOutput) {
528
+ console.log(patentOutput.output);
529
+ return;
530
+ }
531
+ const columns = opts.detailed
532
+ ? [...DETAILED_SEARCH_COLUMNS]
533
+ : [...DEFAULT_SEARCH_COLUMNS];
534
+ const records = opts.includeRaw ? fused : stripRaw(fused);
535
+ console.log(format(records, columns, fmt, ctx));
536
+ }
537
+ async function runGet(program, publicationNumber, opts = {}) {
538
+ const startedAt = Date.now();
539
+ const requestedFormat = opts.format ?? program.opts().format;
540
+ const fmt = detectFormat(requestedFormat === "jsonl"
541
+ ? "json"
542
+ : requestedFormat);
543
+ const ctx = makeCtx("patent.get", startedAt);
544
+ const source = routeByPublicationPrefix(publicationNumber);
545
+ if (!source) {
546
+ ctx.error = {
547
+ code: "PATENT_INVALID_NUMBER",
548
+ message: `publication number "${publicationNumber}" has no recognised ST.16 jurisdiction prefix`,
549
+ suggestion: "Use a publication number with a two-letter country code prefix (US, EP, JP, KR, CN, DE, FR, GB, CA, AU, BR, RU).",
550
+ retryable: false,
551
+ };
552
+ ctx.duration_ms = Date.now() - startedAt;
553
+ console.error(format(null, undefined, fmt, ctx));
554
+ process.exit(ExitCode.USAGE_ERROR);
555
+ }
556
+ const outcome = await runAdapterCommand(source, "patent.get", {
557
+ publication_number: publicationNumber,
558
+ });
559
+ ctx.duration_ms = Date.now() - startedAt;
560
+ ctx.surface = "web";
561
+ if (outcome.error || outcome.records.length === 0) {
562
+ ctx.error = {
563
+ code: outcome.error ? "PATENT_NOT_FOUND" : "PATENT_NOT_FOUND",
564
+ message: outcome.error?.message ??
565
+ `${source} returned no record for ${publicationNumber}`,
566
+ suggestion: `Verify the number on ${source}'s primary search, or retry against --sources all.`,
567
+ retryable: false,
568
+ };
569
+ console.error(format(null, undefined, fmt, ctx));
570
+ process.exit(ExitCode.EMPTY_RESULT);
571
+ }
572
+ const patentOutput = renderPatentOutput(outcome.records, {
573
+ detailed: opts.detailed,
574
+ format: requestedFormat,
575
+ includeRaw: opts.includeRaw,
576
+ });
577
+ if (patentOutput) {
578
+ console.log(patentOutput.output);
579
+ return;
580
+ }
581
+ const records = opts.includeRaw ? outcome.records : stripRaw(outcome.records);
582
+ console.log(format(records, undefined, fmt, ctx));
583
+ }
584
+ async function runFamily(program, publicationNumber) {
585
+ const startedAt = Date.now();
586
+ const fmt = detectFormat(program.opts().format);
587
+ const ctx = makeCtx("patent.family", startedAt);
588
+ if (!routeByPublicationPrefix(publicationNumber)) {
589
+ ctx.error = {
590
+ code: "PATENT_INVALID_NUMBER",
591
+ message: `publication number "${publicationNumber}" has no recognised ST.16 jurisdiction prefix`,
592
+ suggestion: "Family lookups need a CC-prefixed publication number so we know which jurisdiction issued it.",
593
+ retryable: false,
594
+ };
595
+ ctx.duration_ms = Date.now() - startedAt;
596
+ console.error(format(null, undefined, fmt, ctx));
597
+ process.exit(ExitCode.USAGE_ERROR);
598
+ }
599
+ // EPO is the canonical INPADOC family broker — try it first.
600
+ const primary = await runAdapterCommand(FAMILY_BROKER, "patent.family", {
601
+ publication_number: publicationNumber,
602
+ });
603
+ ctx.duration_ms = Date.now() - startedAt;
604
+ ctx.surface = "web";
605
+ if (primary.error || primary.records.length === 0) {
606
+ const fallback = await runAdapterCommand(routeByPublicationPrefix(publicationNumber), "patent.family", { publication_number: publicationNumber });
607
+ ctx.duration_ms = Date.now() - startedAt;
608
+ if (fallback.error || fallback.records.length === 0) {
609
+ ctx.error = {
610
+ code: "PATENT_FAMILY_BROKER_DOWN",
611
+ message: `EPO Espacenet family lookup failed and home-office fallback returned empty for ${publicationNumber}`,
612
+ suggestion: "Retry with --sources all or check unicli patent doctor.",
613
+ retryable: true,
614
+ };
615
+ console.error(format(null, undefined, fmt, ctx));
616
+ process.exit(ExitCode.SERVICE_UNAVAILABLE);
617
+ }
618
+ console.log(format(fallback.records, undefined, fmt, ctx));
619
+ return;
620
+ }
621
+ console.log(format(primary.records, undefined, fmt, ctx));
622
+ }
623
+ async function runCitations(program, publicationNumber, opts) {
624
+ const startedAt = Date.now();
625
+ const fmt = detectFormat(program.opts().format);
626
+ const ctx = makeCtx("patent.citations", startedAt);
627
+ const source = routeByPublicationPrefix(publicationNumber);
628
+ if (!source) {
629
+ ctx.error = {
630
+ code: "PATENT_INVALID_NUMBER",
631
+ message: `publication number "${publicationNumber}" has no recognised ST.16 jurisdiction prefix`,
632
+ suggestion: "Provide a CC-prefixed publication number.",
633
+ retryable: false,
634
+ };
635
+ ctx.duration_ms = Date.now() - startedAt;
636
+ console.error(format(null, undefined, fmt, ctx));
637
+ process.exit(ExitCode.USAGE_ERROR);
638
+ }
639
+ const direction = opts.direction ?? "citing";
640
+ const outcome = await runAdapterCommand(source, "patent.citations", {
641
+ publication_number: publicationNumber,
642
+ direction,
643
+ });
644
+ ctx.duration_ms = Date.now() - startedAt;
645
+ ctx.surface = "web";
646
+ if (outcome.error) {
647
+ ctx.error = {
648
+ code: "PATENT_NOT_FOUND",
649
+ message: outcome.error.message,
650
+ suggestion: `Verify ${publicationNumber} exists at ${source}, then retry.`,
651
+ retryable: false,
652
+ };
653
+ console.error(format(null, undefined, fmt, ctx));
654
+ process.exit(ExitCode.EMPTY_RESULT);
655
+ }
656
+ console.log(format(outcome.records, undefined, fmt, ctx));
657
+ }
658
+ async function runLegalStatus(program, publicationNumbers) {
659
+ const startedAt = Date.now();
660
+ const fmt = detectFormat(program.opts().format);
661
+ const ctx = makeCtx("patent.legal-status", startedAt);
662
+ const invalid = publicationNumbers.filter((n) => !routeByPublicationPrefix(n));
663
+ if (invalid.length > 0) {
664
+ ctx.error = {
665
+ code: "PATENT_INVALID_NUMBER",
666
+ message: `unrecognised jurisdiction prefix on: ${invalid.join(", ")}`,
667
+ suggestion: "Every number must carry a two-letter ST.16 country code.",
668
+ retryable: false,
669
+ };
670
+ ctx.duration_ms = Date.now() - startedAt;
671
+ console.error(format(null, undefined, fmt, ctx));
672
+ process.exit(ExitCode.USAGE_ERROR);
673
+ }
674
+ const byJurisdiction = new Map();
675
+ for (const number of publicationNumbers) {
676
+ const source = routeByPublicationPrefix(number);
677
+ const bucket = byJurisdiction.get(source) ?? [];
678
+ bucket.push(number);
679
+ byJurisdiction.set(source, bucket);
680
+ }
681
+ const all = [];
682
+ for (const [source, numbers] of byJurisdiction.entries()) {
683
+ const outcome = await runAdapterCommand(source, "patent.legal-status", {
684
+ publication_numbers: numbers,
685
+ });
686
+ all.push(...outcome.records);
687
+ }
688
+ ctx.duration_ms = Date.now() - startedAt;
689
+ ctx.surface = "web";
690
+ console.log(format(all, ["publication_number", "legal_status", "source_adapter"], fmt, ctx));
691
+ }
692
+ async function runPriorArt(program, opts) {
693
+ const startedAt = Date.now();
694
+ const requestedFormat = opts.format ?? program.opts().format;
695
+ const fmt = detectFormat(requestedFormat === "jsonl"
696
+ ? "json"
697
+ : requestedFormat);
698
+ const ctx = makeCtx("patent.prior-art", startedAt);
699
+ if (!opts.abstract) {
700
+ ctx.error = {
701
+ code: "invalid_input",
702
+ message: "patent prior-art requires --abstract <text>",
703
+ suggestion: "Pass the candidate abstract as a single string: --abstract 'A method for …'.",
704
+ retryable: false,
705
+ };
706
+ ctx.duration_ms = Date.now() - startedAt;
707
+ console.error(format(null, undefined, fmt, ctx));
708
+ process.exit(ExitCode.USAGE_ERROR);
709
+ }
710
+ const top = Number(opts.top ?? "20");
711
+ const sources = resolveSources(opts.sources, [
712
+ "pqai",
713
+ "google-patents-bq",
714
+ "epo",
715
+ ]);
716
+ const outcomes = [];
717
+ for (const source of sources) {
718
+ outcomes.push(await runAdapterCommand(source, "patent.prior-art", {
719
+ abstract: opts.abstract,
720
+ limit: top,
721
+ }));
722
+ }
723
+ const lists = outcomes.map((o) => o.records);
724
+ const fused = reciprocalRankFusion(lists, { topN: top });
725
+ ctx.duration_ms = Date.now() - startedAt;
726
+ ctx.surface = "web";
727
+ if (fused.length === 0) {
728
+ const errors = outcomes.filter((o) => o.error);
729
+ ctx.error = {
730
+ code: "PATENT_NOT_FOUND",
731
+ message: `no prior-art candidates returned across [${sources.join(", ")}]`,
732
+ suggestion: errors.length > 0
733
+ ? `Per-source errors: ${errors.map((e) => `${e.source}: ${e.error?.code}`).join("; ")}`
734
+ : "Try widening --sources, or supply more of the abstract.",
735
+ retryable: false,
736
+ };
737
+ console.error(format(null, undefined, fmt, ctx));
738
+ process.exit(ExitCode.EMPTY_RESULT);
739
+ }
740
+ const patentOutput = renderPatentOutput(fused, {
741
+ detailed: opts.detailed,
742
+ format: requestedFormat,
743
+ includeRaw: opts.includeRaw,
744
+ });
745
+ if (patentOutput) {
746
+ console.log(patentOutput.output);
747
+ return;
748
+ }
749
+ const columns = opts.detailed
750
+ ? [...DETAILED_SEARCH_COLUMNS]
751
+ : ["publication_number", "title", "abstract", "source_adapter"];
752
+ const records = opts.includeRaw ? fused : stripRaw(fused);
753
+ console.log(format(records, columns, fmt, ctx));
754
+ }
755
+ async function runDoctor(program, opts) {
756
+ const startedAt = Date.now();
757
+ const fmt = detectFormat(program.opts().format);
758
+ const ctx = makeCtx("patent.doctor", startedAt);
759
+ const adapters = listPatentAdapters().filter((a) => {
760
+ const list = resolveSources(opts.sources, listPatentAdapters().map((x) => x.name));
761
+ return list.includes(a.name);
762
+ });
763
+ const rows = [];
764
+ let anyError = false;
765
+ let anyDishonesty = false;
766
+ for (const adapter of adapters) {
767
+ const caps = new Set();
768
+ for (const cmd of Object.values(adapter.commands)) {
769
+ for (const cap of cmd.capabilities ?? []) {
770
+ if (cap.startsWith("patent."))
771
+ caps.add(cap);
772
+ }
773
+ }
774
+ const commandNames = Object.keys(adapter.commands);
775
+ const verificationStatus = resolveAdapterVerificationStatus(adapter.name, commandNames);
776
+ const healthCmd = resolveCommand(adapter.name, "health");
777
+ if (!healthCmd) {
778
+ rows.push({
779
+ source: adapter.name,
780
+ capabilities: [...caps].sort(),
781
+ health: "skipped",
782
+ verification_status: verificationStatus,
783
+ detail: "no `health` command — adapter passes by introspection only",
784
+ });
785
+ // Honesty gate: an adapter claiming "verified" must own a reachable
786
+ // health probe. Without one, the claim is unfalsifiable in this
787
+ // session — surface the mismatch as an error.
788
+ if (verificationStatus === "verified") {
789
+ anyDishonesty = true;
790
+ }
791
+ continue;
792
+ }
793
+ const strategy = commandStrategy(adapter, healthCmd.command);
794
+ if (strategy !== undefined && strategy !== Strategy.PUBLIC) {
795
+ rows.push({
796
+ source: adapter.name,
797
+ capabilities: [...caps].sort(),
798
+ health: "blocked",
799
+ verification_status: verificationStatus,
800
+ detail: `health probe requires ${strategy} auth — skipped`,
801
+ });
802
+ // A `verified` header must not co-exist with a strategy gate; the
803
+ // probe could not have run in CI without auth. Flag the mismatch.
804
+ if (verificationStatus === "verified") {
805
+ anyDishonesty = true;
806
+ }
807
+ continue;
808
+ }
809
+ const inv = buildInvocation("cli", adapter.name, "health", { args: {}, source: "internal" }, { approved: true });
810
+ if (!inv) {
811
+ rows.push({
812
+ source: adapter.name,
813
+ capabilities: [...caps].sort(),
814
+ health: "error",
815
+ verification_status: verificationStatus,
816
+ detail: "could not build invocation for health command",
817
+ });
818
+ anyError = true;
819
+ continue;
820
+ }
821
+ const result = await execute(inv);
822
+ if (result.error) {
823
+ rows.push({
824
+ source: adapter.name,
825
+ capabilities: [...caps].sort(),
826
+ health: "error",
827
+ verification_status: verificationStatus,
828
+ detail: result.error.message ?? result.error.code ?? "health failed",
829
+ });
830
+ anyError = true;
831
+ if (verificationStatus === "verified") {
832
+ anyDishonesty = true;
833
+ }
834
+ continue;
835
+ }
836
+ rows.push({
837
+ source: adapter.name,
838
+ capabilities: [...caps].sort(),
839
+ health: "ok",
840
+ verification_status: verificationStatus,
841
+ detail: "health probe returned successfully",
842
+ });
843
+ }
844
+ ctx.duration_ms = Date.now() - startedAt;
845
+ ctx.surface = "web";
846
+ console.log(format(rows, ["source", "capabilities", "health", "verification_status", "detail"], fmt, ctx));
847
+ if (anyError || anyDishonesty)
848
+ process.exit(ExitCode.GENERIC_ERROR);
849
+ }
850
+ // ── Registration ────────────────────────────────────────────────────────
851
+ export function registerPatentCommand(program) {
852
+ const patent = program
853
+ .command("patent")
854
+ .description("Patent meta-command — search, retrieve, and audit across L0/L1/L2 patent adapters");
855
+ patent
856
+ .command("search <query>")
857
+ .description("Free-text + filter search across patent sources")
858
+ .option("--sources <csv>", "comma-separated source list, or `all` for every patent adapter")
859
+ .option("--limit <n>", "maximum fused result count", "20")
860
+ .option("--since <YYYY>", "earliest filing year")
861
+ .option("--cpc <csv>", "Cooperative Patent Classification filter")
862
+ .option("-D, --detailed", "emit every PatentRecord field (rich block / wide table)")
863
+ .option("--include-raw", "preserve the upstream `raw` payload on each record")
864
+ .option("-f, --format <fmt>", "output format: md (default), json, jsonl, csv — overrides global -f")
865
+ .action(async (query, opts) => {
866
+ await runSearch(program, query, opts);
867
+ });
868
+ patent
869
+ .command("get <publication-number>")
870
+ .description("Retrieve one patent record by ST.16 publication number")
871
+ .option("-D, --detailed", "emit every PatentRecord field (rich block)")
872
+ .option("--include-raw", "preserve the upstream `raw` payload")
873
+ .option("-f, --format <fmt>", "output format: md (default), json, jsonl, csv")
874
+ .action(async (publicationNumber, opts) => {
875
+ await runGet(program, publicationNumber, opts);
876
+ });
877
+ patent
878
+ .command("family <publication-number>")
879
+ .description("INPADOC / DOCDB family lookup brokered through EPO Espacenet")
880
+ .action(async (publicationNumber) => {
881
+ await runFamily(program, publicationNumber);
882
+ });
883
+ patent
884
+ .command("citations <publication-number>")
885
+ .description("Citing / cited records for the given publication number")
886
+ .option("--direction <dir>", "citing (records that cite this) or cited (records this cites)", "citing")
887
+ .action(async (publicationNumber, opts) => {
888
+ await runCitations(program, publicationNumber, opts);
889
+ });
890
+ patent
891
+ .command("legal-status <publication-numbers...>")
892
+ .description("Prosecution / grant status for one or more publication numbers")
893
+ .action(async (publicationNumbers) => {
894
+ await runLegalStatus(program, publicationNumbers);
895
+ });
896
+ patent
897
+ .command("prior-art")
898
+ .description("Semantic + keyword + CPC prior-art fusion against an abstract")
899
+ .requiredOption("--abstract <text>", "candidate abstract text")
900
+ .option("--sources <csv>", "comma-separated source list (default: pqai,google-patents-bq,epo)")
901
+ .option("--top <n>", "top-N fused results to return", "20")
902
+ .option("-D, --detailed", "emit every PatentRecord field (rich block)")
903
+ .option("--include-raw", "preserve the upstream `raw` payload")
904
+ .option("-f, --format <fmt>", "output format: md (default), json, jsonl, csv")
905
+ .action(async (opts) => {
906
+ await runPriorArt(program, opts);
907
+ });
908
+ patent
909
+ .command("doctor")
910
+ .description("Probe each registered patent adapter for health and schema drift")
911
+ .option("--sources <csv>", "limit to a comma-separated source list")
912
+ .action(async (opts) => {
913
+ await runDoctor(program, opts);
914
+ });
915
+ }
916
+ // Re-export some helpers so the test suite can exercise them without
917
+ // going through Commander; rule 03 forbids mocking owned modules.
918
+ export { JURISDICTION_ADAPTERS, FAMILY_BROKER, DEFAULT_SOURCES };
919
+ //# sourceMappingURL=patent.js.map