argo-search 1.0.1 → 2.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/LICENSE +201 -21
  2. package/README.en.md +735 -0
  3. package/README.es.md +777 -0
  4. package/README.ja.md +777 -0
  5. package/README.ko.md +777 -0
  6. package/README.md +778 -320
  7. package/SKILL.md +135 -0
  8. package/assets/readme/hero.svg +121 -0
  9. package/assets/readme/made-with-beautify.svg +10 -0
  10. package/assets/readme/proof-routes.svg +73 -0
  11. package/assets/readme/why-better.svg +104 -0
  12. package/assets/readme/workflow.svg +83 -0
  13. package/backends/domain_profiles.json +2356 -96
  14. package/backends/engine_registry.yaml +3428 -503
  15. package/backends/query_synonyms_cn.json +35 -0
  16. package/backends/quota_profiles.json +2488 -160
  17. package/backends/source_types_cn.json +109 -0
  18. package/backends/text_lexicon.json +44 -0
  19. package/bin/argo +772 -0
  20. package/bin/argo.js +102 -39
  21. package/config.yaml +5139 -367
  22. package/cordis.patch.yml +21 -0
  23. package/data/countries.json +40 -0
  24. package/data/lang_matrix/lang_capability.json +842 -0
  25. package/data/lang_matrix/matrix_2026-09-10.json +11921 -0
  26. package/data/regions_cn.json +1 -0
  27. package/docs/ADDING_NEW_ENGINE.md +184 -0
  28. package/docs/ARGO_INTRO.md +124 -0
  29. package/docs/DATA_SOURCE_GAPS.md +81 -0
  30. package/docs/DESIGN_LOCAL_DATA_FUSION.md +90 -0
  31. package/docs/ENGINE_CATALOG.md +575 -0
  32. package/docs/MCP_SETUP.md +108 -0
  33. package/docs/PLATFORM_PATHS.md +111 -0
  34. package/docs/RELEASE_NOTES_v2.8.0.md +71 -0
  35. package/docs/RELEASE_NOTES_v2.8.2.md +63 -0
  36. package/docs/RELEASE_NOTES_v2.8.3.md +55 -0
  37. package/docs/RELEASE_NOTES_v2.8.4.md +79 -0
  38. package/docs/RELEASE_NOTES_v2.8.5.md +102 -0
  39. package/docs/RELEASE_NOTES_v2.8.6.md +66 -0
  40. package/docs/RELEASE_NOTES_v2.8.7.md +65 -0
  41. package/docs/RELEASE_NOTES_v2.8.8.md +67 -0
  42. package/docs/RELEASE_NOTES_v2.8.9.md +67 -0
  43. package/docs/RELEASE_NOTES_v2.9.0.md +64 -0
  44. package/docs/RELEASE_NOTES_v2.9.8.md +73 -0
  45. package/docs/SEARCH_ARCHIVE.md +115 -0
  46. package/docs/assets/architecture.svg +104 -0
  47. package/docs/assets/hero.svg +98 -0
  48. package/docs/engines/bangumi.md +27 -0
  49. package/docs/engines/biorxiv.md +27 -0
  50. package/docs/engines/clawhub.md +27 -0
  51. package/docs/engines/crt_sh.md +27 -0
  52. package/docs/engines/datacite.md +27 -0
  53. package/docs/engines/deps_dev.md +27 -0
  54. package/docs/engines/douban_movie.md +27 -0
  55. package/docs/engines/endoflife.md +27 -0
  56. package/docs/engines/flk_law.md +27 -0
  57. package/docs/engines/fxtwitter.md +27 -0
  58. package/docs/engines/google_news.md +27 -0
  59. package/docs/engines/gov_regulations.md +27 -0
  60. package/docs/engines/guqian.md +27 -0
  61. package/docs/engines/hackernews.md +27 -0
  62. package/docs/engines/iplant.md +27 -0
  63. package/docs/engines/keenable.md +27 -0
  64. package/docs/engines/met_museum.md +27 -0
  65. package/docs/engines/nasa_images.md +27 -0
  66. package/docs/engines/netease_music.md +27 -0
  67. package/docs/engines/npm.md +27 -0
  68. package/docs/engines/nvd.md +27 -0
  69. package/docs/engines/openreview.md +27 -0
  70. package/docs/engines/openstd.md +27 -0
  71. package/docs/engines/parallel_free.md +27 -0
  72. package/docs/engines/people_daily.md +27 -0
  73. package/docs/engines/realtime_index.md +27 -0
  74. package/docs/engines/sec_edgar.md +27 -0
  75. package/docs/engines/seltz.md +27 -0
  76. package/docs/engines/skillsmp.md +27 -0
  77. package/docs/engines/sspai.md +27 -0
  78. package/docs/engines/std_samr.md +27 -0
  79. package/docs/engines/train.md +27 -0
  80. package/docs/engines/un_comtrade.md +27 -0
  81. package/docs/engines/wikisource.md +27 -0
  82. package/docs/engines/zdic.md +27 -0
  83. package/docs/engines/zenodo.md +27 -0
  84. package/docs/engines/zhihu_hot_app.md +27 -0
  85. package/docs//344/270/272/344/273/200/344/271/210/351/200/211/346/213/251argo.md +176 -0
  86. package/engines/README.md +10 -0
  87. package/engines/_template_cli.yaml +39 -0
  88. package/engines/_template_http.yaml +37 -0
  89. package/engines/plugins/_template_plugin.py +50 -0
  90. package/engines/specs/_cisa_kev.yaml +54 -0
  91. package/engines/specs/_osv.yaml +63 -0
  92. package/engines/specs/aljazeera.yaml +43 -0
  93. package/engines/specs/antara.yaml +43 -0
  94. package/engines/specs/artic.yaml +20 -0
  95. package/engines/specs/aviation_weather.yaml +39 -0
  96. package/engines/specs/bangumi.yaml +21 -0
  97. package/engines/specs/biorxiv.yaml +24 -0
  98. package/engines/specs/carbon_intensity.yaml +32 -0
  99. package/engines/specs/cfets.yaml +41 -0
  100. package/engines/specs/cinii.yaml +44 -0
  101. package/engines/specs/clawhub.yaml +49 -0
  102. package/engines/specs/cleveland.yaml +19 -0
  103. package/engines/specs/cn_ai_news.yaml +43 -0
  104. package/engines/specs/core.yaml +35 -0
  105. package/engines/specs/crt_sh.yaml +38 -0
  106. package/engines/specs/ct24.yaml +43 -0
  107. package/engines/specs/datacite.yaml +27 -0
  108. package/engines/specs/datagov_uk.yaml +41 -0
  109. package/engines/specs/ddgs_images.yaml +45 -0
  110. package/engines/specs/deepseek_search.yaml +74 -0
  111. package/engines/specs/deezer.yaml +20 -0
  112. package/engines/specs/defillama.yaml +38 -0
  113. package/engines/specs/deps_dev.yaml +21 -0
  114. package/engines/specs/digup.yaml +36 -0
  115. package/engines/specs/doi.yaml +39 -0
  116. package/engines/specs/douban_movie.yaml +22 -0
  117. package/engines/specs/dw_news.yaml +47 -0
  118. package/engines/specs/egov_law.yaml +21 -0
  119. package/engines/specs/elpais.yaml +43 -0
  120. package/engines/specs/endoflife.yaml +21 -0
  121. package/engines/specs/energy_charts.yaml +21 -0
  122. package/engines/specs/factcheck_org.yaml +52 -0
  123. package/engines/specs/faz.yaml +43 -0
  124. package/engines/specs/federal_register.yaml +57 -0
  125. package/engines/specs/firecrawl.yaml +53 -0
  126. package/engines/specs/flk_law.yaml +24 -0
  127. package/engines/specs/folha.yaml +43 -0
  128. package/engines/specs/france24.yaml +44 -0
  129. package/engines/specs/full_fact.yaml +47 -0
  130. package/engines/specs/fxtwitter.yaml +36 -0
  131. package/engines/specs/gbfs_nyc.yaml +32 -0
  132. package/engines/specs/gdacs.yaml +21 -0
  133. package/engines/specs/gdelt.yaml +64 -0
  134. package/engines/specs/gleif.yaml +55 -0
  135. package/engines/specs/google_news.yaml +22 -0
  136. package/engines/specs/gov_regulations.yaml +38 -0
  137. package/engines/specs/guardian_rss.yaml +59 -0
  138. package/engines/specs/hkex.yaml +40 -0
  139. package/engines/specs/hurriyet.yaml +43 -0
  140. package/engines/specs/iplant.yaml +25 -0
  141. package/engines/specs/jikan.yaml +24 -0
  142. package/engines/specs/jolpica.yaml +20 -0
  143. package/engines/specs/k10plus.yaml +20 -0
  144. package/engines/specs/keenable.yaml +44 -0
  145. package/engines/specs/keenable_free.yaml +36 -0
  146. package/engines/specs/lefigaro.yaml +43 -0
  147. package/engines/specs/listenbrainz.yaml +19 -0
  148. package/engines/specs/matichon.yaml +43 -0
  149. package/engines/specs/met_museum.yaml +21 -0
  150. package/engines/specs/nasa_images.yaml +45 -0
  151. package/engines/specs/netease_music.yaml +38 -0
  152. package/engines/specs/nhk.yaml +43 -0
  153. package/engines/specs/nhtsa_vpic.yaml +20 -0
  154. package/engines/specs/noaa_swpc.yaml +20 -0
  155. package/engines/specs/nvd.yaml +38 -0
  156. package/engines/specs/obis.yaml +20 -0
  157. package/engines/specs/opencitations.yaml +52 -0
  158. package/engines/specs/openf1.yaml +21 -0
  159. package/engines/specs/openfoodfacts.yaml +45 -0
  160. package/engines/specs/openligadb.yaml +20 -0
  161. package/engines/specs/openreview.yaml +37 -0
  162. package/engines/specs/openstd.yaml +21 -0
  163. package/engines/specs/people_daily.yaml +22 -0
  164. package/engines/specs/realtime_index.yaml +34 -0
  165. package/engines/specs/ror.yaml +20 -0
  166. package/engines/specs/satnogs.yaml +20 -0
  167. package/engines/specs/sec_edgar.yaml +46 -0
  168. package/engines/specs/shfe.yaml +39 -0
  169. package/engines/specs/skillsmp.yaml +50 -0
  170. package/engines/specs/sogou_weixin_browser.yaml +30 -0
  171. package/engines/specs/soilgrids.yaml +25 -0
  172. package/engines/specs/sspai.yaml +36 -0
  173. package/engines/specs/stackexchange.yaml +46 -0
  174. package/engines/specs/std_samr.yaml +21 -0
  175. package/engines/specs/tass.yaml +43 -0
  176. package/engines/specs/tavily_free.yaml +56 -0
  177. package/engines/specs/tle_mirror.yaml +21 -0
  178. package/engines/specs/train.yaml +37 -0
  179. package/engines/specs/tvmaze.yaml +20 -0
  180. package/engines/specs/twitter_syndication.yaml +31 -0
  181. package/engines/specs/un_comtrade.yaml +23 -0
  182. package/engines/specs/unpaywall.yaml +54 -0
  183. package/engines/specs/vnpress.yaml +43 -0
  184. package/engines/specs/weather.yaml +32 -0
  185. package/engines/specs/who_don.yaml +21 -0
  186. package/engines/specs/who_gho.yaml +20 -0
  187. package/engines/specs/wikimedia_commons.yaml +34 -0
  188. package/engines/specs/wikisource.yaml +22 -0
  189. package/engines/specs/worms.yaml +19 -0
  190. package/engines/specs/x_trending.yaml +34 -0
  191. package/engines/specs/yna.yaml +43 -0
  192. package/engines/specs/ynet.yaml +43 -0
  193. package/engines/specs/yuanbao.yaml +93 -0
  194. package/engines/specs/zbmath.yaml +40 -0
  195. package/engines/specs/zdic.yaml +22 -0
  196. package/engines/specs/zenodo.yaml +27 -0
  197. package/engines/specs/zhihu_hot_app.yaml +35 -0
  198. package/mcp/clients.yaml +106 -0
  199. package/package.json +55 -12
  200. package/packages/dsh-plugin/README.md +152 -0
  201. package/packages/dsh-plugin/cordis.patch.yml +48 -0
  202. package/packages/dsh-plugin/dsh/client.js +1533 -0
  203. package/packages/dsh-plugin/dsh/diagnostics.js +54 -0
  204. package/packages/dsh-plugin/dsh/index.js +527 -0
  205. package/packages/dsh-plugin/dsh/native-tools.mjs +749 -0
  206. package/packages/dsh-plugin/dsh/runner.js +666 -0
  207. package/packages/dsh-plugin/dsh/wide-research.js +878 -0
  208. package/packages/dsh-plugin/package.json +38 -0
  209. package/packages/dsh-plugin/scripts/sync-local.sh +70 -0
  210. package/packages/dsh-plugin/test/client.test.js +406 -0
  211. package/packages/dsh-plugin/test/config.test.js +30 -0
  212. package/packages/dsh-plugin/test/diagnostics.test.js +28 -0
  213. package/packages/dsh-plugin/test/index.test.js +1382 -0
  214. package/references/query-templates.md +96 -0
  215. package/references/query-templates.yaml +118 -0
  216. package/scripts/ab_eval.py +219 -0
  217. package/scripts/ab_eval_p0p1.py +266 -0
  218. package/scripts/adaptive.py +294 -70
  219. package/scripts/answer.py +154 -0
  220. package/scripts/archive_run.py +591 -0
  221. package/scripts/argo_engine_registry.py +172 -0
  222. package/scripts/argo_paths.py +728 -0
  223. package/scripts/article.py +136 -0
  224. package/scripts/article_meta.py +131 -0
  225. package/scripts/arxiv_source.py +201 -0
  226. package/scripts/badcase_ledger.py +252 -0
  227. package/scripts/batch_probe.py +241 -0
  228. package/scripts/benchmark.py +19 -0
  229. package/scripts/bounded_run.py +144 -0
  230. package/scripts/browser_auth.py +323 -0
  231. package/scripts/budget.py +247 -0
  232. package/scripts/budget_cli.py +93 -0
  233. package/scripts/build_release_assets.sh +65 -0
  234. package/scripts/burstiness_detector.py +147 -0
  235. package/scripts/cache.py +1066 -67
  236. package/scripts/cache_guard.py +271 -0
  237. package/scripts/cache_key_vdom.py +56 -0
  238. package/scripts/cache_similarity.py +170 -0
  239. package/scripts/caliber_isolation.py +195 -0
  240. package/scripts/calibrate_lowquality.py +210 -0
  241. package/scripts/candidate_envelope.py +366 -0
  242. package/scripts/chrome_cdp.py +871 -0
  243. package/scripts/ci_drift_gate.py +90 -0
  244. package/scripts/circuit_breaker.py +389 -0
  245. package/scripts/citations.py +334 -0
  246. package/scripts/cite_cli.py +59 -0
  247. package/scripts/cjk_tokens.py +95 -0
  248. package/scripts/clarify.py +47 -24
  249. package/scripts/cli_io.py +240 -0
  250. package/scripts/config.py +683 -45
  251. package/scripts/config_cache.py +402 -0
  252. package/scripts/conn_pool.py +220 -0
  253. package/scripts/content_boundary.py +46 -0
  254. package/scripts/content_security.py +685 -0
  255. package/scripts/content_signals.py +751 -0
  256. package/scripts/crawl.py +74 -35
  257. package/scripts/cross_lingual_trace.py +293 -0
  258. package/scripts/daemon.py +866 -0
  259. package/scripts/digup_bridge.py +214 -0
  260. package/scripts/dispatch_result.py +98 -0
  261. package/scripts/engine_admission.py +366 -0
  262. package/scripts/engine_dispatch.py +982 -0
  263. package/scripts/engine_env.py +539 -0
  264. package/scripts/engine_failure.py +411 -0
  265. package/scripts/engine_families.py +738 -0
  266. package/scripts/engine_policy.py +218 -0
  267. package/scripts/engine_requires.py +190 -0
  268. package/scripts/engine_status.py +322 -0
  269. package/scripts/engine_validate.py +638 -0
  270. package/scripts/engines.py +799 -294
  271. package/scripts/engines_base.py +1521 -0
  272. package/scripts/engines_builders.py +310 -0
  273. package/scripts/engines_builders_batch10.py +204 -0
  274. package/scripts/engines_builders_batch11.py +174 -0
  275. package/scripts/engines_builders_batch12.py +103 -0
  276. package/scripts/engines_builders_batch13.py +340 -0
  277. package/scripts/engines_builders_batch14.py +656 -0
  278. package/scripts/engines_builders_batch15.py +249 -0
  279. package/scripts/engines_builders_batch9.py +1468 -0
  280. package/scripts/engines_builders_cn.py +2283 -0
  281. package/scripts/engines_builders_data.py +2923 -0
  282. package/scripts/engines_builders_data_macro.py +775 -0
  283. package/scripts/engines_builders_feeds.py +255 -0
  284. package/scripts/engines_builders_intl.py +1239 -0
  285. package/scripts/engines_builders_keyless.py +111 -0
  286. package/scripts/engines_builders_search.py +440 -0
  287. package/scripts/engines_builders_tech.py +1359 -0
  288. package/scripts/evidence.py +620 -166
  289. package/scripts/evidence_loop.py +624 -0
  290. package/scripts/evidence_tier.py +298 -0
  291. package/scripts/except_sets.py +25 -0
  292. package/scripts/extract.py +55 -16
  293. package/scripts/fact_align.py +207 -0
  294. package/scripts/fact_crosscheck.py +230 -0
  295. package/scripts/fetch.py +25 -12
  296. package/scripts/fetch_browser.py +87 -0
  297. package/scripts/fetch_jsrun.py +122 -0
  298. package/scripts/fetch_quality.py +225 -0
  299. package/scripts/fetch_render_tinyfish.py +117 -0
  300. package/scripts/fetch_v3.py +1804 -0
  301. package/scripts/focus_extract.py +350 -0
  302. package/scripts/fulltext_store.py +169 -0
  303. package/scripts/fxtwitter_api.py +325 -0
  304. package/scripts/gen_engine_catalog.py +364 -0
  305. package/scripts/gen_native_tools.py +91 -0
  306. package/scripts/gen_routing_snapshot.py +195 -0
  307. package/scripts/health_check.py +140 -402
  308. package/scripts/health_probe.py +23 -8
  309. package/scripts/hot_state.py +134 -0
  310. package/scripts/http_client.py +1053 -0
  311. package/scripts/image_ops.py +430 -0
  312. package/scripts/image_vision/vision_probe.swift +126 -0
  313. package/scripts/install.ps1 +166 -0
  314. package/scripts/install.sh +221 -0
  315. package/scripts/job.py +1186 -0
  316. package/scripts/jump_resolver.py +235 -0
  317. package/scripts/lang_capability.py +150 -0
  318. package/scripts/lang_detect.py +289 -0
  319. package/scripts/lang_matrix_probe.py +329 -0
  320. package/scripts/lang_pref.py +311 -0
  321. package/scripts/link_source.py +340 -0
  322. package/scripts/local_image.py +970 -0
  323. package/scripts/local_seek.py +402 -0
  324. package/scripts/macro_countries.py +97 -0
  325. package/scripts/matrix_search_eval.py +1187 -0
  326. package/scripts/mcp_compact.py +234 -0
  327. package/scripts/mcp_diag.py +8 -3
  328. package/scripts/mcp_handlers.py +492 -0
  329. package/scripts/mcp_handlers_surface.py +157 -0
  330. package/scripts/mcp_launch.sh +124 -0
  331. package/scripts/mcp_local.py +241 -0
  332. package/scripts/mcp_server.py +125 -419
  333. package/scripts/mcp_setup.py +431 -0
  334. package/scripts/mcp_tools.py +331 -0
  335. package/scripts/mcp_transport.py +191 -0
  336. package/scripts/mcp_warm.py +127 -0
  337. package/scripts/min_version_scan.py +335 -0
  338. package/scripts/net_proxy.py +258 -0
  339. package/scripts/network_aware.py +152 -0
  340. package/scripts/output_transparency.py +200 -0
  341. package/scripts/paid_policy.py +98 -0
  342. package/scripts/paper.py +274 -0
  343. package/scripts/pdf_extract.py +518 -0
  344. package/scripts/phrase_filter.py +66 -0
  345. package/scripts/plan.py +422 -0
  346. package/scripts/postprocess_signals.py +150 -0
  347. package/scripts/pre-commit +48 -0
  348. package/scripts/query_enhance.py +157 -0
  349. package/scripts/query_operators.py +196 -0
  350. package/scripts/query_rewriter.py +116 -7
  351. package/scripts/query_signals.py +300 -0
  352. package/scripts/query_templates.py +167 -0
  353. package/scripts/query_understanding.py +411 -0
  354. package/scripts/query_variants.py +298 -0
  355. package/scripts/quota.py +633 -76
  356. package/scripts/rank_signals.py +775 -0
  357. package/scripts/ranking_eval.py +319 -0
  358. package/scripts/readability_extract.py +433 -0
  359. package/scripts/recompute.py +415 -0
  360. package/scripts/recovery.py +616 -0
  361. package/scripts/recovery_l0.py +160 -0
  362. package/scripts/recovery_runner.py +150 -0
  363. package/scripts/redskill/redskill_engine.py +332 -0
  364. package/scripts/regression_p0p1.py +318 -0
  365. package/scripts/replay_eval.py +658 -0
  366. package/scripts/rerank_tfidf.py +225 -0
  367. package/scripts/research.py +607 -403
  368. package/scripts/research_cli.py +361 -0
  369. package/scripts/research_dossier.py +492 -0
  370. package/scripts/research_expand.py +169 -0
  371. package/scripts/research_gates.py +272 -0
  372. package/scripts/research_report.py +209 -0
  373. package/scripts/research_strategy.py +37 -0
  374. package/scripts/research_work_packages.py +195 -0
  375. package/scripts/result_lang.py +311 -0
  376. package/scripts/retry_policy.py +76 -0
  377. package/scripts/robots_guard.py +259 -0
  378. package/scripts/route.py +921 -254
  379. package/scripts/route_bangs.py +40 -0
  380. package/scripts/route_cache.py +243 -0
  381. package/scripts/route_combo.py +917 -0
  382. package/scripts/route_domains.py +341 -0
  383. package/scripts/route_lang.py +605 -0
  384. package/scripts/route_log.py +63 -0
  385. package/scripts/route_policy.py +250 -0
  386. package/scripts/rrf_classic.py +66 -0
  387. package/scripts/screenshot.py +73 -0
  388. package/scripts/search.py +886 -406
  389. package/scripts/search_attribution.py +185 -0
  390. package/scripts/search_benchmark.py +407 -0
  391. package/scripts/search_cli.py +640 -0
  392. package/scripts/search_entry.py +395 -0
  393. package/scripts/search_output.py +690 -0
  394. package/scripts/search_pipeline.py +926 -0
  395. package/scripts/search_rank.py +991 -0
  396. package/scripts/search_types.py +129 -16
  397. package/scripts/seek_locator.py +68 -0
  398. package/scripts/semantic_evidence.py +391 -0
  399. package/scripts/serp_guard.py +336 -0
  400. package/scripts/serp_kill_log.py +74 -0
  401. package/scripts/single_flight.py +120 -0
  402. package/scripts/social_engines/__init__.py +10 -0
  403. package/scripts/social_engines/bilibili_engine.py +131 -0
  404. package/scripts/social_engines/hackernews_engine.py +113 -0
  405. package/scripts/social_engines/reddit_engine.py +172 -0
  406. package/scripts/social_engines/twitter_engine.py +397 -0
  407. package/scripts/social_engines/v2ex_engine.py +101 -0
  408. package/scripts/social_engines/weibo_engine.py +128 -0
  409. package/scripts/social_engines/xiaohongshu_engine.py +137 -0
  410. package/scripts/social_engines/zhihu_engine.py +139 -0
  411. package/scripts/social_research.py +201 -0
  412. package/scripts/sogou_weixin_browser.py +318 -0
  413. package/scripts/source_ledger.py +249 -0
  414. package/scripts/stage_timing.py +58 -0
  415. package/scripts/stats_cli.py +107 -0
  416. package/scripts/straggler_policy.py +60 -0
  417. package/scripts/stylometry_detector.py +231 -0
  418. package/scripts/sync_backends.py +353 -0
  419. package/scripts/sync_installs.py +29 -0
  420. package/scripts/template_cli.py +127 -0
  421. package/scripts/text_lexicon.py +72 -0
  422. package/scripts/tfidf_router.py +109 -37
  423. package/scripts/time_utils.py +233 -0
  424. package/scripts/topic_research_profiles.py +643 -0
  425. package/scripts/train.py +737 -0
  426. package/scripts/tweet.py +596 -0
  427. package/scripts/url_canon.py +217 -0
  428. package/scripts/url_optimize.py +56 -0
  429. package/scripts/url_safety.py +236 -0
  430. package/scripts/usage_log.py +116 -0
  431. package/scripts/usage_log_clean.py +113 -0
  432. package/scripts/v2ex_nodes.py +311 -0
  433. package/scripts/variant_recall.py +188 -0
  434. package/scripts/watch.py +205 -0
  435. package/scripts/wechat_article.py +258 -0
  436. package/scripts/wide_research.py +447 -0
  437. package/scripts/wx.py +349 -0
  438. package/scripts/x_article_fetch.py +141 -0
  439. package/scripts/x_search.py +234 -0
  440. package/scripts/x_trending.py +189 -0
  441. package/scripts/yaml_load.py +67 -0
  442. package/scripts/zhihu_quota.py +139 -0
  443. package/sub-skills/ego-search/SKILL.md +253 -0
  444. package/sub-skills/ego-search/scripts/ego_search.py +800 -0
  445. package/sub-skills/ego-search/scripts/merge.py +226 -0
  446. package/sub-skills/ego-search/scripts/quality.py +64 -0
  447. package/sub-skills/ego-search/scripts/runtime.py +172 -0
  448. package/sub-skills/ego-search/scripts/safety.py +109 -0
  449. package/sub-skills/ego-search/scripts/serp_spec.py +109 -0
  450. package/sub-skills/ego-search/scripts/webbridge_adapter.py +374 -0
  451. package/sub-skills/js-run/SKILL.md +67 -0
  452. package/sub-skills/js-run/scripts/jsrun.py +112 -0
  453. package/sub-skills/js-run/scripts/shim.js +188 -0
  454. package/sub-skills/local-search/SKILL.md +50 -53
  455. package/sub-skills/local-search/bm25.py +186 -0
  456. package/sub-skills/local-search/cli_runner.py +331 -0
  457. package/sub-skills/local-search/config.yaml +152 -55
  458. package/sub-skills/local-search/engine_registry.py +91 -26
  459. package/sub-skills/local-search/{health_check.py → local_health_check.py} +140 -10
  460. package/sub-skills/local-search/local_search_adapter.py +13 -2
  461. package/sub-skills/local-search/parse_maps.yaml +40 -42
  462. package/sub-skills/local-search/parsers.py +328 -0
  463. package/sub-skills/local-search/search_v3.py +716 -242
  464. package/sub-skills/local-search/smart_router.py +47 -7
  465. package/sub-skills/local-seek/SKILL.md +110 -0
  466. package/sub-skills/local-seek/config/domains.yaml +20 -0
  467. package/sub-skills/local-seek/scripts/eval_rank.py +216 -0
  468. package/sub-skills/local-seek/scripts/eval_seek.py +224 -0
  469. package/sub-skills/local-seek/scripts/seek.py +1465 -0
  470. package/sub-skills/local-seek/scripts/seek_frecency.py +126 -0
  471. package/sub-skills/local-seek/scripts/seek_fts.py +430 -0
  472. package/sub-skills/local-seek/scripts/seek_rank.py +197 -0
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@taxueseek/argo-dsh",
3
+ "version": "2.9.8",
4
+ "description": "Argo 阿尔戈 — DSH plugin:argo 原生搜索/抓取工具 + web_search seam + wide_research 编排;自带设置页(接入/工具/API 密钥/研究/高级);argo MCP 19 工具全量面按需挂载。安装:dsh plugin --profile web add \"github:taxueseek/argo#main&path:packages/dsh-plugin\"",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dsh/index.js",
8
+ "exports": {
9
+ ".": "./dsh/index.js",
10
+ "./dsh": "./dsh/index.js",
11
+ "./client": "./dsh/client.js",
12
+ "./cordis.patch.yml": "./cordis.patch.yml",
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "dsh",
17
+ "cordis.patch.yml",
18
+ "README.md"
19
+ ],
20
+ "dsh": {
21
+ "bundle": {
22
+ "patch": "./cordis.patch.yml"
23
+ },
24
+ "client": {
25
+ "platform": "web",
26
+ "inject": [
27
+ "@deepseek-ai/dsh-client-ui-settings"
28
+ ]
29
+ }
30
+ },
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "scripts": {
35
+ "check": "node --check dsh/index.js && node --check dsh/runner.js && node --check dsh/wide-research.js && node --check dsh/diagnostics.js && node --check dsh/client.js",
36
+ "test": "node --test test/*.test.js"
37
+ }
38
+ }
@@ -0,0 +1,70 @@
1
+ #!/bin/bash
2
+ # sync-local.sh — 把本仓 packages/dsh-plugin 同步进本机已安装的 DSH profile。
3
+ #
4
+ # 为什么需要:profile 的依赖是 `file:`,pnpm 安装时是**复制**进
5
+ # node_modules,不是软链。改完源码不跑本脚本,DSH 设置页读到的仍是旧
6
+ # bundle(2026-10-22 实测:缓存开关与付费额度都已改,面板仍显示旧版)。
7
+ #
8
+ # 用法:
9
+ # packages/dsh-plugin/scripts/sync-local.sh # 同步 web+desktop(存在才同步)
10
+ # packages/dsh-plugin/scripts/sync-local.sh web # 只同步指定 profile
11
+ # packages/dsh-plugin/scripts/sync-local.sh --check # 只比对,不写;有漂移退出 1
12
+ # packages/dsh-plugin/scripts/sync-local.sh --restart # 同步后重启 DSH web
13
+ #
14
+ # 环境变量:DSH_HOME 覆盖 ~/.dsh;DSH_PROFILES 逗号分隔(默认 web,desktop)。
15
+ set -euo pipefail
16
+
17
+ SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
18
+ DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
19
+ MODE="sync"
20
+ RESTART=0
21
+ PROFILES=()
22
+
23
+ for arg in "$@"; do
24
+ case "$arg" in
25
+ --check) MODE="check" ;;
26
+ --restart) RESTART=1 ;;
27
+ -h|--help) sed -n '2,20p' "${BASH_SOURCE[0]}"; exit 0 ;;
28
+ *) PROFILES+=("$arg") ;;
29
+ esac
30
+ done
31
+
32
+ if [ ${#PROFILES[@]} -eq 0 ]; then
33
+ IFS=',' read -r -a PROFILES <<<"${DSH_PROFILES:-web,desktop}"
34
+ fi
35
+
36
+ TS="$(date +%Y%m%d-%H%M%S)"
37
+ drift=0
38
+
39
+ for p in "${PROFILES[@]}"; do
40
+ DEST="$DSH_HOME/profiles/$p/node_modules/@taxueseek/argo-dsh"
41
+ if [ ! -d "$DEST" ]; then
42
+ echo "[skip] $p: 未安装 $DEST"
43
+ continue
44
+ fi
45
+ if [ "$MODE" = check ]; then
46
+ if diff -rq "$SRC/dsh" "$DEST/dsh" >/dev/null 2>&1 \
47
+ && cmp -s "$SRC/package.json" "$DEST/package.json"; then
48
+ echo "[ok] $p"
49
+ else
50
+ echo "[drift] $p: 源码与已安装 bundle 不一致,跑一次不带 --check 的同步"
51
+ drift=1
52
+ fi
53
+ continue
54
+ fi
55
+ cp -a "$DEST" "$DSH_HOME/backups/argo-dsh-sync-$TS-$p"
56
+ rsync -a --delete "$SRC/dsh/" "$DEST/dsh/"
57
+ cp "$SRC/package.json" "$SRC/cordis.patch.yml" "$SRC/README.md" "$DEST/"
58
+ echo "[synced] $p"
59
+ done
60
+
61
+ if [ "$MODE" = check ]; then
62
+ exit "$drift"
63
+ fi
64
+
65
+ if [ "$RESTART" = 1 ] && [ -x "$DSH_HOME/scripts/argo-dsh-restart.sh" ]; then
66
+ echo "[restart] $DSH_HOME/scripts/argo-dsh-restart.sh"
67
+ bash "$DSH_HOME/scripts/argo-dsh-restart.sh"
68
+ else
69
+ echo "重启 DSH web 后生效:bash $DSH_HOME/scripts/argo-dsh-restart.sh"
70
+ fi
@@ -0,0 +1,406 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { readFileSync } from 'node:fs'
4
+ import vm from 'node:vm'
5
+ import { NATIVE_TOOLS } from '../dsh/native-tools.mjs'
6
+ import { ARGO_ENV_SPECS } from '../dsh/index.js'
7
+
8
+ /** 在最小沙箱里执行 lazy-CJS bundle,返回 ModuleLoader 收到的定义。 */
9
+ function loadBundle() {
10
+ let definition
11
+ const sandbox = {
12
+ window: { __ModuleLoader__: { load: (value) => { definition = value } } },
13
+ console,
14
+ }
15
+ sandbox.globalThis = sandbox
16
+ vm.runInNewContext(readFileSync(new URL('../dsh/client.js', import.meta.url), 'utf8'), sandbox)
17
+ return definition
18
+ }
19
+
20
+ function factory(definition) {
21
+ const React = {
22
+ createElement: () => null,
23
+ useState: () => [undefined, () => {}],
24
+ useEffect: () => {},
25
+ useRef: () => ({ current: new Set() }),
26
+ }
27
+ return definition.factory((id) => {
28
+ if (id === 'react') return React
29
+ throw new Error(`unexpected require: ${id}`)
30
+ })
31
+ }
32
+
33
+ /** 带状态的 React 替身:单次渲染口径,足够驱动 ArgoPanel 完整执行一遍。
34
+ * register 测试用无状态 factory(只要模块能加载);渲染测试要真跑组件函数,
35
+ * 所以 useState/useRef 按调用序记进 hooks,测试可预置 tab 再渲染。 */
36
+ function statefulReact() {
37
+ const hooks = []
38
+ let idx = 0
39
+ const react = {
40
+ createElement: (type, props, ...children) => ({ type, props: props || {}, children }),
41
+ useState: (init) => {
42
+ const i = idx++
43
+ if (!(i in hooks)) hooks[i] = typeof init === 'function' ? init() : init
44
+ return [hooks[i], (v) => { hooks[i] = typeof v === 'function' ? v(hooks[i]) : v }]
45
+ },
46
+ useEffect: () => {},
47
+ useRef: (init) => {
48
+ const i = idx++
49
+ if (!(i in hooks)) hooks[i] = { current: init }
50
+ return hooks[i]
51
+ },
52
+ }
53
+ return { react, hooks, reset: () => { idx = 0 } }
54
+ }
55
+
56
+ test('client bundle: registers both settings seats for the wide-research namespace', () => {
57
+ const definition = loadBundle()
58
+ assert.equal(definition.id, '@taxueseek/argo-dsh')
59
+ const mod = factory(definition)
60
+ assert.equal(JSON.stringify(mod.inject), JSON.stringify(['slots', 'locale', 'configForms', 'remote', 'remote.credentials']))
61
+ assert.equal(typeof mod.ArgoPanel, 'function')
62
+
63
+ const registered = []
64
+ let served = 0
65
+ const form = {
66
+ getSnapshot: () => ({ status: 'ready', value: {}, writable: true }),
67
+ subscribe: () => () => {},
68
+ set: async () => true,
69
+ mutate: async () => true,
70
+ }
71
+ const ctx = {
72
+ configForms: {
73
+ get: (namespace) => {
74
+ assert.equal(namespace, 'wide-research')
75
+ return form
76
+ },
77
+ whileServed: (namespaces, register) => {
78
+ served += 1
79
+ assert.equal(JSON.stringify(namespaces), JSON.stringify(['wide-research']))
80
+ return register(new Set(namespaces))
81
+ },
82
+ },
83
+ slots: {
84
+ inject: (_name, register) => register(),
85
+ register: (options, component) => {
86
+ registered.push({ options, component })
87
+ return () => {}
88
+ },
89
+ },
90
+ locale: { getSnapshot: () => ({ active: 'zh' }), subscribe: () => () => {} },
91
+ remote: { credentials: {}, $on: () => () => {} },
92
+ effect: (callback) => {
93
+ const off = callback()
94
+ if (typeof off === 'function') off()
95
+ },
96
+ }
97
+
98
+ mod.apply(ctx)
99
+
100
+ assert.equal(served, 1)
101
+ assert.deepEqual(
102
+ registered.map((entry) => entry.options.name).sort(),
103
+ ['plugins.item', 'settings.section'],
104
+ )
105
+ for (const entry of registered) {
106
+ assert.equal(entry.options.id, 'argo')
107
+ assert.equal(entry.options.order, entry.options.name === 'settings.section' ? 26 : 50)
108
+ assert.equal(typeof entry.component, 'function')
109
+ const injected = entry.options.inject()
110
+ assert.equal(injected.hooks.argoForm, form)
111
+ assert.equal(typeof injected.set, 'function')
112
+ assert.equal(typeof injected.mutate, 'function')
113
+ }
114
+ })
115
+
116
+ test('client TOOLS mirror native-tools.mjs (drift gate)', () => {
117
+ // 面板工具表是 native-tools.mjs(mcp_tools.py 生成产物)的手工镜像:argo
118
+ // 增删工具时两边必须同步,否则面板会给出不存在的勾选或漏掉新工具。
119
+ // 注意 [...mod.TOOLS]:client.js 跑在 vm 沙箱里,其数组带沙箱的
120
+ // Array.prototype,直接 map/deepStrictEqual 会因原型不同假红。
121
+ const mod = factory(loadBundle())
122
+ const specNames = Object.keys(NATIVE_TOOLS).sort()
123
+ const toolIds = [...mod.TOOLS].map((tool) => tool.id).sort()
124
+ assert.deepEqual(toolIds, specNames, '面板 TOOLS 与原生工具规格表漂移,请同步两边')
125
+ for (const tool of mod.TOOLS) {
126
+ assert.ok(tool.zh && tool.en, `tool ${tool.id} 缺双语标签`)
127
+ assert.ok(tool.group === 'core' || tool.group === 'long', `tool ${tool.id} 分组非法`)
128
+ }
129
+ })
130
+
131
+ test('client ENGINE_TOGGLES mirror ARGO_ENV_SPECS (drift gate)', () => {
132
+ // 面板引擎开关与 index.js 白名单同一份名单:名字、双语标签、数值域都要对得上。
133
+ const mod = factory(loadBundle())
134
+ const specNames = ARGO_ENV_SPECS.map((spec) => spec.name).sort()
135
+ const toggleNames = [...mod.ENGINE_TOGGLES].map((spec) => spec.name).sort()
136
+ assert.deepEqual(toggleNames, specNames, '面板 ENGINE_TOGGLES 与 index.js ARGO_ENV_SPECS 漂移,请同步两边')
137
+ const specByName = new Map(ARGO_ENV_SPECS.map((spec) => [spec.name, spec]))
138
+ for (const toggle of mod.ENGINE_TOGGLES) {
139
+ const spec = specByName.get(toggle.name)
140
+ assert.equal(toggle.kind, spec.kind, `${toggle.name} kind 不一致`)
141
+ assert.equal(toggle.group, spec.group, `${toggle.name} group 不一致`)
142
+ assert.equal(toggle.invert, spec.invert, `${toggle.name} invert 不一致`)
143
+ assert.equal(toggle.def, spec.def, `${toggle.name} 默认值不一致`)
144
+ assert.ok(toggle.zh && toggle.en && toggle.hintZh && toggle.hintEn, `${toggle.name} 缺双语文案`)
145
+ if (toggle.kind === 'num') {
146
+ assert.ok(toggle.min <= toggle.max, `${toggle.name} min>max`)
147
+ assert.ok(toggle.def >= toggle.min && toggle.def <= toggle.max, `${toggle.name} 默认值越界`)
148
+ }
149
+ }
150
+ })
151
+
152
+ test('cache switch defaults to cache enabled and never inverts', () => {
153
+ const spec = ARGO_ENV_SPECS.find((s) => s.name === 'ARGO_NO_CACHE')
154
+ assert.ok(spec, 'ARGO_NO_CACHE 不在白名单')
155
+ assert.equal(spec.def, false, '缓存默认必须是开(NO_CACHE 默认 0)')
156
+ assert.match(spec.zh, /测试模式/, '缓存开关文案应说明这是测试模式')
157
+ assert.equal(spec.invert, undefined, '负向开关不再用 invert 反转')
158
+ assert.deepEqual(ARGO_ENV_SPECS.filter((s) => s.invert).map((s) => s.name), [],
159
+ '任何开关都不应再依赖 invert:文案与 env 必须同向')
160
+ })
161
+
162
+ test('paid quota switches distinguish daily and monthly limits', () => {
163
+ const byName = new Map(ARGO_ENV_SPECS.map((s) => [s.name, s]))
164
+ const daily = ['ARGO_QUOTA_ANYSEARCH_LIMIT', 'ARGO_QUOTA_ZHIHU_LIMIT']
165
+ const monthly = ['ARGO_QUOTA_YUANBAO_LIMIT', 'ARGO_QUOTA_TAVILY_LIMIT']
166
+ for (const name of daily) {
167
+ const spec = byName.get(name)
168
+ assert.ok(spec, `${name} 不在白名单`)
169
+ assert.match(spec.zh, /每日/, `${name} 的文案必须写明按日`)
170
+ }
171
+ for (const name of monthly) {
172
+ const spec = byName.get(name)
173
+ assert.ok(spec, `${name} 不在白名单`)
174
+ assert.match(spec.zh, /每月/, `${name} 的文案必须写明按月`)
175
+ }
176
+ for (const name of [...daily, ...monthly]) {
177
+ const spec = byName.get(name)
178
+ assert.equal(spec.kind, 'num', `${name} 必须是数字`)
179
+ assert.equal(spec.group, 'paid', `${name} 应归入付费搜索组`)
180
+ assert.equal(spec.min, 0, `${name} 允许 0=不限制`)
181
+ }
182
+ assert.equal(byName.get('ARGO_QUOTA_YUANBAO_LIMIT').def, 2000,
183
+ '元宝默认 2000/月')
184
+ // 博查按量付费:不应再有固定次数项(2026-10-22 用户更正)
185
+ assert.equal(byName.has('ARGO_QUOTA_BOCHA_LIMIT'), false,
186
+ '博查按量付费,不应暴露次数上限')
187
+ })
188
+
189
+ test('Exa uses a monthly USD budget, not a call count', () => {
190
+ const byName = new Map(ARGO_ENV_SPECS.map((s) => [s.name, s]))
191
+ const exa = byName.get('ARGO_BUDGET_EXA')
192
+ assert.ok(exa, 'ARGO_BUDGET_EXA 不在白名单')
193
+ assert.equal(exa.kind, 'num')
194
+ assert.equal(exa.group, 'paid')
195
+ assert.equal(exa.def, 10, 'Exa 默认 $10/月')
196
+ assert.match(exa.zh, /美元|USD/)
197
+ assert.equal(byName.has('ARGO_QUOTA_EXA_LIMIT'), false,
198
+ 'Exa 是金额额度,不应再暴露成次数上限')
199
+ })
200
+
201
+ test('keys tab exposes the Yuanbao credential ref', () => {
202
+ // KEYS 是面板「密钥」页的清单;元宝已成为中文通用域第 2 位对冲源,
203
+ // key 不在清单里就只能手改 ~/.config/argo/env(2026-10-22 用户反馈)。
204
+ const src = readFileSync(new URL('../dsh/client.js', import.meta.url), 'utf8')
205
+ assert.match(src, /ref: 'ARGO_YUANBAO_API_KEY'/, '密钥页缺腾讯元宝')
206
+ })
207
+
208
+ test('ArgoPanel renders every tab without undefined references', () => {
209
+ // node --check 只查语法,查不出渲染路径里的未定义引用;注册测试又不跑组件。
210
+ // 这里用带状态的 React 替身把六个 tab 各完整渲染一遍:任何 tab 的渲染函数
211
+ // 抛错(拼错变量、漏定义助手)都会在这里红,而不是等用户在浏览器里点开。
212
+ const definition = loadBundle()
213
+ const { react, hooks, reset } = statefulReact()
214
+ const mod = definition.factory((id) => {
215
+ if (id === 'react') return react
216
+ throw new Error(`unexpected require: ${id}`)
217
+ })
218
+ const value = {
219
+ nativeTools: ['argo_search', 'argo_fetch'],
220
+ keyRefs: ['DEEPSEEK_API_KEY'],
221
+ argoEnv: ['ARGO_NO_CACHE=1', 'ARGO_PROXY=http://127.0.0.1:7890', 'ARGO_MINHASH_THRESHOLD=0.9'],
222
+ searchProviderEnabled: true,
223
+ fetchProviderEnabled: true,
224
+ searchCommand: 'python3',
225
+ searchArgs: ['mcp_server.py'],
226
+ }
227
+ const props = {
228
+ useArgoForm: (select) => select({ status: 'ready', writable: true, value }),
229
+ set: async () => true,
230
+ mutate: async () => true,
231
+ credentials: {
232
+ describe: async () => ({ ok: true, value: { DEEPSEEK_API_KEY: { configured: true, writable: true, source: 'env' } } }),
233
+ set: async () => ({ ok: true }),
234
+ unset: async () => ({ ok: true }),
235
+ },
236
+ remote: { $on: () => () => {} },
237
+ locale: { getSnapshot: () => ({ active: 'zh' }), subscribe: () => () => {} },
238
+ view: 'page',
239
+ }
240
+ for (const tab of ['dashboard', 'access', 'tools', 'engines', 'keys', 'research', 'advanced']) {
241
+ reset()
242
+ hooks.length = 0
243
+ hooks[1] = tab // tab 是 ArgoPanel 的第 2 个 useState
244
+ const tree = mod.ArgoPanel(props)
245
+ assert.ok(tree && tree.type === 'div', `${tab} 应渲染出面板根节点`)
246
+ }
247
+ // 摘要视图(plugins.item 卡片的一行摘要)
248
+ reset()
249
+ hooks.length = 0
250
+ assert.equal(typeof mod.ArgoPanel({ ...props, view: 'summary' }), 'string')
251
+
252
+ // 引擎 tab 真实产出:七个分组卡 + 输入控件(防「渲染不抛错但内容是空」)
253
+ reset()
254
+ hooks.length = 0
255
+ hooks[1] = 'engines'
256
+ const flat = []
257
+ // 迷你渲染器:createElement 替身不调用函数组件,这里补上——Card/Switch/
258
+ // Field/PrimaryButton 都要真跑一遍,section/input 节点才存在,组件体里的
259
+ // 未定义引用也才会在这里暴露。注意 React 语义:children 在 props.children
260
+ // 上递给组件(单个 child 不是数组),替身只把 children 挂在节点上,这里补映射。
261
+ const walk = (node) => {
262
+ if (node === null || node === undefined) return
263
+ if (Array.isArray(node)) { node.forEach(walk); return }
264
+ if (typeof node !== 'object') return
265
+ if (typeof node.type === 'function') {
266
+ const props = { ...node.props }
267
+ if (node.children && node.children.length > 0) {
268
+ props.children = node.children.length === 1 ? node.children[0] : node.children
269
+ }
270
+ walk(node.type(props))
271
+ return
272
+ }
273
+ flat.push(node)
274
+ ;(node.children || []).forEach(walk)
275
+ }
276
+ walk(mod.ArgoPanel(props))
277
+ const sections = flat.filter((node) => node.type === 'section')
278
+ assert.ok(sections.length >= 7, `引擎 tab 应渲染 7 个分组卡,实际 ${sections.length}`)
279
+ assert.ok(flat.some((node) => node.type === 'input'), '引擎 tab 应渲染输入控件')
280
+ assert.ok(flat.some((node) => node.type === 'button'), '引擎 tab 应渲染保存按钮')
281
+
282
+ // 只读态:表单不可写时渲染不崩(控件禁用由 props 下沉)
283
+ reset()
284
+ hooks.length = 0
285
+ hooks[1] = 'engines'
286
+ const roTree = mod.ArgoPanel({ ...props, useArgoForm: (select) => select({ status: 'ready', writable: false, value }) })
287
+ assert.ok(roTree && roTree.type === 'div')
288
+ })
289
+
290
+ test('engines tab renders a control for every ENGINE_TOGGLES switch (no invisible group)', () => {
291
+ // 上面的 mirror 测试只核对「index.js 白名单 ↔ client.js 镜像」同名同字段,
292
+ // 核对不到「这个 group 到底渲不渲染」。2026-10-21 实锤:`paid` 分组没进渲染
293
+ // 清单,ARGO_PAID_SEARCH 与 ARGO_BUDGET_DEEPSEEK_SEARCH 在白名单、镜像、
294
+ // 双语文案三处都齐全,面板上却完全不显示——用户配了预算却找不到入口,
295
+ // 只会以为插件没装。
296
+ //
297
+ // 判据取「每个开关的标签都必须出现在渲染树里」,而不是「分组数量对得上」:
298
+ // 后者在两组互相吞并、或多渲染一个空组时仍可能凑巧通过,前者直接锁住
299
+ // 「用户能看见并可操作这一项」这件事本身。
300
+ const definition = loadBundle()
301
+ const { react, hooks, reset } = statefulReact()
302
+ const mod = definition.factory((id) => {
303
+ if (id === 'react') return react
304
+ throw new Error(`unexpected require: ${id}`)
305
+ })
306
+ const props = {
307
+ useArgoForm: (select) => select({
308
+ status: 'ready',
309
+ writable: true,
310
+ value: { nativeTools: [], keyRefs: [], argoEnv: [] },
311
+ }),
312
+ set: async () => true,
313
+ mutate: async () => true,
314
+ credentials: {
315
+ describe: async () => ({ ok: true, value: {} }),
316
+ set: async () => ({ ok: true }),
317
+ unset: async () => ({ ok: true }),
318
+ },
319
+ remote: { $on: () => () => {} },
320
+ locale: { getSnapshot: () => ({ active: 'zh' }), subscribe: () => () => {} },
321
+ view: 'page',
322
+ }
323
+ reset()
324
+ hooks.length = 0
325
+ hooks[1] = 'engines'
326
+ const texts = new Set()
327
+ // 与「七个分组卡」那条测试同一套迷你渲染器(函数组件要真跑,children 要补映射)
328
+ const walk = (node) => {
329
+ if (node === null || node === undefined) return
330
+ if (Array.isArray(node)) { node.forEach(walk); return }
331
+ if (typeof node !== 'object') {
332
+ if (typeof node === 'string' || typeof node === 'number') texts.add(String(node))
333
+ return
334
+ }
335
+ if (typeof node.type === 'function') {
336
+ const nextProps = { ...node.props }
337
+ if (node.children && node.children.length > 0) {
338
+ nextProps.children = node.children.length === 1 ? node.children[0] : node.children
339
+ }
340
+ walk(node.type(nextProps))
341
+ return
342
+ }
343
+ ;(node.children || []).forEach(walk)
344
+ }
345
+ walk(mod.ArgoPanel(props))
346
+ // 标签带 suffix(未配置时追加「(默认)」),故用前缀匹配而非全等。
347
+ const visible = (label) => [...texts].some((text) => String(text).startsWith(label))
348
+ const missing = [...mod.ENGINE_TOGGLES]
349
+ .filter((spec) => !visible(spec.zh))
350
+ .map((spec) => `${spec.name}(${spec.group})`)
351
+ assert.deepEqual(
352
+ missing, [],
353
+ `这些开关定义了但面板上没有控件(分组未进渲染清单?):${missing.join(', ')}`,
354
+ )
355
+ })
356
+
357
+ test('dashboard tab renders metrics, workflow presets, and engine topology matrix', () => {
358
+ const definition = loadBundle()
359
+ const { react, hooks, reset } = statefulReact()
360
+ const mod = definition.factory((id) => {
361
+ if (id === 'react') return react
362
+ throw new Error(`unexpected require: ${id}`)
363
+ })
364
+ const props = {
365
+ useArgoForm: (select) => select({
366
+ status: 'ready',
367
+ writable: true,
368
+ value: { nativeTools: ['argo_search'], keyRefs: ['DEEPSEEK_API_KEY'], argoEnv: [] },
369
+ }),
370
+ set: async () => true,
371
+ mutate: async () => true,
372
+ credentials: {
373
+ describe: async () => ({ ok: true, value: { DEEPSEEK_API_KEY: { configured: true } } }),
374
+ set: async () => ({ ok: true }),
375
+ unset: async () => ({ ok: true }),
376
+ },
377
+ remote: { $on: () => () => {} },
378
+ locale: { getSnapshot: () => ({ active: 'zh' }), subscribe: () => () => {} },
379
+ view: 'page',
380
+ }
381
+ reset()
382
+ hooks.length = 0
383
+ hooks[1] = 'dashboard'
384
+ const flat = []
385
+ const walk = (node) => {
386
+ if (node === null || node === undefined) return
387
+ if (Array.isArray(node)) { node.forEach(walk); return }
388
+ if (typeof node !== 'object') return
389
+ flat.push(node)
390
+ if (typeof node.type === 'function') {
391
+ const nextProps = { ...node.props }
392
+ if (node.children && node.children.length > 0) {
393
+ nextProps.children = node.children.length === 1 ? node.children[0] : node.children
394
+ }
395
+ walk(node.type(nextProps))
396
+ return
397
+ }
398
+ ;(node.children || []).forEach(walk)
399
+ }
400
+ walk(mod.ArgoPanel(props))
401
+ const sections = flat.filter((node) => node.type === 'section')
402
+ assert.ok(sections.length >= 2, `dashboard tab 应渲染预设和矩阵卡,实际 ${sections.length}`)
403
+ assert.ok(flat.some((node) => node.props && node.props.key === 'metrics'), 'dashboard 应包含 KPI 指标卡')
404
+ assert.ok(flat.some((node) => node.props && node.props.key === 'presets'), 'dashboard 应包含工作流预设')
405
+ assert.ok(flat.some((node) => node.props && node.props.key === 'matrix'), 'dashboard 应包含核心引擎拓扑矩阵')
406
+ })
@@ -0,0 +1,30 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { Config } from '../dsh/index.js'
4
+
5
+ function plain(parsed, key) {
6
+ const value = parsed[key]
7
+ return value !== null && typeof value === 'object' && typeof value.get === 'function' ? value.get() : value
8
+ }
9
+
10
+ test('Config: volatile settings expose defaults and reject unknown native tools', (t) => {
11
+ // 仓内单测没有 node_modules;schema 只在装进 DSH profile 后可用。
12
+ if (Config === undefined) {
13
+ t.skip('@deepseek-ai/schemastery not installed in this checkout')
14
+ return
15
+ }
16
+
17
+ const parsed = Config({})
18
+ assert.equal(plain(parsed, 'searchProviderEnabled'), true)
19
+ assert.equal(plain(parsed, 'fetchProviderEnabled'), true)
20
+ assert.equal(plain(parsed, 'nativeTimeoutMs'), 60000)
21
+ assert.equal(plain(parsed, 'searchConcurrency'), 3)
22
+ assert.deepEqual(plain(parsed, 'nativeTools'), ['argo_search', 'argo_fetch'])
23
+ assert.deepEqual(plain(parsed, 'keyRefs'), ['DEEPSEEK_API_KEY'])
24
+
25
+ const source = parsed.nativeTools
26
+ assert.equal(typeof source.get, 'function', 'nativeTools 必须是 volatile 引用,设置页才能热改')
27
+
28
+ assert.throws(() => Config({ nativeTools: ['argo_nope'] }), /expected "argo_search"/)
29
+ assert.throws(() => Config({ searchTimeoutMs: 'abc' }), /expected number/)
30
+ })
@@ -0,0 +1,28 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { getMcpPoolStats, resetMcpPool } from '../dsh/index.js'
4
+
5
+ test('diagnostics: getMcpPoolStats returns pool status and active loads', () => {
6
+ const dummyPool = [
7
+ { load: () => 0, proc: {} },
8
+ { load: () => 2, proc: {} },
9
+ ]
10
+ const stats = getMcpPoolStats(dummyPool)
11
+ assert.equal(stats.poolSize, 2)
12
+ assert.equal(stats.activeConnections, 1)
13
+ assert.equal(stats.totalLoad, 2)
14
+ assert.equal(stats.connections.length, 2)
15
+ assert.equal(stats.connections[1].load, 2)
16
+ })
17
+
18
+ test('diagnostics: resetMcpPool evicts connections safely', () => {
19
+ let closed = 0
20
+ const dummyPool = [
21
+ { close: () => { closed += 1 } },
22
+ { close: () => { closed += 1 } },
23
+ ]
24
+ const result = resetMcpPool(dummyPool)
25
+ assert.equal(result.evictedCount, 2)
26
+ assert.equal(result.poolSizeNow, 0)
27
+ assert.equal(closed, 2)
28
+ })