@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,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::fips::get
|
|
3
|
+
* @does Browser-driven Rospatent FIPS single-document retrieval at www.fips.ru — extracts the bibliographic block from the document detail page.
|
|
4
|
+
* @needs src/engine/transport/mcp-browser.ts, src/engine/normalizer/patent-envelope.ts, src/adapters/fips/_shared.ts, src/registry.ts
|
|
5
|
+
* @feeds src/commands/patent.ts (capability tag patent.get)
|
|
6
|
+
* @breaks PATENT_INVALID_NUMBER, PATENT_NOT_FOUND, PATENT_API_DEPRECATED with MCP_BUS_MISSING, PATENT_REGION_BLOCKED when .ru is unreachable
|
|
7
|
+
* @invariants output row is a canonical PatentRecord
|
|
8
|
+
* @side-effects controls Chrome via MCP
|
|
9
|
+
* @perf single navigate + evaluate
|
|
10
|
+
* @concurrency safe
|
|
11
|
+
* @test tests/unit/adapters/fips/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 { assemblePatentRecord } from "../../engine/normalizer/patent-envelope.js";
|
|
20
|
+
import {
|
|
21
|
+
fipsEnvelope,
|
|
22
|
+
fipsNavigateAndExtract,
|
|
23
|
+
transportErrorToFipsEnvelope,
|
|
24
|
+
} from "./_shared.js";
|
|
25
|
+
|
|
26
|
+
const ADAPTER_PATH = "src/adapters/fips/get.ts";
|
|
27
|
+
|
|
28
|
+
interface FipsDetail {
|
|
29
|
+
publication_number?: string;
|
|
30
|
+
application_number?: string;
|
|
31
|
+
title?: string;
|
|
32
|
+
abstract?: string;
|
|
33
|
+
applicant?: string;
|
|
34
|
+
inventor?: string;
|
|
35
|
+
publication_date?: string;
|
|
36
|
+
filing_date?: string;
|
|
37
|
+
source_url?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const DETAIL_EXTRACTOR = `(() => {
|
|
41
|
+
const text = (sel) => {
|
|
42
|
+
const node = document.querySelector(sel);
|
|
43
|
+
return node ? (node.textContent || '').trim() : '';
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
publication_number: text('.doc-number, [data-field="docNumber"]'),
|
|
47
|
+
application_number: text('.app-number, [data-field="appNumber"]'),
|
|
48
|
+
title: text('.doc-title, h1.title, [data-field="title"]'),
|
|
49
|
+
abstract: text('.abstract, [data-field="abstract"]'),
|
|
50
|
+
applicant: text('.applicant, [data-field="applicant"]'),
|
|
51
|
+
inventor: text('.inventor, [data-field="inventor"]'),
|
|
52
|
+
publication_date: text('.publication-date, [data-field="pubDate"]'),
|
|
53
|
+
filing_date: text('.filing-date, [data-field="filingDate"]'),
|
|
54
|
+
source_url: location.href,
|
|
55
|
+
};
|
|
56
|
+
})()`;
|
|
57
|
+
|
|
58
|
+
function detailUrlFor(pubNo: string): string {
|
|
59
|
+
const stripped = pubNo.replace(/^RU[-]?/, "");
|
|
60
|
+
const params = new URLSearchParams({ DB: "RUPAT", DocNumber: stripped });
|
|
61
|
+
return `https://www1.fips.ru/fips_servl/fips_servlet?${params.toString()}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function runFipsGet(kwargs: {
|
|
65
|
+
publication_number: string;
|
|
66
|
+
}): Promise<unknown[]> {
|
|
67
|
+
const pubNo = String(kwargs.publication_number ?? "").trim();
|
|
68
|
+
if (pubNo.length === 0) {
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
envelope: fipsEnvelope(
|
|
72
|
+
"PATENT_INVALID_NUMBER",
|
|
73
|
+
ADAPTER_PATH,
|
|
74
|
+
"validate",
|
|
75
|
+
"fips get requires a non-empty publication_number",
|
|
76
|
+
),
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
const url = detailUrlFor(pubNo);
|
|
81
|
+
let detail: FipsDetail;
|
|
82
|
+
try {
|
|
83
|
+
const result = await fipsNavigateAndExtract<FipsDetail>(
|
|
84
|
+
url,
|
|
85
|
+
DETAIL_EXTRACTOR,
|
|
86
|
+
);
|
|
87
|
+
if (!result.data) {
|
|
88
|
+
return [
|
|
89
|
+
{
|
|
90
|
+
envelope: fipsEnvelope(
|
|
91
|
+
"PATENT_SCHEMA_DRIFT",
|
|
92
|
+
ADAPTER_PATH,
|
|
93
|
+
"evaluate",
|
|
94
|
+
"fips detail evaluate returned no data",
|
|
95
|
+
),
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
detail = result.data;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
if (err instanceof TransportError) {
|
|
102
|
+
return [
|
|
103
|
+
{
|
|
104
|
+
envelope: transportErrorToFipsEnvelope(err, ADAPTER_PATH, "navigate"),
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
if (!detail.publication_number && !detail.title) {
|
|
111
|
+
return [
|
|
112
|
+
{
|
|
113
|
+
envelope: fipsEnvelope(
|
|
114
|
+
"PATENT_NOT_FOUND",
|
|
115
|
+
ADAPTER_PATH,
|
|
116
|
+
"evaluate",
|
|
117
|
+
`fips detail page for "${pubNo}" yielded no bibliographic fields`,
|
|
118
|
+
["espacenet"],
|
|
119
|
+
),
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
const rawPubNo = detail.publication_number || pubNo;
|
|
124
|
+
const canonicalPubNo = /^RU[-]?/.test(rawPubNo) ? rawPubNo : `RU${rawPubNo}`;
|
|
125
|
+
try {
|
|
126
|
+
return [
|
|
127
|
+
assemblePatentRecord({
|
|
128
|
+
publication_number: canonicalPubNo,
|
|
129
|
+
application_number: detail.application_number || undefined,
|
|
130
|
+
title: detail.title || undefined,
|
|
131
|
+
abstract: detail.abstract || undefined,
|
|
132
|
+
assignees: detail.applicant
|
|
133
|
+
? [{ name: detail.applicant, country: "RU" }]
|
|
134
|
+
: undefined,
|
|
135
|
+
inventors: detail.inventor
|
|
136
|
+
? [{ name: detail.inventor, country: "RU" }]
|
|
137
|
+
: undefined,
|
|
138
|
+
publication_date: detail.publication_date || undefined,
|
|
139
|
+
filing_date: detail.filing_date || undefined,
|
|
140
|
+
source_adapter: "fips",
|
|
141
|
+
source_url: detail.source_url || url,
|
|
142
|
+
} as Parameters<typeof assemblePatentRecord>[0]),
|
|
143
|
+
];
|
|
144
|
+
} catch {
|
|
145
|
+
return [
|
|
146
|
+
{
|
|
147
|
+
envelope: fipsEnvelope(
|
|
148
|
+
"PATENT_NOT_FOUND",
|
|
149
|
+
ADAPTER_PATH,
|
|
150
|
+
"normalize",
|
|
151
|
+
`fips detail page for "${pubNo}" did not yield a valid publication_number`,
|
|
152
|
+
["espacenet"],
|
|
153
|
+
),
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
cli({
|
|
160
|
+
site: "fips",
|
|
161
|
+
name: "get",
|
|
162
|
+
description: "Retrieve a single Rospatent FIPS document (browser-only)",
|
|
163
|
+
domain: "www.fips.ru",
|
|
164
|
+
strategy: Strategy.PUBLIC,
|
|
165
|
+
adapter_path: ADAPTER_PATH,
|
|
166
|
+
args: [
|
|
167
|
+
{
|
|
168
|
+
name: "publication_number",
|
|
169
|
+
type: "str",
|
|
170
|
+
required: true,
|
|
171
|
+
positional: true,
|
|
172
|
+
description: "Russian patent publication number",
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
columns: [
|
|
176
|
+
"publication_number",
|
|
177
|
+
"title",
|
|
178
|
+
"publication_date",
|
|
179
|
+
"filing_date",
|
|
180
|
+
"source_url",
|
|
181
|
+
],
|
|
182
|
+
capabilities: ["mcp-browser.navigate", "mcp-browser.evaluate", "patent.get"],
|
|
183
|
+
minimum_capability: "mcp-browser.evaluate",
|
|
184
|
+
func: async (_page, kwargs) =>
|
|
185
|
+
runFipsGet(kwargs as { publication_number: string }),
|
|
186
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::fips::search
|
|
3
|
+
* @does Browser-driven Rospatent FIPS search at www.fips.ru — Russia's federal IP office has no open API; the FIPS portal is the only programmatic surface.
|
|
4
|
+
* @needs src/engine/transport/mcp-browser.ts, src/engine/normalizer/patent-envelope.ts, src/adapters/fips/_shared.ts, src/registry.ts
|
|
5
|
+
* @feeds src/commands/patent.ts (capability tag patent.search)
|
|
6
|
+
* @breaks PATENT_UNSUPPORTED_QUERY (empty), PATENT_NOT_FOUND (no rows), PATENT_API_DEPRECATED with MCP_BUS_MISSING; PATENT_REGION_BLOCKED is also possible if the browser cannot reach .ru
|
|
7
|
+
* @invariants rows canonicalized into PatentRecord; source_adapter='fips'
|
|
8
|
+
* @side-effects controls Chrome via MCP
|
|
9
|
+
* @perf single navigate + evaluate
|
|
10
|
+
* @concurrency safe
|
|
11
|
+
* @test tests/unit/adapters/fips/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
|
+
fipsEnvelope,
|
|
22
|
+
fipsNavigateAndExtract,
|
|
23
|
+
transportErrorToFipsEnvelope,
|
|
24
|
+
} from "./_shared.js";
|
|
25
|
+
|
|
26
|
+
const ADAPTER_PATH = "src/adapters/fips/search.ts";
|
|
27
|
+
const FIPS_SEARCH_URL = "https://www.fips.ru/iiss/search.xhtml";
|
|
28
|
+
|
|
29
|
+
interface FipsRow {
|
|
30
|
+
publication_number: string;
|
|
31
|
+
title?: string;
|
|
32
|
+
applicant?: string;
|
|
33
|
+
publication_date?: string;
|
|
34
|
+
source_url?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const EXTRACTOR = `(() => {
|
|
38
|
+
const rows = Array.from(
|
|
39
|
+
document.querySelectorAll('table.results tr.result-row, .search-results .item')
|
|
40
|
+
).map((el) => {
|
|
41
|
+
const text = (sel) => {
|
|
42
|
+
const node = el.querySelector(sel);
|
|
43
|
+
return node ? (node.textContent || '').trim() : '';
|
|
44
|
+
};
|
|
45
|
+
const linkEl = el.querySelector('a[href]');
|
|
46
|
+
return {
|
|
47
|
+
publication_number: text('.doc-number, td.number, [data-field="docNumber"]'),
|
|
48
|
+
title: text('.doc-title, td.title, [data-field="title"]'),
|
|
49
|
+
applicant: text('.applicant, td.applicant, [data-field="applicant"]'),
|
|
50
|
+
publication_date: text('.publication-date, td.pubDate, [data-field="pubDate"]'),
|
|
51
|
+
source_url: linkEl ? linkEl.href : '',
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
return { rows };
|
|
55
|
+
})()`;
|
|
56
|
+
|
|
57
|
+
function buildSearchUrl(query: string): string {
|
|
58
|
+
const params = new URLSearchParams({ q: query });
|
|
59
|
+
return `${FIPS_SEARCH_URL}?${params.toString()}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function runFipsSearch(kwargs: {
|
|
63
|
+
query: string;
|
|
64
|
+
limit?: number;
|
|
65
|
+
}): Promise<unknown[]> {
|
|
66
|
+
const limit =
|
|
67
|
+
typeof kwargs.limit === "number" && Number.isFinite(kwargs.limit)
|
|
68
|
+
? Math.max(1, Math.min(100, Math.floor(kwargs.limit)))
|
|
69
|
+
: 25;
|
|
70
|
+
const query = String(kwargs.query ?? "").trim();
|
|
71
|
+
if (query.length === 0) {
|
|
72
|
+
return [
|
|
73
|
+
{
|
|
74
|
+
envelope: fipsEnvelope(
|
|
75
|
+
"PATENT_UNSUPPORTED_QUERY",
|
|
76
|
+
ADAPTER_PATH,
|
|
77
|
+
"validate",
|
|
78
|
+
"fips search requires a non-empty query",
|
|
79
|
+
),
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
const url = buildSearchUrl(query);
|
|
84
|
+
let extract: { rows: FipsRow[] };
|
|
85
|
+
try {
|
|
86
|
+
const result = await fipsNavigateAndExtract<{ rows: FipsRow[] }>(
|
|
87
|
+
url,
|
|
88
|
+
EXTRACTOR,
|
|
89
|
+
);
|
|
90
|
+
if (!result.data) {
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
envelope: fipsEnvelope(
|
|
94
|
+
"PATENT_SCHEMA_DRIFT",
|
|
95
|
+
ADAPTER_PATH,
|
|
96
|
+
"evaluate",
|
|
97
|
+
"fips evaluate returned no data; selectors likely drifted",
|
|
98
|
+
),
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
extract = result.data;
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof TransportError) {
|
|
105
|
+
return [
|
|
106
|
+
{
|
|
107
|
+
envelope: transportErrorToFipsEnvelope(err, ADAPTER_PATH, "navigate"),
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
if (extract.rows.length === 0) {
|
|
114
|
+
return [
|
|
115
|
+
{
|
|
116
|
+
envelope: fipsEnvelope(
|
|
117
|
+
"PATENT_NOT_FOUND",
|
|
118
|
+
ADAPTER_PATH,
|
|
119
|
+
"evaluate",
|
|
120
|
+
`fips returned no rows for query "${query}"`,
|
|
121
|
+
["espacenet"],
|
|
122
|
+
),
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
const out: unknown[] = [];
|
|
127
|
+
for (const row of extract.rows.slice(0, limit)) {
|
|
128
|
+
if (!row.publication_number) continue;
|
|
129
|
+
const normalizedPub = /^RU[-]?/.test(row.publication_number)
|
|
130
|
+
? row.publication_number
|
|
131
|
+
: `RU${row.publication_number}`;
|
|
132
|
+
try {
|
|
133
|
+
out.push(
|
|
134
|
+
assemblePatentRecord({
|
|
135
|
+
publication_number: normalizedPub,
|
|
136
|
+
title: row.title || undefined,
|
|
137
|
+
assignees: row.applicant
|
|
138
|
+
? [{ name: row.applicant, country: "RU" }]
|
|
139
|
+
: undefined,
|
|
140
|
+
publication_date: row.publication_date || undefined,
|
|
141
|
+
source_adapter: "fips",
|
|
142
|
+
source_url: row.source_url || url,
|
|
143
|
+
} as Parameters<typeof assemblePatentRecord>[0]),
|
|
144
|
+
);
|
|
145
|
+
} catch {
|
|
146
|
+
// Skip malformed rows.
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (out.length === 0) {
|
|
150
|
+
out.push({
|
|
151
|
+
envelope: fipsEnvelope(
|
|
152
|
+
"PATENT_NOT_FOUND",
|
|
153
|
+
ADAPTER_PATH,
|
|
154
|
+
"normalize",
|
|
155
|
+
"fips returned rows but none normalized to a valid publication_number",
|
|
156
|
+
["espacenet"],
|
|
157
|
+
),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
cli({
|
|
164
|
+
site: "fips",
|
|
165
|
+
name: "search",
|
|
166
|
+
description:
|
|
167
|
+
"Search Rospatent FIPS Russian patent database (browser, no API)",
|
|
168
|
+
domain: "www.fips.ru",
|
|
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 for FIPS Russian patent search",
|
|
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
|
+
runFipsSearch(kwargs as { query: string; limit?: number }),
|
|
195
|
+
});
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::freepatentsonline-web::_shared
|
|
3
|
+
* @does Shared HTTP + HTML helpers for the KEYLESS FreePatentsOnline adapter. FPO ships zero JS gate and zero auth; we hit the SSR `result.html` listing and `<id>.html` detail pages directly. Kind codes are decoded from FPO's internal URL routing — `/y<YYYY>/<serial>.html` ↔ US published application (ST.16 kind `A1`), `/<id>.html` (compact ID) ↔ US grant (kind code unknown from URL alone — skip), `/<CCNNNNkc>.html` ↔ explicit kind code (use as-is).
|
|
4
|
+
* @needs src/engine/normalizer/patent-envelope.ts, src/types/patent.ts, node:fetch (global)
|
|
5
|
+
* @feeds src/adapters/freepatentsonline-web/search.ts, src/adapters/freepatentsonline-web/get.ts
|
|
6
|
+
* @breaks throws FpoHttpError on non-2xx; pure-HTML parse functions never throw, they return null on selector miss
|
|
7
|
+
* @invariants every outbound request carries a real-browser User-Agent; publication numbers either arrive with an explicit kind code OR are decoded from FPO's URL convention — adapters NEVER fabricate a kind code from era heuristics
|
|
8
|
+
* @side-effects HTTPS egress to www.freepatentsonline.com only
|
|
9
|
+
* @perf 50-150 KB per page; HTML parse is linear regex
|
|
10
|
+
* @concurrency safe — stateless
|
|
11
|
+
* @test verification proof in docs/skills/patent-cookbook.md
|
|
12
|
+
* @stability experimental
|
|
13
|
+
* @since 2026-05-18
|
|
14
|
+
* @verification keyless-best-effort
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const FPO_UA =
|
|
18
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36";
|
|
19
|
+
|
|
20
|
+
export const FPO_ORIGIN = "https://www.freepatentsonline.com";
|
|
21
|
+
|
|
22
|
+
export class FpoHttpError extends Error {
|
|
23
|
+
constructor(
|
|
24
|
+
public readonly status: number,
|
|
25
|
+
public readonly url: string,
|
|
26
|
+
message: string,
|
|
27
|
+
) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = "FpoHttpError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function fetchFpoHtml(url: string): Promise<string> {
|
|
34
|
+
const response = await fetch(url, {
|
|
35
|
+
method: "GET",
|
|
36
|
+
headers: {
|
|
37
|
+
"User-Agent": FPO_UA,
|
|
38
|
+
Accept: "text/html,application/xhtml+xml",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
throw new FpoHttpError(
|
|
43
|
+
response.status,
|
|
44
|
+
url,
|
|
45
|
+
`FreePatentsOnline returned HTTP ${response.status}`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return await response.text();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function buildFpoSearchUrl(query: string, page: number): string {
|
|
52
|
+
const trimmed = query.trim().replace(/\s+/g, "+");
|
|
53
|
+
const safe = encodeURIComponent(trimmed).replace(/%2B/g, "+");
|
|
54
|
+
return `${FPO_ORIGIN}/result.html?p=${page}&sort=relevance&srch=xprtsrch&query_txt=${safe}&submitted=&patents_us=on&patents_other=on`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function buildFpoDetailUrl(publicationNumber: string): string {
|
|
58
|
+
const compact = publicationNumber.replace(/-/g, "").toUpperCase();
|
|
59
|
+
// Heuristic for US published applications: `US20240220787` → `/y2024/0220787.html`
|
|
60
|
+
const usAppMatch = /^US(\d{4})(\d{7})$/.exec(compact);
|
|
61
|
+
if (usAppMatch) {
|
|
62
|
+
return `${FPO_ORIGIN}/y${usAppMatch[1]}/${usAppMatch[2]}.html`;
|
|
63
|
+
}
|
|
64
|
+
// EP / WO / etc with explicit kind code → /<full>.html
|
|
65
|
+
return `${FPO_ORIGIN}/${encodeURIComponent(compact)}.html`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Convert an MM/DD/YYYY date as FPO renders it into ISO-8601 YYYY-MM-DD.
|
|
70
|
+
* Returns undefined on shape mismatch — we never invent a date.
|
|
71
|
+
*/
|
|
72
|
+
export function fpoDateToIso(raw: string | undefined): string | undefined {
|
|
73
|
+
if (!raw) return undefined;
|
|
74
|
+
const match = /^\s*(\d{2})\/(\d{2})\/(\d{4})\s*$/.exec(raw);
|
|
75
|
+
if (!match) return undefined;
|
|
76
|
+
return `${match[3]}-${match[1]}-${match[2]}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const TAG_RE = /<[^>]+>/g;
|
|
80
|
+
const ENTITY_MAP: Record<string, string> = {
|
|
81
|
+
"&": "&",
|
|
82
|
+
"<": "<",
|
|
83
|
+
">": ">",
|
|
84
|
+
""": '"',
|
|
85
|
+
"'": "'",
|
|
86
|
+
"…": "…",
|
|
87
|
+
" ": " ",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export function stripFpoHtml(input: string | undefined | null): string {
|
|
91
|
+
if (!input) return "";
|
|
92
|
+
return input
|
|
93
|
+
.replace(TAG_RE, " ")
|
|
94
|
+
.replace(/&(?:amp|lt|gt|quot|#39|hellip|nbsp);/g, (m) => ENTITY_MAP[m] ?? m)
|
|
95
|
+
.replace(/\s+/g, " ")
|
|
96
|
+
.trim();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Search-result row as scraped from the FPO listing table. Kind code is
|
|
101
|
+
* decoded from the link path; rows whose link does not encode a kind code
|
|
102
|
+
* are dropped at the search-adapter level rather than fabricated.
|
|
103
|
+
*/
|
|
104
|
+
export interface FpoListingRow {
|
|
105
|
+
publication_number_raw: string;
|
|
106
|
+
publication_number_canonical?: string;
|
|
107
|
+
title: string;
|
|
108
|
+
abstract?: string;
|
|
109
|
+
detail_url: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const LISTING_TABLE_RE =
|
|
113
|
+
/<table[^>]*class="listing_table"[^>]*>([\s\S]*?)<\/table>/i;
|
|
114
|
+
const ROW_RE = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
|
|
115
|
+
const CELL_RE = /<td[^>]*>([\s\S]*?)<\/td>/gi;
|
|
116
|
+
const HREF_RE = /<a[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/i;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Map an FPO listing link path to a canonical ST.16 publication number when
|
|
120
|
+
* the route encodes enough information. Returns undefined when the route
|
|
121
|
+
* is structurally ambiguous (e.g. bare grant numbers without a CC prefix).
|
|
122
|
+
*/
|
|
123
|
+
export function canonicaliseFpoLink(
|
|
124
|
+
link: string,
|
|
125
|
+
rawPubNo: string,
|
|
126
|
+
): string | undefined {
|
|
127
|
+
// Already segmented? e.g. "EP3716153A1" or "WO2024012345A1"
|
|
128
|
+
const explicit = /^([A-Z]{2})(\d+)([A-Z]\d?)$/.exec(
|
|
129
|
+
rawPubNo.replace(/\s+/g, "").toUpperCase(),
|
|
130
|
+
);
|
|
131
|
+
if (explicit) {
|
|
132
|
+
return `${explicit[1]}-${explicit[2]}-${explicit[3]}`;
|
|
133
|
+
}
|
|
134
|
+
// US application route: /y<YYYY>/<NNNNNNN>.html ↔ US-YYYYNNNNNNN-A1
|
|
135
|
+
const usAppMatch = /^\/y(\d{4})\/(\d{7})\.html$/i.exec(link);
|
|
136
|
+
if (usAppMatch) {
|
|
137
|
+
return `US-${usAppMatch[1]}${usAppMatch[2]}-A1`;
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function parseFpoListing(html: string): FpoListingRow[] {
|
|
143
|
+
const tableMatch = LISTING_TABLE_RE.exec(html);
|
|
144
|
+
if (!tableMatch) return [];
|
|
145
|
+
const body = tableMatch[1];
|
|
146
|
+
ROW_RE.lastIndex = 0;
|
|
147
|
+
const out: FpoListingRow[] = [];
|
|
148
|
+
let rowMatch: RegExpExecArray | null;
|
|
149
|
+
while ((rowMatch = ROW_RE.exec(body)) !== null) {
|
|
150
|
+
const cells: string[] = [];
|
|
151
|
+
CELL_RE.lastIndex = 0;
|
|
152
|
+
let cellMatch: RegExpExecArray | null;
|
|
153
|
+
while ((cellMatch = CELL_RE.exec(rowMatch[1])) !== null) {
|
|
154
|
+
cells.push(cellMatch[1]);
|
|
155
|
+
}
|
|
156
|
+
// FPO 2026 layout: <tr> has 4 <td> cells —
|
|
157
|
+
// cells[0] = match index ("1", "2", ...)
|
|
158
|
+
// cells[1] = publication number text (e.g. "US20240220787")
|
|
159
|
+
// cells[2] = <a href>title</a> <br/> abstract-snippet
|
|
160
|
+
// cells[3] = relevance score
|
|
161
|
+
if (cells.length < 3) continue;
|
|
162
|
+
const rawPubNo = stripFpoHtml(cells[1]);
|
|
163
|
+
const titleCell = cells[2];
|
|
164
|
+
const link = HREF_RE.exec(titleCell);
|
|
165
|
+
if (!rawPubNo || !link) continue;
|
|
166
|
+
const title = stripFpoHtml(link[2]);
|
|
167
|
+
// Abstract snippet is the remainder of cells[2] after the </a> tag —
|
|
168
|
+
// FPO emits it inline after a and a <br/>. Strip the <a>…</a>
|
|
169
|
+
// block plus the trailing <br/> and treat what's left as the snippet.
|
|
170
|
+
const afterAnchor = titleCell
|
|
171
|
+
.replace(HREF_RE, "")
|
|
172
|
+
.replace(/<br\s*\/?>/i, "");
|
|
173
|
+
const abstractSnippet = stripFpoHtml(afterAnchor);
|
|
174
|
+
out.push({
|
|
175
|
+
publication_number_raw: rawPubNo,
|
|
176
|
+
publication_number_canonical: canonicaliseFpoLink(link[1], rawPubNo),
|
|
177
|
+
title,
|
|
178
|
+
abstract: abstractSnippet || undefined,
|
|
179
|
+
detail_url: link[1].startsWith("http")
|
|
180
|
+
? link[1]
|
|
181
|
+
: `${FPO_ORIGIN}${link[1]}`,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Bibliographic detail fields parsed from a single FPO patent page. Empty
|
|
189
|
+
* fields are `undefined`, never `null` and never invented.
|
|
190
|
+
*/
|
|
191
|
+
export interface FpoDetail {
|
|
192
|
+
title?: string;
|
|
193
|
+
abstract?: string;
|
|
194
|
+
publication_number?: string;
|
|
195
|
+
application_number?: string;
|
|
196
|
+
publication_date?: string;
|
|
197
|
+
filing_date?: string;
|
|
198
|
+
assignee?: string;
|
|
199
|
+
kind_code?: string;
|
|
200
|
+
doc_type_label?: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const KIND_CODE_RE =
|
|
204
|
+
/<div[^>]*disp_elm_name_kcode[^>]*>[\s\S]*?Kind\s*Code:[\s\S]*?<\/div>\s*<div[^>]*float_left[^>]*>\s*([A-Z0-9]+)\s*<\/div>/i;
|
|
205
|
+
const DOC_TYPE_RE =
|
|
206
|
+
/<div[^>]*disp_elm_text[^>]*style="[^"]*clear:\s*none[^"]*"[^>]*>\s*<label[^>]*>\s*([^<]+?)\s*<\/label>/i;
|
|
207
|
+
|
|
208
|
+
function extractLabelValue(html: string, label: string): string | undefined {
|
|
209
|
+
const re = new RegExp(
|
|
210
|
+
`<div[^>]*disp_elm_title[^>]*>\\s*${label}\\s*:?\\s*<\\/div>\\s*<div[^>]*disp_elm_text[^>]*>([\\s\\S]*?)<\\/div>`,
|
|
211
|
+
"i",
|
|
212
|
+
);
|
|
213
|
+
const match = re.exec(html);
|
|
214
|
+
if (!match) return undefined;
|
|
215
|
+
const value = stripFpoHtml(match[1]);
|
|
216
|
+
return value || undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function parseFpoDetail(html: string): FpoDetail {
|
|
220
|
+
const title = extractLabelValue(html, "Title");
|
|
221
|
+
const abstractRaw = extractLabelValue(html, "Abstract");
|
|
222
|
+
const applicationNumber = extractLabelValue(html, "Application Number");
|
|
223
|
+
const publicationDate = fpoDateToIso(
|
|
224
|
+
extractLabelValue(html, "Publication Date"),
|
|
225
|
+
);
|
|
226
|
+
const filingDate = fpoDateToIso(extractLabelValue(html, "Filing Date"));
|
|
227
|
+
const assignee = extractLabelValue(html, "Assignee");
|
|
228
|
+
const kindMatch = KIND_CODE_RE.exec(html);
|
|
229
|
+
const kindCode = kindMatch ? kindMatch[1].toUpperCase() : undefined;
|
|
230
|
+
const docTypeMatch = DOC_TYPE_RE.exec(html);
|
|
231
|
+
const docTypeLabel = docTypeMatch ? docTypeMatch[1].trim() : undefined;
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
title,
|
|
235
|
+
abstract: abstractRaw,
|
|
236
|
+
application_number: applicationNumber,
|
|
237
|
+
publication_date: publicationDate,
|
|
238
|
+
filing_date: filingDate,
|
|
239
|
+
assignee,
|
|
240
|
+
kind_code: kindCode,
|
|
241
|
+
doc_type_label: docTypeLabel,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Combine the bib label `United States Patent Application 20240220787` and
|
|
247
|
+
* the kind code `A1` extracted separately into the canonical ST.16 form
|
|
248
|
+
* `US-20240220787-A1`. Returns undefined if either piece is missing.
|
|
249
|
+
*/
|
|
250
|
+
export function reconstructCanonicalPubNo(
|
|
251
|
+
docTypeLabel: string | undefined,
|
|
252
|
+
kindCode: string | undefined,
|
|
253
|
+
): string | undefined {
|
|
254
|
+
if (!docTypeLabel || !kindCode) return undefined;
|
|
255
|
+
// Pull the trailing identifier off the label. The label is one of:
|
|
256
|
+
// "United States Patent Application 20240220787" (11-digit US app id)
|
|
257
|
+
// "European Patent Application EP3716153" (CC-prefixed)
|
|
258
|
+
// "United States Patent 12602228" (7-9 digit US grant)
|
|
259
|
+
const idMatch = /([A-Z]{2}\d+|\d{7,})\s*$/.exec(docTypeLabel.trim());
|
|
260
|
+
if (!idMatch) return undefined;
|
|
261
|
+
const rawId = idMatch[1];
|
|
262
|
+
// Prefix? `US…`, `EP…`, etc. — strip then re-emit
|
|
263
|
+
const prefixMatch = /^([A-Z]{2})(\d+)$/.exec(rawId);
|
|
264
|
+
if (prefixMatch) {
|
|
265
|
+
return `${prefixMatch[1]}-${prefixMatch[2]}-${kindCode}`;
|
|
266
|
+
}
|
|
267
|
+
// Bare numeric — could be a 7-9 digit US grant or an 11-digit US
|
|
268
|
+
// published-application identifier. Both belong to the US namespace.
|
|
269
|
+
if (/^\d{7,11}$/.test(rawId)) {
|
|
270
|
+
return `US-${rawId}-${kindCode}`;
|
|
271
|
+
}
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|