@jackwener/opencli 1.7.12 → 1.7.14
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/README.md +8 -7
- package/README.zh-CN.md +9 -8
- package/cli-manifest.json +12128 -6665
- package/clis/1point3acres/digest.js +35 -0
- package/clis/1point3acres/forum.js +51 -0
- package/clis/1point3acres/forums.js +44 -0
- package/clis/1point3acres/hot.js +35 -0
- package/clis/1point3acres/latest.js +35 -0
- package/clis/1point3acres/notifications.js +64 -0
- package/clis/1point3acres/search.js +71 -0
- package/clis/1point3acres/thread.js +117 -0
- package/clis/1point3acres/user.js +77 -0
- package/clis/1point3acres/utils.js +247 -0
- package/clis/_shared/desktop-commands.js +4 -0
- package/clis/aibase/news.js +110 -0
- package/clis/aibase/news.test.js +59 -0
- package/clis/amazon/discussion.test.js +1 -28
- package/clis/antigravity/watch.js +3 -2
- package/clis/arxiv/author.js +44 -0
- package/clis/baidu-scholar/search.js +0 -1
- package/clis/bbc/topic.js +57 -0
- package/clis/bbc/utils.js +79 -0
- package/clis/chaoxing/assignments.js +1 -1
- package/clis/chaoxing/exams.js +1 -1
- package/clis/chatgpt/ask.js +57 -0
- package/clis/chatgpt/commands.test.js +45 -0
- package/clis/chatgpt/detail.js +46 -0
- package/clis/chatgpt/history.js +39 -0
- package/clis/chatgpt/image.js +12 -11
- package/clis/chatgpt/image.test.js +23 -0
- package/clis/chatgpt/new.js +25 -0
- package/clis/chatgpt/read.js +43 -0
- package/clis/chatgpt/send.js +46 -0
- package/clis/chatgpt/status.js +29 -0
- package/clis/chatgpt/utils.js +294 -4
- package/clis/chatgpt/utils.test.js +13 -0
- package/clis/chatgpt-app/ask.js +6 -3
- package/clis/chatwise/ask.js +16 -43
- package/clis/chatwise/composer.test.js +186 -0
- package/clis/chatwise/send.js +2 -24
- package/clis/chatwise/utils.js +143 -0
- package/clis/claude/ask.js +1 -1
- package/clis/claude/detail.js +1 -0
- package/clis/claude/history.js +1 -0
- package/clis/claude/new.js +1 -0
- package/clis/claude/read.js +1 -0
- package/clis/claude/send.js +1 -0
- package/clis/claude/status.js +1 -0
- package/clis/codex/ask.js +15 -9
- package/clis/codex/history.js +16 -33
- package/clis/codex/projects.js +28 -0
- package/clis/codex/read.js +10 -4
- package/clis/codex/send.js +10 -3
- package/clis/codex/sidebar.js +356 -0
- package/clis/codex/sidebar.test.js +329 -0
- package/clis/coingecko/categories.js +75 -0
- package/clis/coingecko/coin.js +107 -0
- package/clis/coingecko/coingecko.test.js +109 -0
- package/clis/coingecko/derivatives.js +84 -0
- package/clis/coingecko/exchanges.js +74 -0
- package/clis/coingecko/global.js +71 -0
- package/clis/coingecko/top.js +64 -0
- package/clis/coingecko/trending.js +55 -0
- package/clis/coupang/add-to-cart.js +21 -13
- package/clis/coupang/coupang.test.js +159 -0
- package/clis/coupang/product.js +257 -0
- package/clis/coupang/search.js +38 -16
- package/clis/coupang/utils.js +55 -1
- package/clis/crates/crate.js +62 -0
- package/clis/crates/search.js +44 -0
- package/clis/crates/utils.js +72 -0
- package/clis/ctrip/ctrip.test.js +234 -0
- package/clis/ctrip/hotel-suggest.js +45 -0
- package/clis/ctrip/search.js +22 -68
- package/clis/ctrip/utils.js +175 -0
- package/clis/cursor/ask.js +6 -3
- package/clis/dblp/author.js +133 -0
- package/clis/dblp/venue.js +64 -0
- package/clis/deepseek/ask.js +12 -7
- package/clis/deepseek/ask.test.js +13 -13
- package/clis/deepseek/detail.js +38 -0
- package/clis/deepseek/detail.test.js +81 -0
- package/clis/deepseek/history.js +1 -0
- package/clis/deepseek/new.js +1 -0
- package/clis/deepseek/read.js +1 -0
- package/clis/deepseek/send.js +140 -0
- package/clis/deepseek/send.test.js +107 -0
- package/clis/deepseek/status.js +1 -0
- package/clis/deepseek/utils.js +66 -0
- package/clis/deepseek/utils.test.js +107 -1
- package/clis/defillama/defillama.test.js +99 -0
- package/clis/defillama/protocol.js +84 -0
- package/clis/defillama/protocols.js +55 -0
- package/clis/defillama/utils.js +99 -0
- package/clis/devto/latest.js +74 -0
- package/clis/dockerhub/image.js +52 -0
- package/clis/dockerhub/search.js +47 -0
- package/clis/dockerhub/utils.js +100 -0
- package/clis/doubao/ask.js +7 -3
- package/clis/doubao/detail.js +1 -0
- package/clis/doubao/history.js +1 -0
- package/clis/doubao/meeting-summary.js +1 -0
- package/clis/doubao/meeting-transcript.js +1 -0
- package/clis/doubao/new.js +1 -0
- package/clis/doubao/read.js +1 -0
- package/clis/doubao/send.js +1 -0
- package/clis/doubao/status.js +1 -0
- package/clis/douyin/draft.test.js +1 -30
- package/clis/endoflife/endoflife.test.js +51 -0
- package/clis/endoflife/product.js +55 -0
- package/clis/endoflife/utils.js +89 -0
- package/clis/facebook/__fixtures__/notifications-page.html +13 -0
- package/clis/facebook/notifications.js +326 -30
- package/clis/facebook/notifications.test.js +458 -0
- package/clis/flathub/app.js +71 -0
- package/clis/flathub/flathub.test.js +90 -0
- package/clis/flathub/search.js +80 -0
- package/clis/flathub/utils.js +114 -0
- package/clis/gemini/ask.js +7 -3
- package/clis/gemini/ask.test.js +2 -2
- package/clis/gemini/deep-research-result.js +6 -2
- package/clis/gemini/deep-research-result.test.js +15 -14
- package/clis/gemini/deep-research.js +8 -4
- package/clis/gemini/deep-research.test.js +15 -18
- package/clis/gemini/image.js +7 -2
- package/clis/gemini/new.js +1 -0
- package/clis/gemini/utils.js +0 -4
- package/clis/google-scholar/cite.js +0 -1
- package/clis/google-scholar/profile.js +0 -1
- package/clis/google-scholar/search.js +0 -1
- package/clis/goproxy/goproxy.test.js +103 -0
- package/clis/goproxy/module.js +47 -0
- package/clis/goproxy/utils.js +165 -0
- package/clis/goproxy/versions.js +59 -0
- package/clis/gov-law/recent.js +0 -1
- package/clis/gov-law/search.js +0 -1
- package/clis/gov-policy/__fixtures__/recent.html +16 -0
- package/clis/gov-policy/__fixtures__/search.html +41 -0
- package/clis/gov-policy/gov-policy.test.js +224 -0
- package/clis/gov-policy/recent.js +66 -24
- package/clis/gov-policy/search.js +65 -23
- package/clis/gov-policy/utils.js +54 -0
- package/clis/grok/ask.js +49 -265
- package/clis/grok/ask.test.js +21 -46
- package/clis/grok/detail.js +60 -0
- package/clis/grok/history.js +48 -0
- package/clis/grok/{image.ts → image.js} +56 -70
- package/clis/grok/image.test.ts +20 -0
- package/clis/grok/new.js +20 -0
- package/clis/grok/read.js +39 -0
- package/clis/grok/send.js +50 -0
- package/clis/grok/status.js +41 -0
- package/clis/grok/utils.js +326 -0
- package/clis/grok/utils.test.js +103 -0
- package/clis/hf/datasets.js +88 -0
- package/clis/hf/hf.test.js +16 -0
- package/clis/hf/models.js +91 -0
- package/clis/hf/paper.js +79 -0
- package/clis/hf/spaces.js +101 -0
- package/clis/hf/top.js +1 -0
- package/clis/homebrew/cask.js +39 -0
- package/clis/homebrew/formula.js +41 -0
- package/clis/homebrew/popular.js +54 -0
- package/clis/homebrew/utils.js +100 -0
- package/clis/hupu/__fixtures__/hot-home.html +64 -0
- package/clis/hupu/detail.js +0 -1
- package/clis/hupu/hot.js +156 -35
- package/clis/hupu/hot.test.js +224 -0
- package/clis/hupu/search.js +0 -1
- package/clis/instagram/note.js +1 -1
- package/clis/instagram/note.test.js +1 -29
- package/clis/instagram/post.js +1 -1
- package/clis/instagram/post.test.js +1 -1
- package/clis/instagram/reel.js +1 -1
- package/clis/instagram/story.js +1 -1
- package/clis/instagram/story.test.js +1 -34
- package/clis/jd/commands.test.js +1 -24
- package/clis/lichess/lichess.test.js +85 -0
- package/clis/lichess/top.js +46 -0
- package/clis/lichess/user.js +91 -0
- package/clis/lichess/utils.js +97 -0
- package/clis/linkedin/search.js +107 -10
- package/clis/linkedin/search.test.js +222 -0
- package/clis/linux-do/feed.js +2 -5
- package/clis/linux-do/feed.test.js +35 -0
- package/clis/lobsters/domain.js +92 -0
- package/clis/maven/artifact.js +49 -0
- package/clis/maven/search.js +51 -0
- package/clis/maven/utils.js +110 -0
- package/clis/mdn/search.js +97 -0
- package/clis/medium/tag.js +135 -0
- package/clis/npm/downloads.js +59 -0
- package/clis/npm/package.js +70 -0
- package/clis/npm/search.js +49 -0
- package/clis/npm/utils.js +76 -0
- package/clis/nuget/nuget.test.js +111 -0
- package/clis/nuget/package.js +101 -0
- package/clis/nuget/search.js +69 -0
- package/clis/nuget/utils.js +87 -0
- package/clis/nvd/cve.js +121 -0
- package/clis/oeis/oeis.test.js +88 -0
- package/clis/oeis/search.js +63 -0
- package/clis/oeis/sequence.js +71 -0
- package/clis/oeis/utils.js +88 -0
- package/clis/openalex/search.js +69 -0
- package/clis/openalex/utils.js +160 -0
- package/clis/openalex/work.js +65 -0
- package/clis/openfda/drug-label.js +74 -0
- package/clis/openfda/food-recall.js +65 -0
- package/clis/openfda/openfda.test.js +114 -0
- package/clis/openfda/utils.js +67 -0
- package/clis/osv/osv.test.js +97 -0
- package/clis/osv/query.js +72 -0
- package/clis/osv/utils.js +169 -0
- package/clis/osv/vulnerability.js +54 -0
- package/clis/packagist/package.js +49 -0
- package/clis/packagist/search.js +43 -0
- package/clis/packagist/utils.js +113 -0
- package/clis/paperreview/feedback.js +1 -1
- package/clis/paperreview/review.js +1 -1
- package/clis/paperreview/submit.js +1 -1
- package/clis/pixiv/download.test.js +1 -1
- package/clis/pixiv/illusts.test.js +1 -1
- package/clis/pixiv/search.test.js +1 -1
- package/clis/pubmed/article.js +50 -0
- package/clis/pubmed/author.js +64 -0
- package/clis/pubmed/citations.js +36 -0
- package/clis/pubmed/pubmed.test.js +276 -0
- package/clis/pubmed/related.js +45 -0
- package/clis/pubmed/search.js +75 -0
- package/clis/pubmed/utils.js +309 -0
- package/clis/pypi/downloads.js +66 -0
- package/clis/pypi/package.js +79 -0
- package/clis/pypi/utils.js +55 -0
- package/clis/quark/mv.js +1 -1
- package/clis/quark/save.js +1 -1
- package/clis/qwen/ask.js +85 -0
- package/clis/qwen/detail.js +62 -0
- package/clis/qwen/history.js +61 -0
- package/clis/qwen/image.js +179 -0
- package/clis/qwen/new.js +23 -0
- package/clis/qwen/read.js +41 -0
- package/clis/qwen/send.js +55 -0
- package/clis/qwen/status.js +37 -0
- package/clis/qwen/utils.js +409 -0
- package/clis/qwen/utils.test.js +45 -0
- package/clis/rest-countries/country.js +65 -0
- package/clis/rest-countries/region.js +64 -0
- package/clis/rest-countries/rest-countries.test.js +83 -0
- package/clis/rest-countries/utils.js +126 -0
- package/clis/reuters/article-detail.js +53 -0
- package/clis/reuters/reuters.test.js +299 -0
- package/clis/reuters/search.js +45 -34
- package/clis/reuters/utils.js +159 -0
- package/clis/rfc/rfc.js +52 -0
- package/clis/rfc/rfc.test.js +74 -0
- package/clis/rfc/utils.js +72 -0
- package/clis/rubygems/gem.js +42 -0
- package/clis/rubygems/search.js +47 -0
- package/clis/rubygems/utils.js +86 -0
- package/clis/stackoverflow/related.js +66 -0
- package/clis/stackoverflow/stackoverflow.test.js +58 -0
- package/clis/stackoverflow/tag.js +60 -0
- package/clis/stackoverflow/user.js +50 -0
- package/clis/stackoverflow/utils.js +118 -0
- package/clis/steam/app.js +67 -0
- package/clis/steam/search.js +58 -0
- package/clis/steam/steam.test.js +46 -0
- package/clis/steam/utils.js +107 -0
- package/clis/taobao/commands.test.js +1 -24
- package/clis/test-utils.js +61 -0
- package/clis/tieba/hot.js +0 -1
- package/clis/tiktok/comment.js +128 -41
- package/clis/tiktok/creator-videos.js +270 -0
- package/clis/tiktok/creator-videos.test.js +113 -0
- package/clis/tiktok/explore.js +137 -29
- package/clis/tiktok/follow.js +115 -33
- package/clis/tiktok/following.js +157 -36
- package/clis/tiktok/friends.js +139 -37
- package/clis/tiktok/live.js +137 -41
- package/clis/tiktok/notifications.js +141 -38
- package/clis/tiktok/refactor.test.js +389 -0
- package/clis/tiktok/unfollow.js +124 -38
- package/clis/tiktok/user.js +203 -29
- package/clis/tiktok/utils.js +505 -0
- package/clis/tiktok/write-refactor.test.js +370 -0
- package/clis/toutiao/articles.js +36 -62
- package/clis/toutiao/hot.js +63 -0
- package/clis/toutiao/toutiao.test.js +378 -0
- package/clis/toutiao/utils.js +161 -0
- package/clis/tvmaze/search.js +61 -0
- package/clis/tvmaze/show.js +60 -0
- package/clis/tvmaze/tvmaze.test.js +93 -0
- package/clis/tvmaze/utils.js +110 -0
- package/clis/twitter/accept.js +1 -1
- package/clis/twitter/followers.js +134 -69
- package/clis/twitter/quote.js +139 -0
- package/clis/twitter/quote.test.js +106 -0
- package/clis/twitter/reply-dm.js +1 -1
- package/clis/twitter/reply.test.js +1 -29
- package/clis/twitter/retweet.js +99 -0
- package/clis/twitter/retweet.test.js +69 -0
- package/clis/twitter/shared.js +38 -0
- package/clis/twitter/shared.test.js +28 -1
- package/clis/twitter/unlike.js +87 -0
- package/clis/twitter/unlike.test.js +72 -0
- package/clis/twitter/unretweet.js +99 -0
- package/clis/twitter/unretweet.test.js +69 -0
- package/clis/uisdc/news.js +105 -0
- package/clis/uisdc/news.test.js +66 -0
- package/clis/wanfang/search.js +0 -1
- package/clis/web/read.js +47 -17
- package/clis/web/read.test.js +101 -1
- package/clis/weixin/create-draft.js +1 -1
- package/clis/weixin/drafts.js +1 -1
- package/clis/weixin/drafts.test.js +5 -1
- package/clis/weixin/search.js +157 -0
- package/clis/weixin/search.test.js +227 -0
- package/clis/wikidata/entity.js +60 -0
- package/clis/wikidata/search.js +50 -0
- package/clis/wikidata/utils.js +117 -0
- package/clis/wikidata/wikidata.test.js +83 -0
- package/clis/wikipedia/page.js +95 -0
- package/clis/wttr/current.js +63 -0
- package/clis/wttr/forecast.js +71 -0
- package/clis/wttr/utils.js +50 -0
- package/clis/wttr/wttr.test.js +84 -0
- package/clis/xianyu/chat.js +16 -4
- package/clis/xianyu/chat.test.js +64 -0
- package/clis/xianyu/publish.js +485 -0
- package/clis/xianyu/publish.test.js +220 -0
- package/clis/xiaoe/catalog.js +105 -40
- package/clis/xiaoe/content.js +164 -29
- package/clis/xiaoe/courses.js +86 -29
- package/clis/xiaoe/xiaoe.test.js +486 -0
- package/clis/xiaohongshu/creator-notes-summary.js +1 -1
- package/clis/xiaohongshu/publish.js +16 -3
- package/clis/xiaohongshu/publish.test.js +46 -1
- package/clis/youtube/transcript.js +13 -19
- package/clis/youtube/transcript.test.js +17 -0
- package/clis/yuanbao/ask.js +17 -66
- package/clis/yuanbao/ask.test.js +5 -5
- package/clis/yuanbao/detail.js +65 -0
- package/clis/yuanbao/history.js +51 -0
- package/clis/yuanbao/new.js +1 -0
- package/clis/yuanbao/read.js +38 -0
- package/clis/yuanbao/send.js +57 -0
- package/clis/yuanbao/shared.js +297 -5
- package/clis/yuanbao/shared.test.js +80 -0
- package/clis/yuanbao/status.js +44 -0
- package/clis/zlibrary/commands.test.js +1 -11
- package/dist/src/browser/base-page.d.ts +9 -0
- package/dist/src/browser/base-page.js +44 -1
- package/dist/src/browser/base-page.test.js +66 -0
- package/dist/src/browser/bridge.js +47 -45
- package/dist/src/browser/cdp.d.ts +1 -0
- package/dist/src/browser/cdp.js +51 -9
- package/dist/src/browser/daemon-client.d.ts +4 -0
- package/dist/src/browser/errors.js +1 -1
- package/dist/src/browser/page.d.ts +1 -1
- package/dist/src/browser/page.js +3 -1
- package/dist/src/browser/page.test.js +29 -0
- package/dist/src/browser/target-errors.d.ts +2 -1
- package/dist/src/browser/target-errors.js +1 -0
- package/dist/src/browser/target-resolver.d.ts +25 -0
- package/dist/src/browser/target-resolver.js +43 -0
- package/dist/src/browser.test.js +18 -0
- package/dist/src/build-manifest.js +9 -4
- package/dist/src/build-manifest.test.js +2 -8
- package/dist/src/capabilityRouting.d.ts +16 -1
- package/dist/src/capabilityRouting.js +24 -1
- package/dist/src/capabilityRouting.test.js +19 -1
- package/dist/src/cli.js +76 -11
- package/dist/src/cli.test.js +241 -1
- package/dist/src/commanderAdapter.js +23 -9
- package/dist/src/commanderAdapter.test.js +0 -1
- package/dist/src/discovery.js +2 -5
- package/dist/src/errors.js +1 -1
- package/dist/src/execution.d.ts +1 -1
- package/dist/src/execution.js +111 -27
- package/dist/src/execution.test.js +326 -17
- package/dist/src/help.d.ts +27 -2
- package/dist/src/help.js +196 -23
- package/dist/src/help.test.d.ts +1 -0
- package/dist/src/help.test.js +54 -0
- package/dist/src/main.js +14 -1
- package/dist/src/manifest-types.d.ts +5 -3
- package/dist/src/pipeline/executor.js +1 -1
- package/dist/src/pipeline/executor.test.js +8 -0
- package/dist/src/pipeline/registry.d.ts +9 -0
- package/dist/src/pipeline/registry.js +13 -1
- package/dist/src/pipeline/steps/browser.d.ts +1 -0
- package/dist/src/pipeline/steps/browser.js +10 -0
- package/dist/src/pipeline/steps/download.test.js +1 -0
- package/dist/src/registry-api.d.ts +1 -1
- package/dist/src/registry.d.ts +12 -11
- package/dist/src/registry.js +16 -6
- package/dist/src/registry.test.js +2 -2
- package/dist/src/runtime.d.ts +2 -1
- package/dist/src/runtime.js +1 -1
- package/dist/src/serialization.d.ts +2 -2
- package/dist/src/serialization.js +4 -6
- package/dist/src/serialization.test.js +17 -0
- package/dist/src/types.d.ts +17 -0
- package/dist/src/validate.js +15 -11
- package/dist/src/validate.test.d.ts +9 -0
- package/dist/src/validate.test.js +90 -0
- package/package.json +1 -1
- package/scripts/fetch-adapters.js +1 -1
- package/scripts/typed-error-lint-baseline.json +5 -77
- package/clis/ctrip/search.test.js +0 -64
- package/clis/gov-policy/commands.test.js +0 -27
- package/clis/linux-do/category.js +0 -37
- package/clis/linux-do/hot.js +0 -26
- package/clis/linux-do/latest.js +0 -19
- package/clis/pixiv/test-utils.js +0 -23
- package/clis/toutiao/articles.test.js +0 -30
- package/dist/src/analysis.d.ts +0 -40
- package/dist/src/analysis.js +0 -172
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { getRegistry } from '@jackwener/opencli/registry';
|
|
3
|
+
import { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';
|
|
4
|
+
import './module.js';
|
|
5
|
+
import './versions.js';
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
vi.unstubAllGlobals();
|
|
9
|
+
vi.restoreAllMocks();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('goproxy module adapter', () => {
|
|
13
|
+
const cmd = getRegistry().get('goproxy/module');
|
|
14
|
+
|
|
15
|
+
it('rejects malformed module path before fetching', async () => {
|
|
16
|
+
const fetchMock = vi.fn();
|
|
17
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
18
|
+
|
|
19
|
+
await expect(cmd.func({ module: '' })).rejects.toThrow(ArgumentError);
|
|
20
|
+
await expect(cmd.func({ module: 'noslash' })).rejects.toThrow(ArgumentError);
|
|
21
|
+
await expect(cmd.func({ module: 'has spaces/foo' })).rejects.toThrow(ArgumentError);
|
|
22
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('maps HTTP 410 (gone, e.g. retracted) to EmptyResultError', async () => {
|
|
26
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response('gone', { status: 410 })));
|
|
27
|
+
await expect(cmd.func({ module: 'github.com/foo/bar' })).rejects.toThrow(EmptyResultError);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('maps HTTP 429 to CommandExecutionError', async () => {
|
|
31
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response('rate limited', { status: 429 })));
|
|
32
|
+
await expect(cmd.func({ module: 'github.com/foo/bar' })).rejects.toThrow(CommandExecutionError);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('returns latest version with origin metadata', async () => {
|
|
36
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(JSON.stringify({
|
|
37
|
+
Version: 'v1.12.0',
|
|
38
|
+
Time: '2026-02-28T10:10:09Z',
|
|
39
|
+
Origin: { VCS: 'git', URL: 'https://github.com/gin-gonic/gin', Hash: 'abc123', Ref: 'refs/tags/v1.12.0' },
|
|
40
|
+
}), { status: 200 })));
|
|
41
|
+
|
|
42
|
+
const rows = await cmd.func({ module: 'github.com/gin-gonic/gin' });
|
|
43
|
+
expect(rows).toEqual([expect.objectContaining({
|
|
44
|
+
module: 'github.com/gin-gonic/gin',
|
|
45
|
+
version: 'v1.12.0',
|
|
46
|
+
publishedAt: '2026-02-28T10:10:09Z',
|
|
47
|
+
vcs: 'git',
|
|
48
|
+
commit: 'abc123',
|
|
49
|
+
})]);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('goproxy versions adapter', () => {
|
|
54
|
+
const cmd = getRegistry().get('goproxy/versions');
|
|
55
|
+
|
|
56
|
+
it('rejects bad limit before fetching', async () => {
|
|
57
|
+
const fetchMock = vi.fn();
|
|
58
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
59
|
+
|
|
60
|
+
await expect(cmd.func({ module: 'github.com/foo/bar', limit: 0 })).rejects.toThrow(ArgumentError);
|
|
61
|
+
await expect(cmd.func({ module: 'github.com/foo/bar', limit: 500 })).rejects.toThrow(ArgumentError);
|
|
62
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('throws EmptyResultError when @v/list is empty', async () => {
|
|
66
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response('', { status: 200 })));
|
|
67
|
+
await expect(cmd.func({ module: 'github.com/foo/bar', limit: 5 }))
|
|
68
|
+
.rejects.toThrow(EmptyResultError);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('sorts version tags semver-descending and round-trips module path', async () => {
|
|
72
|
+
// Note: this list is intentionally unsorted in calendar order.
|
|
73
|
+
const list = [
|
|
74
|
+
'v1.9.0', 'v1.10.0', 'v1.2.0', 'v1.10.1', 'v0.0.1', 'v2.0.0',
|
|
75
|
+
'v2.0.0-rc.2', 'v2.0.0-rc.10',
|
|
76
|
+
].join('\n');
|
|
77
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(list, { status: 200 })));
|
|
78
|
+
|
|
79
|
+
const rows = await cmd.func({ module: 'github.com/foo/bar', limit: 10 });
|
|
80
|
+
expect(rows.map((r) => r.version)).toEqual([
|
|
81
|
+
'v2.0.0', 'v2.0.0-rc.10', 'v2.0.0-rc.2',
|
|
82
|
+
'v1.10.1', 'v1.10.0', 'v1.9.0', 'v1.2.0', 'v0.0.1',
|
|
83
|
+
]);
|
|
84
|
+
// module column round-trips to goproxy module <module>
|
|
85
|
+
expect(rows[0]).toMatchObject({ rank: 1, module: 'github.com/foo/bar', publishedAt: null });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('fetches publish times only when --with-time is set', async () => {
|
|
89
|
+
const fetchMock = vi.fn()
|
|
90
|
+
.mockResolvedValueOnce(new Response('v1.0.0\nv0.9.0', { status: 200 }))
|
|
91
|
+
.mockResolvedValueOnce(new Response(JSON.stringify({ Time: '2024-01-02T03:04:05Z' }), { status: 200 }))
|
|
92
|
+
.mockResolvedValueOnce(new Response(JSON.stringify({ Time: '2023-12-31T01:02:03Z' }), { status: 200 }));
|
|
93
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
94
|
+
|
|
95
|
+
const rows = await cmd.func({ module: 'github.com/foo/bar', limit: 2, 'with-time': true });
|
|
96
|
+
|
|
97
|
+
expect(fetchMock).toHaveBeenCalledTimes(3);
|
|
98
|
+
expect(rows.map((r) => [r.version, r.publishedAt])).toEqual([
|
|
99
|
+
['v1.0.0', '2024-01-02T03:04:05Z'],
|
|
100
|
+
['v0.9.0', '2023-12-31T01:02:03Z'],
|
|
101
|
+
]);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// goproxy module — latest version + origin metadata for one Go module.
|
|
2
|
+
//
|
|
3
|
+
// Hits `https://proxy.golang.org/<module>/@latest`, returning the canonical
|
|
4
|
+
// version, publish time, and the upstream VCS / commit / tag the proxy resolved.
|
|
5
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
6
|
+
import { EmptyResultError } from '@jackwener/opencli/errors';
|
|
7
|
+
import { GOPROXY_BASE, goproxyJson, requireModulePath, trimDate } from './utils.js';
|
|
8
|
+
|
|
9
|
+
cli({
|
|
10
|
+
site: 'goproxy',
|
|
11
|
+
name: 'module',
|
|
12
|
+
access: 'read',
|
|
13
|
+
description: 'Latest version + VCS origin metadata for a Go module on proxy.golang.org',
|
|
14
|
+
domain: 'proxy.golang.org',
|
|
15
|
+
strategy: Strategy.PUBLIC,
|
|
16
|
+
browser: false,
|
|
17
|
+
args: [
|
|
18
|
+
{ name: 'module', positional: true, type: 'string', required: true, help: 'Go module path (e.g. "github.com/gin-gonic/gin", "golang.org/x/net")' },
|
|
19
|
+
],
|
|
20
|
+
columns: [
|
|
21
|
+
'module', 'version', 'publishedAt', 'vcs', 'repository',
|
|
22
|
+
'commit', 'ref', 'pkgGoDevUrl', 'url',
|
|
23
|
+
],
|
|
24
|
+
func: async (args) => {
|
|
25
|
+
const modulePath = requireModulePath(args.module);
|
|
26
|
+
// GOPROXY spec requires lowercase percent-encoding for capital letters in the
|
|
27
|
+
// module path (e.g. github.com/Foo/Bar -> github.com/!foo/!bar). Module paths
|
|
28
|
+
// we accept here are lowercase-only by convention; we still encode each segment.
|
|
29
|
+
const encoded = modulePath.split('/').map(encodeURIComponent).join('/');
|
|
30
|
+
const detail = await goproxyJson(`${GOPROXY_BASE}/${encoded}/@latest`, `goproxy module ${modulePath}`);
|
|
31
|
+
if (!detail || !detail.Version) {
|
|
32
|
+
throw new EmptyResultError('goproxy module', `proxy.golang.org returned no @latest entry for "${modulePath}".`);
|
|
33
|
+
}
|
|
34
|
+
const origin = detail.Origin && typeof detail.Origin === 'object' ? detail.Origin : {};
|
|
35
|
+
return [{
|
|
36
|
+
module: modulePath,
|
|
37
|
+
version: String(detail.Version),
|
|
38
|
+
publishedAt: trimDate(detail.Time),
|
|
39
|
+
vcs: String(origin.VCS ?? '').trim(),
|
|
40
|
+
repository: String(origin.URL ?? '').trim(),
|
|
41
|
+
commit: String(origin.Hash ?? '').trim(),
|
|
42
|
+
ref: String(origin.Ref ?? '').trim(),
|
|
43
|
+
pkgGoDevUrl: `https://pkg.go.dev/${modulePath}`,
|
|
44
|
+
url: `${GOPROXY_BASE}/${encoded}/@latest`,
|
|
45
|
+
}];
|
|
46
|
+
},
|
|
47
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Shared helpers for the Go module proxy adapters.
|
|
2
|
+
//
|
|
3
|
+
// proxy.golang.org is the canonical Go module proxy. It is unauthenticated
|
|
4
|
+
// and serves the GOPROXY protocol (`@latest`, `@v/list`, `@v/<ver>.info|mod|zip`).
|
|
5
|
+
// Spec: https://go.dev/ref/mod#goproxy-protocol
|
|
6
|
+
import { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';
|
|
7
|
+
|
|
8
|
+
export const GOPROXY_BASE = 'https://proxy.golang.org';
|
|
9
|
+
const UA = 'opencli-goproxy-adapter (+https://github.com/jackwener/opencli)';
|
|
10
|
+
|
|
11
|
+
// Module paths look like host/path/...; conservative shape: at least one slash,
|
|
12
|
+
// host segment is alnum + dots, path segments are alnum + dashes/dots/underscores/slashes.
|
|
13
|
+
// We enforce at most 200 chars and reject characters that would need URL-escaping.
|
|
14
|
+
const MODULE_PATH = /^[A-Za-z0-9][A-Za-z0-9._\/-]{0,199}$/;
|
|
15
|
+
|
|
16
|
+
// Go semver tags include "v" prefix; we accept the GOPROXY canonical form.
|
|
17
|
+
const VERSION_TAG = /^v[0-9]+(\.[0-9]+)*([-+][A-Za-z0-9._-]+)?$/;
|
|
18
|
+
|
|
19
|
+
export function requireModulePath(value) {
|
|
20
|
+
const s = String(value ?? '').trim();
|
|
21
|
+
if (!s) {
|
|
22
|
+
throw new ArgumentError(
|
|
23
|
+
'goproxy module path is required (e.g. "github.com/gin-gonic/gin", "golang.org/x/net")',
|
|
24
|
+
'Use the canonical module path that appears in `go.mod`.',
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
if (!MODULE_PATH.test(s) || !s.includes('/')) {
|
|
28
|
+
throw new ArgumentError(
|
|
29
|
+
`goproxy module path "${value}" is not a recognised Go module path`,
|
|
30
|
+
'Module paths look like "github.com/<org>/<repo>" or "golang.org/x/<name>".',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return s;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function requireVersionTag(value) {
|
|
37
|
+
const s = String(value ?? '').trim();
|
|
38
|
+
if (!s) throw new ArgumentError('goproxy --version cannot be empty');
|
|
39
|
+
if (!VERSION_TAG.test(s)) {
|
|
40
|
+
throw new ArgumentError(
|
|
41
|
+
`goproxy --version "${value}" is not a valid Go semver tag`,
|
|
42
|
+
'Use the GOPROXY canonical form like "v1.2.3" or "v0.0.0-20240101010101-abcdef012345".',
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return s;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function requireBoundedInt(value, defaultValue, maxValue, label = 'limit') {
|
|
49
|
+
const raw = value ?? defaultValue;
|
|
50
|
+
const n = typeof raw === 'number' ? raw : Number(raw);
|
|
51
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
52
|
+
throw new ArgumentError(`goproxy ${label} must be a positive integer`);
|
|
53
|
+
}
|
|
54
|
+
if (n > maxValue) {
|
|
55
|
+
throw new ArgumentError(`goproxy ${label} must be <= ${maxValue}`);
|
|
56
|
+
}
|
|
57
|
+
return n;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function rawFetch(url, label) {
|
|
61
|
+
let resp;
|
|
62
|
+
try {
|
|
63
|
+
resp = await fetch(url, { headers: { 'user-agent': UA } });
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
throw new CommandExecutionError(
|
|
67
|
+
`${label} request failed: ${err?.message ?? err}`,
|
|
68
|
+
'Check that proxy.golang.org is reachable from this network.',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (resp.status === 404 || resp.status === 410) {
|
|
72
|
+
throw new EmptyResultError(label, `proxy.golang.org returned ${resp.status} for ${url}.`);
|
|
73
|
+
}
|
|
74
|
+
if (resp.status === 429) {
|
|
75
|
+
throw new CommandExecutionError(`${label} returned HTTP 429 (rate limited)`);
|
|
76
|
+
}
|
|
77
|
+
if (!resp.ok) {
|
|
78
|
+
throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
|
|
79
|
+
}
|
|
80
|
+
return resp;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function goproxyJson(url, label) {
|
|
84
|
+
const resp = await rawFetch(url, label);
|
|
85
|
+
let body;
|
|
86
|
+
try {
|
|
87
|
+
body = await resp.json();
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
|
|
91
|
+
}
|
|
92
|
+
return body;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function goproxyText(url, label) {
|
|
96
|
+
const resp = await rawFetch(url, label);
|
|
97
|
+
return resp.text();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Sort version tags by their numeric components, newest first. Pre-release tags
|
|
101
|
+
// (anything after "-" that isn't a pure number) sort lower than the matching
|
|
102
|
+
// release. Returns a new sorted array; non-tag inputs are dropped.
|
|
103
|
+
export function sortVersionsDescending(versions) {
|
|
104
|
+
return versions
|
|
105
|
+
.filter((v) => typeof v === 'string' && VERSION_TAG.test(v))
|
|
106
|
+
.map((v) => ({ v, parts: parseSemver(v) }))
|
|
107
|
+
.sort((a, b) => compareParts(b.parts, a.parts))
|
|
108
|
+
.map((entry) => entry.v);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function parseSemver(tag) {
|
|
112
|
+
// Strip leading "v"; split into <numbers>(-<pre>)?(+<build>)?
|
|
113
|
+
const stripped = tag.replace(/^v/, '');
|
|
114
|
+
const plusIdx = stripped.indexOf('+');
|
|
115
|
+
const noBuild = plusIdx >= 0 ? stripped.slice(0, plusIdx) : stripped;
|
|
116
|
+
const dashIdx = noBuild.indexOf('-');
|
|
117
|
+
const head = dashIdx >= 0 ? noBuild.slice(0, dashIdx) : noBuild;
|
|
118
|
+
const pre = dashIdx >= 0 ? noBuild.slice(dashIdx + 1) : '';
|
|
119
|
+
const numbers = head.split('.').map((s) => Number.parseInt(s, 10)).map((n) => Number.isFinite(n) ? n : 0);
|
|
120
|
+
return { numbers, pre };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function compareParts(a, b) {
|
|
124
|
+
const len = Math.max(a.numbers.length, b.numbers.length);
|
|
125
|
+
for (let i = 0; i < len; i += 1) {
|
|
126
|
+
const ai = a.numbers[i] ?? 0;
|
|
127
|
+
const bi = b.numbers[i] ?? 0;
|
|
128
|
+
if (ai !== bi) return ai - bi;
|
|
129
|
+
}
|
|
130
|
+
// No pre-release sorts higher than has pre-release.
|
|
131
|
+
if (a.pre === '' && b.pre !== '') return 1;
|
|
132
|
+
if (a.pre !== '' && b.pre === '') return -1;
|
|
133
|
+
if (a.pre === b.pre) return 0;
|
|
134
|
+
return comparePrerelease(a.pre, b.pre);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function comparePrerelease(a, b) {
|
|
138
|
+
const aParts = a.split('.');
|
|
139
|
+
const bParts = b.split('.');
|
|
140
|
+
const len = Math.max(aParts.length, bParts.length);
|
|
141
|
+
for (let i = 0; i < len; i += 1) {
|
|
142
|
+
const ai = aParts[i];
|
|
143
|
+
const bi = bParts[i];
|
|
144
|
+
if (ai == null) return -1;
|
|
145
|
+
if (bi == null) return 1;
|
|
146
|
+
const aNum = /^\d+$/.test(ai);
|
|
147
|
+
const bNum = /^\d+$/.test(bi);
|
|
148
|
+
if (aNum && bNum) {
|
|
149
|
+
const diff = Number(ai) - Number(bi);
|
|
150
|
+
if (diff !== 0) return diff;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (aNum !== bNum) return aNum ? -1 : 1;
|
|
154
|
+
if (ai !== bi) return ai < bi ? -1 : 1;
|
|
155
|
+
}
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Normalise GOPROXY ISO-Z timestamps to second precision (no fractional ms).
|
|
160
|
+
export function trimDate(value) {
|
|
161
|
+
const s = String(value ?? '').trim();
|
|
162
|
+
if (!s) return null;
|
|
163
|
+
const noFrac = s.replace(/\.\d+/, '');
|
|
164
|
+
return noFrac.endsWith('Z') ? noFrac : (s.length >= 10 ? s.slice(0, 10) : null);
|
|
165
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// goproxy versions — published version tags for one Go module, newest first.
|
|
2
|
+
//
|
|
3
|
+
// Hits `https://proxy.golang.org/<module>/@v/list` (plain text, one tag per line).
|
|
4
|
+
// Sort by semver (descending) and return up to `--limit` rows; per-tag publish
|
|
5
|
+
// time comes from `@v/<ver>.info` and is fetched only when `--with-time` is set,
|
|
6
|
+
// since it costs one HTTP request per row.
|
|
7
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
8
|
+
import { EmptyResultError } from '@jackwener/opencli/errors';
|
|
9
|
+
import {
|
|
10
|
+
GOPROXY_BASE, goproxyJson, goproxyText, requireBoundedInt, requireModulePath, sortVersionsDescending, trimDate,
|
|
11
|
+
} from './utils.js';
|
|
12
|
+
|
|
13
|
+
cli({
|
|
14
|
+
site: 'goproxy',
|
|
15
|
+
name: 'versions',
|
|
16
|
+
access: 'read',
|
|
17
|
+
description: 'Published version tags for a Go module (newest first), optionally with publish times',
|
|
18
|
+
domain: 'proxy.golang.org',
|
|
19
|
+
strategy: Strategy.PUBLIC,
|
|
20
|
+
browser: false,
|
|
21
|
+
args: [
|
|
22
|
+
{ name: 'module', positional: true, type: 'string', required: true, help: 'Go module path (e.g. "github.com/gin-gonic/gin")' },
|
|
23
|
+
{ name: 'limit', type: 'int', default: 30, help: 'Max rows to return (1-200)' },
|
|
24
|
+
{ name: 'with-time', type: 'boolean', default: false, help: 'Fetch each version\'s publish time (one extra request per row)' },
|
|
25
|
+
],
|
|
26
|
+
columns: [
|
|
27
|
+
'rank', 'module', 'version', 'publishedAt', 'url',
|
|
28
|
+
],
|
|
29
|
+
func: async (args) => {
|
|
30
|
+
const modulePath = requireModulePath(args.module);
|
|
31
|
+
const limit = requireBoundedInt(args.limit, 30, 200, 'limit');
|
|
32
|
+
const withTime = args['with-time'] === true;
|
|
33
|
+
const encoded = modulePath.split('/').map(encodeURIComponent).join('/');
|
|
34
|
+
const text = await goproxyText(`${GOPROXY_BASE}/${encoded}/@v/list`, `goproxy versions ${modulePath}`);
|
|
35
|
+
const raw = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
36
|
+
if (raw.length === 0) {
|
|
37
|
+
throw new EmptyResultError('goproxy versions', `proxy.golang.org returned no published versions for "${modulePath}".`);
|
|
38
|
+
}
|
|
39
|
+
const sorted = sortVersionsDescending(raw).slice(0, limit);
|
|
40
|
+
if (sorted.length === 0) {
|
|
41
|
+
throw new EmptyResultError('goproxy versions', `"${modulePath}" has no semver-shaped tags on proxy.golang.org.`);
|
|
42
|
+
}
|
|
43
|
+
const rows = sorted.map((v, i) => ({
|
|
44
|
+
rank: i + 1,
|
|
45
|
+
module: modulePath,
|
|
46
|
+
version: v,
|
|
47
|
+
publishedAt: null,
|
|
48
|
+
url: `${GOPROXY_BASE}/${encoded}/@v/${encodeURIComponent(v)}.info`,
|
|
49
|
+
}));
|
|
50
|
+
if (withTime) {
|
|
51
|
+
// Sequential to keep the proxy happy; cap at limit which is already <=200.
|
|
52
|
+
for (const row of rows) {
|
|
53
|
+
const info = await goproxyJson(`${GOPROXY_BASE}/${encoded}/@v/${encodeURIComponent(row.version)}.info`, `goproxy versions ${modulePath} ${row.version}`);
|
|
54
|
+
row.publishedAt = trimDate(info?.Time);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return rows;
|
|
58
|
+
},
|
|
59
|
+
});
|
package/clis/gov-law/recent.js
CHANGED
|
@@ -14,7 +14,6 @@ cli({
|
|
|
14
14
|
{ name: 'limit', type: 'int', default: 10, help: '返回结果数量 (max 20)' },
|
|
15
15
|
],
|
|
16
16
|
columns: ['rank', 'title', 'status', 'publish_date', 'type', 'department'],
|
|
17
|
-
navigateBefore: false,
|
|
18
17
|
func: async (page, kwargs) => {
|
|
19
18
|
const limit = clampInt(kwargs.limit, 10, 1, 20);
|
|
20
19
|
await navigateViaVueRouter(page, {});
|
package/clis/gov-law/search.js
CHANGED
|
@@ -15,7 +15,6 @@ cli({
|
|
|
15
15
|
{ name: 'limit', type: 'int', default: 10, help: '返回结果数量 (max 20)' },
|
|
16
16
|
],
|
|
17
17
|
columns: ['rank', 'title', 'status', 'publish_date', 'type', 'department'],
|
|
18
|
-
navigateBefore: false,
|
|
19
18
|
func: async (page, kwargs) => {
|
|
20
19
|
const limit = clampInt(kwargs.limit, 10, 1, 20);
|
|
21
20
|
const query = requireNonEmptyQuery(kwargs.query);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>国务院最新政策文件</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<div class="news_box">
|
|
9
|
+
<div class="list list_1 list_2">
|
|
10
|
+
<ul id="list-1-ajax-id">
|
|
11
|
+
<li><h4><a href="https://www.gov.cn/zhengce/202604/content_7066998.htm" target="_blank">中共中央办公厅 国务院办公厅关于加强新就业群体服务管理的意见</a><span class="date">2026-04-26</span></h4></li><li><h4><a href="https://www.gov.cn/zhengce/202604/content_7066695.htm" target="_blank">中共中央办公厅 国务院办公厅印发《碳达峰碳中和综合评价考核办法》</a><span class="date">2026-04-23</span></h4></li><li><h4><a href="https://www.gov.cn/zhengce/202604/content_7066623.htm" target="_blank">中办国办关于更高水平更高质量做好节能降碳工作的意见</a><span class="date">2026-04-22</span></h4></li><li><h4><a href="https://www.gov.cn/zhengce/content/202604/content_7066483.htm" target="_blank">国务院关于推进服务业扩能提质的意见</a><span class="date">2026-04-21</span></h4></li><li><h4 class="line"><a href="https://www.gov.cn/zhengce/content/202604/content_7066113.htm" target="_blank">国务院办公厅转发海关总署《关于促进综合保税区扩能提质的若干措施》的通知</a><span class="date">2026-04-17</span></h4></li>
|
|
12
|
+
</ul>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>中国政府网站内搜索 - 数字经济</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<div class="basic_result_content js_basic_result_content clearfix">
|
|
9
|
+
<div class="item hasImg is-news" data-documentid="thirdparty_code_107_t_17e00f1efbb_25992758">
|
|
10
|
+
<div class="partitions"></div>
|
|
11
|
+
<a class="title log-anchor" title="经济数据速览:10组数字看一季度中国经济" href="https://www.gov.cn/zhengce/jiedu/tujie/202604/content_7065945.htm" target="_blank">
|
|
12
|
+
<span class="type_title">要闻</span><em>经济</em>数据速览:10组<em>数字</em>看一季度中国<em>经济</em>
|
|
13
|
+
</a>
|
|
14
|
+
<div class="description">
|
|
15
|
+
<img src="placeholder.png" aria-label="经济数据速览:10组数字看一季度中国经济">
|
|
16
|
+
<span class="sourceTime">发布时间:2026-4-16</span>
|
|
17
|
+
</div>
|
|
18
|
+
</div><div class="item hasImg is-news" data-documentid="thirdparty_code_107_t_17e00f1efbb_25839485">
|
|
19
|
+
<div class="partitions"></div>
|
|
20
|
+
<a class="title log-anchor" title="何立峰会见法国经济、财政和工业、能源与数字主权部部长莱斯屈尔" href="https://www.gov.cn/yaowen/liebiao/202603/content_7062985.htm" target="_blank">
|
|
21
|
+
<span class="type_title">要闻</span>何立峰会见法国<em>经济</em>、财政和工业、能源与<em>数字</em>主权部部长莱斯屈尔
|
|
22
|
+
</a>
|
|
23
|
+
<div class="description">
|
|
24
|
+
<div class="detail js_limit_text">
|
|
25
|
+
<p class="js_text line_clamp_two">新华社巴黎3月16日电 当地时间3月16日,中法高级别经济财金对话中方牵头人、国务院副总理何立峰在中美经贸巴黎磋商之后应约会见对话法方牵头人,法国经济、财政和工业、能源与数字主权部部长莱斯屈尔,就近期中法经济财金合作和双方共同关心的问题深入交换意见。 何立峰表示,中方愿与法方一道,落实好习近平主席与马克龙总统重要共识,进一步深化中法经济财金各领域交流与合作,推动中法经济关系行稳致远。何立峰还应询介</p>
|
|
26
|
+
</div>
|
|
27
|
+
<span class="sourceTime">发布时间:2026-3-17</span>
|
|
28
|
+
</div>
|
|
29
|
+
</div><div class="item hasImg is-news" data-documentid="thirdparty_code_107_t_17e00f1efbb_25836739">
|
|
30
|
+
<div class="partitions"></div>
|
|
31
|
+
<a class="title log-anchor" title="经济数据速览:7组数字看1—2月份中国经济" href="https://www.gov.cn/zhengce/jiedu/tujie/202603/content_7062831.htm" target="_blank">
|
|
32
|
+
<span class="type_title">要闻</span><em>经济</em>数据速览:7组<em>数字</em>看1—2月份中国<em>经济</em>
|
|
33
|
+
</a>
|
|
34
|
+
<div class="description">
|
|
35
|
+
<img src="placeholder.png" aria-label="经济数据速览:7组数字看1—2月份中国经济">
|
|
36
|
+
<span class="sourceTime">发布时间:2026-3-16</span>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</body>
|
|
41
|
+
</html>
|