@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.
- package/AGENTS.md +28 -6
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/dist/adapters/bilibili/comments.js +66 -4
- package/dist/adapters/bilibili/comments.js.map +1 -1
- package/dist/adapters/bilibili/compat.js +2 -2
- package/dist/adapters/bilibili/compat.js.map +1 -1
- package/dist/adapters/bilibili/download.js +4 -4
- package/dist/adapters/bilibili/download.js.map +1 -1
- package/dist/adapters/bilibili/wbi.d.ts.map +1 -1
- package/dist/adapters/bilibili/wbi.js +3 -3
- package/dist/adapters/bilibili/wbi.js.map +1 -1
- package/dist/adapters/cipo/_shared.d.ts +21 -0
- package/dist/adapters/cipo/_shared.d.ts.map +1 -0
- package/dist/adapters/cipo/_shared.js +67 -0
- package/dist/adapters/cipo/_shared.js.map +1 -0
- package/dist/adapters/cipo/get.d.ts +19 -0
- package/dist/adapters/cipo/get.d.ts.map +1 -0
- package/dist/adapters/cipo/get.js +140 -0
- package/dist/adapters/cipo/get.js.map +1 -0
- package/dist/adapters/cipo/legal-status.d.ts +19 -0
- package/dist/adapters/cipo/legal-status.d.ts.map +1 -0
- package/dist/adapters/cipo/legal-status.js +111 -0
- package/dist/adapters/cipo/legal-status.js.map +1 -0
- package/dist/adapters/cipo/search.d.ts +20 -0
- package/dist/adapters/cipo/search.d.ts.map +1 -0
- package/dist/adapters/cipo/search.js +148 -0
- package/dist/adapters/cipo/search.js.map +1 -0
- package/dist/adapters/cnipa/_shared.d.ts +47 -0
- package/dist/adapters/cnipa/_shared.d.ts.map +1 -0
- package/dist/adapters/cnipa/_shared.js +97 -0
- package/dist/adapters/cnipa/_shared.js.map +1 -0
- package/dist/adapters/cnipa/get.d.ts +19 -0
- package/dist/adapters/cnipa/get.d.ts.map +1 -0
- package/dist/adapters/cnipa/get.js +149 -0
- package/dist/adapters/cnipa/get.js.map +1 -0
- package/dist/adapters/cnipa/legal-status.d.ts +19 -0
- package/dist/adapters/cnipa/legal-status.d.ts.map +1 -0
- package/dist/adapters/cnipa/legal-status.js +119 -0
- package/dist/adapters/cnipa/legal-status.js.map +1 -0
- package/dist/adapters/cnipa/search.d.ts +21 -0
- package/dist/adapters/cnipa/search.d.ts.map +1 -0
- package/dist/adapters/cnipa/search.js +170 -0
- package/dist/adapters/cnipa/search.js.map +1 -0
- package/dist/adapters/espacenet/_shared.d.ts +21 -0
- package/dist/adapters/espacenet/_shared.d.ts.map +1 -0
- package/dist/adapters/espacenet/_shared.js +67 -0
- package/dist/adapters/espacenet/_shared.js.map +1 -0
- package/dist/adapters/espacenet/family.d.ts +19 -0
- package/dist/adapters/espacenet/family.d.ts.map +1 -0
- package/dist/adapters/espacenet/family.js +118 -0
- package/dist/adapters/espacenet/family.js.map +1 -0
- package/dist/adapters/espacenet/get.d.ts +19 -0
- package/dist/adapters/espacenet/get.d.ts.map +1 -0
- package/dist/adapters/espacenet/get.js +130 -0
- package/dist/adapters/espacenet/get.js.map +1 -0
- package/dist/adapters/espacenet/legal-status.d.ts +19 -0
- package/dist/adapters/espacenet/legal-status.d.ts.map +1 -0
- package/dist/adapters/espacenet/legal-status.js +110 -0
- package/dist/adapters/espacenet/legal-status.js.map +1 -0
- package/dist/adapters/espacenet/search.d.ts +20 -0
- package/dist/adapters/espacenet/search.d.ts.map +1 -0
- package/dist/adapters/espacenet/search.js +165 -0
- package/dist/adapters/espacenet/search.js.map +1 -0
- package/dist/adapters/facebook/subtitles.d.ts +9 -0
- package/dist/adapters/facebook/subtitles.d.ts.map +1 -0
- package/dist/adapters/facebook/subtitles.js +42 -0
- package/dist/adapters/facebook/subtitles.js.map +1 -0
- package/dist/adapters/fips/_shared.d.ts +21 -0
- package/dist/adapters/fips/_shared.d.ts.map +1 -0
- package/dist/adapters/fips/_shared.js +77 -0
- package/dist/adapters/fips/_shared.js.map +1 -0
- package/dist/adapters/fips/get.d.ts +19 -0
- package/dist/adapters/fips/get.d.ts.map +1 -0
- package/dist/adapters/fips/get.js +139 -0
- package/dist/adapters/fips/get.js.map +1 -0
- package/dist/adapters/fips/search.d.ts +20 -0
- package/dist/adapters/fips/search.d.ts.map +1 -0
- package/dist/adapters/fips/search.js +148 -0
- package/dist/adapters/fips/search.js.map +1 -0
- package/dist/adapters/freepatentsonline-web/_shared.d.ts +72 -0
- package/dist/adapters/freepatentsonline-web/_shared.d.ts.map +1 -0
- package/dist/adapters/freepatentsonline-web/_shared.js +216 -0
- package/dist/adapters/freepatentsonline-web/_shared.js.map +1 -0
- package/dist/adapters/freepatentsonline-web/get.d.ts +21 -0
- package/dist/adapters/freepatentsonline-web/get.d.ts.map +1 -0
- package/dist/adapters/freepatentsonline-web/get.js +127 -0
- package/dist/adapters/freepatentsonline-web/get.js.map +1 -0
- package/dist/adapters/freepatentsonline-web/search.d.ts +22 -0
- package/dist/adapters/freepatentsonline-web/search.d.ts.map +1 -0
- package/dist/adapters/freepatentsonline-web/search.js +149 -0
- package/dist/adapters/freepatentsonline-web/search.js.map +1 -0
- package/dist/adapters/google-patents-web/_shared.d.ts +110 -0
- package/dist/adapters/google-patents-web/_shared.d.ts.map +1 -0
- package/dist/adapters/google-patents-web/_shared.js +164 -0
- package/dist/adapters/google-patents-web/_shared.js.map +1 -0
- package/dist/adapters/google-patents-web/get.d.ts +36 -0
- package/dist/adapters/google-patents-web/get.d.ts.map +1 -0
- package/dist/adapters/google-patents-web/get.js +187 -0
- package/dist/adapters/google-patents-web/get.js.map +1 -0
- package/dist/adapters/google-patents-web/search.d.ts +23 -0
- package/dist/adapters/google-patents-web/search.d.ts.map +1 -0
- package/dist/adapters/google-patents-web/search.js +169 -0
- package/dist/adapters/google-patents-web/search.js.map +1 -0
- package/dist/adapters/inpi-br/_shared.d.ts +21 -0
- package/dist/adapters/inpi-br/_shared.d.ts.map +1 -0
- package/dist/adapters/inpi-br/_shared.js +67 -0
- package/dist/adapters/inpi-br/_shared.js.map +1 -0
- package/dist/adapters/inpi-br/get.d.ts +19 -0
- package/dist/adapters/inpi-br/get.d.ts.map +1 -0
- package/dist/adapters/inpi-br/get.js +142 -0
- package/dist/adapters/inpi-br/get.js.map +1 -0
- package/dist/adapters/inpi-br/search.d.ts +20 -0
- package/dist/adapters/inpi-br/search.d.ts.map +1 -0
- package/dist/adapters/inpi-br/search.js +154 -0
- package/dist/adapters/inpi-br/search.js.map +1 -0
- package/dist/adapters/instagram/subtitles.d.ts +9 -0
- package/dist/adapters/instagram/subtitles.d.ts.map +1 -0
- package/dist/adapters/instagram/subtitles.js +42 -0
- package/dist/adapters/instagram/subtitles.js.map +1 -0
- package/dist/adapters/mastodon/statuses.d.ts +40 -0
- package/dist/adapters/mastodon/statuses.d.ts.map +1 -0
- package/dist/adapters/mastodon/statuses.js +153 -0
- package/dist/adapters/mastodon/statuses.js.map +1 -0
- package/dist/adapters/reddit/comments.d.ts +9 -0
- package/dist/adapters/reddit/comments.d.ts.map +1 -0
- package/dist/adapters/reddit/comments.js +124 -0
- package/dist/adapters/reddit/comments.js.map +1 -0
- package/dist/adapters/threads/post.d.ts +32 -0
- package/dist/adapters/threads/post.d.ts.map +1 -0
- package/dist/adapters/threads/post.js +287 -0
- package/dist/adapters/threads/post.js.map +1 -0
- package/dist/adapters/tiktok/subtitles.d.ts +9 -0
- package/dist/adapters/tiktok/subtitles.d.ts.map +1 -0
- package/dist/adapters/tiktok/subtitles.js +42 -0
- package/dist/adapters/tiktok/subtitles.js.map +1 -0
- package/dist/adapters/twitter/accept.js +2 -2
- package/dist/adapters/twitter/accept.js.map +1 -1
- package/dist/adapters/twitter/browser-fallback.d.ts +26 -0
- package/dist/adapters/twitter/browser-fallback.d.ts.map +1 -0
- package/dist/adapters/twitter/browser-fallback.js +93 -0
- package/dist/adapters/twitter/browser-fallback.js.map +1 -0
- package/dist/adapters/twitter/browser-state.d.ts +11 -0
- package/dist/adapters/twitter/browser-state.d.ts.map +1 -0
- package/dist/adapters/twitter/browser-state.js +46 -0
- package/dist/adapters/twitter/browser-state.js.map +1 -0
- package/dist/adapters/twitter/client.d.ts.map +1 -1
- package/dist/adapters/twitter/client.js +36 -13
- package/dist/adapters/twitter/client.js.map +1 -1
- package/dist/adapters/twitter/reply-dm.js +2 -2
- package/dist/adapters/twitter/reply-dm.js.map +1 -1
- package/dist/adapters/twitter/reply.js +1 -0
- package/dist/adapters/twitter/reply.js.map +1 -1
- package/dist/adapters/twitter/search.js +11 -18
- package/dist/adapters/twitter/search.js.map +1 -1
- package/dist/adapters/twitter/thread.d.ts +14 -0
- package/dist/adapters/twitter/thread.d.ts.map +1 -1
- package/dist/adapters/twitter/thread.js +28 -2
- package/dist/adapters/twitter/thread.js.map +1 -1
- package/dist/adapters/twitter/trending.js +13 -59
- package/dist/adapters/twitter/trending.js.map +1 -1
- package/dist/adapters/xiaohongshu/browser-state.d.ts +19 -0
- package/dist/adapters/xiaohongshu/browser-state.d.ts.map +1 -0
- package/dist/adapters/xiaohongshu/browser-state.js +67 -0
- package/dist/adapters/xiaohongshu/browser-state.js.map +1 -0
- package/dist/adapters/xiaohongshu/comments.js +28 -5
- package/dist/adapters/xiaohongshu/comments.js.map +1 -1
- package/dist/adapters/xiaohongshu/download.js +49 -11
- package/dist/adapters/xiaohongshu/download.js.map +1 -1
- package/dist/adapters/xiaohongshu/search.d.ts.map +1 -1
- package/dist/adapters/xiaohongshu/search.js +11 -5
- package/dist/adapters/xiaohongshu/search.js.map +1 -1
- package/dist/adapters/xiaohongshu/trending.d.ts +9 -0
- package/dist/adapters/xiaohongshu/trending.d.ts.map +1 -0
- package/dist/adapters/xiaohongshu/trending.js +94 -0
- package/dist/adapters/xiaohongshu/trending.js.map +1 -0
- package/dist/adapters/youtube/comments.d.ts +80 -0
- package/dist/adapters/youtube/comments.d.ts.map +1 -1
- package/dist/adapters/youtube/comments.js +108 -12
- package/dist/adapters/youtube/comments.js.map +1 -1
- package/dist/adapters/youtube/subtitles.d.ts +9 -0
- package/dist/adapters/youtube/subtitles.d.ts.map +1 -0
- package/dist/adapters/youtube/subtitles.js +42 -0
- package/dist/adapters/youtube/subtitles.js.map +1 -0
- package/dist/adapters/yt-dlp/subtitles.d.ts +9 -0
- package/dist/adapters/yt-dlp/subtitles.d.ts.map +1 -0
- package/dist/adapters/yt-dlp/subtitles.js +41 -0
- package/dist/adapters/yt-dlp/subtitles.js.map +1 -0
- package/dist/adapters/zhihu/answer-detail.d.ts +39 -0
- package/dist/adapters/zhihu/answer-detail.d.ts.map +1 -0
- package/dist/adapters/zhihu/answer-detail.js +204 -0
- package/dist/adapters/zhihu/answer-detail.js.map +1 -0
- package/dist/adapters/zhihu/comment.d.ts +9 -0
- package/dist/adapters/zhihu/comment.d.ts.map +1 -0
- package/dist/adapters/zhihu/comment.js +149 -0
- package/dist/adapters/zhihu/comment.js.map +1 -0
- package/dist/adapters/zhihu/recommend.d.ts +36 -0
- package/dist/adapters/zhihu/recommend.d.ts.map +1 -0
- package/dist/adapters/zhihu/recommend.js +151 -0
- package/dist/adapters/zhihu/recommend.js.map +1 -0
- package/dist/browser/bridge.d.ts.map +1 -1
- package/dist/browser/bridge.js +14 -3
- package/dist/browser/bridge.js.map +1 -1
- package/dist/browser/daemon-client.d.ts +6 -0
- package/dist/browser/daemon-client.d.ts.map +1 -1
- package/dist/browser/daemon-client.js +75 -15
- package/dist/browser/daemon-client.js.map +1 -1
- package/dist/browser/daemon.js +39 -15
- package/dist/browser/daemon.js.map +1 -1
- package/dist/browser/protocol.d.ts +1 -0
- package/dist/browser/protocol.d.ts.map +1 -1
- package/dist/browser/protocol.js +1 -0
- package/dist/browser/protocol.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/approvals.d.ts.map +1 -1
- package/dist/commands/approvals.js +1 -37
- package/dist/commands/approvals.js.map +1 -1
- package/dist/commands/browser/index.d.ts.map +1 -1
- package/dist/commands/browser/index.js +7 -2
- package/dist/commands/browser/index.js.map +1 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +7 -3
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/dispatch.js +27 -3
- package/dist/commands/dispatch.js.map +1 -1
- package/dist/commands/patent-doctor.d.ts +48 -0
- package/dist/commands/patent-doctor.d.ts.map +1 -0
- package/dist/commands/patent-doctor.js +109 -0
- package/dist/commands/patent-doctor.js.map +1 -0
- package/dist/commands/patent.d.ts +78 -0
- package/dist/commands/patent.d.ts.map +1 -0
- package/dist/commands/patent.js +919 -0
- package/dist/commands/patent.js.map +1 -0
- package/dist/commands/social.d.ts +19 -0
- package/dist/commands/social.d.ts.map +1 -0
- package/dist/commands/social.js +236 -0
- package/dist/commands/social.js.map +1 -0
- package/dist/core/registry.d.ts +1 -1
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +11 -2
- package/dist/core/registry.js.map +1 -1
- package/dist/discovery/loader.d.ts.map +1 -1
- package/dist/discovery/loader.js +4 -0
- package/dist/discovery/loader.js.map +1 -1
- package/dist/engine/approval-presenter.d.ts +10 -0
- package/dist/engine/approval-presenter.d.ts.map +1 -0
- package/dist/engine/approval-presenter.js +45 -0
- package/dist/engine/approval-presenter.js.map +1 -0
- package/dist/engine/approval-store.d.ts +4 -0
- package/dist/engine/approval-store.d.ts.map +1 -1
- package/dist/engine/approval-store.js +85 -11
- package/dist/engine/approval-store.js.map +1 -1
- package/dist/engine/auth/oauth2-cc.d.ts +67 -0
- package/dist/engine/auth/oauth2-cc.d.ts.map +1 -0
- package/dist/engine/auth/oauth2-cc.js +120 -0
- package/dist/engine/auth/oauth2-cc.js.map +1 -0
- package/dist/engine/cookies.d.ts +10 -0
- package/dist/engine/cookies.d.ts.map +1 -1
- package/dist/engine/cookies.js +64 -0
- package/dist/engine/cookies.js.map +1 -1
- package/dist/engine/download.d.ts +5 -0
- package/dist/engine/download.d.ts.map +1 -1
- package/dist/engine/download.js +11 -4
- package/dist/engine/download.js.map +1 -1
- package/dist/engine/executor.d.ts +1 -0
- package/dist/engine/executor.d.ts.map +1 -1
- package/dist/engine/executor.js +25 -0
- package/dist/engine/executor.js.map +1 -1
- package/dist/engine/framework.d.ts +5 -5
- package/dist/engine/framework.js +5 -5
- package/dist/engine/harden.d.ts +1 -1
- package/dist/engine/harden.js +1 -1
- package/dist/engine/kernel/stages.d.ts.map +1 -1
- package/dist/engine/kernel/stages.js +2 -1
- package/dist/engine/kernel/stages.js.map +1 -1
- package/dist/engine/normalizer/patent-envelope.d.ts +61 -0
- package/dist/engine/normalizer/patent-envelope.d.ts.map +1 -0
- package/dist/engine/normalizer/patent-envelope.js +132 -0
- package/dist/engine/normalizer/patent-envelope.js.map +1 -0
- package/dist/engine/research.d.ts +5 -7
- package/dist/engine/research.d.ts.map +1 -1
- package/dist/engine/research.js +6 -9
- package/dist/engine/research.js.map +1 -1
- package/dist/engine/steps/browser-helpers.d.ts +2 -2
- package/dist/engine/steps/browser-helpers.d.ts.map +1 -1
- package/dist/engine/steps/browser-helpers.js +39 -16
- package/dist/engine/steps/browser-helpers.js.map +1 -1
- package/dist/engine/steps/download.d.ts +1 -0
- package/dist/engine/steps/download.d.ts.map +1 -1
- package/dist/engine/steps/download.js +3 -1
- package/dist/engine/steps/download.js.map +1 -1
- package/dist/engine/steps/index.d.ts +2 -0
- package/dist/engine/steps/index.d.ts.map +1 -1
- package/dist/engine/steps/index.js +2 -0
- package/dist/engine/steps/index.js.map +1 -1
- package/dist/engine/steps/oauth2-token.d.ts +41 -0
- package/dist/engine/steps/oauth2-token.d.ts.map +1 -0
- package/dist/engine/steps/oauth2-token.js +115 -0
- package/dist/engine/steps/oauth2-token.js.map +1 -0
- package/dist/engine/steps/select-xml.d.ts +34 -0
- package/dist/engine/steps/select-xml.d.ts.map +1 -0
- package/dist/engine/steps/select-xml.js +222 -0
- package/dist/engine/steps/select-xml.js.map +1 -0
- package/dist/engine/template.d.ts.map +1 -1
- package/dist/engine/template.js +7 -0
- package/dist/engine/template.js.map +1 -1
- package/dist/engine/transport/mcp-browser.d.ts +128 -0
- package/dist/engine/transport/mcp-browser.d.ts.map +1 -0
- package/dist/engine/transport/mcp-browser.js +120 -0
- package/dist/engine/transport/mcp-browser.js.map +1 -0
- package/dist/fast-path/handlers/approvals.d.ts +11 -0
- package/dist/fast-path/handlers/approvals.d.ts.map +1 -0
- package/dist/fast-path/handlers/approvals.js +136 -0
- package/dist/fast-path/handlers/approvals.js.map +1 -0
- package/dist/fast-path/manifest.d.ts +1 -0
- package/dist/fast-path/manifest.d.ts.map +1 -1
- package/dist/fast-path/manifest.js.map +1 -1
- package/dist/fast-path.d.ts.map +1 -1
- package/dist/fast-path.js +3 -0
- package/dist/fast-path.js.map +1 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest-compact.txt +3 -3
- package/dist/manifest-search.json +1 -1
- package/dist/manifest.json +2239 -176
- package/dist/output/auth-guidance.d.ts +14 -0
- package/dist/output/auth-guidance.d.ts.map +1 -0
- package/dist/output/auth-guidance.js +50 -0
- package/dist/output/auth-guidance.js.map +1 -0
- package/dist/output/error-map.d.ts +1 -1
- package/dist/output/error-map.d.ts.map +1 -1
- package/dist/output/error-map.js +28 -4
- package/dist/output/error-map.js.map +1 -1
- package/dist/output/next-actions.d.ts.map +1 -1
- package/dist/output/next-actions.js +19 -3
- package/dist/output/next-actions.js.map +1 -1
- package/dist/registry.d.ts +18 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +5 -0
- package/dist/registry.js.map +1 -1
- package/dist/social/browser-errors.d.ts +13 -0
- package/dist/social/browser-errors.d.ts.map +1 -0
- package/dist/social/browser-errors.js +36 -0
- package/dist/social/browser-errors.js.map +1 -0
- package/dist/social/capabilities.d.ts +29 -0
- package/dist/social/capabilities.d.ts.map +1 -0
- package/dist/social/capabilities.js +448 -0
- package/dist/social/capabilities.js.map +1 -0
- package/dist/social/comments.d.ts +26 -0
- package/dist/social/comments.d.ts.map +1 -0
- package/dist/social/comments.js +97 -0
- package/dist/social/comments.js.map +1 -0
- package/dist/social/video-text.d.ts +27 -0
- package/dist/social/video-text.d.ts.map +1 -0
- package/dist/social/video-text.js +140 -0
- package/dist/social/video-text.js.map +1 -0
- package/dist/types/patent.d.ts +160 -0
- package/dist/types/patent.d.ts.map +1 -0
- package/dist/types/patent.js +16 -0
- package/dist/types/patent.js.map +1 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -4
- package/server.json +3 -3
- package/skills/unicli/SKILL.md +1 -1
- package/skills/unicli-claude-code/SKILL.md +1 -1
- package/skills/unicli-hermes/SKILL.md +1 -1
- package/src/adapters/bilibili/comments-tree.test.ts +41 -0
- package/src/adapters/bilibili/comments.ts +78 -4
- package/src/adapters/bilibili/compat.ts +5 -2
- package/src/adapters/bilibili/download.ts +7 -4
- package/src/adapters/bilibili/wbi.ts +6 -3
- package/src/adapters/brave/search.yaml +53 -0
- package/src/adapters/cipo/_shared.ts +98 -0
- package/src/adapters/cipo/get.ts +188 -0
- package/src/adapters/cipo/legal-status.ts +148 -0
- package/src/adapters/cipo/search.ts +195 -0
- package/src/adapters/cnipa/_shared.ts +138 -0
- package/src/adapters/cnipa/get.ts +199 -0
- package/src/adapters/cnipa/legal-status.ts +162 -0
- package/src/adapters/cnipa/search.ts +229 -0
- package/src/adapters/dpma/get.yaml +67 -0
- package/src/adapters/dpma/search.yaml +77 -0
- package/src/adapters/duckduckgo/search.yaml +54 -0
- package/src/adapters/duckduckgo/suggest.yaml +52 -0
- package/src/adapters/epo/family.yaml +69 -0
- package/src/adapters/epo/get.yaml +74 -0
- package/src/adapters/epo/legal-status.yaml +63 -0
- package/src/adapters/epo/search.yaml +84 -0
- package/src/adapters/espacenet/_shared.ts +98 -0
- package/src/adapters/espacenet/family.ts +161 -0
- package/src/adapters/espacenet/get.ts +185 -0
- package/src/adapters/espacenet/legal-status.ts +151 -0
- package/src/adapters/espacenet/search.ts +229 -0
- package/src/adapters/facebook/subtitles.ts +44 -0
- package/src/adapters/fips/_shared.ts +109 -0
- package/src/adapters/fips/get.ts +186 -0
- package/src/adapters/fips/search.ts +195 -0
- package/src/adapters/freepatentsonline-web/_shared.ts +273 -0
- package/src/adapters/freepatentsonline-web/get.ts +144 -0
- package/src/adapters/freepatentsonline-web/search.ts +170 -0
- package/src/adapters/google-patents-bq/prior-art.yaml +80 -0
- package/src/adapters/google-patents-bq/search.yaml +97 -0
- package/src/adapters/google-patents-web/_shared.ts +242 -0
- package/src/adapters/google-patents-web/get.ts +224 -0
- package/src/adapters/google-patents-web/search.ts +196 -0
- package/src/adapters/inpi-br/_shared.ts +98 -0
- package/src/adapters/inpi-br/get.ts +193 -0
- package/src/adapters/inpi-br/search.ts +206 -0
- package/src/adapters/inpi-fr/get.yaml +62 -0
- package/src/adapters/inpi-fr/search.yaml +74 -0
- package/src/adapters/instagram/subtitles.ts +44 -0
- package/src/adapters/ipaustralia/get.yaml +67 -0
- package/src/adapters/ipaustralia/search.yaml +74 -0
- package/src/adapters/jpo/get.yaml +63 -0
- package/src/adapters/jpo/search.yaml +76 -0
- package/src/adapters/kipris/get.yaml +69 -0
- package/src/adapters/kipris/legal-status.yaml +58 -0
- package/src/adapters/kipris/search.yaml +79 -0
- package/src/adapters/lens/get.yaml +64 -0
- package/src/adapters/lens/search.yaml +82 -0
- package/src/adapters/mastodon/statuses.test.ts +82 -0
- package/src/adapters/mastodon/statuses.ts +208 -0
- package/src/adapters/patsnap/get.yaml +65 -0
- package/src/adapters/patsnap/search.yaml +77 -0
- package/src/adapters/pqai/prior-art.yaml +59 -0
- package/src/adapters/pqai/search.yaml +60 -0
- package/src/adapters/reddit/comments-tree.test.ts +79 -0
- package/src/adapters/reddit/comments.ts +159 -0
- package/src/adapters/threads/post.test.ts +64 -0
- package/src/adapters/threads/post.ts +366 -0
- package/src/adapters/threads/user.yaml +73 -0
- package/src/adapters/tiktok/subtitles.ts +44 -0
- package/src/adapters/twitter/accept.ts +5 -2
- package/src/adapters/twitter/browser-fallback.ts +138 -0
- package/src/adapters/twitter/browser-state.ts +74 -0
- package/src/adapters/twitter/client.ts +51 -21
- package/src/adapters/twitter/reply-dm.ts +5 -2
- package/src/adapters/twitter/reply.ts +1 -0
- package/src/adapters/twitter/search.ts +12 -38
- package/src/adapters/twitter/thread.test.ts +43 -0
- package/src/adapters/twitter/thread.ts +44 -2
- package/src/adapters/twitter/trending.ts +14 -95
- package/src/adapters/ukipo/info.yaml +43 -0
- package/src/adapters/uspto/get.yaml +67 -0
- package/src/adapters/uspto/legal-status.yaml +58 -0
- package/src/adapters/uspto/search.yaml +88 -0
- package/src/adapters/wipo-patentscope/info.yaml +43 -0
- package/src/adapters/xiaohongshu/browser-state.ts +95 -0
- package/src/adapters/xiaohongshu/comments.ts +29 -6
- package/src/adapters/xiaohongshu/download.ts +60 -11
- package/src/adapters/xiaohongshu/search.ts +18 -6
- package/src/adapters/xiaohongshu/trending.ts +112 -0
- package/src/adapters/yahoo/search.yaml +52 -0
- package/src/adapters/youtube/comments-microformat.test.ts +35 -0
- package/src/adapters/youtube/comments-tree.test.ts +74 -0
- package/src/adapters/youtube/comments.ts +166 -12
- package/src/adapters/youtube/subtitles.ts +44 -0
- package/src/adapters/yt-dlp/subtitles.ts +43 -0
- package/src/adapters/zhihu/answer-detail.test.ts +83 -0
- package/src/adapters/zhihu/answer-detail.ts +275 -0
- package/src/adapters/zhihu/comment-tree.test.ts +57 -0
- package/src/adapters/zhihu/comment.ts +186 -0
- package/src/adapters/zhihu/recommend.test.ts +65 -0
- package/src/adapters/zhihu/recommend.ts +207 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::cipo::search
|
|
3
|
+
* @does Browser-driven search of the Canadian Patents Database at cipo.ic.gc.ca/opic-cipo/cpd/eng/ — CIPO exposes no general open API for the patents corpus, so the browser route is the only programmatic surface.
|
|
4
|
+
* @needs src/engine/transport/mcp-browser.ts, src/engine/normalizer/patent-envelope.ts, src/adapters/cipo/_shared.ts, src/registry.ts
|
|
5
|
+
* @feeds src/commands/patent.ts (capability tag patent.search)
|
|
6
|
+
* @breaks PATENT_UNSUPPORTED_QUERY (empty), PATENT_NOT_FOUND (empty result), PATENT_API_DEPRECATED with MCP_BUS_MISSING (no outbound MCP transport)
|
|
7
|
+
* @invariants rows are canonical PatentRecord; source_adapter='cipo'
|
|
8
|
+
* @side-effects controls Chrome via MCP
|
|
9
|
+
* @perf single navigate + evaluate
|
|
10
|
+
* @concurrency safe
|
|
11
|
+
* @test tests/unit/adapters/cipo/search.test.ts
|
|
12
|
+
* @stability experimental
|
|
13
|
+
* @since 2026-05-18
|
|
14
|
+
* @verification browser-only
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { cli, Strategy } from "../../registry.js";
|
|
18
|
+
import { TransportError } from "../../engine/transport/mcp-browser.js";
|
|
19
|
+
import { assemblePatentRecord } from "../../engine/normalizer/patent-envelope.js";
|
|
20
|
+
import {
|
|
21
|
+
cipoEnvelope,
|
|
22
|
+
cipoNavigateAndExtract,
|
|
23
|
+
transportErrorToCipoEnvelope,
|
|
24
|
+
} from "./_shared.js";
|
|
25
|
+
|
|
26
|
+
const ADAPTER_PATH = "src/adapters/cipo/search.ts";
|
|
27
|
+
const CIPO_SEARCH_URL =
|
|
28
|
+
"https://cipo.ic.gc.ca/opic-cipo/cpd/eng/search/basic.html";
|
|
29
|
+
|
|
30
|
+
interface CipoRow {
|
|
31
|
+
publication_number: string;
|
|
32
|
+
title?: string;
|
|
33
|
+
applicant?: string;
|
|
34
|
+
publication_date?: string;
|
|
35
|
+
source_url?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const EXTRACTOR = `(() => {
|
|
39
|
+
const rows = Array.from(
|
|
40
|
+
document.querySelectorAll('table.results tr.result-row, .search-results .result')
|
|
41
|
+
).map((el) => {
|
|
42
|
+
const text = (sel) => {
|
|
43
|
+
const node = el.querySelector(sel);
|
|
44
|
+
return node ? (node.textContent || '').trim() : '';
|
|
45
|
+
};
|
|
46
|
+
const linkEl = el.querySelector('a[href*="patent/"], a[href]');
|
|
47
|
+
return {
|
|
48
|
+
publication_number: text('.patent-number, td.patent-no, [data-field="patentNo"]'),
|
|
49
|
+
title: text('.patent-title, td.title, [data-field="title"]'),
|
|
50
|
+
applicant: text('.applicant-name, td.applicant, [data-field="applicant"]'),
|
|
51
|
+
publication_date: text('.publication-date, td.pubDate, [data-field="pubDate"]'),
|
|
52
|
+
source_url: linkEl ? linkEl.href : '',
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
return { rows };
|
|
56
|
+
})()`;
|
|
57
|
+
|
|
58
|
+
function buildSearchUrl(query: string): string {
|
|
59
|
+
const params = new URLSearchParams({ search: query });
|
|
60
|
+
return `${CIPO_SEARCH_URL}?${params.toString()}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function runCipoSearch(kwargs: {
|
|
64
|
+
query: string;
|
|
65
|
+
limit?: number;
|
|
66
|
+
}): Promise<unknown[]> {
|
|
67
|
+
const limit =
|
|
68
|
+
typeof kwargs.limit === "number" && Number.isFinite(kwargs.limit)
|
|
69
|
+
? Math.max(1, Math.min(100, Math.floor(kwargs.limit)))
|
|
70
|
+
: 25;
|
|
71
|
+
const query = String(kwargs.query ?? "").trim();
|
|
72
|
+
if (query.length === 0) {
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
envelope: cipoEnvelope(
|
|
76
|
+
"PATENT_UNSUPPORTED_QUERY",
|
|
77
|
+
ADAPTER_PATH,
|
|
78
|
+
"validate",
|
|
79
|
+
"cipo search requires a non-empty query",
|
|
80
|
+
),
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
const url = buildSearchUrl(query);
|
|
85
|
+
let extract: { rows: CipoRow[] };
|
|
86
|
+
try {
|
|
87
|
+
const result = await cipoNavigateAndExtract<{ rows: CipoRow[] }>(
|
|
88
|
+
url,
|
|
89
|
+
EXTRACTOR,
|
|
90
|
+
);
|
|
91
|
+
if (!result.data) {
|
|
92
|
+
return [
|
|
93
|
+
{
|
|
94
|
+
envelope: cipoEnvelope(
|
|
95
|
+
"PATENT_SCHEMA_DRIFT",
|
|
96
|
+
ADAPTER_PATH,
|
|
97
|
+
"evaluate",
|
|
98
|
+
"cipo evaluate returned no data; selector schema likely changed",
|
|
99
|
+
),
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
extract = result.data;
|
|
104
|
+
} catch (err) {
|
|
105
|
+
if (err instanceof TransportError) {
|
|
106
|
+
return [
|
|
107
|
+
{
|
|
108
|
+
envelope: transportErrorToCipoEnvelope(err, ADAPTER_PATH, "navigate"),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
if (extract.rows.length === 0) {
|
|
115
|
+
return [
|
|
116
|
+
{
|
|
117
|
+
envelope: cipoEnvelope(
|
|
118
|
+
"PATENT_NOT_FOUND",
|
|
119
|
+
ADAPTER_PATH,
|
|
120
|
+
"evaluate",
|
|
121
|
+
`cipo returned no rows for query "${query}"`,
|
|
122
|
+
["uspto", "epo"],
|
|
123
|
+
),
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
const out: unknown[] = [];
|
|
128
|
+
for (const row of extract.rows.slice(0, limit)) {
|
|
129
|
+
if (!row.publication_number) continue;
|
|
130
|
+
const normalizedPub = /^CA[-]?/.test(row.publication_number)
|
|
131
|
+
? row.publication_number
|
|
132
|
+
: `CA${row.publication_number}`;
|
|
133
|
+
try {
|
|
134
|
+
out.push(
|
|
135
|
+
assemblePatentRecord({
|
|
136
|
+
publication_number: normalizedPub,
|
|
137
|
+
title: row.title || undefined,
|
|
138
|
+
assignees: row.applicant
|
|
139
|
+
? [{ name: row.applicant, country: "CA" }]
|
|
140
|
+
: undefined,
|
|
141
|
+
publication_date: row.publication_date || undefined,
|
|
142
|
+
source_adapter: "cipo",
|
|
143
|
+
source_url: row.source_url || url,
|
|
144
|
+
} as Parameters<typeof assemblePatentRecord>[0]),
|
|
145
|
+
);
|
|
146
|
+
} catch {
|
|
147
|
+
// Skip malformed rows.
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (out.length === 0) {
|
|
151
|
+
out.push({
|
|
152
|
+
envelope: cipoEnvelope(
|
|
153
|
+
"PATENT_NOT_FOUND",
|
|
154
|
+
ADAPTER_PATH,
|
|
155
|
+
"normalize",
|
|
156
|
+
"cipo returned rows but none carried a valid publication_number",
|
|
157
|
+
["uspto"],
|
|
158
|
+
),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
cli({
|
|
165
|
+
site: "cipo",
|
|
166
|
+
name: "search",
|
|
167
|
+
description: "Search the Canadian Patents Database (CIPO, browser, no API)",
|
|
168
|
+
domain: "cipo.ic.gc.ca",
|
|
169
|
+
strategy: Strategy.PUBLIC,
|
|
170
|
+
adapter_path: ADAPTER_PATH,
|
|
171
|
+
args: [
|
|
172
|
+
{
|
|
173
|
+
name: "query",
|
|
174
|
+
type: "str",
|
|
175
|
+
required: true,
|
|
176
|
+
positional: true,
|
|
177
|
+
description: "Free-text query — matched on title/abstract/applicant",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "limit",
|
|
181
|
+
type: "int",
|
|
182
|
+
default: 25,
|
|
183
|
+
description: "Max results (1-100)",
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
columns: ["publication_number", "title", "publication_date", "source_url"],
|
|
187
|
+
capabilities: [
|
|
188
|
+
"mcp-browser.navigate",
|
|
189
|
+
"mcp-browser.evaluate",
|
|
190
|
+
"patent.search",
|
|
191
|
+
],
|
|
192
|
+
minimum_capability: "mcp-browser.evaluate",
|
|
193
|
+
func: async (_page, kwargs) =>
|
|
194
|
+
runCipoSearch(kwargs as { query: string; limit?: number }),
|
|
195
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::cnipa::_shared
|
|
3
|
+
* @does Shared helpers for CNIPA browser-driven adapter commands — emits structured PATENT_BROWSER_CAPTCHA / MCP_BUS_MISSING / PATENT_NOT_FOUND error rows so adapters never synthesize results from a failed page.
|
|
4
|
+
* @needs src/engine/transport/mcp-browser.ts, src/engine/normalizer/patent-envelope.ts, src/types/patent.ts
|
|
5
|
+
* @feeds src/adapters/cnipa/search.ts, src/adapters/cnipa/get.ts, src/adapters/cnipa/legal-status.ts
|
|
6
|
+
* @breaks none — pure helpers; bubble TransportError out to the registry func contract
|
|
7
|
+
* @invariants every error path maps to a PatentEnvelope with adapter_path stamped; no silent fallbacks
|
|
8
|
+
* @side-effects none
|
|
9
|
+
* @perf n/a
|
|
10
|
+
* @concurrency safe
|
|
11
|
+
* @test covered transitively by src/adapters/cnipa/*.test.ts
|
|
12
|
+
* @stability experimental
|
|
13
|
+
* @since 2026-05-18
|
|
14
|
+
* @verification browser-only
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
TransportError,
|
|
19
|
+
initMcpBrowserTransport,
|
|
20
|
+
mcpBrowserEvaluate,
|
|
21
|
+
mcpBrowserNavigate,
|
|
22
|
+
type McpBrowserResult,
|
|
23
|
+
} from "../../engine/transport/mcp-browser.js";
|
|
24
|
+
import { buildPatentEnvelope } from "../../engine/normalizer/patent-envelope.js";
|
|
25
|
+
import type { PatentEnvelope, PatentErrorCode } from "../../types/patent.js";
|
|
26
|
+
|
|
27
|
+
export interface CnipaErrorRow {
|
|
28
|
+
publication_number: string;
|
|
29
|
+
title: string;
|
|
30
|
+
envelope: PatentEnvelope;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function cnipaEnvelope(
|
|
34
|
+
code: PatentErrorCode,
|
|
35
|
+
adapter_path: string,
|
|
36
|
+
step: string,
|
|
37
|
+
suggestion: string,
|
|
38
|
+
alternatives: string[] = [],
|
|
39
|
+
): PatentEnvelope {
|
|
40
|
+
return buildPatentEnvelope({
|
|
41
|
+
code,
|
|
42
|
+
adapter_path,
|
|
43
|
+
step,
|
|
44
|
+
suggestion,
|
|
45
|
+
alternatives,
|
|
46
|
+
retryable: code === "PATENT_BROWSER_CAPTCHA" ? true : false,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Wrap a TransportError from mcp-browser into a PatentEnvelope row.
|
|
52
|
+
* MCP_BUS_MISSING is the production state today — surfaced honestly so the
|
|
53
|
+
* agent's repair loop knows the engine is missing the outbound MCP transport.
|
|
54
|
+
*/
|
|
55
|
+
export function transportErrorToEnvelope(
|
|
56
|
+
err: TransportError,
|
|
57
|
+
adapter_path: string,
|
|
58
|
+
step: string,
|
|
59
|
+
): PatentEnvelope {
|
|
60
|
+
if (err.code === "MCP_BUS_MISSING") {
|
|
61
|
+
return buildPatentEnvelope({
|
|
62
|
+
code: "PATENT_API_DEPRECATED",
|
|
63
|
+
adapter_path,
|
|
64
|
+
step,
|
|
65
|
+
suggestion:
|
|
66
|
+
"engine has no outbound MCP transport wired into the bus today; install an McpResolver via src/engine/transport/mcp-browser.installMcpResolver() before invoking this adapter, or try a non-browser source first",
|
|
67
|
+
alternatives: ["uspto", "epo", "lens"],
|
|
68
|
+
retryable: false,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return buildPatentEnvelope({
|
|
72
|
+
code: "PATENT_API_DEPRECATED",
|
|
73
|
+
adapter_path,
|
|
74
|
+
step,
|
|
75
|
+
suggestion: `mcp-browser transport error (${err.code}): ${err.message}`,
|
|
76
|
+
alternatives: ["uspto", "epo"],
|
|
77
|
+
retryable: false,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Navigate then evaluate a DOM-extraction expression. Returns the evaluate
|
|
83
|
+
* result on the happy path, or throws a TransportError with a structured
|
|
84
|
+
* code so callers map to a PatentEnvelope row.
|
|
85
|
+
*
|
|
86
|
+
* Adapters MUST treat the result as untrusted and validate shape before
|
|
87
|
+
* normalization — captcha pages tend to return zero-length arrays without
|
|
88
|
+
* throwing.
|
|
89
|
+
*/
|
|
90
|
+
export async function navigateAndExtract<T>(
|
|
91
|
+
url: string,
|
|
92
|
+
expression: string,
|
|
93
|
+
): Promise<McpBrowserResult<T>> {
|
|
94
|
+
const init = await initMcpBrowserTransport();
|
|
95
|
+
if (init.active_server === "none") {
|
|
96
|
+
const code =
|
|
97
|
+
init.reason === "bus-missing" ? "MCP_BUS_MISSING" : "MCP_NO_SERVER";
|
|
98
|
+
throw new TransportError(code, `mcp-browser unavailable (${init.reason})`);
|
|
99
|
+
}
|
|
100
|
+
const navResult = await mcpBrowserNavigate({ url });
|
|
101
|
+
if (!navResult.ok) {
|
|
102
|
+
throw new TransportError(
|
|
103
|
+
navResult.code ?? "MCP_NAVIGATE_FAILED",
|
|
104
|
+
navResult.message ?? "navigate did not succeed",
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
const evalResult = await mcpBrowserEvaluate<T>({
|
|
108
|
+
expression,
|
|
109
|
+
tab: navResult.tab,
|
|
110
|
+
});
|
|
111
|
+
if (!evalResult.ok) {
|
|
112
|
+
throw new TransportError(
|
|
113
|
+
evalResult.code ?? "MCP_EVALUATE_FAILED",
|
|
114
|
+
evalResult.message ?? "evaluate did not succeed",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return evalResult;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Heuristic — CNIPA public-search captcha is a 验证码 iframe. When the
|
|
122
|
+
* evaluator returns a zero-length result array, the safest assumption is
|
|
123
|
+
* captcha; emit a structured envelope rather than a "no results" message
|
|
124
|
+
* the agent cannot disambiguate.
|
|
125
|
+
*/
|
|
126
|
+
export function looksLikeCaptcha(
|
|
127
|
+
rowCount: number,
|
|
128
|
+
htmlMarker?: string,
|
|
129
|
+
): boolean {
|
|
130
|
+
if (rowCount > 0) return false;
|
|
131
|
+
if (!htmlMarker) return true; // empty result on a browser path = assume captcha
|
|
132
|
+
const lowered = htmlMarker.toLowerCase();
|
|
133
|
+
return (
|
|
134
|
+
lowered.includes("captcha") ||
|
|
135
|
+
lowered.includes("验证码") ||
|
|
136
|
+
lowered.includes("verifycode")
|
|
137
|
+
);
|
|
138
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::cnipa::get
|
|
3
|
+
* @does Browser-driven retrieval of a single CNIPA patent document by publication number; loads the detail page through the mcp-browser transport and extracts the bibliographic fields into a PatentRecord.
|
|
4
|
+
* @needs src/engine/transport/mcp-browser.ts, src/engine/normalizer/patent-envelope.ts, src/adapters/cnipa/_shared.ts, src/registry.ts
|
|
5
|
+
* @feeds src/commands/patent.ts (capability tag patent.get)
|
|
6
|
+
* @breaks emits PATENT_INVALID_NUMBER when input is empty or fails canonicalization; emits PATENT_NOT_FOUND when the detail page returns no fields; PATENT_BROWSER_CAPTCHA when the page is captcha-gated; PATENT_API_DEPRECATED with MCP_BUS_MISSING context when no outbound MCP transport is registered
|
|
7
|
+
* @invariants output is a single PatentRecord row when the upstream answers; never falls back to a stub record
|
|
8
|
+
* @side-effects controls the user's Chrome via MCP
|
|
9
|
+
* @perf single navigate + one evaluate
|
|
10
|
+
* @concurrency safe
|
|
11
|
+
* @test tests/unit/adapters/cnipa/search.test.ts (transport-error path) — get-specific tests deferred to integration
|
|
12
|
+
* @stability experimental
|
|
13
|
+
* @since 2026-05-18
|
|
14
|
+
* @verification browser-only
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { cli, Strategy } from "../../registry.js";
|
|
18
|
+
import { TransportError } from "../../engine/transport/mcp-browser.js";
|
|
19
|
+
import { assemblePatentRecord } from "../../engine/normalizer/patent-envelope.js";
|
|
20
|
+
import {
|
|
21
|
+
cnipaEnvelope,
|
|
22
|
+
looksLikeCaptcha,
|
|
23
|
+
navigateAndExtract,
|
|
24
|
+
transportErrorToEnvelope,
|
|
25
|
+
} from "./_shared.js";
|
|
26
|
+
|
|
27
|
+
const ADAPTER_PATH = "src/adapters/cnipa/get.ts";
|
|
28
|
+
const CNIPA_DETAIL_URL =
|
|
29
|
+
"https://pss-system.cponline.cnipa.gov.cn/conventionalDetail";
|
|
30
|
+
|
|
31
|
+
interface CnipaDetail {
|
|
32
|
+
publication_number?: string;
|
|
33
|
+
application_number?: string;
|
|
34
|
+
title?: string;
|
|
35
|
+
abstract?: string;
|
|
36
|
+
applicant?: string;
|
|
37
|
+
inventor?: string;
|
|
38
|
+
publication_date?: string;
|
|
39
|
+
filing_date?: string;
|
|
40
|
+
grant_date?: string;
|
|
41
|
+
legal_status?: string;
|
|
42
|
+
source_url?: string;
|
|
43
|
+
html_marker?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const DETAIL_EXTRACTOR = `(() => {
|
|
47
|
+
const text = (sel) => {
|
|
48
|
+
const node = document.querySelector(sel);
|
|
49
|
+
return node ? (node.textContent || '').trim() : '';
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
publication_number: text('[data-field="pubNo"], .publication-number'),
|
|
53
|
+
application_number: text('[data-field="appNo"], .application-number'),
|
|
54
|
+
title: text('[data-field="title"], h1.title, .invention-title'),
|
|
55
|
+
abstract: text('[data-field="abstract"], .abstract'),
|
|
56
|
+
applicant: text('[data-field="applicant"], .applicant'),
|
|
57
|
+
inventor: text('[data-field="inventor"], .inventor'),
|
|
58
|
+
publication_date: text('[data-field="pubDate"], .publication-date'),
|
|
59
|
+
filing_date: text('[data-field="appDate"], .filing-date'),
|
|
60
|
+
grant_date: text('[data-field="grantDate"], .grant-date'),
|
|
61
|
+
legal_status: text('[data-field="legalStatus"], .legal-status'),
|
|
62
|
+
source_url: location.href,
|
|
63
|
+
html_marker: document.body && document.body.innerText
|
|
64
|
+
? document.body.innerText.slice(0, 200)
|
|
65
|
+
: '',
|
|
66
|
+
};
|
|
67
|
+
})()`;
|
|
68
|
+
|
|
69
|
+
export async function runCnipaGet(kwargs: {
|
|
70
|
+
publication_number: string;
|
|
71
|
+
}): Promise<unknown[]> {
|
|
72
|
+
const pubNo = String(kwargs.publication_number ?? "").trim();
|
|
73
|
+
if (pubNo.length === 0) {
|
|
74
|
+
return [
|
|
75
|
+
{
|
|
76
|
+
envelope: cnipaEnvelope(
|
|
77
|
+
"PATENT_INVALID_NUMBER",
|
|
78
|
+
ADAPTER_PATH,
|
|
79
|
+
"validate",
|
|
80
|
+
"cnipa get requires a non-empty publication_number",
|
|
81
|
+
),
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
const url = `${CNIPA_DETAIL_URL}?pubNo=${encodeURIComponent(pubNo)}`;
|
|
86
|
+
let detail: CnipaDetail;
|
|
87
|
+
try {
|
|
88
|
+
const result = await navigateAndExtract<CnipaDetail>(url, DETAIL_EXTRACTOR);
|
|
89
|
+
if (!result.data) {
|
|
90
|
+
return [
|
|
91
|
+
{
|
|
92
|
+
envelope: cnipaEnvelope(
|
|
93
|
+
"PATENT_SCHEMA_DRIFT",
|
|
94
|
+
ADAPTER_PATH,
|
|
95
|
+
"evaluate",
|
|
96
|
+
"mcp-browser evaluate returned no data; check selector schema in src/adapters/cnipa/get.ts",
|
|
97
|
+
),
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
detail = result.data;
|
|
102
|
+
} catch (err) {
|
|
103
|
+
if (err instanceof TransportError) {
|
|
104
|
+
return [
|
|
105
|
+
{
|
|
106
|
+
envelope: transportErrorToEnvelope(err, ADAPTER_PATH, "navigate"),
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Captcha heuristic: detail page returned without any of the expected
|
|
114
|
+
// bibliographic fields → almost certainly captcha-gated.
|
|
115
|
+
const fieldCount = [
|
|
116
|
+
detail.publication_number,
|
|
117
|
+
detail.title,
|
|
118
|
+
detail.application_number,
|
|
119
|
+
detail.publication_date,
|
|
120
|
+
].filter(Boolean).length;
|
|
121
|
+
if (looksLikeCaptcha(fieldCount, detail.html_marker)) {
|
|
122
|
+
return [
|
|
123
|
+
{
|
|
124
|
+
envelope: cnipaEnvelope(
|
|
125
|
+
"PATENT_BROWSER_CAPTCHA",
|
|
126
|
+
ADAPTER_PATH,
|
|
127
|
+
"evaluate",
|
|
128
|
+
"CNIPA detail page returned no bibliographic fields; solve the 验证码 in the open browser tab and re-run",
|
|
129
|
+
["espacenet"],
|
|
130
|
+
),
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const canonicalPubNo = detail.publication_number || pubNo;
|
|
136
|
+
try {
|
|
137
|
+
return [
|
|
138
|
+
assemblePatentRecord({
|
|
139
|
+
publication_number: canonicalPubNo,
|
|
140
|
+
application_number: detail.application_number || undefined,
|
|
141
|
+
title: detail.title || undefined,
|
|
142
|
+
abstract: detail.abstract || undefined,
|
|
143
|
+
assignees: detail.applicant
|
|
144
|
+
? [{ name: detail.applicant, country: "CN" }]
|
|
145
|
+
: undefined,
|
|
146
|
+
inventors: detail.inventor
|
|
147
|
+
? [{ name: detail.inventor, country: "CN" }]
|
|
148
|
+
: undefined,
|
|
149
|
+
publication_date: detail.publication_date || undefined,
|
|
150
|
+
filing_date: detail.filing_date || undefined,
|
|
151
|
+
grant_date: detail.grant_date || undefined,
|
|
152
|
+
legal_status: detail.legal_status || undefined,
|
|
153
|
+
source_adapter: "cnipa",
|
|
154
|
+
source_url: detail.source_url || url,
|
|
155
|
+
} as Parameters<typeof assemblePatentRecord>[0]),
|
|
156
|
+
];
|
|
157
|
+
} catch {
|
|
158
|
+
return [
|
|
159
|
+
{
|
|
160
|
+
envelope: cnipaEnvelope(
|
|
161
|
+
"PATENT_NOT_FOUND",
|
|
162
|
+
ADAPTER_PATH,
|
|
163
|
+
"normalize",
|
|
164
|
+
`cnipa detail page did not yield a valid publication_number for "${pubNo}"`,
|
|
165
|
+
["espacenet"],
|
|
166
|
+
),
|
|
167
|
+
},
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
cli({
|
|
173
|
+
site: "cnipa",
|
|
174
|
+
name: "get",
|
|
175
|
+
description: "Retrieve a single CNIPA patent document by publication number",
|
|
176
|
+
domain: "pss-system.cponline.cnipa.gov.cn",
|
|
177
|
+
strategy: Strategy.PUBLIC,
|
|
178
|
+
adapter_path: ADAPTER_PATH,
|
|
179
|
+
args: [
|
|
180
|
+
{
|
|
181
|
+
name: "publication_number",
|
|
182
|
+
type: "str",
|
|
183
|
+
required: true,
|
|
184
|
+
positional: true,
|
|
185
|
+
description: "ST.16 publication number (e.g. CN114123456A)",
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
columns: [
|
|
189
|
+
"publication_number",
|
|
190
|
+
"title",
|
|
191
|
+
"publication_date",
|
|
192
|
+
"legal_status",
|
|
193
|
+
"source_url",
|
|
194
|
+
],
|
|
195
|
+
capabilities: ["mcp-browser.navigate", "mcp-browser.evaluate", "patent.get"],
|
|
196
|
+
minimum_capability: "mcp-browser.evaluate",
|
|
197
|
+
func: async (_page, kwargs) =>
|
|
198
|
+
runCnipaGet(kwargs as { publication_number: string }),
|
|
199
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::cnipa::legal-status
|
|
3
|
+
* @does Browser-driven CNIPA legal-status lookup — scrapes the prosecution timeline + grant status from the 公众查询 detail page; emits structured envelopes for captcha / not-found / MCP-bus gaps.
|
|
4
|
+
* @needs src/engine/transport/mcp-browser.ts, src/engine/normalizer/patent-envelope.ts, src/adapters/cnipa/_shared.ts, src/registry.ts
|
|
5
|
+
* @feeds src/commands/patent.ts (capability tag patent.legal-status)
|
|
6
|
+
* @breaks emits PATENT_INVALID_NUMBER, PATENT_BROWSER_CAPTCHA, PATENT_API_DEPRECATED (with MCP_BUS_MISSING context); never synthesizes a status
|
|
7
|
+
* @invariants output row carries `legal_status` exactly as scraped; no normalization beyond trim
|
|
8
|
+
* @side-effects controls Chrome via MCP
|
|
9
|
+
* @perf single navigate + evaluate
|
|
10
|
+
* @concurrency safe
|
|
11
|
+
* @test tests/unit/adapters/cnipa/search.test.ts (shared transport-error path)
|
|
12
|
+
* @stability experimental
|
|
13
|
+
* @since 2026-05-18
|
|
14
|
+
* @verification browser-only
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { cli, Strategy } from "../../registry.js";
|
|
18
|
+
import { TransportError } from "../../engine/transport/mcp-browser.js";
|
|
19
|
+
import {
|
|
20
|
+
cnipaEnvelope,
|
|
21
|
+
looksLikeCaptcha,
|
|
22
|
+
navigateAndExtract,
|
|
23
|
+
transportErrorToEnvelope,
|
|
24
|
+
} from "./_shared.js";
|
|
25
|
+
|
|
26
|
+
const ADAPTER_PATH = "src/adapters/cnipa/legal-status.ts";
|
|
27
|
+
const CNIPA_LEGAL_URL = "https://pss-system.cponline.cnipa.gov.cn/legalStatus";
|
|
28
|
+
|
|
29
|
+
interface CnipaLegalRow {
|
|
30
|
+
publication_number: string;
|
|
31
|
+
legal_status?: string;
|
|
32
|
+
status_date?: string;
|
|
33
|
+
source_url?: string;
|
|
34
|
+
html_marker?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const EXTRACTOR = `(() => {
|
|
38
|
+
const text = (sel) => {
|
|
39
|
+
const node = document.querySelector(sel);
|
|
40
|
+
return node ? (node.textContent || '').trim() : '';
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
publication_number: text('[data-field="pubNo"], .publication-number'),
|
|
44
|
+
legal_status: text('[data-field="legalStatus"], .legal-status'),
|
|
45
|
+
status_date: text('[data-field="statusDate"], .status-date'),
|
|
46
|
+
source_url: location.href,
|
|
47
|
+
html_marker: document.body && document.body.innerText
|
|
48
|
+
? document.body.innerText.slice(0, 200)
|
|
49
|
+
: '',
|
|
50
|
+
};
|
|
51
|
+
})()`;
|
|
52
|
+
|
|
53
|
+
export async function runCnipaLegalStatus(kwargs: {
|
|
54
|
+
publication_number: string;
|
|
55
|
+
}): Promise<unknown[]> {
|
|
56
|
+
const pubNo = String(kwargs.publication_number ?? "").trim();
|
|
57
|
+
if (pubNo.length === 0) {
|
|
58
|
+
return [
|
|
59
|
+
{
|
|
60
|
+
envelope: cnipaEnvelope(
|
|
61
|
+
"PATENT_INVALID_NUMBER",
|
|
62
|
+
ADAPTER_PATH,
|
|
63
|
+
"validate",
|
|
64
|
+
"cnipa legal-status requires a non-empty publication_number",
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
const url = `${CNIPA_LEGAL_URL}?pubNo=${encodeURIComponent(pubNo)}`;
|
|
70
|
+
let detail: CnipaLegalRow;
|
|
71
|
+
try {
|
|
72
|
+
const result = await navigateAndExtract<CnipaLegalRow>(url, EXTRACTOR);
|
|
73
|
+
if (!result.data) {
|
|
74
|
+
return [
|
|
75
|
+
{
|
|
76
|
+
envelope: cnipaEnvelope(
|
|
77
|
+
"PATENT_SCHEMA_DRIFT",
|
|
78
|
+
ADAPTER_PATH,
|
|
79
|
+
"evaluate",
|
|
80
|
+
"mcp-browser evaluate returned no data",
|
|
81
|
+
),
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
detail = result.data;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
if (err instanceof TransportError) {
|
|
88
|
+
return [
|
|
89
|
+
{
|
|
90
|
+
envelope: transportErrorToEnvelope(err, ADAPTER_PATH, "navigate"),
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
const fieldCount = [detail.publication_number, detail.legal_status].filter(
|
|
97
|
+
Boolean,
|
|
98
|
+
).length;
|
|
99
|
+
if (looksLikeCaptcha(fieldCount, detail.html_marker)) {
|
|
100
|
+
return [
|
|
101
|
+
{
|
|
102
|
+
envelope: cnipaEnvelope(
|
|
103
|
+
"PATENT_BROWSER_CAPTCHA",
|
|
104
|
+
ADAPTER_PATH,
|
|
105
|
+
"evaluate",
|
|
106
|
+
"CNIPA legal-status page returned no fields; solve the 验证码 and re-run",
|
|
107
|
+
["espacenet"],
|
|
108
|
+
),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
if (!detail.legal_status) {
|
|
113
|
+
return [
|
|
114
|
+
{
|
|
115
|
+
envelope: cnipaEnvelope(
|
|
116
|
+
"PATENT_NOT_FOUND",
|
|
117
|
+
ADAPTER_PATH,
|
|
118
|
+
"normalize",
|
|
119
|
+
`cnipa returned no legal_status for "${pubNo}"`,
|
|
120
|
+
["espacenet"],
|
|
121
|
+
),
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
return [
|
|
126
|
+
{
|
|
127
|
+
publication_number: detail.publication_number || pubNo,
|
|
128
|
+
legal_status: detail.legal_status,
|
|
129
|
+
status_date: detail.status_date,
|
|
130
|
+
source_adapter: "cnipa",
|
|
131
|
+
source_url: detail.source_url || url,
|
|
132
|
+
retrieved_at: new Date().toISOString(),
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
cli({
|
|
138
|
+
site: "cnipa",
|
|
139
|
+
name: "legal-status",
|
|
140
|
+
description: "CNIPA legal-status / prosecution timeline lookup (browser)",
|
|
141
|
+
domain: "pss-system.cponline.cnipa.gov.cn",
|
|
142
|
+
strategy: Strategy.PUBLIC,
|
|
143
|
+
adapter_path: ADAPTER_PATH,
|
|
144
|
+
args: [
|
|
145
|
+
{
|
|
146
|
+
name: "publication_number",
|
|
147
|
+
type: "str",
|
|
148
|
+
required: true,
|
|
149
|
+
positional: true,
|
|
150
|
+
description: "ST.16 publication number",
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
columns: ["publication_number", "legal_status", "status_date", "source_url"],
|
|
154
|
+
capabilities: [
|
|
155
|
+
"mcp-browser.navigate",
|
|
156
|
+
"mcp-browser.evaluate",
|
|
157
|
+
"patent.legal-status",
|
|
158
|
+
],
|
|
159
|
+
minimum_capability: "mcp-browser.evaluate",
|
|
160
|
+
func: async (_page, kwargs) =>
|
|
161
|
+
runCnipaLegalStatus(kwargs as { publication_number: string }),
|
|
162
|
+
});
|