@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,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::google-patents-web::_shared
|
|
3
|
+
* @does Shared HTTP helpers and types for the KEYLESS Google Patents web adapter — distinct from the BigQuery-backed google-patents-bq adapter (which requires a billed GCP project). This adapter uses the public XHR endpoint that drives patents.google.com and the SSR /patent/<id>/en bibliography page, both of which are reachable with only a real-browser User-Agent header.
|
|
4
|
+
* @needs src/engine/normalizer/patent-envelope.ts, src/types/patent.ts, node:fetch (global)
|
|
5
|
+
* @feeds src/adapters/google-patents-web/search.yaml, src/adapters/google-patents-web/get.ts
|
|
6
|
+
* @breaks throws GooglePatentsHttpError when the endpoint returns non-2xx or unparseable JSON; callers surface PATENT_API_DEPRECATED to the meta-command
|
|
7
|
+
* @invariants every outbound request carries a real-browser User-Agent — Google rejects bare `node-fetch`/`python-requests`; the @keyless-best-effort verification tag is emitted in adapter file headers
|
|
8
|
+
* @side-effects HTTPS egress to patents.google.com only; no env reads, no cookies
|
|
9
|
+
* @perf single request per call; XHR responses are ~40-100 KB
|
|
10
|
+
* @concurrency safe — pure functions plus stateless `fetch`
|
|
11
|
+
* @test covered transitively via tests/unit/adapters/google-patents-web/*.test.ts (none yet — adapters ship with live verification proof in docs/skills/patent-cookbook.md)
|
|
12
|
+
* @stability experimental — Google does not publish a stability contract for this XHR
|
|
13
|
+
* @since 2026-05-18
|
|
14
|
+
* @verification keyless-best-effort
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { PatentRecord } from "../../types/patent.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Local verification-status alias for the keyless-web adapter family.
|
|
21
|
+
*
|
|
22
|
+
* Distinct from the canonical `PatentVerificationStatus` union in
|
|
23
|
+
* `src/types/patent.ts` — adding a new variant there is owned by another
|
|
24
|
+
* agent in this batch, so we expose it as a *local* string-literal type
|
|
25
|
+
* alias the file headers can advertise without forcing a cross-cutting
|
|
26
|
+
* type change. The meta-command does not branch on this value; it is
|
|
27
|
+
* documentation-grade only.
|
|
28
|
+
*/
|
|
29
|
+
export type KeylessBestEffortStatus = "keyless-best-effort";
|
|
30
|
+
export const KEYLESS_BEST_EFFORT: KeylessBestEffortStatus =
|
|
31
|
+
"keyless-best-effort";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Real-browser User-Agent string. Google's XHR endpoint returns 403 for the
|
|
35
|
+
* default `node/undici` UA; a Chrome string fixes that. Mirror the version
|
|
36
|
+
* Chrome currently ships rather than freezing on an old number so the header
|
|
37
|
+
* stays plausible if Google starts UA-version-gating in future.
|
|
38
|
+
*/
|
|
39
|
+
export const GOOGLE_PATENTS_UA =
|
|
40
|
+
"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";
|
|
41
|
+
|
|
42
|
+
export class GooglePatentsHttpError extends Error {
|
|
43
|
+
constructor(
|
|
44
|
+
public readonly status: number,
|
|
45
|
+
public readonly url: string,
|
|
46
|
+
message: string,
|
|
47
|
+
) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.name = "GooglePatentsHttpError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* GET <url> with a real-browser UA. Returns parsed JSON; throws
|
|
55
|
+
* GooglePatentsHttpError on non-2xx or JSON parse failure.
|
|
56
|
+
*/
|
|
57
|
+
export async function fetchGooglePatentsJson<T>(url: string): Promise<T> {
|
|
58
|
+
const response = await fetch(url, {
|
|
59
|
+
method: "GET",
|
|
60
|
+
headers: {
|
|
61
|
+
"User-Agent": GOOGLE_PATENTS_UA,
|
|
62
|
+
Accept: "application/json",
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
throw new GooglePatentsHttpError(
|
|
67
|
+
response.status,
|
|
68
|
+
url,
|
|
69
|
+
`Google Patents XHR returned HTTP ${response.status}`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
let parsed: unknown;
|
|
73
|
+
try {
|
|
74
|
+
parsed = await response.json();
|
|
75
|
+
} catch (err) {
|
|
76
|
+
throw new GooglePatentsHttpError(
|
|
77
|
+
response.status,
|
|
78
|
+
url,
|
|
79
|
+
`Google Patents XHR returned non-JSON body: ${(err as Error).message}`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return parsed as T;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* GET <url> with a real-browser UA, return raw HTML text. Throws
|
|
87
|
+
* GooglePatentsHttpError on non-2xx.
|
|
88
|
+
*/
|
|
89
|
+
export async function fetchGooglePatentsHtml(url: string): Promise<string> {
|
|
90
|
+
const response = await fetch(url, {
|
|
91
|
+
method: "GET",
|
|
92
|
+
headers: {
|
|
93
|
+
"User-Agent": GOOGLE_PATENTS_UA,
|
|
94
|
+
Accept: "text/html,application/xhtml+xml",
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
throw new GooglePatentsHttpError(
|
|
99
|
+
response.status,
|
|
100
|
+
url,
|
|
101
|
+
`Google Patents detail page returned HTTP ${response.status}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return await response.text();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Build the public XHR query URL. Accepts a plain free-text query string and
|
|
109
|
+
* an optional limit (`num` parameter) and earliest-publication-year filter.
|
|
110
|
+
*
|
|
111
|
+
* The XHR endpoint expects a percent-encoded *inner* query string under the
|
|
112
|
+
* `url` parameter — the outer query is `?url=q%3Dfoo&exp=`. We percent-encode
|
|
113
|
+
* the inner once.
|
|
114
|
+
*/
|
|
115
|
+
export function buildGooglePatentsXhrUrl(
|
|
116
|
+
query: string,
|
|
117
|
+
limit?: number,
|
|
118
|
+
sinceYear?: string,
|
|
119
|
+
): string {
|
|
120
|
+
const cleaned = query.trim();
|
|
121
|
+
let inner = `q=${cleaned.replace(/\s+/g, "+")}`;
|
|
122
|
+
if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
|
|
123
|
+
inner += `&num=${Math.min(100, Math.max(1, Math.floor(limit)))}`;
|
|
124
|
+
}
|
|
125
|
+
if (sinceYear && /^\d{4}$/.test(sinceYear)) {
|
|
126
|
+
inner += `&after=publication:${sinceYear}0101`;
|
|
127
|
+
}
|
|
128
|
+
return `https://patents.google.com/xhr/query?url=${encodeURIComponent(inner)}&exp=`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Build the public detail page URL for a given publication number. Google
|
|
133
|
+
* accepts the segmented form `US-11741188-B2` and the compact form
|
|
134
|
+
* `US11741188B2`; we send the compact form to keep the URL short.
|
|
135
|
+
*/
|
|
136
|
+
export function buildGooglePatentsDetailUrl(pubNo: string): string {
|
|
137
|
+
const compact = pubNo.replace(/-/g, "").toUpperCase();
|
|
138
|
+
return `https://patents.google.com/patent/${encodeURIComponent(compact)}/en`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* XHR response shape. We intentionally type only the fields we read —
|
|
143
|
+
* Google ships many more (figure thumbnails, family metadata, language) that
|
|
144
|
+
* we either ignore or carry through verbatim in `raw`.
|
|
145
|
+
*/
|
|
146
|
+
export interface GoogleXhrResultRow {
|
|
147
|
+
id: string;
|
|
148
|
+
rank?: number;
|
|
149
|
+
patent?: {
|
|
150
|
+
title?: string;
|
|
151
|
+
snippet?: string;
|
|
152
|
+
publication_number?: string;
|
|
153
|
+
inventor?: string;
|
|
154
|
+
assignee?: string;
|
|
155
|
+
filing_date?: string;
|
|
156
|
+
publication_date?: string;
|
|
157
|
+
grant_date?: string;
|
|
158
|
+
priority_date?: string;
|
|
159
|
+
language?: string;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface GoogleXhrResponse {
|
|
164
|
+
results?: {
|
|
165
|
+
cluster?: Array<{ result?: GoogleXhrResultRow[] }>;
|
|
166
|
+
total_num_results?: number;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Flatten the nested `results.cluster[].result[]` into a single array, in
|
|
172
|
+
* server-supplied order.
|
|
173
|
+
*/
|
|
174
|
+
export function flattenGoogleXhrResults(
|
|
175
|
+
body: GoogleXhrResponse,
|
|
176
|
+
): GoogleXhrResultRow[] {
|
|
177
|
+
const clusters = body.results?.cluster ?? [];
|
|
178
|
+
const out: GoogleXhrResultRow[] = [];
|
|
179
|
+
for (const cluster of clusters) {
|
|
180
|
+
const rows = cluster.result ?? [];
|
|
181
|
+
for (const row of rows) out.push(row);
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Project a single XHR row to the upstream-facing partial-PatentRecord
|
|
188
|
+
* shape. Empty / missing fields are left undefined rather than synthesised.
|
|
189
|
+
* Snippets contain HTML markup (e.g. `<b>` tags) which we strip.
|
|
190
|
+
*/
|
|
191
|
+
export function projectGoogleRowToRecord(
|
|
192
|
+
row: GoogleXhrResultRow,
|
|
193
|
+
sourceUrl: string,
|
|
194
|
+
): Partial<PatentRecord> & {
|
|
195
|
+
publication_number: string;
|
|
196
|
+
source_adapter: string;
|
|
197
|
+
} {
|
|
198
|
+
const patent = row.patent ?? {};
|
|
199
|
+
const pubNo =
|
|
200
|
+
(typeof patent.publication_number === "string" &&
|
|
201
|
+
patent.publication_number) ||
|
|
202
|
+
deriveCanonicalFromId(row.id);
|
|
203
|
+
const cleanSnippet = patent.snippet ? stripHtml(patent.snippet) : undefined;
|
|
204
|
+
return {
|
|
205
|
+
publication_number: pubNo,
|
|
206
|
+
title: patent.title ? patent.title.trim() : undefined,
|
|
207
|
+
abstract: cleanSnippet,
|
|
208
|
+
inventors: patent.inventor ? [{ name: patent.inventor.trim() }] : undefined,
|
|
209
|
+
assignees: patent.assignee ? [{ name: patent.assignee.trim() }] : undefined,
|
|
210
|
+
filing_date: patent.filing_date || undefined,
|
|
211
|
+
publication_date: patent.publication_date || undefined,
|
|
212
|
+
grant_date: patent.grant_date || undefined,
|
|
213
|
+
priority_date: patent.priority_date || undefined,
|
|
214
|
+
source_adapter: "google-patents-web",
|
|
215
|
+
source_url: sourceUrl,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** `patent/US11741188B2/en` → `US11741188B2`. */
|
|
220
|
+
function deriveCanonicalFromId(id: string): string {
|
|
221
|
+
const match = /patent\/([^/]+)/.exec(id);
|
|
222
|
+
return match ? match[1] : id;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const TAG_RE = /<[^>]+>/g;
|
|
226
|
+
const ENTITY_MAP: Record<string, string> = {
|
|
227
|
+
"&": "&",
|
|
228
|
+
"<": "<",
|
|
229
|
+
">": ">",
|
|
230
|
+
""": '"',
|
|
231
|
+
"'": "'",
|
|
232
|
+
"…": "…",
|
|
233
|
+
" ": " ",
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export function stripHtml(input: string): string {
|
|
237
|
+
return input
|
|
238
|
+
.replace(TAG_RE, "")
|
|
239
|
+
.replace(/&(?:amp|lt|gt|quot|#39|hellip|nbsp);/g, (m) => ENTITY_MAP[m] ?? m)
|
|
240
|
+
.replace(/\s+/g, " ")
|
|
241
|
+
.trim();
|
|
242
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::google-patents-web::get
|
|
3
|
+
* @does Keyless single-publication detail fetch — parses the SSR meta-tag and `<abstract>` block on https://patents.google.com/patent/<id>/en using regex projection (no DOM, no Chrome).
|
|
4
|
+
* @needs src/adapters/google-patents-web/_shared.ts, src/engine/normalizer/patent-envelope.ts, src/registry.ts
|
|
5
|
+
* @feeds src/commands/patent.ts (capability tag patent.get)
|
|
6
|
+
* @breaks PATENT_NOT_FOUND when the detail page returns 200 but carries no DC.title; PATENT_INVALID_NUMBER for empty input; PATENT_API_DEPRECATED on non-2xx HTTP
|
|
7
|
+
* @invariants output is a single canonical PatentRecord; abstract whitespace is normalised; HTML entities decoded; source_url is the literal page we hit
|
|
8
|
+
* @side-effects HTTPS egress to patents.google.com only
|
|
9
|
+
* @perf single page load (~350 KB); regex parse is linear in document length
|
|
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
|
+
import { cli, Strategy } from "../../registry.js";
|
|
18
|
+
import { assemblePatentRecord } from "../../engine/normalizer/patent-envelope.js";
|
|
19
|
+
import { buildPatentEnvelope } from "../../engine/normalizer/patent-envelope.js";
|
|
20
|
+
import {
|
|
21
|
+
GooglePatentsHttpError,
|
|
22
|
+
buildGooglePatentsDetailUrl,
|
|
23
|
+
fetchGooglePatentsHtml,
|
|
24
|
+
stripHtml,
|
|
25
|
+
} from "./_shared.js";
|
|
26
|
+
|
|
27
|
+
const ADAPTER_PATH = "src/adapters/google-patents-web/get.ts";
|
|
28
|
+
|
|
29
|
+
interface DetailPageFields {
|
|
30
|
+
title?: string;
|
|
31
|
+
abstract?: string;
|
|
32
|
+
inventors: string[];
|
|
33
|
+
assignees: string[];
|
|
34
|
+
filing_date?: string;
|
|
35
|
+
publication_date?: string;
|
|
36
|
+
application_number?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function extractMetaContent(html: string, name: string): string[] {
|
|
40
|
+
const out: string[] = [];
|
|
41
|
+
const re = new RegExp(
|
|
42
|
+
`<meta[^>]+name=["']${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["'][^>]*content=["']([^"']*)["']`,
|
|
43
|
+
"gi",
|
|
44
|
+
);
|
|
45
|
+
let match: RegExpExecArray | null;
|
|
46
|
+
while ((match = re.exec(html)) !== null) {
|
|
47
|
+
if (match[1]) out.push(match[1].trim());
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Parse the Google Patents detail HTML into the subset of fields we surface
|
|
54
|
+
* on PatentRecord. The DC.* meta tags are the documented machine-readable
|
|
55
|
+
* surface (Dublin Core), and citation_* mirrors what Google Scholar's
|
|
56
|
+
* indexing crawler consumes.
|
|
57
|
+
*/
|
|
58
|
+
export function parseGooglePatentsDetail(html: string): DetailPageFields {
|
|
59
|
+
const titleRaw =
|
|
60
|
+
extractMetaContent(html, "DC.title")[0] ??
|
|
61
|
+
extractMetaContent(html, "citation_title")[0];
|
|
62
|
+
const dcDates = extractMetaContent(html, "DC.date");
|
|
63
|
+
const dcContributors = extractMetaContent(html, "DC.contributor");
|
|
64
|
+
|
|
65
|
+
// DC.date is emitted twice on Google Patents: filing then publication.
|
|
66
|
+
const filingDate = dcDates[0];
|
|
67
|
+
const publicationDate = dcDates[1];
|
|
68
|
+
|
|
69
|
+
// DC.contributor mixes inventors (personal names) and assignees (org
|
|
70
|
+
// names). Google's convention is inventors first, the assignee last —
|
|
71
|
+
// approximate that, with the last-item-is-assignee heuristic.
|
|
72
|
+
const inventors: string[] = [];
|
|
73
|
+
let assignee: string | undefined;
|
|
74
|
+
if (dcContributors.length > 0) {
|
|
75
|
+
assignee = dcContributors[dcContributors.length - 1];
|
|
76
|
+
for (let i = 0; i < dcContributors.length - 1; i++) {
|
|
77
|
+
inventors.push(dcContributors[i]);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const applicationNumber = extractMetaContent(
|
|
82
|
+
html,
|
|
83
|
+
"citation_patent_application_number",
|
|
84
|
+
)[0];
|
|
85
|
+
|
|
86
|
+
// The bibliographic abstract sits in a single `<abstract …>...</abstract>`
|
|
87
|
+
// element. Use a non-greedy match because some pages embed multiple
|
|
88
|
+
// localised abstracts; we want the first.
|
|
89
|
+
let abstractText: string | undefined;
|
|
90
|
+
const abstractMatch = /<abstract\b[^>]*>([\s\S]*?)<\/abstract>/i.exec(html);
|
|
91
|
+
if (abstractMatch) {
|
|
92
|
+
const cleaned = stripHtml(abstractMatch[1]);
|
|
93
|
+
if (cleaned.length > 0) abstractText = cleaned;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
title: titleRaw ? titleRaw.trim() : undefined,
|
|
98
|
+
abstract: abstractText,
|
|
99
|
+
inventors,
|
|
100
|
+
assignees: assignee ? [assignee] : [],
|
|
101
|
+
filing_date: filingDate,
|
|
102
|
+
publication_date: publicationDate,
|
|
103
|
+
application_number: applicationNumber,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function runGooglePatentsWebGet(kwargs: {
|
|
108
|
+
publication_number?: unknown;
|
|
109
|
+
}): Promise<unknown[]> {
|
|
110
|
+
const raw = String(kwargs.publication_number ?? "").trim();
|
|
111
|
+
if (raw.length === 0) {
|
|
112
|
+
return [
|
|
113
|
+
{
|
|
114
|
+
envelope: buildPatentEnvelope({
|
|
115
|
+
code: "PATENT_INVALID_NUMBER",
|
|
116
|
+
adapter_path: ADAPTER_PATH,
|
|
117
|
+
step: "validate",
|
|
118
|
+
suggestion:
|
|
119
|
+
"google-patents-web get requires a publication_number (e.g. US11741188B2)",
|
|
120
|
+
}),
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
const url = buildGooglePatentsDetailUrl(raw);
|
|
125
|
+
let html: string;
|
|
126
|
+
try {
|
|
127
|
+
html = await fetchGooglePatentsHtml(url);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
if (err instanceof GooglePatentsHttpError) {
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
envelope: buildPatentEnvelope({
|
|
133
|
+
code:
|
|
134
|
+
err.status === 404 ? "PATENT_NOT_FOUND" : "PATENT_API_DEPRECATED",
|
|
135
|
+
adapter_path: ADAPTER_PATH,
|
|
136
|
+
step: "fetch",
|
|
137
|
+
suggestion: `Google Patents detail page returned HTTP ${err.status}; verify the publication number or retry against --sources espacenet`,
|
|
138
|
+
alternatives: ["espacenet", "freepatentsonline-web"],
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
const detail = parseGooglePatentsDetail(html);
|
|
146
|
+
if (!detail.title) {
|
|
147
|
+
return [
|
|
148
|
+
{
|
|
149
|
+
envelope: buildPatentEnvelope({
|
|
150
|
+
code: "PATENT_NOT_FOUND",
|
|
151
|
+
adapter_path: ADAPTER_PATH,
|
|
152
|
+
step: "select",
|
|
153
|
+
suggestion: `google-patents-web detail page for "${raw}" yielded no DC.title meta tag`,
|
|
154
|
+
alternatives: ["espacenet", "freepatentsonline-web"],
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
return [
|
|
161
|
+
assemblePatentRecord({
|
|
162
|
+
publication_number: raw,
|
|
163
|
+
application_number: detail.application_number || undefined,
|
|
164
|
+
title: detail.title,
|
|
165
|
+
abstract: detail.abstract,
|
|
166
|
+
inventors:
|
|
167
|
+
detail.inventors.length > 0
|
|
168
|
+
? detail.inventors.map((name) => ({ name }))
|
|
169
|
+
: undefined,
|
|
170
|
+
assignees:
|
|
171
|
+
detail.assignees.length > 0
|
|
172
|
+
? detail.assignees.map((name) => ({ name }))
|
|
173
|
+
: undefined,
|
|
174
|
+
filing_date: detail.filing_date,
|
|
175
|
+
publication_date: detail.publication_date,
|
|
176
|
+
source_adapter: "google-patents-web",
|
|
177
|
+
source_url: url,
|
|
178
|
+
} as Parameters<typeof assemblePatentRecord>[0]),
|
|
179
|
+
];
|
|
180
|
+
} catch {
|
|
181
|
+
return [
|
|
182
|
+
{
|
|
183
|
+
envelope: buildPatentEnvelope({
|
|
184
|
+
code: "PATENT_NOT_FOUND",
|
|
185
|
+
adapter_path: ADAPTER_PATH,
|
|
186
|
+
step: "normalize",
|
|
187
|
+
suggestion: `google-patents-web detail page for "${raw}" did not yield a canonicalisable publication_number`,
|
|
188
|
+
alternatives: ["espacenet"],
|
|
189
|
+
}),
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
cli({
|
|
196
|
+
site: "google-patents-web",
|
|
197
|
+
name: "get",
|
|
198
|
+
description:
|
|
199
|
+
"Retrieve a single Google Patents bibliographic record (keyless, no Chrome)",
|
|
200
|
+
domain: "patents.google.com",
|
|
201
|
+
strategy: Strategy.PUBLIC,
|
|
202
|
+
adapter_path: ADAPTER_PATH,
|
|
203
|
+
args: [
|
|
204
|
+
{
|
|
205
|
+
name: "publication_number",
|
|
206
|
+
type: "str",
|
|
207
|
+
required: true,
|
|
208
|
+
positional: true,
|
|
209
|
+
description:
|
|
210
|
+
"ST.16 publication number, compact or segmented (e.g. US11741188B2 or US-11741188-B2)",
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
columns: [
|
|
214
|
+
"publication_number",
|
|
215
|
+
"title",
|
|
216
|
+
"publication_date",
|
|
217
|
+
"assignees",
|
|
218
|
+
"source_url",
|
|
219
|
+
],
|
|
220
|
+
capabilities: ["http.fetch", "patent.get"],
|
|
221
|
+
minimum_capability: "http.fetch",
|
|
222
|
+
func: async (_page, kwargs) =>
|
|
223
|
+
runGooglePatentsWebGet(kwargs as { publication_number?: unknown }),
|
|
224
|
+
});
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::google-patents-web::search
|
|
3
|
+
* @does Keyless free-text patent search via the public XHR endpoint that drives patents.google.com — distinct from src/adapters/google-patents-bq which talks to BigQuery and needs a billed GCP project.
|
|
4
|
+
* @needs src/adapters/google-patents-web/_shared.ts, src/engine/normalizer/patent-envelope.ts, src/registry.ts
|
|
5
|
+
* @feeds src/commands/patent.ts (capability tag patent.search)
|
|
6
|
+
* @breaks PATENT_API_DEPRECATED with status code embedded when the XHR returns non-2xx (typically 403 → suspected UA gating); PATENT_NOT_FOUND on empty result; PATENT_SCHEMA_DRIFT when the JSON shape no longer carries results.cluster[].result[]
|
|
7
|
+
* @invariants output rows are canonical PatentRecord; source_adapter='google-patents-web'; results are server-ordered (Google's relevance ranking)
|
|
8
|
+
* @side-effects HTTPS egress to patents.google.com only — no env reads, no cookies, no auth
|
|
9
|
+
* @perf single XHR request, ~40-100 KB; results capped at min(limit, 100) per call
|
|
10
|
+
* @concurrency safe — stateless
|
|
11
|
+
* @test verification proof captured in docs/skills/patent-cookbook.md (recipe: day-zero search)
|
|
12
|
+
* @stability experimental
|
|
13
|
+
* @since 2026-05-18
|
|
14
|
+
* @verification keyless-best-effort — Google does not publish a stability contract for this XHR; we live-call it from the same User-Agent the public site uses
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { cli, Strategy } from "../../registry.js";
|
|
18
|
+
import { assemblePatentRecord } from "../../engine/normalizer/patent-envelope.js";
|
|
19
|
+
import { buildPatentEnvelope } from "../../engine/normalizer/patent-envelope.js";
|
|
20
|
+
import {
|
|
21
|
+
GooglePatentsHttpError,
|
|
22
|
+
buildGooglePatentsXhrUrl,
|
|
23
|
+
fetchGooglePatentsJson,
|
|
24
|
+
flattenGoogleXhrResults,
|
|
25
|
+
projectGoogleRowToRecord,
|
|
26
|
+
type GoogleXhrResponse,
|
|
27
|
+
} from "./_shared.js";
|
|
28
|
+
|
|
29
|
+
const ADAPTER_PATH = "src/adapters/google-patents-web/search.ts";
|
|
30
|
+
|
|
31
|
+
interface SearchArgs {
|
|
32
|
+
query?: unknown;
|
|
33
|
+
limit?: unknown;
|
|
34
|
+
since?: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function clampLimit(raw: unknown): number {
|
|
38
|
+
const num = typeof raw === "number" ? raw : Number(raw);
|
|
39
|
+
if (!Number.isFinite(num) || num <= 0) return 25;
|
|
40
|
+
return Math.min(100, Math.max(1, Math.floor(num)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normaliseSince(raw: unknown): string | undefined {
|
|
44
|
+
if (typeof raw !== "string") return undefined;
|
|
45
|
+
const trimmed = raw.trim();
|
|
46
|
+
return /^\d{4}$/.test(trimmed) ? trimmed : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function runGooglePatentsWebSearch(
|
|
50
|
+
kwargs: SearchArgs,
|
|
51
|
+
): Promise<unknown[]> {
|
|
52
|
+
const query = String(kwargs.query ?? "").trim();
|
|
53
|
+
if (query.length === 0) {
|
|
54
|
+
return [
|
|
55
|
+
{
|
|
56
|
+
envelope: buildPatentEnvelope({
|
|
57
|
+
code: "PATENT_UNSUPPORTED_QUERY",
|
|
58
|
+
adapter_path: ADAPTER_PATH,
|
|
59
|
+
step: "validate",
|
|
60
|
+
suggestion: "google-patents-web search requires a non-empty query",
|
|
61
|
+
alternatives: ["freepatentsonline-web"],
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
const limit = clampLimit(kwargs.limit);
|
|
67
|
+
const since = normaliseSince(kwargs.since);
|
|
68
|
+
const url = buildGooglePatentsXhrUrl(query, limit, since);
|
|
69
|
+
|
|
70
|
+
let body: GoogleXhrResponse;
|
|
71
|
+
try {
|
|
72
|
+
body = await fetchGooglePatentsJson<GoogleXhrResponse>(url);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
if (err instanceof GooglePatentsHttpError) {
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
envelope: buildPatentEnvelope({
|
|
78
|
+
code:
|
|
79
|
+
err.status === 429
|
|
80
|
+
? "PATENT_RATE_LIMIT"
|
|
81
|
+
: err.status === 403
|
|
82
|
+
? "PATENT_REGION_BLOCKED"
|
|
83
|
+
: "PATENT_API_DEPRECATED",
|
|
84
|
+
adapter_path: ADAPTER_PATH,
|
|
85
|
+
step: "fetch",
|
|
86
|
+
suggestion: `Google Patents XHR returned HTTP ${err.status} for ${err.url}; retry with --sources freepatentsonline-web, or check if Google now requires a session cookie`,
|
|
87
|
+
alternatives: ["freepatentsonline-web", "espacenet"],
|
|
88
|
+
retryable: err.status === 429,
|
|
89
|
+
}),
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const rows = flattenGoogleXhrResults(body);
|
|
97
|
+
if (rows.length === 0) {
|
|
98
|
+
if (!body.results || !Array.isArray(body.results.cluster)) {
|
|
99
|
+
return [
|
|
100
|
+
{
|
|
101
|
+
envelope: buildPatentEnvelope({
|
|
102
|
+
code: "PATENT_SCHEMA_DRIFT",
|
|
103
|
+
adapter_path: ADAPTER_PATH,
|
|
104
|
+
step: "select",
|
|
105
|
+
suggestion:
|
|
106
|
+
"Google Patents XHR no longer carries results.cluster[].result[]; the adapter's flattenGoogleXhrResults projection needs updating",
|
|
107
|
+
alternatives: ["freepatentsonline-web"],
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
return [
|
|
113
|
+
{
|
|
114
|
+
envelope: buildPatentEnvelope({
|
|
115
|
+
code: "PATENT_NOT_FOUND",
|
|
116
|
+
adapter_path: ADAPTER_PATH,
|
|
117
|
+
step: "select",
|
|
118
|
+
suggestion: `google-patents-web returned no rows for "${query}"`,
|
|
119
|
+
alternatives: ["freepatentsonline-web", "espacenet"],
|
|
120
|
+
}),
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const out: unknown[] = [];
|
|
126
|
+
for (const row of rows.slice(0, limit)) {
|
|
127
|
+
const partial = projectGoogleRowToRecord(row, url);
|
|
128
|
+
if (!partial.publication_number) continue;
|
|
129
|
+
try {
|
|
130
|
+
out.push(
|
|
131
|
+
assemblePatentRecord(
|
|
132
|
+
partial as Parameters<typeof assemblePatentRecord>[0],
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
} catch {
|
|
136
|
+
// Rows whose publication_number cannot be canonicalised are skipped
|
|
137
|
+
// rather than synthesised — we never invent ST.16 fields.
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (out.length === 0) {
|
|
141
|
+
return [
|
|
142
|
+
{
|
|
143
|
+
envelope: buildPatentEnvelope({
|
|
144
|
+
code: "PATENT_NOT_FOUND",
|
|
145
|
+
adapter_path: ADAPTER_PATH,
|
|
146
|
+
step: "normalize",
|
|
147
|
+
suggestion: `google-patents-web returned ${rows.length} rows but none carried a canonicalisable publication_number`,
|
|
148
|
+
alternatives: ["freepatentsonline-web", "espacenet"],
|
|
149
|
+
}),
|
|
150
|
+
},
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
cli({
|
|
157
|
+
site: "google-patents-web",
|
|
158
|
+
name: "search",
|
|
159
|
+
description:
|
|
160
|
+
"Keyless Google Patents search via the public XHR endpoint (no API key, no Chrome required)",
|
|
161
|
+
domain: "patents.google.com",
|
|
162
|
+
strategy: Strategy.PUBLIC,
|
|
163
|
+
adapter_path: ADAPTER_PATH,
|
|
164
|
+
args: [
|
|
165
|
+
{
|
|
166
|
+
name: "query",
|
|
167
|
+
type: "str",
|
|
168
|
+
required: true,
|
|
169
|
+
positional: true,
|
|
170
|
+
description: "Free-text query (Google Patents smart-search syntax)",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: "limit",
|
|
174
|
+
type: "int",
|
|
175
|
+
default: 25,
|
|
176
|
+
description: "Max results to return (1-100)",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "since",
|
|
180
|
+
type: "str",
|
|
181
|
+
description:
|
|
182
|
+
"Earliest publication year (YYYY); translated to after:publication:YYYY0101",
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
columns: [
|
|
186
|
+
"publication_number",
|
|
187
|
+
"title",
|
|
188
|
+
"publication_date",
|
|
189
|
+
"assignees",
|
|
190
|
+
"source_url",
|
|
191
|
+
],
|
|
192
|
+
capabilities: ["http.fetch", "patent.search"],
|
|
193
|
+
minimum_capability: "http.fetch",
|
|
194
|
+
func: async (_page, kwargs) =>
|
|
195
|
+
runGooglePatentsWebSearch(kwargs as SearchArgs),
|
|
196
|
+
});
|