argo-search 1.0.1 → 2.9.7
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/LICENSE +201 -21
- package/README.en.md +822 -0
- package/README.es.md +864 -0
- package/README.ja.md +864 -0
- package/README.ko.md +864 -0
- package/README.md +867 -320
- package/SKILL.md +135 -0
- package/assets/readme/hero.svg +121 -0
- package/assets/readme/made-with-beautify.svg +10 -0
- package/assets/readme/proof-routes.svg +73 -0
- package/assets/readme/settings-keys.png +0 -0
- package/assets/readme/settings-tools.png +0 -0
- package/assets/readme/why-better.svg +104 -0
- package/assets/readme/workflow.svg +83 -0
- package/backends/domain_profiles.json +2308 -96
- package/backends/engine_registry.yaml +3314 -503
- package/backends/query_synonyms_cn.json +35 -0
- package/backends/quota_profiles.json +2409 -161
- package/backends/source_types_cn.json +83 -0
- package/backends/text_lexicon.json +44 -0
- package/bin/argo +703 -0
- package/bin/argo.js +71 -39
- package/config.yaml +4976 -363
- package/cordis.patch.yml +21 -0
- package/data/countries.json +40 -0
- package/data/lang_matrix/lang_capability.json +842 -0
- package/data/lang_matrix/matrix_2026-09-10.json +11921 -0
- package/data/regions_cn.json +1 -0
- package/docs/ADDING_NEW_ENGINE.md +184 -0
- package/docs/ARGO_INTRO.md +124 -0
- package/docs/DATA_SOURCE_GAPS.md +81 -0
- package/docs/DESIGN_LOCAL_DATA_FUSION.md +90 -0
- package/docs/ENGINE_CATALOG.md +564 -0
- package/docs/MCP_SETUP.md +106 -0
- package/docs/PLATFORM_PATHS.md +111 -0
- package/docs/RELEASE_NOTES_v2.8.0.md +71 -0
- package/docs/RELEASE_NOTES_v2.8.2.md +63 -0
- package/docs/RELEASE_NOTES_v2.8.3.md +55 -0
- package/docs/RELEASE_NOTES_v2.8.4.md +79 -0
- package/docs/RELEASE_NOTES_v2.8.5.md +102 -0
- package/docs/RELEASE_NOTES_v2.8.6.md +66 -0
- package/docs/RELEASE_NOTES_v2.8.7.md +65 -0
- package/docs/RELEASE_NOTES_v2.8.8.md +67 -0
- package/docs/RELEASE_NOTES_v2.8.9.md +67 -0
- package/docs/RELEASE_NOTES_v2.9.0.md +64 -0
- package/docs/SEARCH_ARCHIVE.md +115 -0
- package/docs/assets/architecture.svg +104 -0
- package/docs/assets/hero.svg +98 -0
- package/docs/engines/bangumi.md +27 -0
- package/docs/engines/biorxiv.md +27 -0
- package/docs/engines/clawhub.md +27 -0
- package/docs/engines/crt_sh.md +27 -0
- package/docs/engines/datacite.md +27 -0
- package/docs/engines/deps_dev.md +27 -0
- package/docs/engines/douban_movie.md +27 -0
- package/docs/engines/endoflife.md +27 -0
- package/docs/engines/flk_law.md +27 -0
- package/docs/engines/fxtwitter.md +27 -0
- package/docs/engines/google_news.md +27 -0
- package/docs/engines/gov_regulations.md +27 -0
- package/docs/engines/guqian.md +27 -0
- package/docs/engines/hackernews.md +27 -0
- package/docs/engines/iplant.md +27 -0
- package/docs/engines/keenable.md +27 -0
- package/docs/engines/met_museum.md +27 -0
- package/docs/engines/nasa_images.md +27 -0
- package/docs/engines/netease_music.md +27 -0
- package/docs/engines/npm.md +27 -0
- package/docs/engines/nvd.md +27 -0
- package/docs/engines/openreview.md +27 -0
- package/docs/engines/openstd.md +27 -0
- package/docs/engines/parallel_free.md +27 -0
- package/docs/engines/people_daily.md +27 -0
- package/docs/engines/realtime_index.md +27 -0
- package/docs/engines/sec_edgar.md +27 -0
- package/docs/engines/seltz.md +27 -0
- package/docs/engines/skillsmp.md +27 -0
- package/docs/engines/sspai.md +27 -0
- package/docs/engines/std_samr.md +27 -0
- package/docs/engines/train.md +27 -0
- package/docs/engines/un_comtrade.md +27 -0
- package/docs/engines/wikisource.md +27 -0
- package/docs/engines/zdic.md +27 -0
- package/docs/engines/zenodo.md +27 -0
- package/docs/engines/zhihu_hot_app.md +27 -0
- package/docs//344/270/272/344/273/200/344/271/210/351/200/211/346/213/251argo.md +176 -0
- package/engines/README.md +10 -0
- package/engines/_template_cli.yaml +39 -0
- package/engines/_template_http.yaml +37 -0
- package/engines/plugins/_template_plugin.py +50 -0
- package/engines/specs/_cisa_kev.yaml +54 -0
- package/engines/specs/_osv.yaml +63 -0
- package/engines/specs/aljazeera.yaml +43 -0
- package/engines/specs/antara.yaml +43 -0
- package/engines/specs/artic.yaml +20 -0
- package/engines/specs/aviation_weather.yaml +39 -0
- package/engines/specs/bangumi.yaml +21 -0
- package/engines/specs/biorxiv.yaml +24 -0
- package/engines/specs/carbon_intensity.yaml +32 -0
- package/engines/specs/cinii.yaml +44 -0
- package/engines/specs/clawhub.yaml +49 -0
- package/engines/specs/cleveland.yaml +19 -0
- package/engines/specs/cn_ai_news.yaml +43 -0
- package/engines/specs/core.yaml +35 -0
- package/engines/specs/crt_sh.yaml +38 -0
- package/engines/specs/ct24.yaml +43 -0
- package/engines/specs/datacite.yaml +27 -0
- package/engines/specs/datagov_uk.yaml +41 -0
- package/engines/specs/ddgs_images.yaml +45 -0
- package/engines/specs/deepseek_search.yaml +74 -0
- package/engines/specs/deezer.yaml +20 -0
- package/engines/specs/deps_dev.yaml +21 -0
- package/engines/specs/digup.yaml +36 -0
- package/engines/specs/doi.yaml +39 -0
- package/engines/specs/douban_movie.yaml +22 -0
- package/engines/specs/dw_news.yaml +47 -0
- package/engines/specs/egov_law.yaml +21 -0
- package/engines/specs/elpais.yaml +43 -0
- package/engines/specs/endoflife.yaml +21 -0
- package/engines/specs/energy_charts.yaml +21 -0
- package/engines/specs/factcheck_org.yaml +52 -0
- package/engines/specs/faz.yaml +43 -0
- package/engines/specs/federal_register.yaml +57 -0
- package/engines/specs/firecrawl.yaml +53 -0
- package/engines/specs/flk_law.yaml +24 -0
- package/engines/specs/folha.yaml +43 -0
- package/engines/specs/france24.yaml +44 -0
- package/engines/specs/full_fact.yaml +47 -0
- package/engines/specs/fxtwitter.yaml +31 -0
- package/engines/specs/gbfs_nyc.yaml +32 -0
- package/engines/specs/gdacs.yaml +21 -0
- package/engines/specs/gdelt.yaml +64 -0
- package/engines/specs/google_news.yaml +22 -0
- package/engines/specs/gov_regulations.yaml +38 -0
- package/engines/specs/guardian_rss.yaml +59 -0
- package/engines/specs/hurriyet.yaml +43 -0
- package/engines/specs/iplant.yaml +25 -0
- package/engines/specs/jikan.yaml +24 -0
- package/engines/specs/jolpica.yaml +20 -0
- package/engines/specs/k10plus.yaml +20 -0
- package/engines/specs/keenable.yaml +44 -0
- package/engines/specs/lefigaro.yaml +43 -0
- package/engines/specs/listenbrainz.yaml +19 -0
- package/engines/specs/matichon.yaml +43 -0
- package/engines/specs/met_museum.yaml +21 -0
- package/engines/specs/nasa_images.yaml +45 -0
- package/engines/specs/netease_music.yaml +38 -0
- package/engines/specs/nhk.yaml +43 -0
- package/engines/specs/nhtsa_vpic.yaml +20 -0
- package/engines/specs/noaa_swpc.yaml +20 -0
- package/engines/specs/nvd.yaml +38 -0
- package/engines/specs/obis.yaml +20 -0
- package/engines/specs/opencitations.yaml +52 -0
- package/engines/specs/openf1.yaml +21 -0
- package/engines/specs/openfoodfacts.yaml +45 -0
- package/engines/specs/openligadb.yaml +20 -0
- package/engines/specs/openreview.yaml +37 -0
- package/engines/specs/openstd.yaml +21 -0
- package/engines/specs/people_daily.yaml +22 -0
- package/engines/specs/realtime_index.yaml +34 -0
- package/engines/specs/ror.yaml +20 -0
- package/engines/specs/satnogs.yaml +20 -0
- package/engines/specs/sec_edgar.yaml +46 -0
- package/engines/specs/skillsmp.yaml +50 -0
- package/engines/specs/sogou_weixin_browser.yaml +30 -0
- package/engines/specs/soilgrids.yaml +25 -0
- package/engines/specs/sspai.yaml +36 -0
- package/engines/specs/stackexchange.yaml +46 -0
- package/engines/specs/std_samr.yaml +21 -0
- package/engines/specs/tass.yaml +43 -0
- package/engines/specs/tle_mirror.yaml +21 -0
- package/engines/specs/train.yaml +37 -0
- package/engines/specs/tvmaze.yaml +20 -0
- package/engines/specs/twitter_syndication.yaml +31 -0
- package/engines/specs/un_comtrade.yaml +23 -0
- package/engines/specs/unpaywall.yaml +54 -0
- package/engines/specs/vnpress.yaml +43 -0
- package/engines/specs/weather.yaml +32 -0
- package/engines/specs/who_don.yaml +21 -0
- package/engines/specs/who_gho.yaml +20 -0
- package/engines/specs/wikimedia_commons.yaml +34 -0
- package/engines/specs/wikisource.yaml +22 -0
- package/engines/specs/worms.yaml +19 -0
- package/engines/specs/x_trending.yaml +34 -0
- package/engines/specs/yna.yaml +43 -0
- package/engines/specs/ynet.yaml +43 -0
- package/engines/specs/zbmath.yaml +40 -0
- package/engines/specs/zdic.yaml +22 -0
- package/engines/specs/zenodo.yaml +27 -0
- package/engines/specs/zhihu_hot_app.yaml +35 -0
- package/mcp/clients.yaml +74 -0
- package/package.json +51 -12
- package/packages/dsh-plugin/README.md +134 -0
- package/packages/dsh-plugin/cordis.patch.yml +48 -0
- package/packages/dsh-plugin/dsh/client.js +1244 -0
- package/packages/dsh-plugin/dsh/index.js +2290 -0
- package/packages/dsh-plugin/dsh/native-tools.mjs +743 -0
- package/packages/dsh-plugin/package.json +38 -0
- package/packages/dsh-plugin/test/client.test.js +232 -0
- package/packages/dsh-plugin/test/config.test.js +30 -0
- package/packages/dsh-plugin/test/index.test.js +1338 -0
- package/references/query-templates.md +96 -0
- package/references/query-templates.yaml +118 -0
- package/scripts/ab_eval.py +219 -0
- package/scripts/ab_eval_p0p1.py +266 -0
- package/scripts/adaptive.py +271 -70
- package/scripts/answer.py +154 -0
- package/scripts/archive_run.py +591 -0
- package/scripts/argo_engine_registry.py +172 -0
- package/scripts/argo_paths.py +728 -0
- package/scripts/article.py +136 -0
- package/scripts/arxiv_source.py +201 -0
- package/scripts/badcase_ledger.py +252 -0
- package/scripts/batch_probe.py +241 -0
- package/scripts/benchmark.py +19 -0
- package/scripts/bounded_run.py +144 -0
- package/scripts/browser_auth.py +323 -0
- package/scripts/burstiness_detector.py +147 -0
- package/scripts/cache.py +1078 -67
- package/scripts/cache_guard.py +271 -0
- package/scripts/cache_key_vdom.py +56 -0
- package/scripts/cache_similarity.py +170 -0
- package/scripts/caliber_isolation.py +195 -0
- package/scripts/calibrate_lowquality.py +210 -0
- package/scripts/candidate_envelope.py +366 -0
- package/scripts/chrome_cdp.py +871 -0
- package/scripts/circuit_breaker.py +389 -0
- package/scripts/citations.py +334 -0
- package/scripts/cite_cli.py +59 -0
- package/scripts/cjk_tokens.py +95 -0
- package/scripts/clarify.py +47 -24
- package/scripts/cli_io.py +240 -0
- package/scripts/config.py +683 -45
- package/scripts/config_cache.py +402 -0
- package/scripts/conn_pool.py +220 -0
- package/scripts/content_security.py +685 -0
- package/scripts/content_signals.py +730 -0
- package/scripts/crawl.py +74 -35
- package/scripts/daemon.py +866 -0
- package/scripts/digup_bridge.py +214 -0
- package/scripts/engine_admission.py +366 -0
- package/scripts/engine_dispatch.py +1019 -0
- package/scripts/engine_env.py +534 -0
- package/scripts/engine_failure.py +411 -0
- package/scripts/engine_families.py +732 -0
- package/scripts/engine_policy.py +218 -0
- package/scripts/engine_requires.py +190 -0
- package/scripts/engine_status.py +322 -0
- package/scripts/engine_validate.py +638 -0
- package/scripts/engines.py +778 -294
- package/scripts/engines_base.py +1521 -0
- package/scripts/engines_builders.py +298 -0
- package/scripts/engines_builders_batch10.py +159 -0
- package/scripts/engines_builders_batch11.py +174 -0
- package/scripts/engines_builders_batch12.py +103 -0
- package/scripts/engines_builders_batch13.py +340 -0
- package/scripts/engines_builders_batch9.py +1468 -0
- package/scripts/engines_builders_cn.py +2283 -0
- package/scripts/engines_builders_data.py +2923 -0
- package/scripts/engines_builders_data_macro.py +775 -0
- package/scripts/engines_builders_feeds.py +255 -0
- package/scripts/engines_builders_intl.py +1239 -0
- package/scripts/engines_builders_search.py +440 -0
- package/scripts/engines_builders_tech.py +1360 -0
- package/scripts/evidence.py +588 -162
- package/scripts/evidence_loop.py +592 -0
- package/scripts/evidence_tier.py +298 -0
- package/scripts/except_sets.py +25 -0
- package/scripts/extract.py +55 -16
- package/scripts/fact_align.py +207 -0
- package/scripts/fact_crosscheck.py +230 -0
- package/scripts/fetch.py +25 -12
- package/scripts/fetch_browser.py +87 -0
- package/scripts/fetch_jsrun.py +122 -0
- package/scripts/fetch_quality.py +225 -0
- package/scripts/fetch_render_tinyfish.py +117 -0
- package/scripts/fetch_v3.py +1779 -0
- package/scripts/focus_extract.py +350 -0
- package/scripts/fulltext_store.py +169 -0
- package/scripts/fxtwitter_api.py +325 -0
- package/scripts/gen_engine_catalog.py +364 -0
- package/scripts/gen_native_tools.py +91 -0
- package/scripts/gen_routing_snapshot.py +184 -0
- package/scripts/health_check.py +140 -402
- package/scripts/health_probe.py +23 -8
- package/scripts/hot_state.py +134 -0
- package/scripts/http_client.py +1053 -0
- package/scripts/image_ops.py +430 -0
- package/scripts/image_vision/vision_probe +0 -0
- package/scripts/image_vision/vision_probe.swift +126 -0
- package/scripts/install.ps1 +148 -0
- package/scripts/install.sh +206 -0
- package/scripts/job.py +1186 -0
- package/scripts/jump_resolver.py +235 -0
- package/scripts/lang_capability.py +150 -0
- package/scripts/lang_detect.py +289 -0
- package/scripts/lang_matrix_probe.py +329 -0
- package/scripts/lang_pref.py +311 -0
- package/scripts/link_source.py +340 -0
- package/scripts/local_engines.py +321 -0
- package/scripts/local_image.py +970 -0
- package/scripts/local_seek.py +366 -0
- package/scripts/macro_countries.py +82 -0
- package/scripts/matrix_search_eval.py +1105 -0
- package/scripts/mcp_diag.py +8 -3
- package/scripts/mcp_handlers.py +1074 -0
- package/scripts/mcp_handlers_surface.py +157 -0
- package/scripts/mcp_launch.sh +122 -0
- package/scripts/mcp_server.py +118 -417
- package/scripts/mcp_setup.py +407 -0
- package/scripts/mcp_tools.py +330 -0
- package/scripts/mcp_transport.py +191 -0
- package/scripts/min_version_scan.py +335 -0
- package/scripts/net_proxy.py +258 -0
- package/scripts/network_aware.py +152 -0
- package/scripts/paper.py +274 -0
- package/scripts/pdf_extract.py +518 -0
- package/scripts/phrase_filter.py +66 -0
- package/scripts/plan.py +422 -0
- package/scripts/postprocess_signals.py +150 -0
- package/scripts/pre-commit +48 -0
- package/scripts/query_enhance.py +157 -0
- package/scripts/query_operators.py +196 -0
- package/scripts/query_rewriter.py +116 -7
- package/scripts/query_signals.py +300 -0
- package/scripts/query_templates.py +167 -0
- package/scripts/query_understanding.py +411 -0
- package/scripts/query_variants.py +298 -0
- package/scripts/quota.py +488 -76
- package/scripts/rank_signals.py +775 -0
- package/scripts/ranking_eval.py +319 -0
- package/scripts/readability_extract.py +433 -0
- package/scripts/recompute.py +415 -0
- package/scripts/recovery.py +616 -0
- package/scripts/recovery_l0.py +160 -0
- package/scripts/redskill/redskill_engine.py +332 -0
- package/scripts/regression_p0p1.py +318 -0
- package/scripts/replay_eval.py +640 -0
- package/scripts/rerank_tfidf.py +225 -0
- package/scripts/research.py +591 -403
- package/scripts/research_cli.py +361 -0
- package/scripts/research_dossier.py +492 -0
- package/scripts/research_expand.py +146 -0
- package/scripts/research_gates.py +272 -0
- package/scripts/research_report.py +209 -0
- package/scripts/research_strategy.py +37 -0
- package/scripts/research_work_packages.py +195 -0
- package/scripts/result_lang.py +311 -0
- package/scripts/robots_guard.py +259 -0
- package/scripts/route.py +921 -254
- package/scripts/route_bangs.py +40 -0
- package/scripts/route_cache.py +225 -0
- package/scripts/route_combo.py +910 -0
- package/scripts/route_domains.py +328 -0
- package/scripts/route_lang.py +605 -0
- package/scripts/route_log.py +63 -0
- package/scripts/route_policy.py +250 -0
- package/scripts/rrf_classic.py +66 -0
- package/scripts/screenshot.py +73 -0
- package/scripts/search.py +897 -405
- package/scripts/search_attribution.py +88 -0
- package/scripts/search_benchmark.py +407 -0
- package/scripts/search_cli.py +627 -0
- package/scripts/search_entry.py +389 -0
- package/scripts/search_output.py +606 -0
- package/scripts/search_pipeline.py +1077 -0
- package/scripts/search_rank.py +991 -0
- package/scripts/search_types.py +27 -12
- package/scripts/seek_locator.py +68 -0
- package/scripts/semantic_evidence.py +215 -0
- package/scripts/serp_guard.py +228 -0
- package/scripts/serp_kill_log.py +51 -0
- package/scripts/single_flight.py +120 -0
- package/scripts/social_engines/__init__.py +10 -0
- package/scripts/social_engines/bilibili_engine.py +131 -0
- package/scripts/social_engines/hackernews_engine.py +113 -0
- package/scripts/social_engines/reddit_engine.py +172 -0
- package/scripts/social_engines/twitter_engine.py +397 -0
- package/scripts/social_engines/v2ex_engine.py +101 -0
- package/scripts/social_engines/weibo_engine.py +128 -0
- package/scripts/social_engines/xiaohongshu_engine.py +137 -0
- package/scripts/social_engines/zhihu_engine.py +139 -0
- package/scripts/social_research.py +201 -0
- package/scripts/sogou_weixin_browser.py +318 -0
- package/scripts/source_ledger.py +249 -0
- package/scripts/stage_timing.py +58 -0
- package/scripts/stats_cli.py +95 -0
- package/scripts/straggler_policy.py +60 -0
- package/scripts/stylometry_detector.py +231 -0
- package/scripts/sync_backends.py +353 -0
- package/scripts/sync_installs.py +29 -0
- package/scripts/template_cli.py +127 -0
- package/scripts/text_lexicon.py +72 -0
- package/scripts/tfidf_router.py +109 -37
- package/scripts/time_utils.py +233 -0
- package/scripts/topic_research_profiles.py +643 -0
- package/scripts/train.py +737 -0
- package/scripts/tweet.py +596 -0
- package/scripts/url_canon.py +217 -0
- package/scripts/url_optimize.py +56 -0
- package/scripts/url_safety.py +236 -0
- package/scripts/usage_log.py +116 -0
- package/scripts/usage_log_clean.py +113 -0
- package/scripts/v2ex_nodes.py +311 -0
- package/scripts/variant_recall.py +188 -0
- package/scripts/watch.py +205 -0
- package/scripts/wechat_article.py +258 -0
- package/scripts/wide_research.py +402 -0
- package/scripts/wx.py +349 -0
- package/scripts/x_article_fetch.py +141 -0
- package/scripts/x_search.py +234 -0
- package/scripts/x_trending.py +189 -0
- package/scripts/yaml_load.py +67 -0
- package/sub-skills/ego-search/SKILL.md +253 -0
- package/sub-skills/ego-search/references/browser-runtime.md +43 -0
- package/sub-skills/ego-search/references/install.md +67 -0
- package/sub-skills/ego-search/references/original-ego-upgrade.md +51 -0
- package/sub-skills/ego-search/scripts/ego_search.py +800 -0
- package/sub-skills/ego-search/scripts/merge.py +226 -0
- package/sub-skills/ego-search/scripts/quality.py +64 -0
- package/sub-skills/ego-search/scripts/runtime.py +172 -0
- package/sub-skills/ego-search/scripts/safety.py +109 -0
- package/sub-skills/ego-search/scripts/serp_spec.py +109 -0
- package/sub-skills/ego-search/scripts/webbridge_adapter.py +374 -0
- package/sub-skills/ego-search/tests/test_query_normalize.py +68 -0
- package/sub-skills/js-run/SKILL.md +67 -0
- package/sub-skills/js-run/scripts/jsrun.py +112 -0
- package/sub-skills/js-run/scripts/shim.js +188 -0
- package/sub-skills/js-run/tests/fixtures/challenge_like.js +27 -0
- package/sub-skills/js-run/tests/test_jsrun.py +165 -0
- package/sub-skills/local-search/SKILL.md +50 -53
- package/sub-skills/local-search/bm25.py +162 -0
- package/sub-skills/local-search/config.yaml +152 -55
- package/sub-skills/local-search/engine_registry.py +91 -26
- package/sub-skills/local-search/{health_check.py → local_health_check.py} +140 -10
- package/sub-skills/local-search/local_search_adapter.py +13 -2
- package/sub-skills/local-search/parse_maps.yaml +40 -42
- package/sub-skills/local-search/search_v3.py +1182 -65
- package/sub-skills/local-search/smart_router.py +47 -7
- package/sub-skills/local-seek/SKILL.md +110 -0
- package/sub-skills/local-seek/config/domains.yaml +20 -0
- package/sub-skills/local-seek/references/auto-behaviors.md +24 -0
- package/sub-skills/local-seek/references/fd-mdfind-recipes.md +62 -0
- package/sub-skills/local-seek/references/git-integration.md +54 -0
- package/sub-skills/local-seek/references/remote-handoff.md +46 -0
- package/sub-skills/local-seek/references/rg-recipes.md +96 -0
- package/sub-skills/local-seek/references/strategies.md +52 -0
- package/sub-skills/local-seek/references/structural-search.md +59 -0
- package/sub-skills/local-seek/scripts/eval_rank.py +216 -0
- package/sub-skills/local-seek/scripts/eval_seek.py +224 -0
- package/sub-skills/local-seek/scripts/seek.py +1465 -0
- package/sub-skills/local-seek/scripts/seek_frecency.py +126 -0
- package/sub-skills/local-seek/scripts/seek_fts.py +430 -0
- package/sub-skills/local-seek/scripts/seek_rank.py +193 -0
- package/sub-skills/local-seek/tests/test_content_rank.py +222 -0
- package/sub-skills/local-seek/tests/test_determinism_20261006.py +145 -0
- package/sub-skills/local-seek/tests/test_noise_tier.py +101 -0
- package/sub-skills/local-seek/tests/test_pcre2_probe.py +136 -0
- package/sub-skills/local-seek/tests/test_rank_pinyin.py +77 -0
- package/sub-skills/local-seek/tests/test_regression_20261003.py +169 -0
- package/sub-skills/local-seek/tests/test_regression_20261006.py +146 -0
- package/sub-skills/local-seek/tests/test_seek_degradation.py +111 -0
- package/sub-skills/local-seek/tests/test_seek_fts.py +313 -0
|
@@ -0,0 +1,1244 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @taxueseek/argo-dsh — 浏览器端设置页(无需构建步骤)
|
|
3
|
+
*
|
|
4
|
+
* 由 DSH ModuleLoader 以 lazy-CJS 方式加载:工厂函数里只 require('react'),
|
|
5
|
+
* 其余能力(slots / configForms / remote.credentials / locale)全部由宿主
|
|
6
|
+
* 通过 cordis 服务注入。设置值走 configForms(settings namespace =
|
|
7
|
+
* 'wide-research'),密钥走 credentials 域——两者都不经过插件配置文件,
|
|
8
|
+
* 密钥更不会回显明文。
|
|
9
|
+
*
|
|
10
|
+
* 注册两个位置:
|
|
11
|
+
* settings.section —— 设置弹窗左侧一级菜单(主入口)
|
|
12
|
+
* plugins.item —— 设置 > 插件 页里的卡片(摘要 + 同一面板)
|
|
13
|
+
*/
|
|
14
|
+
window.__ModuleLoader__.load({
|
|
15
|
+
id: '@taxueseek/argo-dsh',
|
|
16
|
+
factory: (require) => {
|
|
17
|
+
var module = { exports: {} };
|
|
18
|
+
var exports = module.exports;
|
|
19
|
+
|
|
20
|
+
const react = require('react');
|
|
21
|
+
const h = react.createElement;
|
|
22
|
+
const useState = react.useState;
|
|
23
|
+
const useEffect = react.useEffect;
|
|
24
|
+
const useRef = react.useRef;
|
|
25
|
+
|
|
26
|
+
const NS = 'wide-research';
|
|
27
|
+
const SECTION_ID = 'argo';
|
|
28
|
+
|
|
29
|
+
const DICT = {
|
|
30
|
+
zh: {
|
|
31
|
+
title: 'Argo 搜索配置',
|
|
32
|
+
intro: '接入方式、原生工具、API 密钥与研究编排。保存后即时生效;工具名与 provider 属结构性字段,仍需在 profile patch 中修改。',
|
|
33
|
+
loading: '配置加载中…',
|
|
34
|
+
unavailable: '本插件的配置服务不可用(未挂载或只读)。',
|
|
35
|
+
readonly: '当前配置只读。',
|
|
36
|
+
saved: '已保存',
|
|
37
|
+
refused: '宿主拒绝写入(可能只读或字段不可热更新)',
|
|
38
|
+
save: '保存',
|
|
39
|
+
saving: '保存中…',
|
|
40
|
+
on: '开',
|
|
41
|
+
off: '关',
|
|
42
|
+
showMore: '显示更多',
|
|
43
|
+
showLess: '收起',
|
|
44
|
+
tabAccess: '接入',
|
|
45
|
+
tabTools: '工具',
|
|
46
|
+
tabKeys: '密钥',
|
|
47
|
+
tabResearch: '研究',
|
|
48
|
+
tabAdvanced: '高级',
|
|
49
|
+
summary: 'Argo · web_search {search} · web_fetch {fetch} · 原生工具 {tools} 个 · 研究 {workers}/{max}',
|
|
50
|
+
accessTitle: '接入方式',
|
|
51
|
+
searchSeam: '宿主 web_search 走 argo',
|
|
52
|
+
searchSeamHint: '关闭后 web_search 回到宿主默认搜索后端(web 行 searchProvider)。',
|
|
53
|
+
fetchSeam: '宿主 web_fetch 走 argo',
|
|
54
|
+
fetchSeamHint: '开启前必须在 web 配置行加 fetchProvider: argo;否则两个 provider 同时可用会报 WEB_PROVIDER_AMBIGUOUS。',
|
|
55
|
+
entryTitle: '引擎入口',
|
|
56
|
+
entryHint: 'npx 模式填 npx,参数 -y / github:taxueseek/argo;本机模式填 python3,参数填 mcp_server.py 的绝对路径。',
|
|
57
|
+
command: '命令',
|
|
58
|
+
args: '参数(一行一个)',
|
|
59
|
+
searchTimeout: '搜索超时 (ms)',
|
|
60
|
+
fetchTimeout: '抓取超时 (ms)',
|
|
61
|
+
fetchChars: '抓取正文上限(字符)',
|
|
62
|
+
idle: '连接空闲回收 (ms)',
|
|
63
|
+
concurrency: '并发连接数',
|
|
64
|
+
toolsTitle: '原生工具',
|
|
65
|
+
toolsHint: '勾选即注册为原生一等工具(CLI 单发,不依赖 MCP)。长尾工具只在需要时开,避免每轮工具面膨胀。',
|
|
66
|
+
coreTools: '高频路径',
|
|
67
|
+
longTools: '长尾取证',
|
|
68
|
+
nativeTimeout: '单次调用超时 (ms)',
|
|
69
|
+
nativeChars: '返回文本上限(字符)',
|
|
70
|
+
keysTitle: 'API 密钥',
|
|
71
|
+
keysHint: '密钥写入 DSH 凭证库(与 Models 页同一处),不会写进插件配置、不回显明文。只有勾选「转发」的引用才会作为环境变量传给 argo 子进程。「清除」只删凭证库里的值,环境变量或 .env 提供的 key 仍会生效。',
|
|
72
|
+
commonKeys: '常用引擎',
|
|
73
|
+
moreKeys: '更多引擎',
|
|
74
|
+
configured: '已配置',
|
|
75
|
+
notConfigured: '未配置',
|
|
76
|
+
forward: '转发',
|
|
77
|
+
keyPlaceholderSet: '输入新值(留空保留现有密钥)',
|
|
78
|
+
keyPlaceholderEmpty: '粘贴 API Key',
|
|
79
|
+
saveKey: '保存',
|
|
80
|
+
clearKey: '清除',
|
|
81
|
+
keySaved: '密钥已保存;下一次新建 argo 连接生效(池内旧连接最长 60s 回收)。',
|
|
82
|
+
keyCleared: '密钥已清除。',
|
|
83
|
+
keyUnavailable: '凭证服务不可用。',
|
|
84
|
+
researchTitle: '研究编排',
|
|
85
|
+
workers: '默认 worker 数',
|
|
86
|
+
maxWorkers: 'worker 上限',
|
|
87
|
+
tracks: '轨道上限',
|
|
88
|
+
sources: '每轨信源上限',
|
|
89
|
+
workerTokens: 'worker token 上限',
|
|
90
|
+
synthTokens: '综合 token 上限',
|
|
91
|
+
reportDir: '报告落盘目录',
|
|
92
|
+
advancedTitle: '高级',
|
|
93
|
+
allow: 'worker 工具白名单(一行一个)',
|
|
94
|
+
allowHint: '留空表示使用内置默认(MCP 与原生工具双写)。wide_research 自身与 argo_research 会被硬排除。',
|
|
95
|
+
deny: 'worker 工具拒绝名单(一行一个)',
|
|
96
|
+
keyRefs: '凭证转发名单(一行一个)',
|
|
97
|
+
keyRefsHint: '这些引用名会经 DSH credentials 解析后作为环境变量传给 argo 子进程;没有对应凭证的项会被跳过。',
|
|
98
|
+
structural: '结构性字段',
|
|
99
|
+
structuralHint: 'toolName / provider / searchProviderId / fetchProviderId 只能改 profile patch,改后重启 DSH 生效。',
|
|
100
|
+
mcpHint: 'MCP 全量工具面(mcp__argo__*)是 profile 里的独立 mcp-argo 行,不在本页开关;需要时在 patch 中取消注释并重启。',
|
|
101
|
+
noKey: '(未配置)',
|
|
102
|
+
tabEngines: '引擎',
|
|
103
|
+
enginesTitle: 'argo 引擎开关',
|
|
104
|
+
enginesHint: '这些开关原先是 argo 的环境变量,只能改 ~/.config/argo/env 或启动环境;现在写进插件配置,随 argo 子进程生效。改动对下一次新建连接生效(池内旧连接按「连接空闲回收」时长退出,默认 60s)。密钥不在这里配,请用「密钥」页;本页也不收授权类开关(可复算执行、内网 URL)。',
|
|
105
|
+
groupCache: '缓存与排序',
|
|
106
|
+
groupFetch: '抓取层',
|
|
107
|
+
groupSemantic: '语义与自适应',
|
|
108
|
+
groupEngines: '引擎启停',
|
|
109
|
+
groupNet: '网络出口',
|
|
110
|
+
groupLocal: '本机检索',
|
|
111
|
+
groupMisc: '其他',
|
|
112
|
+
envDefault: '默认',
|
|
113
|
+
envRange: '需在 {min}–{max} 之间',
|
|
114
|
+
keySourceFile: '凭证库',
|
|
115
|
+
keySourceEnv: '启动环境',
|
|
116
|
+
keySourceProjectEnv: '项目 .env',
|
|
117
|
+
keySourceUserEnv: '家目录 .env',
|
|
118
|
+
keySourceOther: '其他层',
|
|
119
|
+
keyReadonly: '只读',
|
|
120
|
+
keyOverrideHint: '保存后将覆盖当前层',
|
|
121
|
+
deepseekShared: '与宿主 web_search 官方后端共用同一把 key,改动同时影响两边',
|
|
122
|
+
},
|
|
123
|
+
en: {
|
|
124
|
+
title: 'Argo Search Settings',
|
|
125
|
+
intro: 'Access mode, native tools, API keys and research orchestration. Saves apply immediately; tool name and provider remain structural profile settings.',
|
|
126
|
+
loading: 'Loading configuration…',
|
|
127
|
+
unavailable: 'Configuration service unavailable (not mounted or read-only).',
|
|
128
|
+
readonly: 'Configuration is read-only.',
|
|
129
|
+
saved: 'Saved',
|
|
130
|
+
refused: 'Host refused the write (read-only or not live-editable)',
|
|
131
|
+
save: 'Save',
|
|
132
|
+
saving: 'Saving…',
|
|
133
|
+
on: 'on',
|
|
134
|
+
off: 'off',
|
|
135
|
+
showMore: 'Show more',
|
|
136
|
+
showLess: 'Show less',
|
|
137
|
+
tabAccess: 'Access',
|
|
138
|
+
tabTools: 'Tools',
|
|
139
|
+
tabKeys: 'Keys',
|
|
140
|
+
tabResearch: 'Research',
|
|
141
|
+
tabAdvanced: 'Advanced',
|
|
142
|
+
summary: 'Argo · web_search {search} · web_fetch {fetch} · {tools} native tools · research {workers}/{max}',
|
|
143
|
+
accessTitle: 'Access',
|
|
144
|
+
searchSeam: 'Route host web_search to argo',
|
|
145
|
+
searchSeamHint: 'When off, web_search falls back to the host provider configured on the web row.',
|
|
146
|
+
fetchSeam: 'Route host web_fetch to argo',
|
|
147
|
+
fetchSeamHint: 'Requires fetchProvider: argo on the web row; otherwise two available providers trigger WEB_PROVIDER_AMBIGUOUS.',
|
|
148
|
+
entryTitle: 'Engine entry',
|
|
149
|
+
entryHint: 'npx mode: command npx, args -y / github:taxueseek/argo. Local mode: python3 plus the absolute mcp_server.py path.',
|
|
150
|
+
command: 'Command',
|
|
151
|
+
args: 'Args (one per line)',
|
|
152
|
+
searchTimeout: 'Search timeout (ms)',
|
|
153
|
+
fetchTimeout: 'Fetch timeout (ms)',
|
|
154
|
+
fetchChars: 'Fetch body cap (chars)',
|
|
155
|
+
idle: 'Idle recycle (ms)',
|
|
156
|
+
concurrency: 'Concurrent connections',
|
|
157
|
+
toolsTitle: 'Native tools',
|
|
158
|
+
toolsHint: 'Checked tools register as native first-class tools (single-shot CLI, no MCP). Enable long-tail tools only when needed.',
|
|
159
|
+
coreTools: 'Hot path',
|
|
160
|
+
longTools: 'Long tail',
|
|
161
|
+
nativeTimeout: 'Per-call timeout (ms)',
|
|
162
|
+
nativeChars: 'Returned text cap (chars)',
|
|
163
|
+
keysTitle: 'API keys',
|
|
164
|
+
keysHint: 'Keys are stored in the DSH credential store (same place as the Models page), never in plugin config and never echoed. Only refs marked Forward reach the argo child process. Clear removes the stored value only; a key supplied by the environment or .env still applies.',
|
|
165
|
+
commonKeys: 'Common engines',
|
|
166
|
+
moreKeys: 'More engines',
|
|
167
|
+
configured: 'Configured',
|
|
168
|
+
notConfigured: 'Not configured',
|
|
169
|
+
forward: 'Forward',
|
|
170
|
+
keyPlaceholderSet: 'Enter a new value (leave blank to keep the current key)',
|
|
171
|
+
keyPlaceholderEmpty: 'Paste API key',
|
|
172
|
+
saveKey: 'Save',
|
|
173
|
+
clearKey: 'Clear',
|
|
174
|
+
keySaved: 'Key saved; effective on the next new argo connection (old pooled connections recycle within ~60s).',
|
|
175
|
+
keyCleared: 'Key cleared.',
|
|
176
|
+
keyUnavailable: 'Credential service unavailable.',
|
|
177
|
+
researchTitle: 'Research',
|
|
178
|
+
workers: 'Default workers',
|
|
179
|
+
maxWorkers: 'Worker cap',
|
|
180
|
+
tracks: 'Track cap',
|
|
181
|
+
sources: 'Sources per track',
|
|
182
|
+
workerTokens: 'Worker token cap',
|
|
183
|
+
synthTokens: 'Synthesis token cap',
|
|
184
|
+
reportDir: 'Report directory',
|
|
185
|
+
advancedTitle: 'Advanced',
|
|
186
|
+
allow: 'Worker tool allowlist (one per line)',
|
|
187
|
+
allowHint: 'Empty uses the built-in default (MCP + native tools). wide_research itself and argo_research are always excluded.',
|
|
188
|
+
deny: 'Worker tool denylist (one per line)',
|
|
189
|
+
keyRefs: 'Credential forwarding refs (one per line)',
|
|
190
|
+
keyRefsHint: 'These refs resolve through DSH credentials and reach the argo child process as environment variables. Missing refs are skipped.',
|
|
191
|
+
structural: 'Structural fields',
|
|
192
|
+
structuralHint: 'toolName / provider / searchProviderId / fetchProviderId are profile-patch only and need a DSH restart.',
|
|
193
|
+
mcpHint: 'The full MCP tool face (mcp__argo__*) is a separate mcp-argo entry in the profile patch; enable it there and restart.',
|
|
194
|
+
noKey: '(not configured)',
|
|
195
|
+
tabEngines: 'Engines',
|
|
196
|
+
enginesTitle: 'argo engine switches',
|
|
197
|
+
enginesHint: 'These switches used to be argo environment variables, editable only in ~/.config/argo/env or the launch environment. They now live in plugin config and reach the argo child process. Changes apply to the next new connection (pooled connections retire after the idle recycle window, 60s by default). Keys do not belong here; use the Keys tab. Authorization switches (recompute execution, private URLs) are deliberately not exposed.',
|
|
198
|
+
groupCache: 'Cache & ranking',
|
|
199
|
+
groupFetch: 'Fetch layer',
|
|
200
|
+
groupSemantic: 'Semantics & adaptivity',
|
|
201
|
+
groupEngines: 'Engine on-off',
|
|
202
|
+
groupNet: 'Network',
|
|
203
|
+
groupLocal: 'Local search',
|
|
204
|
+
groupMisc: 'Misc',
|
|
205
|
+
envDefault: 'default',
|
|
206
|
+
envRange: 'must be between {min} and {max}',
|
|
207
|
+
keySourceFile: 'credential store',
|
|
208
|
+
keySourceEnv: 'launch environment',
|
|
209
|
+
keySourceProjectEnv: 'project .env',
|
|
210
|
+
keySourceUserEnv: 'home .env',
|
|
211
|
+
keySourceOther: 'other layer',
|
|
212
|
+
keyReadonly: 'read-only',
|
|
213
|
+
keyOverrideHint: 'saving overrides the current layer',
|
|
214
|
+
deepseekShared: 'Shared with the host web_search backend; changing it affects both',
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const TOOLS = [
|
|
219
|
+
{ id: 'argo_search', group: 'core', zh: '统一搜索', en: 'Unified search' },
|
|
220
|
+
{ id: 'argo_fetch', group: 'core', zh: '智能抓取', en: 'Smart fetch' },
|
|
221
|
+
{ id: 'argo_evidence', group: 'long', zh: '来源可信度评估', en: 'Source credibility' },
|
|
222
|
+
{ id: 'argo_local_search', group: 'long', zh: '本机文件搜索', en: 'Local file search' },
|
|
223
|
+
{ id: 'argo_local_read', group: 'long', zh: '本机文件读取', en: 'Local file read' },
|
|
224
|
+
{ id: 'argo_recompute', group: 'long', zh: '可复算执行器', en: 'Recompute' },
|
|
225
|
+
{ id: 'argo_clarify', group: 'long', zh: '意图消歧', en: 'Intent clarification' },
|
|
226
|
+
{ id: 'argo_crawl', group: 'long', zh: '站点级爬取', en: 'Site crawl' },
|
|
227
|
+
{ id: 'argo_social_search', group: 'long', zh: '社交平台搜索', en: 'Social search' },
|
|
228
|
+
{ id: 'argo_article', group: 'long', zh: '公众号全文', en: 'WeChat article' },
|
|
229
|
+
{ id: 'argo_job', group: 'long', zh: '招聘聚合', en: 'Job aggregation' },
|
|
230
|
+
{ id: 'argo_extract', group: 'long', zh: '结构化提取', en: 'Structured extract' },
|
|
231
|
+
{ id: 'argo_preflight', group: 'long', zh: '批量 URL 预检', en: 'URL preflight' },
|
|
232
|
+
{ id: 'argo_pdf', group: 'long', zh: 'PDF 提取', en: 'PDF extract' },
|
|
233
|
+
{ id: 'argo_screenshot', group: 'long', zh: '网页截图', en: 'Screenshot' },
|
|
234
|
+
{ id: 'argo_answer', group: 'long', zh: '带引用直答', en: 'Cited answer' },
|
|
235
|
+
{ id: 'argo_watch', group: 'long', zh: '网页变化监控', en: 'Page watch' },
|
|
236
|
+
{ id: 'argo_cite', group: 'long', zh: 'DOI 引用生成', en: 'DOI citation' },
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
const KEYS = [
|
|
240
|
+
{ ref: 'DEEPSEEK_API_KEY', common: true, zh: 'DeepSeek 官方搜索', en: 'DeepSeek official search' },
|
|
241
|
+
{ ref: 'TAVILY_API_KEY', common: true, zh: 'Tavily', en: 'Tavily' },
|
|
242
|
+
{ ref: 'EXA_API_KEY', common: true, zh: 'Exa', en: 'Exa' },
|
|
243
|
+
{ ref: 'BOCHA_API_KEY', common: true, zh: '博查', en: 'Bocha' },
|
|
244
|
+
{ ref: 'BRAVE_API_KEY', common: true, zh: 'Brave Search', en: 'Brave Search' },
|
|
245
|
+
{ ref: 'TINYFISH_API_KEY', common: true, zh: 'TinyFish(抓取渲染)', en: 'TinyFish (fetch render)' },
|
|
246
|
+
{ ref: 'WEB_SEARCH_API_KEY', zh: '火山 / ByteDance', en: 'ByteDance search' },
|
|
247
|
+
{ ref: 'OCTEN_API_KEY', zh: 'Octen', en: 'Octen' },
|
|
248
|
+
{ ref: 'FELO_API_KEY', zh: 'Felo', en: 'Felo' },
|
|
249
|
+
{ ref: 'METASO_API_KEY', zh: '秘塔 Metaso', en: 'Metaso' },
|
|
250
|
+
{ ref: 'ANYSEARCH_API_KEY', zh: 'AnySearch(可选)', en: 'AnySearch (optional)' },
|
|
251
|
+
{ ref: 'FIRECRAWL_API_KEY', zh: 'Firecrawl(可选,有免费层)', en: 'Firecrawl (optional, free tier)' },
|
|
252
|
+
{ ref: 'WEREAD_API_KEY', zh: '微信读书', en: 'WeRead' },
|
|
253
|
+
{ ref: 'GITHUB_TOKEN', zh: 'GitHub(仅提高限频)', en: 'GitHub (rate limit)' },
|
|
254
|
+
{ ref: 'ZHIHU_ACCESS_SECRET', zh: '知乎', en: 'Zhihu' },
|
|
255
|
+
{ ref: 'PARALLEL_API_KEY', zh: 'Parallel', en: 'Parallel' },
|
|
256
|
+
{ ref: 'SELTZ_API_KEY', zh: 'Seltz', en: 'Seltz' },
|
|
257
|
+
{ ref: 'YDC_API_KEY', zh: 'You.com', en: 'You.com' },
|
|
258
|
+
{ ref: 'EASTMONEY_APIKEY', zh: '东财妙想', en: 'Eastmoney Miaoxiang' },
|
|
259
|
+
{ ref: 'WOLFRAM_APPID', zh: 'Wolfram Alpha', en: 'Wolfram Alpha' },
|
|
260
|
+
{ ref: 'QWEATHER_KEY', zh: '和风天气', en: 'QWeather' },
|
|
261
|
+
{ ref: 'UNPAYWALL_EMAIL', zh: 'Unpaywall 邮箱(非密钥)', en: 'Unpaywall email (not secret)' },
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
// argo 引擎开关(argoEnv 的 KEY=VALUE 行)。与 index.js 的 ARGO_ENV_SPECS
|
|
265
|
+
// 同一份名单,由 test/client.test.js 的漂移门禁核对;kind 决定控件:
|
|
266
|
+
// bool=开关(invert 语义反转,如 NO_CACHE 是「关缓存」)、num=数字、
|
|
267
|
+
// str=文本、list=名单。def 仅作「未配置时显示什么」,缺省即不写该行。
|
|
268
|
+
const ENGINE_TOGGLES = [
|
|
269
|
+
{ name: 'ARGO_NO_CACHE', kind: 'bool', invert: true, group: 'cache', def: false, zh: '关闭搜索结果缓存', en: 'Disable result cache', hintZh: 'argo 默认缓存 1 小时;调试期关掉缓存看实时结果', hintEn: 'argo caches results for 1h by default; turn off to see live results' },
|
|
270
|
+
{ name: 'ARGO_TFIDF_RERANK', kind: 'bool', group: 'cache', def: true, zh: 'TF-IDF 重排', en: 'TF-IDF rerank', hintZh: '多引擎结果融合前的相关性重排,默认开', hintEn: 'Relevance rerank before fusion, on by default' },
|
|
271
|
+
{ name: 'ARGO_NUMBER_CROSSCHECK', kind: 'bool', group: 'cache', def: true, zh: '数值交叉校验', en: 'Number cross-check', hintZh: '对查询里的数值主张做跨源核对,默认开', hintEn: 'Cross-check numeric claims across sources, on by default' },
|
|
272
|
+
{ name: 'ARGO_MINHASH_DEDUPE', kind: 'bool', group: 'cache', def: true, zh: 'MinHash 去重', en: 'MinHash dedupe', hintZh: '近重复结果合并,默认开', hintEn: 'Merge near-duplicate results, on by default' },
|
|
273
|
+
{ name: 'ARGO_MINHASH_THRESHOLD', kind: 'num', group: 'cache', def: 0.85, min: 0.5, max: 1, step: 0.05, zh: '去重阈值', en: 'Dedupe threshold', hintZh: '越高越严(越多判为不同),默认 0.85', hintEn: 'Higher = stricter, default 0.85' },
|
|
274
|
+
{ name: 'ARGO_SERP_GUARD', kind: 'bool', group: 'cache', def: true, zh: 'SERP 守卫', en: 'SERP guard', hintZh: '拦截壳页/风控页混入结果,默认开;误杀时先关它复现', hintEn: 'Keep shell/blocked pages out of results, on by default' },
|
|
275
|
+
{ name: 'ARGO_FETCH_JINA', kind: 'bool', group: 'fetch', def: true, zh: 'Jina 阅读器抓取', en: 'Jina reader fetch', hintZh: 'r.jina.ai 正文提取,默认开', hintEn: 'r.jina.ai text extraction, on by default' },
|
|
276
|
+
{ name: 'ARGO_FETCH_MOBILE', kind: 'bool', group: 'fetch', def: true, zh: '移动端 UA 抓取', en: 'Mobile UA fetch', hintZh: '按 host 记忆移动端身份,默认开', hintEn: 'Per-host mobile identity, on by default' },
|
|
277
|
+
{ name: 'ARGO_FETCH_PARALLEL', kind: 'bool', group: 'fetch', def: true, zh: 'Parallel 免费渲染抓取', en: 'Parallel free render fetch', hintZh: 'keyless 云端渲染兜底,默认开', hintEn: 'Keyless cloud render fallback, on by default' },
|
|
278
|
+
{ name: 'ARGO_FETCH_DEADLINE_S', kind: 'num', group: 'fetch', def: 60, min: 5, max: 300, step: 5, zh: '抓取总截止(秒)', en: 'Fetch deadline (s)', hintZh: '单次抓取全链路墙钟上限,默认 60', hintEn: 'Wall-clock cap per fetch, default 60' },
|
|
279
|
+
{ name: 'ARGO_SEMANTIC_EVIDENCE', kind: 'bool', group: 'semantic', def: false, zh: '语义证据层', en: 'Semantic evidence', hintZh: '显式核验链路的零样本判定,默认关;开启后多一次外呼', hintEn: 'Zero-shot support check on the verify path, off by default' },
|
|
280
|
+
{ name: 'ARGO_ADAPTIVE_GRACE', kind: 'bool', group: 'semantic', def: true, zh: '对冲宽限自适应', en: 'Adaptive hedge grace', hintZh: '按引擎历史 p75 收窄对冲等待窗,默认开', hintEn: 'Narrow hedge window by engine p75, on by default' },
|
|
281
|
+
{ name: 'ARGO_ADAPTIVE_TAIL', kind: 'bool', group: 'semantic', def: false, zh: '分层尾宽', en: 'Layered straggler tail', hintZh: '尾宽按未回来引擎分层取值,默认关', hintEn: 'Per-engine layered tail width, off by default' },
|
|
282
|
+
{ name: 'ARGO_STRAGGLER_GRACE_S', kind: 'num', group: 'semantic', def: 1.5, min: 0, max: 30, step: 0.5, zh: '掉队宽限(秒)', en: 'Straggler grace (s)', hintZh: '已有引擎交付后还等多久,默认 1.5;0 关闭', hintEn: 'Wait after first delivery, default 1.5; 0 disables' },
|
|
283
|
+
{ name: 'ARGO_SERIAL_STAGGER_S', kind: 'num', group: 'semantic', def: 0.8, min: 0, max: 30, step: 0.5, zh: '串行起步间隔(秒)', en: 'Serial stagger (s)', hintZh: '首引擎超时前补发备选源的提前量,默认 0.8;0 最激进', hintEn: 'Lead before firing a backup source, default 0.8; 0 = fire together' },
|
|
284
|
+
{ name: 'ARGO_ENABLE_ENGINES', kind: 'list', group: 'engines', zh: '额外启用引擎', en: 'Extra enabled engines', hintZh: '逗号分隔;叠加在 config.yaml 启用集之上', hintEn: 'Comma-separated; adds to the config.yaml enabled set' },
|
|
285
|
+
{ name: 'ARGO_DISABLE_ENGINES', kind: 'list', group: 'engines', zh: '停用引擎', en: 'Disabled engines', hintZh: '逗号分隔;优先级高于启用集,调试单引擎时用', hintEn: 'Comma-separated; wins over the enabled set' },
|
|
286
|
+
{ name: 'ARGO_PROXY', kind: 'str', group: 'net', zh: 'argo 全局代理', en: 'argo global proxy', hintZh: 'http:// 代理 URL,或 direct 强制直连;留空走环境变量', hintEn: 'http:// proxy URL, or direct; empty falls back to env' },
|
|
287
|
+
{ name: 'ARGO_GDELT_PROXY', kind: 'str', group: 'net', zh: 'GDELT 代理', en: 'GDELT proxy', hintZh: 'GDELT 按出口 IP 限流,经本地代理更稳;留空自动直连', hintEn: 'GDELT rate-limits by egress IP; empty = direct' },
|
|
288
|
+
{ name: 'ARGO_LOCAL_SEEK_ROOTS', kind: 'list', group: 'local', zh: '本机检索根目录', en: 'Local seek roots', hintZh: '路径分隔符分隔;seek.py 的候选安装根,默认用打包位置', hintEn: 'Path-separated roots for seek.py; default uses the bundled copy' },
|
|
289
|
+
{ name: 'ARGO_LOCAL_SEEK_PATH', kind: 'str', group: 'local', zh: 'seek.py 绝对路径', en: 'seek.py path', hintZh: '显式指定 seek.py,优先于根目录候选', hintEn: 'Explicit seek.py, wins over root candidates' },
|
|
290
|
+
{ name: 'ARGO_LOCAL_READ_DIRS', kind: 'list', group: 'local', zh: '本机读取白名单', en: 'Local read allowlist', hintZh: '逗号分隔;不配置则 local_read 一律不放行', hintEn: 'Comma-separated; empty means local_read allows nothing' },
|
|
291
|
+
{ name: 'ARGO_TEXT_LEXICON', kind: 'str', group: 'local', zh: '词表 JSON 路径', en: 'Lexicon JSON path', hintZh: '文本处理词表覆盖,默认用打包词表', hintEn: 'Text lexicon override; default uses the bundled one' },
|
|
292
|
+
{ name: 'ARGO_NO_PREWARM', kind: 'bool', invert: true, group: 'misc', def: false, zh: '关闭连接预热', en: 'Disable prewarm', hintZh: 'MCP 握手后预热引擎注册表,默认开', hintEn: 'Prewarm the engine registry after handshake, on by default' },
|
|
293
|
+
{ name: 'ARGO_USAGE_LOG', kind: 'bool', group: 'misc', def: true, zh: '使用日志', en: 'Usage log', hintZh: 'argo 侧 JSONL 使用账本,默认开', hintEn: 'argo JSONL usage ledger, on by default' },
|
|
294
|
+
{ name: 'ARGO_ENGINE_HTTP_CLIENT', kind: 'bool', group: 'misc', def: true, zh: 'HttpClient 连接池', en: 'HttpClient pool', hintZh: '引擎 HTTP 走连接池,默认开;=0 回退 urllib(诊断用)', hintEn: 'Pooled engine HTTP, on by default; =0 falls back to urllib' },
|
|
295
|
+
{ name: 'ARGO_ADMISSION_TTL_S', kind: 'num', group: 'misc', def: 1, min: 0, max: 60, step: 0.5, zh: '准入记忆 TTL(秒)', en: 'Admission memo TTL (s)', hintZh: '引擎准入状态记忆时长,默认 1;0 每次现查', hintEn: 'Admission memo window, default 1; 0 = always fresh' },
|
|
296
|
+
{ name: 'ARGO_CONFIG_STAMP_TTL_S', kind: 'num', group: 'misc', def: 1, min: 0, max: 60, step: 0.5, zh: '配置戳记忆 TTL(秒)', en: 'Config stamp memo TTL (s)', hintZh: '外部引擎扫描记忆时长,默认 1;0 每次现扫', hintEn: 'External engine scan memo window, default 1; 0 = always rescan' },
|
|
297
|
+
];
|
|
298
|
+
const ENV_NAMES = new Set(ENGINE_TOGGLES.map((spec) => spec.name));
|
|
299
|
+
|
|
300
|
+
const DEFAULTS = {
|
|
301
|
+
searchCommand: 'npx',
|
|
302
|
+
searchArgs: ['-y', 'github:taxueseek/argo'],
|
|
303
|
+
searchTimeoutMs: 30000,
|
|
304
|
+
fetchTimeoutMs: 25000,
|
|
305
|
+
fetchMaxChars: 50000,
|
|
306
|
+
searchIdleMs: 60000,
|
|
307
|
+
searchConcurrency: 3,
|
|
308
|
+
nativeTimeoutMs: 60000,
|
|
309
|
+
nativeMaxChars: 24000,
|
|
310
|
+
defaultWorkers: 6,
|
|
311
|
+
maxWorkers: 9,
|
|
312
|
+
maxTracks: 9,
|
|
313
|
+
maxSourcesPerTrack: 5,
|
|
314
|
+
workerMaxTokens: 5000,
|
|
315
|
+
synthesisMaxTokens: 7000,
|
|
316
|
+
reportDir: '',
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const S = {
|
|
320
|
+
wrap: {
|
|
321
|
+
display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 800,
|
|
322
|
+
color: 'var(--dsw-alias-label-primary, #1f2328)',
|
|
323
|
+
},
|
|
324
|
+
title: { margin: 0, fontSize: 18, fontWeight: 600, lineHeight: '26px' },
|
|
325
|
+
hint: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary, #7b818b)' },
|
|
326
|
+
notice: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-business-primary, #3b6ef6)' },
|
|
327
|
+
error: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary, #d1242f)' },
|
|
328
|
+
tabs: {
|
|
329
|
+
display: 'flex', gap: 2, flexWrap: 'wrap',
|
|
330
|
+
borderBottom: '.5px solid var(--dsw-alias-border-l2, #e5e7eb)',
|
|
331
|
+
},
|
|
332
|
+
tab: (active) => ({
|
|
333
|
+
padding: '7px 12px 9px', border: 0, background: 'transparent', cursor: 'pointer',
|
|
334
|
+
font: 'inherit', fontSize: 13, lineHeight: '20px', color: active
|
|
335
|
+
? 'var(--dsw-alias-label-primary, #1f2328)'
|
|
336
|
+
: 'var(--dsw-alias-label-tertiary, #7b818b)',
|
|
337
|
+
borderBottom: active ? '2px solid var(--dsw-alias-state-business-primary, #3b6ef6)' : '2px solid transparent',
|
|
338
|
+
marginBottom: -1,
|
|
339
|
+
}),
|
|
340
|
+
card: {
|
|
341
|
+
display: 'flex', flexDirection: 'column', gap: 10, padding: '14px 16px',
|
|
342
|
+
borderRadius: 12, border: '.5px solid var(--dsw-alias-border-l3, #e5e7eb)',
|
|
343
|
+
background: 'var(--dsw-alias-settings-card-fill, #fff)',
|
|
344
|
+
},
|
|
345
|
+
cardTitle: { margin: 0, fontSize: 14, fontWeight: 600, lineHeight: '20px' },
|
|
346
|
+
row: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 },
|
|
347
|
+
field: { display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0 },
|
|
348
|
+
label: { fontSize: 13, fontWeight: 500, lineHeight: '18px' },
|
|
349
|
+
sub: { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary, #7b818b)' },
|
|
350
|
+
input: {
|
|
351
|
+
width: '100%', boxSizing: 'border-box', font: 'inherit', fontSize: 13, lineHeight: '20px',
|
|
352
|
+
padding: '7px 9px', borderRadius: 8,
|
|
353
|
+
border: '.5px solid var(--dsw-alias-border-l4, #d0d7de)',
|
|
354
|
+
background: 'var(--dsw-alias-bg-layer-1, #fff)', color: 'inherit',
|
|
355
|
+
},
|
|
356
|
+
mono: { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', fontSize: 12 },
|
|
357
|
+
textarea: { minHeight: 64, resize: 'vertical' },
|
|
358
|
+
grid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: 10 },
|
|
359
|
+
toolGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))', gap: 6 },
|
|
360
|
+
check: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, cursor: 'pointer' },
|
|
361
|
+
btn: {
|
|
362
|
+
padding: '6px 12px', borderRadius: 8, border: '.5px solid var(--dsw-alias-border-l4, #d0d7de)',
|
|
363
|
+
background: 'var(--dsw-alias-bg-layer-1, #fff)', color: 'inherit', font: 'inherit',
|
|
364
|
+
fontSize: 12, lineHeight: '18px', cursor: 'pointer',
|
|
365
|
+
},
|
|
366
|
+
btnPrimary: {
|
|
367
|
+
padding: '6px 14px', borderRadius: 8, border: '.5px solid var(--dsw-alias-state-business-primary, #3b6ef6)',
|
|
368
|
+
background: 'var(--dsw-alias-state-business-primary, #3b6ef6)', color: '#fff', font: 'inherit',
|
|
369
|
+
fontSize: 12, lineHeight: '18px', cursor: 'pointer',
|
|
370
|
+
},
|
|
371
|
+
chip: (on) => ({
|
|
372
|
+
display: 'inline-flex', alignItems: 'center', gap: 6, padding: '3px 9px',
|
|
373
|
+
borderRadius: 999, fontSize: 11, lineHeight: '16px',
|
|
374
|
+
border: '.5px solid ' + (on ? 'var(--dsw-alias-state-business-primary, #3b6ef6)' : 'var(--dsw-alias-border-l3, #e5e7eb)'),
|
|
375
|
+
color: on ? 'var(--dsw-alias-state-business-primary, #3b6ef6)' : 'var(--dsw-alias-label-tertiary, #7b818b)',
|
|
376
|
+
background: 'transparent',
|
|
377
|
+
}),
|
|
378
|
+
status: { margin: 0, fontSize: 12, minHeight: 18, lineHeight: '18px' },
|
|
379
|
+
keyRow: {
|
|
380
|
+
display: 'grid', gridTemplateColumns: 'minmax(150px, 1fr) minmax(0, 1.5fr)', gap: 10,
|
|
381
|
+
alignItems: 'start', padding: '10px 0',
|
|
382
|
+
borderTop: '.5px solid var(--dsw-alias-border-l2, #eee)',
|
|
383
|
+
},
|
|
384
|
+
keyHead: { display: 'flex', flexDirection: 'column', gap: 2, minWidth: 0 },
|
|
385
|
+
code: { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', fontSize: 10, color: 'var(--dsw-alias-label-tertiary, #7b818b)' },
|
|
386
|
+
dot: (on) => ({
|
|
387
|
+
display: 'inline-block', width: 6, height: 6, borderRadius: 999,
|
|
388
|
+
background: on ? '#2da44e' : 'var(--dsw-alias-border-l4, #d0d7de)',
|
|
389
|
+
marginRight: 5,
|
|
390
|
+
}),
|
|
391
|
+
actions: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' },
|
|
392
|
+
switchWrap: { display: 'flex', alignItems: 'center', gap: 8 },
|
|
393
|
+
divider: { height: '.5px', background: 'var(--dsw-alias-border-l2, #eee)', margin: '2px 0' },
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
function asString(value, fallback) {
|
|
397
|
+
return typeof value === 'string' ? value : (fallback ?? '');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function asNumber(value, fallback) {
|
|
401
|
+
const n = typeof value === 'number' ? value : Number.parseInt(String(value ?? ''), 10);
|
|
402
|
+
return Number.isFinite(n) ? n : fallback;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function asArray(value) {
|
|
406
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function lines(text) {
|
|
410
|
+
return String(text ?? '').split('\n').map((line) => line.trim()).filter(Boolean);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function toDraft(value) {
|
|
414
|
+
return {
|
|
415
|
+
command: asString(value.searchCommand, DEFAULTS.searchCommand),
|
|
416
|
+
argsText: asArray(value.searchArgs).join('\n'),
|
|
417
|
+
searchTimeoutMs: String(asNumber(value.searchTimeoutMs, DEFAULTS.searchTimeoutMs)),
|
|
418
|
+
fetchTimeoutMs: String(asNumber(value.fetchTimeoutMs, DEFAULTS.fetchTimeoutMs)),
|
|
419
|
+
fetchMaxChars: String(asNumber(value.fetchMaxChars, DEFAULTS.fetchMaxChars)),
|
|
420
|
+
searchIdleMs: String(asNumber(value.searchIdleMs, DEFAULTS.searchIdleMs)),
|
|
421
|
+
searchConcurrency: String(asNumber(value.searchConcurrency, DEFAULTS.searchConcurrency)),
|
|
422
|
+
nativeTimeoutMs: String(asNumber(value.nativeTimeoutMs, DEFAULTS.nativeTimeoutMs)),
|
|
423
|
+
nativeMaxChars: String(asNumber(value.nativeMaxChars, DEFAULTS.nativeMaxChars)),
|
|
424
|
+
defaultWorkers: String(asNumber(value.defaultWorkers, DEFAULTS.defaultWorkers)),
|
|
425
|
+
maxWorkers: String(asNumber(value.maxWorkers, DEFAULTS.maxWorkers)),
|
|
426
|
+
maxTracks: String(asNumber(value.maxTracks, DEFAULTS.maxTracks)),
|
|
427
|
+
maxSourcesPerTrack: String(asNumber(value.maxSourcesPerTrack, DEFAULTS.maxSourcesPerTrack)),
|
|
428
|
+
workerMaxTokens: String(asNumber(value.workerMaxTokens, DEFAULTS.workerMaxTokens)),
|
|
429
|
+
synthesisMaxTokens: String(asNumber(value.synthesisMaxTokens, DEFAULTS.synthesisMaxTokens)),
|
|
430
|
+
reportDir: asString(value.reportDir, DEFAULTS.reportDir),
|
|
431
|
+
allowText: asArray(value.childToolAllow).join('\n'),
|
|
432
|
+
denyText: asArray(value.childToolDeny).join('\n'),
|
|
433
|
+
keyRefsText: asArray(value.keyRefs).join('\n'),
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function Card(props) {
|
|
438
|
+
return h('section', { style: S.card }, [
|
|
439
|
+
props.title ? h('h3', { key: 't', style: S.cardTitle }, props.title) : null,
|
|
440
|
+
props.hint ? h('p', { key: 'h', style: S.hint }, props.hint) : null,
|
|
441
|
+
props.children,
|
|
442
|
+
]);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function Field(props) {
|
|
446
|
+
return h('label', { style: S.field }, [
|
|
447
|
+
h('span', { key: 'l', style: S.label }, props.label),
|
|
448
|
+
props.children,
|
|
449
|
+
props.hint ? h('span', { key: 'h', style: S.sub }, props.hint) : null,
|
|
450
|
+
]);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function Switch(props) {
|
|
454
|
+
return h('div', { style: S.row }, [
|
|
455
|
+
h('div', { key: 'txt', style: { minWidth: 0 } }, [
|
|
456
|
+
h('div', { key: 'l', style: S.label }, props.label),
|
|
457
|
+
props.hint ? h('div', { key: 'h', style: S.sub }, props.hint) : null,
|
|
458
|
+
]),
|
|
459
|
+
h('input', {
|
|
460
|
+
key: 'i',
|
|
461
|
+
type: 'checkbox',
|
|
462
|
+
checked: props.checked === true,
|
|
463
|
+
disabled: props.disabled === true,
|
|
464
|
+
onChange: (event) => props.onChange(event.target.checked),
|
|
465
|
+
'aria-label': props.label,
|
|
466
|
+
}),
|
|
467
|
+
]);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function NumberField(props) {
|
|
471
|
+
return h(Field, { label: props.label, hint: props.hint }, h('input', {
|
|
472
|
+
type: 'number',
|
|
473
|
+
style: S.input,
|
|
474
|
+
value: props.value,
|
|
475
|
+
min: props.min,
|
|
476
|
+
step: props.step ?? 1,
|
|
477
|
+
disabled: props.disabled === true,
|
|
478
|
+
onChange: (event) => props.onChange(event.target.value),
|
|
479
|
+
}));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function PrimaryButton(props) {
|
|
483
|
+
return h('button', {
|
|
484
|
+
type: 'button',
|
|
485
|
+
style: props.primary ? S.btnPrimary : S.btn,
|
|
486
|
+
disabled: props.disabled === true,
|
|
487
|
+
onClick: props.onClick,
|
|
488
|
+
}, props.children);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function ArgoPanel(props) {
|
|
492
|
+
const state = props.useArgoForm((snapshot) => snapshot);
|
|
493
|
+
const value = state && state.value ? state.value : {};
|
|
494
|
+
const ready = state && state.status === 'ready';
|
|
495
|
+
const writable = ready && state.writable !== false;
|
|
496
|
+
const disabled = !writable;
|
|
497
|
+
|
|
498
|
+
const locale = props.locale;
|
|
499
|
+
const initialLang = locale && locale.getSnapshot && locale.getSnapshot().active === 'en' ? 'en' : 'zh';
|
|
500
|
+
const [lang, setLang] = useState(initialLang);
|
|
501
|
+
|
|
502
|
+
const [tab, setTab] = useState('access');
|
|
503
|
+
const [draft, setDraft] = useState(() => toDraft(value));
|
|
504
|
+
const [busy, setBusy] = useState('');
|
|
505
|
+
const [message, setMessage] = useState('');
|
|
506
|
+
const [error, setError] = useState(false);
|
|
507
|
+
|
|
508
|
+
const [credentials, setCredentials] = useState({});
|
|
509
|
+
const [keyDraft, setKeyDraft] = useState({});
|
|
510
|
+
const [keyBusy, setKeyBusy] = useState('');
|
|
511
|
+
const [showAllKeys, setShowAllKeys] = useState(false);
|
|
512
|
+
|
|
513
|
+
// argoEnv 引擎开关:bool 类即时写;num/str/list 类走草稿 + 分组保存。
|
|
514
|
+
const [envDraft, setEnvDraft] = useState({});
|
|
515
|
+
const [envBusy, setEnvBusy] = useState('');
|
|
516
|
+
|
|
517
|
+
const dirty = useRef(new Set());
|
|
518
|
+
// 读-改-写共享配置字段(keyRefs / nativeTools / argoEnv)时读实时快照,
|
|
519
|
+
// 不读渲染时快照:保存是 await 之后的异步写,连续两次操作都从渲染值出发
|
|
520
|
+
// 会把前一次的结果挤掉(丢一个转发勾选/工具勾选)。
|
|
521
|
+
const liveValue = () => {
|
|
522
|
+
const snapshot = typeof props.useArgoForm?.getSnapshot === 'function'
|
|
523
|
+
? props.useArgoForm.getSnapshot()
|
|
524
|
+
: undefined;
|
|
525
|
+
return snapshot && snapshot.value ? snapshot.value : value;
|
|
526
|
+
};
|
|
527
|
+
// describe 往返乱序防护:只认最新一次请求的响应(同官方 settings 页口径)。
|
|
528
|
+
const credSeq = useRef(0);
|
|
529
|
+
|
|
530
|
+
const dict = lang === 'en' ? DICT.en : DICT.zh;
|
|
531
|
+
const t = (key) => {
|
|
532
|
+
const text = dict[key] !== undefined ? dict[key] : DICT.zh[key];
|
|
533
|
+
return text !== undefined ? text : key;
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
useEffect(() => {
|
|
537
|
+
const off = locale && locale.subscribe
|
|
538
|
+
? locale.subscribe(() => {
|
|
539
|
+
const next = locale.getSnapshot && locale.getSnapshot().active === 'en' ? 'en' : 'zh';
|
|
540
|
+
setLang(next);
|
|
541
|
+
})
|
|
542
|
+
: undefined;
|
|
543
|
+
return typeof off === 'function' ? off : undefined;
|
|
544
|
+
}, [locale]);
|
|
545
|
+
|
|
546
|
+
useEffect(() => {
|
|
547
|
+
setDraft((previous) => {
|
|
548
|
+
const next = toDraft(value);
|
|
549
|
+
for (const key of dirty.current) {
|
|
550
|
+
if (Object.prototype.hasOwnProperty.call(previous, key)) next[key] = previous[key];
|
|
551
|
+
}
|
|
552
|
+
return next;
|
|
553
|
+
});
|
|
554
|
+
}, [state]);
|
|
555
|
+
|
|
556
|
+
const refreshCredentials = async () => {
|
|
557
|
+
if (!props.credentials || typeof props.credentials.describe !== 'function') return;
|
|
558
|
+
const seq = ++credSeq.current;
|
|
559
|
+
try {
|
|
560
|
+
const response = await props.credentials.describe(KEYS.map((entry) => entry.ref));
|
|
561
|
+
if (seq !== credSeq.current) return;
|
|
562
|
+
if (response && response.ok) setCredentials(response.value || {});
|
|
563
|
+
} catch {
|
|
564
|
+
/* 凭证服务暂不可用:保留上一次状态 */
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
useEffect(() => {
|
|
569
|
+
if (props.view === 'summary') return undefined;
|
|
570
|
+
void refreshCredentials();
|
|
571
|
+
const off = props.remote && typeof props.remote.$on === 'function'
|
|
572
|
+
? props.remote.$on('credentials/reference-updated', () => { void refreshCredentials(); })
|
|
573
|
+
: undefined;
|
|
574
|
+
return typeof off === 'function' ? off : undefined;
|
|
575
|
+
}, [state]);
|
|
576
|
+
|
|
577
|
+
const edit = (key, text) => {
|
|
578
|
+
dirty.current.add(key);
|
|
579
|
+
setDraft((previous) => ({ ...previous, [key]: text }));
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
const report = (ok, text) => {
|
|
583
|
+
setError(!ok);
|
|
584
|
+
setMessage(text);
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
const write = async (ops) => {
|
|
588
|
+
if (disabled) return false;
|
|
589
|
+
setBusy('save');
|
|
590
|
+
try {
|
|
591
|
+
const ok = await props.mutate(ops);
|
|
592
|
+
report(ok, ok ? t('saved') : t('refused'));
|
|
593
|
+
return ok;
|
|
594
|
+
} catch (cause) {
|
|
595
|
+
report(false, cause && cause.message ? cause.message : String(cause));
|
|
596
|
+
return false;
|
|
597
|
+
} finally {
|
|
598
|
+
setBusy('');
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
const setField = async (field, next) => {
|
|
603
|
+
if (disabled) return;
|
|
604
|
+
setBusy(field);
|
|
605
|
+
try {
|
|
606
|
+
const ok = await props.set(field, next);
|
|
607
|
+
report(ok, ok ? t('saved') : t('refused'));
|
|
608
|
+
} catch (cause) {
|
|
609
|
+
report(false, cause && cause.message ? cause.message : String(cause));
|
|
610
|
+
} finally {
|
|
611
|
+
setBusy('');
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
const atLeast = (text, fallback, min) => Math.max(min, asNumber(text, fallback));
|
|
616
|
+
const clearDirty = (keys) => {
|
|
617
|
+
for (const key of keys) dirty.current.delete(key);
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
const saveAccess = async () => {
|
|
621
|
+
const args = lines(draft.argsText);
|
|
622
|
+
const ok = await write([
|
|
623
|
+
{ op: 'set', path: ['searchCommand'], value: draft.command.trim() || DEFAULTS.searchCommand },
|
|
624
|
+
{ op: 'set', path: ['searchArgs'], value: args.length ? args : DEFAULTS.searchArgs },
|
|
625
|
+
{ op: 'set', path: ['searchTimeoutMs'], value: atLeast(draft.searchTimeoutMs, DEFAULTS.searchTimeoutMs, 1000) },
|
|
626
|
+
{ op: 'set', path: ['fetchTimeoutMs'], value: atLeast(draft.fetchTimeoutMs, DEFAULTS.fetchTimeoutMs, 1000) },
|
|
627
|
+
// argo_fetch 工具 schema 上限 50000(native-tools.mjs 由 mcp_tools.py
|
|
628
|
+
// 生成):配更大会被 argo clamp 回 50000,面板显示值与实效不符,这里就地截断。
|
|
629
|
+
{ op: 'set', path: ['fetchMaxChars'], value: Math.min(50_000, atLeast(draft.fetchMaxChars, DEFAULTS.fetchMaxChars, 500)) },
|
|
630
|
+
{ op: 'set', path: ['searchIdleMs'], value: atLeast(draft.searchIdleMs, DEFAULTS.searchIdleMs, 0) },
|
|
631
|
+
{ op: 'set', path: ['searchConcurrency'], value: atLeast(draft.searchConcurrency, DEFAULTS.searchConcurrency, 1) },
|
|
632
|
+
]);
|
|
633
|
+
if (ok) clearDirty(['command', 'argsText', 'searchTimeoutMs', 'fetchTimeoutMs', 'fetchMaxChars', 'searchIdleMs', 'searchConcurrency']);
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
const saveTools = async () => {
|
|
637
|
+
const ok = await write([
|
|
638
|
+
{ op: 'set', path: ['nativeTimeoutMs'], value: atLeast(draft.nativeTimeoutMs, DEFAULTS.nativeTimeoutMs, 1000) },
|
|
639
|
+
{ op: 'set', path: ['nativeMaxChars'], value: atLeast(draft.nativeMaxChars, DEFAULTS.nativeMaxChars, 500) },
|
|
640
|
+
]);
|
|
641
|
+
if (ok) clearDirty(['nativeTimeoutMs', 'nativeMaxChars']);
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
const saveResearch = async () => {
|
|
645
|
+
const workers = atLeast(draft.defaultWorkers, DEFAULTS.defaultWorkers, 1);
|
|
646
|
+
const maxWorkers = Math.max(workers, atLeast(draft.maxWorkers, DEFAULTS.maxWorkers, 1));
|
|
647
|
+
const ok = await write([
|
|
648
|
+
{ op: 'set', path: ['defaultWorkers'], value: workers },
|
|
649
|
+
{ op: 'set', path: ['maxWorkers'], value: maxWorkers },
|
|
650
|
+
{ op: 'set', path: ['maxTracks'], value: atLeast(draft.maxTracks, DEFAULTS.maxTracks, 2) },
|
|
651
|
+
{ op: 'set', path: ['maxSourcesPerTrack'], value: atLeast(draft.maxSourcesPerTrack, DEFAULTS.maxSourcesPerTrack, 1) },
|
|
652
|
+
{ op: 'set', path: ['workerMaxTokens'], value: atLeast(draft.workerMaxTokens, DEFAULTS.workerMaxTokens, 512) },
|
|
653
|
+
{ op: 'set', path: ['synthesisMaxTokens'], value: atLeast(draft.synthesisMaxTokens, DEFAULTS.synthesisMaxTokens, 512) },
|
|
654
|
+
{ op: 'set', path: ['reportDir'], value: draft.reportDir.trim() },
|
|
655
|
+
]);
|
|
656
|
+
if (ok) clearDirty(['defaultWorkers', 'maxWorkers', 'maxTracks', 'maxSourcesPerTrack', 'workerMaxTokens', 'synthesisMaxTokens', 'reportDir']);
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const saveAdvanced = async () => {
|
|
660
|
+
const ok = await write([
|
|
661
|
+
{ op: 'set', path: ['childToolAllow'], value: lines(draft.allowText) },
|
|
662
|
+
{ op: 'set', path: ['childToolDeny'], value: lines(draft.denyText) },
|
|
663
|
+
{ op: 'set', path: ['keyRefs'], value: lines(draft.keyRefsText) },
|
|
664
|
+
]);
|
|
665
|
+
if (ok) clearDirty(['allowText', 'denyText', 'keyRefsText']);
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
const toggleTool = (id) => {
|
|
669
|
+
const current = new Set(asArray(liveValue().nativeTools));
|
|
670
|
+
if (current.has(id)) current.delete(id);
|
|
671
|
+
else current.add(id);
|
|
672
|
+
const ordered = TOOLS.map((tool) => tool.id).filter((toolId) => current.has(toolId));
|
|
673
|
+
for (const extra of current) if (!ordered.includes(extra)) ordered.push(extra);
|
|
674
|
+
void setField('nativeTools', ordered);
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
const saveKey = async (ref) => {
|
|
678
|
+
const key = asString(keyDraft[ref]).trim();
|
|
679
|
+
if (!key || !props.credentials) return;
|
|
680
|
+
setKeyBusy(ref);
|
|
681
|
+
try {
|
|
682
|
+
const response = await props.credentials.set(ref, key);
|
|
683
|
+
if (!response || !response.ok) {
|
|
684
|
+
throw new Error(response && response.error ? response.error.message : t('keyUnavailable'));
|
|
685
|
+
}
|
|
686
|
+
setKeyDraft((previous) => ({ ...previous, [ref]: '' }));
|
|
687
|
+
const refs = asArray(liveValue().keyRefs);
|
|
688
|
+
if (!refs.includes(ref)) await props.set('keyRefs', [...refs, ref]);
|
|
689
|
+
report(true, t('keySaved'));
|
|
690
|
+
await refreshCredentials();
|
|
691
|
+
} catch (cause) {
|
|
692
|
+
report(false, cause && cause.message ? cause.message : String(cause));
|
|
693
|
+
} finally {
|
|
694
|
+
setKeyBusy('');
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
const clearKey = async (ref) => {
|
|
699
|
+
if (!props.credentials) return;
|
|
700
|
+
setKeyBusy(ref);
|
|
701
|
+
try {
|
|
702
|
+
const response = await props.credentials.unset(ref);
|
|
703
|
+
if (!response || !response.ok) {
|
|
704
|
+
throw new Error(response && response.error ? response.error.message : t('keyUnavailable'));
|
|
705
|
+
}
|
|
706
|
+
report(true, t('keyCleared'));
|
|
707
|
+
await refreshCredentials();
|
|
708
|
+
} catch (cause) {
|
|
709
|
+
report(false, cause && cause.message ? cause.message : String(cause));
|
|
710
|
+
} finally {
|
|
711
|
+
setKeyBusy('');
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
const setForward = (ref, on) => {
|
|
716
|
+
const refs = new Set(asArray(liveValue().keyRefs));
|
|
717
|
+
if (on) refs.add(ref);
|
|
718
|
+
else refs.delete(ref);
|
|
719
|
+
void setField('keyRefs', [...refs]);
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
// ── argoEnv 引擎开关:KEY=VALUE 行 ↔ 控件 ──
|
|
723
|
+
// argoEnv 行集合是单一真源:每次写入都基于实时快照读-改-写,只碰白名单
|
|
724
|
+
// 内的名字。白名单外的行**被清掉**,不是原样保留——apply 期对白名单外
|
|
725
|
+
// 名字是 loud fail(整个插件加载失败),直接写 profile patch 写错的开关
|
|
726
|
+
// 会让人卡在「改不回来」;这里任何一次面板写入都顺手清毒,是最容易碰到
|
|
727
|
+
// 的恢复入口。
|
|
728
|
+
const envMap = () => {
|
|
729
|
+
const map = {};
|
|
730
|
+
for (const entry of asArray(liveValue().argoEnv)) {
|
|
731
|
+
const eq = entry.indexOf('=');
|
|
732
|
+
if (eq > 0) map[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
733
|
+
}
|
|
734
|
+
return map;
|
|
735
|
+
};
|
|
736
|
+
// 与 argo 的 env_flag 判定对齐:空串/0/false/no/off/n/disable/disabled/none 算关。
|
|
737
|
+
const ENV_FALSY = ['', '0', 'false', 'no', 'off', 'n', 'disable', 'disabled', 'none'];
|
|
738
|
+
const envBoolState = (spec, map) => {
|
|
739
|
+
const raw = map[spec.name];
|
|
740
|
+
const on = raw === undefined ? spec.def === true : !ENV_FALSY.includes(String(raw).trim().toLowerCase());
|
|
741
|
+
return spec.invert ? !on : on;
|
|
742
|
+
};
|
|
743
|
+
const writeEnvLines = async (updates) => {
|
|
744
|
+
const map = envMap();
|
|
745
|
+
for (const name of Object.keys(updates)) {
|
|
746
|
+
const value = updates[name];
|
|
747
|
+
if (value === null || value === undefined || value === '') delete map[name];
|
|
748
|
+
else map[name] = value;
|
|
749
|
+
}
|
|
750
|
+
const lines = Object.keys(map)
|
|
751
|
+
.filter((name) => ENV_NAMES.has(name))
|
|
752
|
+
.map((name) => `${name}=${map[name]}`);
|
|
753
|
+
return props.set('argoEnv', lines);
|
|
754
|
+
};
|
|
755
|
+
const toggleEnv = (spec, on) => {
|
|
756
|
+
if (disabled) return;
|
|
757
|
+
// invert 语义反转:UI「开」对 invert 开关意味着 env 取 falsy
|
|
758
|
+
// (缓存开 → ARGO_NO_CACHE=0;关 → =1)。
|
|
759
|
+
const value = on === (spec.invert === true) ? '0' : '1';
|
|
760
|
+
void writeEnvLines({ [spec.name]: value })
|
|
761
|
+
.then((ok) => report(ok, ok ? t('saved') : t('refused')))
|
|
762
|
+
.catch((cause) => report(false, cause && cause.message ? cause.message : String(cause)));
|
|
763
|
+
};
|
|
764
|
+
const saveEnvGroup = async (group) => {
|
|
765
|
+
if (disabled) return;
|
|
766
|
+
const updates = {};
|
|
767
|
+
for (const spec of ENGINE_TOGGLES) {
|
|
768
|
+
if (spec.group !== group || spec.kind === 'bool') continue;
|
|
769
|
+
const raw = envDraft[spec.name];
|
|
770
|
+
if (raw === undefined) continue;
|
|
771
|
+
const text = String(raw).trim();
|
|
772
|
+
if (text === '') {
|
|
773
|
+
updates[spec.name] = null;
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
if (spec.kind === 'num') {
|
|
777
|
+
const n = Number(text);
|
|
778
|
+
if (!Number.isFinite(n) || n < spec.min || n > spec.max) {
|
|
779
|
+
report(false, `${lang === 'en' ? spec.en : spec.zh}: ${t('envRange').replace('{min}', String(spec.min)).replace('{max}', String(spec.max))}`);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
updates[spec.name] = text;
|
|
784
|
+
}
|
|
785
|
+
if (Object.keys(updates).length === 0) return;
|
|
786
|
+
setEnvBusy(group);
|
|
787
|
+
try {
|
|
788
|
+
const ok = await writeEnvLines(updates);
|
|
789
|
+
report(ok, ok ? t('saved') : t('refused'));
|
|
790
|
+
if (ok) setEnvDraft({});
|
|
791
|
+
} catch (cause) {
|
|
792
|
+
report(false, cause && cause.message ? cause.message : String(cause));
|
|
793
|
+
} finally {
|
|
794
|
+
setEnvBusy('');
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
if (props.view === 'summary') {
|
|
799
|
+
const summary = t('summary')
|
|
800
|
+
.replace('{search}', value.searchProviderEnabled !== false ? t('on') : t('off'))
|
|
801
|
+
.replace('{fetch}', value.fetchProviderEnabled !== false ? t('on') : t('off'))
|
|
802
|
+
.replace('{tools}', String(asArray(value.nativeTools).length))
|
|
803
|
+
.replace('{workers}', String(asNumber(value.defaultWorkers, DEFAULTS.defaultWorkers)))
|
|
804
|
+
.replace('{max}', String(asNumber(value.maxWorkers, DEFAULTS.maxWorkers)));
|
|
805
|
+
return summary;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (!ready) {
|
|
809
|
+
return h('div', { style: S.wrap }, [
|
|
810
|
+
h('h2', { key: 't', style: S.title }, t('title')),
|
|
811
|
+
h('p', { key: 'p', style: S.hint }, state && state.status === 'unavailable' ? t('unavailable') : t('loading')),
|
|
812
|
+
]);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const tabButton = (id, label) => h('button', {
|
|
816
|
+
key: id,
|
|
817
|
+
type: 'button',
|
|
818
|
+
style: S.tab(tab === id),
|
|
819
|
+
onClick: () => setTab(id),
|
|
820
|
+
}, label);
|
|
821
|
+
|
|
822
|
+
const statusLine = message
|
|
823
|
+
? h('p', { style: error ? S.error : S.notice, role: 'status' }, message)
|
|
824
|
+
: h('p', { style: S.status }, disabled ? t('readonly') : '');
|
|
825
|
+
|
|
826
|
+
const coreTools = TOOLS.filter((tool) => tool.group === 'core');
|
|
827
|
+
const longTools = TOOLS.filter((tool) => tool.group === 'long');
|
|
828
|
+
const toolCheckbox = (tool) => h('label', { key: tool.id, style: S.check }, [
|
|
829
|
+
h('input', {
|
|
830
|
+
key: 'i',
|
|
831
|
+
type: 'checkbox',
|
|
832
|
+
checked: asArray(value.nativeTools).includes(tool.id),
|
|
833
|
+
disabled,
|
|
834
|
+
onChange: () => toggleTool(tool.id),
|
|
835
|
+
}),
|
|
836
|
+
h('span', { key: 't' }, [
|
|
837
|
+
h('span', { key: 'n', style: S.mono }, tool.id),
|
|
838
|
+
h('span', { key: 'd', style: S.sub }, ' ' + (lang === 'en' ? tool.en : tool.zh)),
|
|
839
|
+
]),
|
|
840
|
+
]);
|
|
841
|
+
|
|
842
|
+
const keyRow = (entry) => {
|
|
843
|
+
const info = credentials[entry.ref];
|
|
844
|
+
const configured = info && info.configured === true;
|
|
845
|
+
const writable = !info || info.writable !== false;
|
|
846
|
+
const forwarded = asArray(value.keyRefs).includes(entry.ref);
|
|
847
|
+
// 来源层:回答「这把 key 从哪来」。保存写凭证库文件层,优先级高于
|
|
848
|
+
// 启动环境与 .env,所以来源不是 file 时提示保存后会覆盖当前层。
|
|
849
|
+
const sourceLabel = configured && typeof info.source === 'string'
|
|
850
|
+
? t('keySource' + info.source.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(''))
|
|
851
|
+
: '';
|
|
852
|
+
const statusParts = [configured ? t('configured') : t('notConfigured')];
|
|
853
|
+
if (sourceLabel && sourceLabel !== 'keySource' + info.source) statusParts.push(sourceLabel);
|
|
854
|
+
if (configured && !writable) statusParts.push(t('keyReadonly'));
|
|
855
|
+
if (configured && writable && info.source !== undefined && info.source !== 'file') statusParts.push(t('keyOverrideHint'));
|
|
856
|
+
return h('div', { key: entry.ref, style: S.keyRow }, [
|
|
857
|
+
h('div', { key: 'head', style: S.keyHead }, [
|
|
858
|
+
h('span', { key: 'n', style: S.label }, [
|
|
859
|
+
h('span', { key: 'dot', style: S.dot(configured) }),
|
|
860
|
+
lang === 'en' ? entry.en : entry.zh,
|
|
861
|
+
]),
|
|
862
|
+
h('span', { key: 'ref', style: S.code }, entry.ref),
|
|
863
|
+
h('span', { key: 's', style: S.sub }, statusParts.join(' · ')),
|
|
864
|
+
entry.ref === 'DEEPSEEK_API_KEY'
|
|
865
|
+
? h('span', { key: 'shared', style: S.sub }, t('deepseekShared'))
|
|
866
|
+
: null,
|
|
867
|
+
]),
|
|
868
|
+
h('div', { key: 'body', style: { display: 'flex', flexDirection: 'column', gap: 6 } }, [
|
|
869
|
+
h('input', {
|
|
870
|
+
key: 'i',
|
|
871
|
+
type: 'password',
|
|
872
|
+
autoComplete: 'off',
|
|
873
|
+
spellCheck: false,
|
|
874
|
+
style: S.input,
|
|
875
|
+
value: asString(keyDraft[entry.ref]),
|
|
876
|
+
placeholder: configured ? t('keyPlaceholderSet') : t('keyPlaceholderEmpty'),
|
|
877
|
+
disabled: disabled || !props.credentials || !writable,
|
|
878
|
+
onChange: (event) => setKeyDraft((previous) => ({ ...previous, [entry.ref]: event.target.value })),
|
|
879
|
+
}),
|
|
880
|
+
h('div', { key: 'a', style: S.actions }, [
|
|
881
|
+
h('label', { key: 'f', style: S.check }, [
|
|
882
|
+
h('input', {
|
|
883
|
+
key: 'i',
|
|
884
|
+
type: 'checkbox',
|
|
885
|
+
checked: forwarded,
|
|
886
|
+
disabled,
|
|
887
|
+
onChange: (event) => setForward(entry.ref, event.target.checked),
|
|
888
|
+
}),
|
|
889
|
+
h('span', { key: 't' }, t('forward')),
|
|
890
|
+
]),
|
|
891
|
+
h('span', { key: 'sp', style: { flex: 1 } }),
|
|
892
|
+
h('button', {
|
|
893
|
+
key: 's',
|
|
894
|
+
type: 'button',
|
|
895
|
+
style: S.btn,
|
|
896
|
+
disabled: disabled || !writable || keyBusy === entry.ref || asString(keyDraft[entry.ref]).trim() === '',
|
|
897
|
+
onClick: () => { void saveKey(entry.ref); },
|
|
898
|
+
}, keyBusy === entry.ref ? t('saving') : t('saveKey')),
|
|
899
|
+
configured ? h('button', {
|
|
900
|
+
key: 'c',
|
|
901
|
+
type: 'button',
|
|
902
|
+
style: S.btn,
|
|
903
|
+
disabled: disabled || !writable || keyBusy === entry.ref,
|
|
904
|
+
onClick: () => { void clearKey(entry.ref); },
|
|
905
|
+
}, t('clearKey')) : null,
|
|
906
|
+
]),
|
|
907
|
+
]),
|
|
908
|
+
]);
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
const commonKeys = KEYS.filter((entry) => entry.common);
|
|
912
|
+
const visibleKeys = showAllKeys ? KEYS : commonKeys;
|
|
913
|
+
|
|
914
|
+
const panel = [];
|
|
915
|
+
|
|
916
|
+
if (tab === 'access') {
|
|
917
|
+
panel.push(h(Card, { key: 'seam', title: t('accessTitle') }, [
|
|
918
|
+
h(Switch, {
|
|
919
|
+
key: 's',
|
|
920
|
+
checked: value.searchProviderEnabled !== false,
|
|
921
|
+
disabled,
|
|
922
|
+
label: t('searchSeam'),
|
|
923
|
+
hint: t('searchSeamHint'),
|
|
924
|
+
onChange: (next) => { void setField('searchProviderEnabled', next); },
|
|
925
|
+
}),
|
|
926
|
+
h(Switch, {
|
|
927
|
+
key: 'f',
|
|
928
|
+
checked: value.fetchProviderEnabled !== false,
|
|
929
|
+
disabled,
|
|
930
|
+
label: t('fetchSeam'),
|
|
931
|
+
hint: t('fetchSeamHint'),
|
|
932
|
+
onChange: (next) => { void setField('fetchProviderEnabled', next); },
|
|
933
|
+
}),
|
|
934
|
+
]));
|
|
935
|
+
panel.push(h(Card, { key: 'entry', title: t('entryTitle'), hint: t('entryHint') }, [
|
|
936
|
+
h(Field, { key: 'c', label: t('command') }, h('input', {
|
|
937
|
+
type: 'text',
|
|
938
|
+
style: { ...S.input, ...S.mono },
|
|
939
|
+
value: draft.command,
|
|
940
|
+
disabled,
|
|
941
|
+
spellCheck: false,
|
|
942
|
+
onChange: (event) => edit('command', event.target.value),
|
|
943
|
+
})),
|
|
944
|
+
h(Field, { key: 'a', label: t('args') }, h('textarea', {
|
|
945
|
+
style: { ...S.input, ...S.textarea, ...S.mono },
|
|
946
|
+
value: draft.argsText,
|
|
947
|
+
disabled,
|
|
948
|
+
spellCheck: false,
|
|
949
|
+
onChange: (event) => edit('argsText', event.target.value),
|
|
950
|
+
})),
|
|
951
|
+
h('div', { key: 'g', style: S.grid }, [
|
|
952
|
+
h(NumberField, {
|
|
953
|
+
key: 'st', label: t('searchTimeout'), min: 1000, value: draft.searchTimeoutMs, disabled,
|
|
954
|
+
onChange: (next) => edit('searchTimeoutMs', next),
|
|
955
|
+
}),
|
|
956
|
+
h(NumberField, {
|
|
957
|
+
key: 'ft', label: t('fetchTimeout'), min: 1000, value: draft.fetchTimeoutMs, disabled,
|
|
958
|
+
onChange: (next) => edit('fetchTimeoutMs', next),
|
|
959
|
+
}),
|
|
960
|
+
h(NumberField, {
|
|
961
|
+
key: 'fc', label: t('fetchChars'), min: 500, step: 500, value: draft.fetchMaxChars, disabled,
|
|
962
|
+
onChange: (next) => edit('fetchMaxChars', next),
|
|
963
|
+
}),
|
|
964
|
+
h(NumberField, {
|
|
965
|
+
key: 'id', label: t('idle'), min: 0, step: 1000, value: draft.searchIdleMs, disabled,
|
|
966
|
+
onChange: (next) => edit('searchIdleMs', next),
|
|
967
|
+
}),
|
|
968
|
+
h(NumberField, {
|
|
969
|
+
key: 'cc', label: t('concurrency'), min: 1, value: draft.searchConcurrency, disabled,
|
|
970
|
+
onChange: (next) => edit('searchConcurrency', next),
|
|
971
|
+
}),
|
|
972
|
+
]),
|
|
973
|
+
h('div', { key: 'a2', style: S.actions }, [
|
|
974
|
+
h(PrimaryButton, { primary: true, disabled: disabled || busy === 'save', onClick: () => { void saveAccess(); } },
|
|
975
|
+
busy === 'save' ? t('saving') : t('save')),
|
|
976
|
+
]),
|
|
977
|
+
]));
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if (tab === 'tools') {
|
|
981
|
+
panel.push(h(Card, { key: 'tools', title: t('toolsTitle'), hint: t('toolsHint') }, [
|
|
982
|
+
h('div', { key: 'core', style: { display: 'flex', flexDirection: 'column', gap: 4 } }, [
|
|
983
|
+
h('span', { key: 'l', style: S.label }, t('coreTools')),
|
|
984
|
+
h('div', { key: 'g', style: S.toolGrid }, coreTools.map(toolCheckbox)),
|
|
985
|
+
]),
|
|
986
|
+
h('div', { key: 'div', style: S.divider }),
|
|
987
|
+
h('div', { key: 'long', style: { display: 'flex', flexDirection: 'column', gap: 4 } }, [
|
|
988
|
+
h('span', { key: 'l', style: S.label }, t('longTools')),
|
|
989
|
+
h('div', { key: 'g', style: S.toolGrid }, longTools.map(toolCheckbox)),
|
|
990
|
+
]),
|
|
991
|
+
h('div', { key: 'g2', style: S.grid }, [
|
|
992
|
+
h(NumberField, {
|
|
993
|
+
key: 'nt', label: t('nativeTimeout'), min: 1000, value: draft.nativeTimeoutMs, disabled,
|
|
994
|
+
onChange: (next) => edit('nativeTimeoutMs', next),
|
|
995
|
+
}),
|
|
996
|
+
h(NumberField, {
|
|
997
|
+
key: 'nc', label: t('nativeChars'), min: 500, step: 500, value: draft.nativeMaxChars, disabled,
|
|
998
|
+
onChange: (next) => edit('nativeMaxChars', next),
|
|
999
|
+
}),
|
|
1000
|
+
]),
|
|
1001
|
+
h('div', { key: 'a', style: S.actions }, [
|
|
1002
|
+
h(PrimaryButton, { primary: true, disabled: disabled || busy === 'save', onClick: () => { void saveTools(); } },
|
|
1003
|
+
busy === 'save' ? t('saving') : t('save')),
|
|
1004
|
+
]),
|
|
1005
|
+
]));
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (tab === 'engines') {
|
|
1009
|
+
const map = envMap();
|
|
1010
|
+
const groups = ['cache', 'fetch', 'semantic', 'engines', 'net', 'local', 'misc'];
|
|
1011
|
+
const groupTitle = (group) => t('group' + group.charAt(0).toUpperCase() + group.slice(1));
|
|
1012
|
+
panel.push(h('p', { key: 'hint', style: S.hint }, t('enginesHint')));
|
|
1013
|
+
for (const group of groups) {
|
|
1014
|
+
const specs = ENGINE_TOGGLES.filter((spec) => spec.group === group);
|
|
1015
|
+
if (specs.length === 0) continue;
|
|
1016
|
+
const bools = specs.filter((spec) => spec.kind === 'bool');
|
|
1017
|
+
const fields = specs.filter((spec) => spec.kind !== 'bool');
|
|
1018
|
+
const children = [];
|
|
1019
|
+
for (const spec of bools) {
|
|
1020
|
+
// 未配置时显示 argo 默认态并标注「默认」,配置过的显示自定义值,
|
|
1021
|
+
// 让「这一行到底生不生效」在面板上一眼可辨。
|
|
1022
|
+
const custom = map[spec.name] !== undefined;
|
|
1023
|
+
const suffix = custom ? '' : (lang === 'en' ? ` (${t('envDefault')})` : `(${t('envDefault')})`);
|
|
1024
|
+
children.push(h(Switch, {
|
|
1025
|
+
key: spec.name,
|
|
1026
|
+
checked: envBoolState(spec, map),
|
|
1027
|
+
disabled,
|
|
1028
|
+
label: (lang === 'en' ? spec.en : spec.zh) + suffix,
|
|
1029
|
+
hint: lang === 'en' ? spec.hintEn : spec.hintZh,
|
|
1030
|
+
onChange: (next) => toggleEnv(spec, next),
|
|
1031
|
+
}));
|
|
1032
|
+
}
|
|
1033
|
+
if (fields.length > 0) {
|
|
1034
|
+
const fieldNodes = fields.map((spec) => {
|
|
1035
|
+
const drafted = envDraft[spec.name];
|
|
1036
|
+
const current = drafted !== undefined
|
|
1037
|
+
? drafted
|
|
1038
|
+
: (map[spec.name] !== undefined ? map[spec.name] : (spec.kind === 'num' ? String(spec.def) : ''));
|
|
1039
|
+
const control = spec.kind === 'num'
|
|
1040
|
+
? h('input', {
|
|
1041
|
+
key: 'i',
|
|
1042
|
+
type: 'number',
|
|
1043
|
+
style: S.input,
|
|
1044
|
+
value: current,
|
|
1045
|
+
min: spec.min,
|
|
1046
|
+
max: spec.max,
|
|
1047
|
+
step: spec.step ?? 1,
|
|
1048
|
+
disabled,
|
|
1049
|
+
onChange: (event) => setEnvDraft((previous) => ({ ...previous, [spec.name]: event.target.value })),
|
|
1050
|
+
})
|
|
1051
|
+
: h('input', {
|
|
1052
|
+
key: 'i',
|
|
1053
|
+
type: 'text',
|
|
1054
|
+
style: { ...S.input, ...S.mono },
|
|
1055
|
+
value: current,
|
|
1056
|
+
disabled,
|
|
1057
|
+
spellCheck: false,
|
|
1058
|
+
onChange: (event) => setEnvDraft((previous) => ({ ...previous, [spec.name]: event.target.value })),
|
|
1059
|
+
});
|
|
1060
|
+
return h(Field, {
|
|
1061
|
+
key: spec.name,
|
|
1062
|
+
label: lang === 'en' ? spec.en : spec.zh,
|
|
1063
|
+
hint: lang === 'en' ? spec.hintEn : spec.hintZh,
|
|
1064
|
+
}, control);
|
|
1065
|
+
});
|
|
1066
|
+
children.push(h('div', { key: 'fields', style: S.grid }, fieldNodes));
|
|
1067
|
+
children.push(h('div', { key: 'save', style: S.actions }, [
|
|
1068
|
+
h(PrimaryButton, {
|
|
1069
|
+
key: 'b',
|
|
1070
|
+
primary: true,
|
|
1071
|
+
disabled: disabled || envBusy === group,
|
|
1072
|
+
onClick: () => { void saveEnvGroup(group); },
|
|
1073
|
+
}, envBusy === group ? t('saving') : t('save')),
|
|
1074
|
+
]));
|
|
1075
|
+
}
|
|
1076
|
+
panel.push(h(Card, { key: group, title: groupTitle(group) }, children));
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
if (tab === 'keys') {
|
|
1081
|
+
panel.push(h(Card, { key: 'keys', title: t('keysTitle'), hint: t('keysHint') }, [
|
|
1082
|
+
h('span', { key: 'l', style: S.label }, t('commonKeys')),
|
|
1083
|
+
...visibleKeys.map(keyRow),
|
|
1084
|
+
h('div', { key: 'a', style: S.actions }, [
|
|
1085
|
+
h('button', {
|
|
1086
|
+
key: 'more',
|
|
1087
|
+
type: 'button',
|
|
1088
|
+
style: S.btn,
|
|
1089
|
+
onClick: () => setShowAllKeys((previous) => !previous),
|
|
1090
|
+
}, showAllKeys ? t('showLess') : t('showMore')),
|
|
1091
|
+
props.credentials ? null : h('span', { key: 'u', style: S.sub }, t('keyUnavailable')),
|
|
1092
|
+
]),
|
|
1093
|
+
]));
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
if (tab === 'research') {
|
|
1097
|
+
panel.push(h(Card, { key: 'research', title: t('researchTitle') }, [
|
|
1098
|
+
h('div', { key: 'g', style: S.grid }, [
|
|
1099
|
+
h(NumberField, {
|
|
1100
|
+
key: 'dw', label: t('workers'), min: 1, value: draft.defaultWorkers, disabled,
|
|
1101
|
+
onChange: (next) => edit('defaultWorkers', next),
|
|
1102
|
+
}),
|
|
1103
|
+
h(NumberField, {
|
|
1104
|
+
key: 'mw', label: t('maxWorkers'), min: 1, value: draft.maxWorkers, disabled,
|
|
1105
|
+
onChange: (next) => edit('maxWorkers', next),
|
|
1106
|
+
}),
|
|
1107
|
+
h(NumberField, {
|
|
1108
|
+
key: 'mt', label: t('tracks'), min: 2, value: draft.maxTracks, disabled,
|
|
1109
|
+
onChange: (next) => edit('maxTracks', next),
|
|
1110
|
+
}),
|
|
1111
|
+
h(NumberField, {
|
|
1112
|
+
key: 'ms', label: t('sources'), min: 1, value: draft.maxSourcesPerTrack, disabled,
|
|
1113
|
+
onChange: (next) => edit('maxSourcesPerTrack', next),
|
|
1114
|
+
}),
|
|
1115
|
+
h(NumberField, {
|
|
1116
|
+
key: 'wt', label: t('workerTokens'), min: 512, step: 512, value: draft.workerMaxTokens, disabled,
|
|
1117
|
+
onChange: (next) => edit('workerMaxTokens', next),
|
|
1118
|
+
}),
|
|
1119
|
+
h(NumberField, {
|
|
1120
|
+
key: 'st', label: t('synthTokens'), min: 512, step: 512, value: draft.synthesisMaxTokens, disabled,
|
|
1121
|
+
onChange: (next) => edit('synthesisMaxTokens', next),
|
|
1122
|
+
}),
|
|
1123
|
+
]),
|
|
1124
|
+
h(Field, { key: 'd', label: t('reportDir') }, h('input', {
|
|
1125
|
+
type: 'text',
|
|
1126
|
+
style: { ...S.input, ...S.mono },
|
|
1127
|
+
value: draft.reportDir,
|
|
1128
|
+
disabled,
|
|
1129
|
+
spellCheck: false,
|
|
1130
|
+
onChange: (event) => edit('reportDir', event.target.value),
|
|
1131
|
+
})),
|
|
1132
|
+
h('div', { key: 'a', style: S.actions }, [
|
|
1133
|
+
h(PrimaryButton, { primary: true, disabled: disabled || busy === 'save', onClick: () => { void saveResearch(); } },
|
|
1134
|
+
busy === 'save' ? t('saving') : t('save')),
|
|
1135
|
+
]),
|
|
1136
|
+
]));
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
if (tab === 'advanced') {
|
|
1140
|
+
panel.push(h(Card, { key: 'adv', title: t('advancedTitle') }, [
|
|
1141
|
+
h(Field, { key: 'allow', label: t('allow'), hint: t('allowHint') }, h('textarea', {
|
|
1142
|
+
style: { ...S.input, ...S.textarea, ...S.mono },
|
|
1143
|
+
value: draft.allowText,
|
|
1144
|
+
disabled,
|
|
1145
|
+
spellCheck: false,
|
|
1146
|
+
onChange: (event) => edit('allowText', event.target.value),
|
|
1147
|
+
})),
|
|
1148
|
+
h(Field, { key: 'deny', label: t('deny') }, h('textarea', {
|
|
1149
|
+
style: { ...S.input, ...S.textarea, ...S.mono },
|
|
1150
|
+
value: draft.denyText,
|
|
1151
|
+
disabled,
|
|
1152
|
+
spellCheck: false,
|
|
1153
|
+
onChange: (event) => edit('denyText', event.target.value),
|
|
1154
|
+
})),
|
|
1155
|
+
h(Field, { key: 'refs', label: t('keyRefs'), hint: t('keyRefsHint') }, h('textarea', {
|
|
1156
|
+
style: { ...S.input, ...S.textarea, ...S.mono },
|
|
1157
|
+
value: draft.keyRefsText,
|
|
1158
|
+
disabled,
|
|
1159
|
+
spellCheck: false,
|
|
1160
|
+
onChange: (event) => edit('keyRefsText', event.target.value),
|
|
1161
|
+
})),
|
|
1162
|
+
h('div', { key: 'a', style: S.actions }, [
|
|
1163
|
+
h(PrimaryButton, { primary: true, disabled: disabled || busy === 'save', onClick: () => { void saveAdvanced(); } },
|
|
1164
|
+
busy === 'save' ? t('saving') : t('save')),
|
|
1165
|
+
]),
|
|
1166
|
+
h('div', { key: 'div', style: S.divider }),
|
|
1167
|
+
h('div', { key: 'struct', style: { display: 'flex', flexDirection: 'column', gap: 4 } }, [
|
|
1168
|
+
h('span', { key: 'l', style: S.label }, t('structural')),
|
|
1169
|
+
h('span', { key: 'h', style: S.sub }, t('structuralHint')),
|
|
1170
|
+
h('span', { key: 'm', style: S.sub }, t('mcpHint')),
|
|
1171
|
+
]),
|
|
1172
|
+
]));
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
return h('div', { style: S.wrap }, [
|
|
1176
|
+
h('div', { key: 'head', style: { display: 'flex', flexDirection: 'column', gap: 4 } }, [
|
|
1177
|
+
h('h2', { key: 't', style: S.title }, t('title')),
|
|
1178
|
+
h('p', { key: 'i', style: S.hint }, t('intro')),
|
|
1179
|
+
]),
|
|
1180
|
+
h('div', { key: 'tabs', style: S.tabs }, [
|
|
1181
|
+
tabButton('access', t('tabAccess')),
|
|
1182
|
+
tabButton('tools', t('tabTools')),
|
|
1183
|
+
tabButton('engines', t('tabEngines')),
|
|
1184
|
+
tabButton('keys', t('tabKeys')),
|
|
1185
|
+
tabButton('research', t('tabResearch')),
|
|
1186
|
+
tabButton('advanced', t('tabAdvanced')),
|
|
1187
|
+
]),
|
|
1188
|
+
statusLine,
|
|
1189
|
+
...panel,
|
|
1190
|
+
]);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
const inject = ['slots', 'locale', 'configForms', 'remote', 'remote.credentials'];
|
|
1194
|
+
|
|
1195
|
+
function apply(ctx) {
|
|
1196
|
+
const form = ctx.configForms.get(NS);
|
|
1197
|
+
const locale = ctx.locale ?? (typeof ctx.get === 'function' ? ctx.get('locale') : undefined);
|
|
1198
|
+
const credentials = ctx.remote ? ctx.remote.credentials : undefined;
|
|
1199
|
+
const label = () => 'Argo 搜索';
|
|
1200
|
+
|
|
1201
|
+
const injected = () => ({
|
|
1202
|
+
hooks: { argoForm: form },
|
|
1203
|
+
set: (field, next) => form.set(field, next),
|
|
1204
|
+
mutate: (ops) => form.mutate(ops),
|
|
1205
|
+
credentials,
|
|
1206
|
+
remote: ctx.remote,
|
|
1207
|
+
locale,
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
ctx.effect(() => ctx.configForms.whileServed([NS], () => {
|
|
1211
|
+
const offs = [
|
|
1212
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
1213
|
+
name: 'settings.section',
|
|
1214
|
+
id: SECTION_ID,
|
|
1215
|
+
order: 26,
|
|
1216
|
+
label,
|
|
1217
|
+
inject: injected,
|
|
1218
|
+
}, ArgoPanel)),
|
|
1219
|
+
ctx.slots.inject('plugins.item', () => ctx.slots.register({
|
|
1220
|
+
name: 'plugins.item',
|
|
1221
|
+
id: SECTION_ID,
|
|
1222
|
+
order: 50,
|
|
1223
|
+
label,
|
|
1224
|
+
inject: injected,
|
|
1225
|
+
}, ArgoPanel)),
|
|
1226
|
+
];
|
|
1227
|
+
return () => {
|
|
1228
|
+
for (const off of offs) if (typeof off === 'function') off();
|
|
1229
|
+
};
|
|
1230
|
+
}), 'argo-dsh: settings page');
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
exports.apply = apply;
|
|
1234
|
+
exports.inject = inject;
|
|
1235
|
+
exports.ArgoPanel = ArgoPanel;
|
|
1236
|
+
// 静态数据导出供漂移门禁核对:TOOLS 必须与 native-tools.mjs(mcp_tools.py
|
|
1237
|
+
// 生成产物)同名同集合,ENGINE_TOGGLES 必须与 index.js 的 ARGO_ENV_SPECS
|
|
1238
|
+
// 同名同集合——两边任何一边增删而另一边没跟上,测试即红。
|
|
1239
|
+
exports.TOOLS = TOOLS;
|
|
1240
|
+
exports.ENGINE_TOGGLES = ENGINE_TOGGLES;
|
|
1241
|
+
|
|
1242
|
+
return module.exports;
|
|
1243
|
+
}
|
|
1244
|
+
});
|