argo-search 2.9.7 → 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.
- package/README.en.md +58 -145
- package/README.es.md +58 -145
- package/README.ja.md +64 -151
- package/README.ko.md +63 -150
- package/README.md +65 -154
- package/SKILL.md +3 -3
- package/assets/readme/hero.svg +2 -2
- package/backends/domain_profiles.json +48 -0
- package/backends/engine_registry.yaml +118 -4
- package/backends/quota_profiles.json +92 -12
- package/backends/source_types_cn.json +27 -1
- package/bin/argo +74 -5
- package/bin/argo.js +33 -2
- package/config.yaml +219 -60
- package/docs/ENGINE_CATALOG.md +50 -39
- package/docs/MCP_SETUP.md +3 -1
- package/docs/RELEASE_NOTES_v2.9.8.md +73 -0
- package/engines/specs/cfets.yaml +41 -0
- package/engines/specs/defillama.yaml +38 -0
- package/engines/specs/firecrawl.yaml +1 -1
- package/engines/specs/fxtwitter.yaml +5 -0
- package/engines/specs/gleif.yaml +55 -0
- package/engines/specs/hkex.yaml +40 -0
- package/engines/specs/keenable_free.yaml +36 -0
- package/engines/specs/shfe.yaml +39 -0
- package/engines/specs/stackexchange.yaml +2 -2
- package/engines/specs/tavily_free.yaml +56 -0
- package/engines/specs/yuanbao.yaml +93 -0
- package/mcp/clients.yaml +32 -0
- package/package.json +6 -2
- package/packages/dsh-plugin/README.md +23 -5
- package/packages/dsh-plugin/dsh/client.js +304 -15
- package/packages/dsh-plugin/dsh/diagnostics.js +54 -0
- package/packages/dsh-plugin/dsh/index.js +112 -1875
- package/packages/dsh-plugin/dsh/native-tools.mjs +7 -1
- package/packages/dsh-plugin/dsh/runner.js +666 -0
- package/packages/dsh-plugin/dsh/wide-research.js +878 -0
- package/packages/dsh-plugin/package.json +2 -2
- package/packages/dsh-plugin/scripts/sync-local.sh +70 -0
- package/packages/dsh-plugin/test/client.test.js +175 -1
- package/packages/dsh-plugin/test/diagnostics.test.js +28 -0
- package/packages/dsh-plugin/test/index.test.js +44 -0
- package/scripts/adaptive.py +24 -1
- package/scripts/article_meta.py +131 -0
- package/scripts/budget.py +247 -0
- package/scripts/budget_cli.py +93 -0
- package/scripts/build_release_assets.sh +65 -0
- package/scripts/cache.py +60 -72
- package/scripts/ci_drift_gate.py +90 -0
- package/scripts/content_boundary.py +46 -0
- package/scripts/content_signals.py +26 -5
- package/scripts/cross_lingual_trace.py +293 -0
- package/scripts/dispatch_result.py +98 -0
- package/scripts/engine_dispatch.py +49 -86
- package/scripts/engine_env.py +5 -0
- package/scripts/engine_families.py +6 -0
- package/scripts/engine_policy.py +7 -7
- package/scripts/engines.py +21 -0
- package/scripts/engines_base.py +3 -3
- package/scripts/engines_builders.py +12 -0
- package/scripts/engines_builders_batch10.py +48 -3
- package/scripts/engines_builders_batch14.py +656 -0
- package/scripts/engines_builders_batch15.py +249 -0
- package/scripts/engines_builders_keyless.py +111 -0
- package/scripts/engines_builders_tech.py +0 -1
- package/scripts/evidence.py +31 -3
- package/scripts/evidence_loop.py +54 -22
- package/scripts/fetch_v3.py +25 -0
- package/scripts/gen_routing_snapshot.py +11 -0
- package/scripts/install.ps1 +18 -0
- package/scripts/install.sh +16 -1
- package/scripts/job.py +3 -3
- package/scripts/local_seek.py +41 -5
- package/scripts/macro_countries.py +15 -0
- package/scripts/matrix_search_eval.py +82 -0
- package/scripts/mcp_compact.py +234 -0
- package/scripts/mcp_handlers.py +60 -642
- package/scripts/mcp_launch.sh +10 -8
- package/scripts/mcp_local.py +241 -0
- package/scripts/mcp_server.py +19 -14
- package/scripts/mcp_setup.py +33 -9
- package/scripts/mcp_tools.py +1 -0
- package/scripts/mcp_transport.py +1 -1
- package/scripts/mcp_warm.py +127 -0
- package/scripts/output_transparency.py +200 -0
- package/scripts/paid_policy.py +98 -0
- package/scripts/quota.py +158 -13
- package/scripts/recovery_runner.py +150 -0
- package/scripts/replay_eval.py +18 -0
- package/scripts/research.py +37 -21
- package/scripts/research_expand.py +24 -1
- package/scripts/retry_policy.py +76 -0
- package/scripts/route.py +6 -6
- package/scripts/route_cache.py +18 -0
- package/scripts/route_combo.py +7 -0
- package/scripts/route_domains.py +32 -19
- package/scripts/search.py +15 -27
- package/scripts/search_attribution.py +100 -3
- package/scripts/search_cli.py +24 -11
- package/scripts/search_entry.py +7 -1
- package/scripts/search_output.py +99 -15
- package/scripts/search_pipeline.py +42 -193
- package/scripts/search_types.py +107 -9
- package/scripts/semantic_evidence.py +179 -3
- package/scripts/serp_guard.py +115 -7
- package/scripts/serp_kill_log.py +25 -2
- package/scripts/stats_cli.py +13 -1
- package/scripts/wide_research.py +45 -0
- package/scripts/zhihu_quota.py +139 -0
- package/sub-skills/local-search/bm25.py +27 -3
- package/sub-skills/local-search/cli_runner.py +331 -0
- package/sub-skills/local-search/parsers.py +328 -0
- package/sub-skills/local-search/search_v3.py +31 -674
- package/sub-skills/local-seek/scripts/seek_rank.py +7 -3
- package/assets/readme/settings-keys.png +0 -0
- package/assets/readme/settings-tools.png +0 -0
- package/scripts/image_vision/vision_probe +0 -0
- package/scripts/local_engines.py +0 -321
- package/sub-skills/ego-search/references/browser-runtime.md +0 -43
- package/sub-skills/ego-search/references/install.md +0 -67
- package/sub-skills/ego-search/references/original-ego-upgrade.md +0 -51
- package/sub-skills/ego-search/tests/test_query_normalize.py +0 -68
- package/sub-skills/js-run/tests/fixtures/challenge_like.js +0 -27
- package/sub-skills/js-run/tests/test_jsrun.py +0 -165
- package/sub-skills/local-seek/references/auto-behaviors.md +0 -24
- package/sub-skills/local-seek/references/fd-mdfind-recipes.md +0 -62
- package/sub-skills/local-seek/references/git-integration.md +0 -54
- package/sub-skills/local-seek/references/remote-handoff.md +0 -46
- package/sub-skills/local-seek/references/rg-recipes.md +0 -96
- package/sub-skills/local-seek/references/strategies.md +0 -52
- package/sub-skills/local-seek/references/structural-search.md +0 -59
- package/sub-skills/local-seek/tests/test_content_rank.py +0 -222
- package/sub-skills/local-seek/tests/test_determinism_20261006.py +0 -145
- package/sub-skills/local-seek/tests/test_noise_tier.py +0 -101
- package/sub-skills/local-seek/tests/test_pcre2_probe.py +0 -136
- package/sub-skills/local-seek/tests/test_rank_pinyin.py +0 -77
- package/sub-skills/local-seek/tests/test_regression_20261003.py +0 -169
- package/sub-skills/local-seek/tests/test_regression_20261006.py +0 -146
- package/sub-skills/local-seek/tests/test_seek_degradation.py +0 -111
- package/sub-skills/local-seek/tests/test_seek_fts.py +0 -313
|
@@ -46,6 +46,26 @@ window.__ModuleLoader__.load({
|
|
|
46
46
|
tabKeys: '密钥',
|
|
47
47
|
tabResearch: '研究',
|
|
48
48
|
tabAdvanced: '高级',
|
|
49
|
+
tabDashboard: '看板',
|
|
50
|
+
dashboardTitle: '运行监控与健康态看板',
|
|
51
|
+
dashboardHint: '实时监控常驻 MCP 连接池、上游搜索源拓扑与额度状态,提供一键预设与快速调优。',
|
|
52
|
+
poolTitle: '常驻 MCP 连接池监控',
|
|
53
|
+
poolStatusHealthy: '就绪(正常)',
|
|
54
|
+
presetTitle: '工作流快速预设',
|
|
55
|
+
presetHint: '一键对齐常用工作场景的并发与引擎策略,免去逐项手动调优。',
|
|
56
|
+
presetFast: '极速日常',
|
|
57
|
+
presetFastDesc: '免密免费源优先,关闭 TF-IDF 重排与数值交叉比对,超低延迟',
|
|
58
|
+
presetStandard: '平衡生产(推荐)',
|
|
59
|
+
presetStandardDesc: '开启 TF-IDF 重排、数值交叉核验、主流付费源,标准超时',
|
|
60
|
+
presetDeep: '深度调研',
|
|
61
|
+
presetDeepDesc: '激活更多取证工具、对冲自适应宽限、语义证据层、更大并发',
|
|
62
|
+
presetApplied: '已应用预设:',
|
|
63
|
+
matrixTitle: '核心引擎拓扑与覆盖全景',
|
|
64
|
+
matrixHint: '当前可用引擎的接入层级与角色分工。带免密标识的引擎无需密钥即可直接使用。',
|
|
65
|
+
matrixConfigured: '密钥已就绪',
|
|
66
|
+
matrixKeyless: '免密层可用',
|
|
67
|
+
matrixPending: '待配密钥',
|
|
68
|
+
searchFilterPlaceholder: '输入关键词快速筛选...',
|
|
49
69
|
summary: 'Argo · web_search {search} · web_fetch {fetch} · 原生工具 {tools} 个 · 研究 {workers}/{max}',
|
|
50
70
|
accessTitle: '接入方式',
|
|
51
71
|
searchSeam: '宿主 web_search 走 argo',
|
|
@@ -109,6 +129,8 @@ window.__ModuleLoader__.load({
|
|
|
109
129
|
groupNet: '网络出口',
|
|
110
130
|
groupLocal: '本机检索',
|
|
111
131
|
groupMisc: '其他',
|
|
132
|
+
groupPaid: '额度与预算',
|
|
133
|
+
groupPaidHint: '免费与付费源统一管理:金额计费的源设月度预算,按次数限额的源设日/月次数上限;0=不限制。额度或预算用尽后该源自动跳过并回落免费源。改动对下一次新建连接生效。',
|
|
112
134
|
envDefault: '默认',
|
|
113
135
|
envRange: '需在 {min}–{max} 之间',
|
|
114
136
|
keySourceFile: '凭证库',
|
|
@@ -139,6 +161,26 @@ window.__ModuleLoader__.load({
|
|
|
139
161
|
tabKeys: 'Keys',
|
|
140
162
|
tabResearch: 'Research',
|
|
141
163
|
tabAdvanced: 'Advanced',
|
|
164
|
+
tabDashboard: 'Dashboard',
|
|
165
|
+
dashboardTitle: 'Runtime Monitoring & Health Dashboard',
|
|
166
|
+
dashboardHint: 'Real-time overview of the resident MCP connection pool, upstream search engine matrix, and quick workflow presets.',
|
|
167
|
+
poolTitle: 'Resident MCP Pool Monitoring',
|
|
168
|
+
poolStatusHealthy: 'Ready (Healthy)',
|
|
169
|
+
presetTitle: 'Workflow Presets',
|
|
170
|
+
presetHint: 'One-click presets tailored for common research and search workflows.',
|
|
171
|
+
presetFast: 'Fast & Free',
|
|
172
|
+
presetFastDesc: 'Keyless & free sources, disable rerank & crosscheck for lowest latency',
|
|
173
|
+
presetStandard: 'Balanced (Recommended)',
|
|
174
|
+
presetStandardDesc: 'TF-IDF rerank, numeric crosscheck, mainstream paid sources, standard timeouts',
|
|
175
|
+
presetDeep: 'Deep Research',
|
|
176
|
+
presetDeepDesc: 'Full forensic tools, adaptive hedge grace, semantic evidence layer, max concurrency',
|
|
177
|
+
presetApplied: 'Preset applied:',
|
|
178
|
+
matrixTitle: 'Core Engine Topology & Coverage',
|
|
179
|
+
matrixHint: 'Access tiers and roles of available engines. Keyless engines work without an API key.',
|
|
180
|
+
matrixConfigured: 'Key Ready',
|
|
181
|
+
matrixKeyless: 'Keyless Ready',
|
|
182
|
+
matrixPending: 'Key Required',
|
|
183
|
+
searchFilterPlaceholder: 'Filter by keyword or engine...',
|
|
142
184
|
summary: 'Argo · web_search {search} · web_fetch {fetch} · {tools} native tools · research {workers}/{max}',
|
|
143
185
|
accessTitle: 'Access',
|
|
144
186
|
searchSeam: 'Route host web_search to argo',
|
|
@@ -202,6 +244,8 @@ window.__ModuleLoader__.load({
|
|
|
202
244
|
groupNet: 'Network',
|
|
203
245
|
groupLocal: 'Local search',
|
|
204
246
|
groupMisc: 'Misc',
|
|
247
|
+
groupPaid: 'Quotas & budgets',
|
|
248
|
+
groupPaidHint: 'Free and paid sources share one manager: money-metered sources take a monthly budget, count-capped sources take daily/monthly call caps; 0 = unlimited. When a budget or cap is exhausted, that source is skipped and free fallbacks take over. Changes apply to the next new connection.',
|
|
205
249
|
envDefault: 'default',
|
|
206
250
|
envRange: 'must be between {min} and {max}',
|
|
207
251
|
keySourceFile: 'credential store',
|
|
@@ -238,6 +282,7 @@ window.__ModuleLoader__.load({
|
|
|
238
282
|
|
|
239
283
|
const KEYS = [
|
|
240
284
|
{ ref: 'DEEPSEEK_API_KEY', common: true, zh: 'DeepSeek 官方搜索', en: 'DeepSeek official search' },
|
|
285
|
+
{ ref: 'ARGO_YUANBAO_API_KEY', common: true, zh: '腾讯元宝搜索', en: 'Tencent Yuanbao' },
|
|
241
286
|
{ ref: 'TAVILY_API_KEY', common: true, zh: 'Tavily', en: 'Tavily' },
|
|
242
287
|
{ ref: 'EXA_API_KEY', common: true, zh: 'Exa', en: 'Exa' },
|
|
243
288
|
{ ref: 'BOCHA_API_KEY', common: true, zh: '博查', en: 'Bocha' },
|
|
@@ -261,12 +306,25 @@ window.__ModuleLoader__.load({
|
|
|
261
306
|
{ ref: 'UNPAYWALL_EMAIL', zh: 'Unpaywall 邮箱(非密钥)', en: 'Unpaywall email (not secret)' },
|
|
262
307
|
];
|
|
263
308
|
|
|
309
|
+
const CORE_ENGINES = [
|
|
310
|
+
{ id: 'deepseek', nameZh: 'DeepSeek 搜索', nameEn: 'DeepSeek Search', roleZh: '通用搜索 / 官方对齐', roleEn: 'General / Official Seam', ref: 'DEEPSEEK_API_KEY', keyless: false },
|
|
311
|
+
{ id: 'yuanbao', nameZh: '腾讯元宝', nameEn: 'Tencent Yuanbao', roleZh: '中文对冲主力', roleEn: 'Chinese General Primary', ref: 'ARGO_YUANBAO_API_KEY', keyless: false },
|
|
312
|
+
{ id: 'tavily', nameZh: 'Tavily Search', nameEn: 'Tavily Search', roleZh: 'AI 知识增强检索', roleEn: 'AI Knowledge Retrieval', ref: 'TAVILY_API_KEY', keyless: false },
|
|
313
|
+
{ id: 'exa', nameZh: 'Exa Neural', nameEn: 'Exa Neural', roleZh: '神经语义精准搜索', roleEn: 'Neural Semantic Search', ref: 'EXA_API_KEY', keyless: false },
|
|
314
|
+
{ id: 'brave', nameZh: 'Brave Search', nameEn: 'Brave Search', roleZh: '独立全球 Web 索引', roleEn: 'Independent Global Index', ref: 'BRAVE_API_KEY', keyless: true },
|
|
315
|
+
{ id: 'bocha', nameZh: '博查 Bocha', nameEn: 'Bocha', roleZh: '垂直多源聚合', roleEn: 'Vertical Multi-source', ref: 'BOCHA_API_KEY', keyless: false },
|
|
316
|
+
{ id: 'jina', nameZh: 'Jina Reader', nameEn: 'Jina Reader', roleZh: '正文高保真提取', roleEn: 'Article Text Extraction', ref: '', keyless: true },
|
|
317
|
+
{ id: 'tinyfish', nameZh: 'TinyFish', nameEn: 'TinyFish', roleZh: '无头渲染抓取兜底', roleEn: 'Headless Render Fallback', ref: 'TINYFISH_API_KEY', keyless: true },
|
|
318
|
+
{ id: 'zhihu', nameZh: '知乎 Zhihu', nameEn: 'Zhihu', roleZh: '中文专业问答舆情', roleEn: 'Professional Q&A / Social', ref: 'ZHIHU_ACCESS_SECRET', keyless: true },
|
|
319
|
+
];
|
|
320
|
+
|
|
264
321
|
// argo 引擎开关(argoEnv 的 KEY=VALUE 行)。与 index.js 的 ARGO_ENV_SPECS
|
|
265
322
|
// 同一份名单,由 test/client.test.js 的漂移门禁核对;kind 决定控件:
|
|
266
|
-
// bool
|
|
267
|
-
//
|
|
323
|
+
// bool=开关、num=数字、str=文本、list=名单。def 仅作「未配置时显示什么」,
|
|
324
|
+
// 缺省即不写该行。开关文案写正向行为或明确的关闭动作,不再用 invert 反转
|
|
325
|
+
// (历史 bug 见 index.js 注释)。
|
|
268
326
|
const ENGINE_TOGGLES = [
|
|
269
|
-
{ name: 'ARGO_NO_CACHE', kind: 'bool',
|
|
327
|
+
{ name: 'ARGO_NO_CACHE', kind: 'bool', group: 'cache', def: false, zh: '测试模式(关闭结果缓存)', en: 'Test mode (disable result cache)', hintZh: '日常保持关闭:结果缓存 1 小时。改进 Skill/插件、需要看实时结果时再打开', hintEn: 'Off by default: results are cached for 1h. Enable only when developing the skill/plugin' },
|
|
270
328
|
{ 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
329
|
{ 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
330
|
{ name: 'ARGO_MINHASH_DEDUPE', kind: 'bool', group: 'cache', def: true, zh: 'MinHash 去重', en: 'MinHash dedupe', hintZh: '近重复结果合并,默认开', hintEn: 'Merge near-duplicate results, on by default' },
|
|
@@ -289,7 +347,14 @@ window.__ModuleLoader__.load({
|
|
|
289
347
|
{ 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
348
|
{ 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
349
|
{ 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: '
|
|
350
|
+
{ name: 'ARGO_PAID_SEARCH', kind: 'str', group: 'paid', def: 'auto', zh: '付费源策略', en: 'Paid source policy', hintZh: 'auto=优势域自动使用付费源;off=只用免费源。额度见下方', hintEn: 'auto = use paid sources in proven domains; off = free sources only. See quotas below' },
|
|
351
|
+
{ name: 'ARGO_BUDGET_DEEPSEEK_SEARCH', kind: 'num', group: 'paid', def: 5, min: 0, max: 1000, step: 1, zh: 'DeepSeek 月度预算(元)', en: 'DeepSeek monthly budget (CNY)', hintZh: '0=关闭;¥5≈1000次成功调用(¥0.005/次)', hintEn: '0 = off; CNY 5 ≈ 1000 successful calls (CNY 0.005 each)' },
|
|
352
|
+
{ name: 'ARGO_BUDGET_EXA', kind: 'num', group: 'paid', def: 10, min: 0, max: 10000, step: 1, zh: 'Exa 每月预算(美元)', en: 'Exa monthly budget (USD)', hintZh: '默认 $10/月(约 1000 次);0=关闭自动使用。按成功调用计费', hintEn: 'Default $10/month (~1000 calls); 0 = off. Billed per successful call' },
|
|
353
|
+
{ name: 'ARGO_QUOTA_ANYSEARCH_LIMIT', kind: 'num', group: 'paid', def: 1000, min: 0, max: 1000000, step: 1, zh: 'AnySearch 每日次数上限', en: 'AnySearch daily call cap', hintZh: '默认 1000/日;0=不限制。改动对下一次新建连接生效', hintEn: 'Default 1000/day; 0 = unlimited. Applies to new connections' },
|
|
354
|
+
{ name: 'ARGO_QUOTA_ZHIHU_LIMIT', kind: 'num', group: 'paid', def: 5000, min: 0, max: 1000000, step: 1, zh: '知乎 每日次数上限', en: 'Zhihu daily call cap', hintZh: '默认 5000/日;0=不限制。改动对下一次新建连接生效', hintEn: 'Default 5000/day; 0 = unlimited. Applies to new connections' },
|
|
355
|
+
{ name: 'ARGO_QUOTA_YUANBAO_LIMIT', kind: 'num', group: 'paid', def: 2000, min: 0, max: 1000000, step: 1, zh: '元宝 每月次数上限', en: 'Yuanbao monthly call cap', hintZh: '默认 2000/月;0=不限制。改动对下一次新建连接生效', hintEn: 'Default 2000/month; 0 = unlimited. Applies to new connections' },
|
|
356
|
+
{ name: 'ARGO_QUOTA_TAVILY_LIMIT', kind: 'num', group: 'paid', def: 1000, min: 0, max: 1000000, step: 1, zh: 'Tavily 每月次数上限', en: 'Tavily monthly call cap', hintZh: '默认 1000/月;0=不限制。改动对下一次新建连接生效', hintEn: 'Default 1000/month; 0 = unlimited. Applies to new connections' },
|
|
357
|
+
{ name: 'ARGO_NO_PREWARM', kind: 'bool', group: 'misc', def: false, zh: '关闭连接预热', en: 'Disable prewarm', hintZh: 'MCP 握手后预热引擎注册表,默认开;开启本项即关闭预热', hintEn: 'Prewarm the engine registry after handshake, on by default; enable this to turn it off' },
|
|
293
358
|
{ 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
359
|
{ 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
360
|
{ 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' },
|
|
@@ -391,6 +456,43 @@ window.__ModuleLoader__.load({
|
|
|
391
456
|
actions: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' },
|
|
392
457
|
switchWrap: { display: 'flex', alignItems: 'center', gap: 8 },
|
|
393
458
|
divider: { height: '.5px', background: 'var(--dsw-alias-border-l2, #eee)', margin: '2px 0' },
|
|
459
|
+
metricGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: 10 },
|
|
460
|
+
metricCard: {
|
|
461
|
+
display: 'flex', flexDirection: 'column', gap: 6, padding: '12px 14px',
|
|
462
|
+
borderRadius: 10, border: '.5px solid var(--dsw-alias-border-l3, #e5e7eb)',
|
|
463
|
+
background: 'var(--dsw-alias-bg-layer-2, #f8fafc)',
|
|
464
|
+
},
|
|
465
|
+
metricLabel: { fontSize: 11, fontWeight: 500, color: 'var(--dsw-alias-label-tertiary, #7b818b)', textTransform: 'uppercase', letterSpacing: '0.04em' },
|
|
466
|
+
metricValue: { fontSize: 18, fontWeight: 600, lineHeight: '22px', color: 'var(--dsw-alias-label-primary, #1f2328)' },
|
|
467
|
+
metricSub: { fontSize: 11, lineHeight: '16px', color: 'var(--dsw-alias-label-secondary, #4b5563)' },
|
|
468
|
+
presetGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 },
|
|
469
|
+
presetCard: (active) => ({
|
|
470
|
+
display: 'flex', flexDirection: 'column', gap: 6, padding: '12px 14px',
|
|
471
|
+
borderRadius: 10, cursor: 'pointer', textAlign: 'left',
|
|
472
|
+
border: active ? '1.5px solid var(--dsw-alias-state-business-primary, #3b6ef6)' : '.5px solid var(--dsw-alias-border-l3, #e5e7eb)',
|
|
473
|
+
background: active ? 'var(--dsw-alias-bg-layer-1, #fff)' : 'var(--dsw-alias-settings-card-fill, #fff)',
|
|
474
|
+
boxShadow: active ? '0 1px 3px rgba(59, 110, 246, 0.12)' : 'none',
|
|
475
|
+
}),
|
|
476
|
+
matrixGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 8 },
|
|
477
|
+
matrixCard: {
|
|
478
|
+
display: 'flex', flexDirection: 'column', gap: 4, padding: '10px 12px',
|
|
479
|
+
borderRadius: 8, border: '.5px solid var(--dsw-alias-border-l3, #e5e7eb)',
|
|
480
|
+
background: 'var(--dsw-alias-bg-layer-1, #fff)',
|
|
481
|
+
},
|
|
482
|
+
badge: (color) => ({
|
|
483
|
+
display: 'inline-flex', alignItems: 'center', gap: 4, padding: '2px 8px',
|
|
484
|
+
borderRadius: 999, fontSize: 10, fontWeight: 500, lineHeight: '14px',
|
|
485
|
+
background: color === 'green' ? 'rgba(46, 160, 67, 0.12)' : (color === 'blue' ? 'rgba(59, 110, 246, 0.12)' : 'rgba(100, 116, 139, 0.12)'),
|
|
486
|
+
color: color === 'green' ? '#2da44e' : (color === 'blue' ? '#3b6ef6' : '#64748b'),
|
|
487
|
+
border: '.5px solid ' + (color === 'green' ? '#2da44e' : (color === 'blue' ? '#3b6ef6' : '#64748b')),
|
|
488
|
+
}),
|
|
489
|
+
filterInput: {
|
|
490
|
+
width: '100%', boxSizing: 'border-box', font: 'inherit', fontSize: 12, lineHeight: '18px',
|
|
491
|
+
padding: '6px 10px', borderRadius: 8,
|
|
492
|
+
border: '.5px solid var(--dsw-alias-border-l4, #d0d7de)',
|
|
493
|
+
background: 'var(--dsw-alias-bg-layer-1, #fff)', color: 'inherit',
|
|
494
|
+
marginBottom: 8,
|
|
495
|
+
},
|
|
394
496
|
};
|
|
395
497
|
|
|
396
498
|
function asString(value, fallback) {
|
|
@@ -499,7 +601,7 @@ window.__ModuleLoader__.load({
|
|
|
499
601
|
const initialLang = locale && locale.getSnapshot && locale.getSnapshot().active === 'en' ? 'en' : 'zh';
|
|
500
602
|
const [lang, setLang] = useState(initialLang);
|
|
501
603
|
|
|
502
|
-
const [tab, setTab] = useState('
|
|
604
|
+
const [tab, setTab] = useState('dashboard');
|
|
503
605
|
const [draft, setDraft] = useState(() => toDraft(value));
|
|
504
606
|
const [busy, setBusy] = useState('');
|
|
505
607
|
const [message, setMessage] = useState('');
|
|
@@ -513,6 +615,8 @@ window.__ModuleLoader__.load({
|
|
|
513
615
|
// argoEnv 引擎开关:bool 类即时写;num/str/list 类走草稿 + 分组保存。
|
|
514
616
|
const [envDraft, setEnvDraft] = useState({});
|
|
515
617
|
const [envBusy, setEnvBusy] = useState('');
|
|
618
|
+
const [engineFilter, setEngineFilter] = useState('');
|
|
619
|
+
const [keyFilter, setKeyFilter] = useState('');
|
|
516
620
|
|
|
517
621
|
const dirty = useRef(new Set());
|
|
518
622
|
// 读-改-写共享配置字段(keyRefs / nativeTools / argoEnv)时读实时快照,
|
|
@@ -737,8 +841,9 @@ window.__ModuleLoader__.load({
|
|
|
737
841
|
const ENV_FALSY = ['', '0', 'false', 'no', 'off', 'n', 'disable', 'disabled', 'none'];
|
|
738
842
|
const envBoolState = (spec, map) => {
|
|
739
843
|
const raw = map[spec.name];
|
|
740
|
-
|
|
741
|
-
|
|
844
|
+
return raw === undefined
|
|
845
|
+
? spec.def === true
|
|
846
|
+
: !ENV_FALSY.includes(String(raw).trim().toLowerCase());
|
|
742
847
|
};
|
|
743
848
|
const writeEnvLines = async (updates) => {
|
|
744
849
|
const map = envMap();
|
|
@@ -754,9 +859,8 @@ window.__ModuleLoader__.load({
|
|
|
754
859
|
};
|
|
755
860
|
const toggleEnv = (spec, on) => {
|
|
756
861
|
if (disabled) return;
|
|
757
|
-
//
|
|
758
|
-
|
|
759
|
-
const value = on === (spec.invert === true) ? '0' : '1';
|
|
862
|
+
// 开关状态直接落 0/1:文案与 env 同向(开=1,关=0),不做反转。
|
|
863
|
+
const value = on ? '1' : '0';
|
|
760
864
|
void writeEnvLines({ [spec.name]: value })
|
|
761
865
|
.then((ok) => report(ok, ok ? t('saved') : t('refused')))
|
|
762
866
|
.catch((cause) => report(false, cause && cause.message ? cause.message : String(cause)));
|
|
@@ -795,6 +899,54 @@ window.__ModuleLoader__.load({
|
|
|
795
899
|
}
|
|
796
900
|
};
|
|
797
901
|
|
|
902
|
+
const applyPreset = async (type) => {
|
|
903
|
+
if (disabled) return;
|
|
904
|
+
setBusy('preset');
|
|
905
|
+
try {
|
|
906
|
+
const map = envMap();
|
|
907
|
+
const ops = [];
|
|
908
|
+
if (type === 'fast') {
|
|
909
|
+
map.ARGO_TFIDF_RERANK = '0';
|
|
910
|
+
map.ARGO_NUMBER_CROSSCHECK = '0';
|
|
911
|
+
map.ARGO_PAID_SEARCH = 'off';
|
|
912
|
+
map.ARGO_ADAPTIVE_GRACE = '1';
|
|
913
|
+
map.ARGO_SERIAL_STAGGER_S = '0.5';
|
|
914
|
+
ops.push({ op: 'set', path: ['searchTimeoutMs'], value: 15000 });
|
|
915
|
+
ops.push({ op: 'set', path: ['fetchTimeoutMs'], value: 15000 });
|
|
916
|
+
} else if (type === 'standard') {
|
|
917
|
+
map.ARGO_TFIDF_RERANK = '1';
|
|
918
|
+
map.ARGO_NUMBER_CROSSCHECK = '1';
|
|
919
|
+
map.ARGO_MINHASH_DEDUPE = '1';
|
|
920
|
+
map.ARGO_PAID_SEARCH = 'auto';
|
|
921
|
+
map.ARGO_ADAPTIVE_GRACE = '1';
|
|
922
|
+
map.ARGO_SERIAL_STAGGER_S = '0.8';
|
|
923
|
+
ops.push({ op: 'set', path: ['searchTimeoutMs'], value: 30000 });
|
|
924
|
+
ops.push({ op: 'set', path: ['fetchTimeoutMs'], value: 25000 });
|
|
925
|
+
} else if (type === 'deep') {
|
|
926
|
+
map.ARGO_TFIDF_RERANK = '1';
|
|
927
|
+
map.ARGO_NUMBER_CROSSCHECK = '1';
|
|
928
|
+
map.ARGO_MINHASH_DEDUPE = '1';
|
|
929
|
+
map.ARGO_SEMANTIC_EVIDENCE = '1';
|
|
930
|
+
map.ARGO_ADAPTIVE_GRACE = '1';
|
|
931
|
+
map.ARGO_PAID_SEARCH = 'auto';
|
|
932
|
+
ops.push({ op: 'set', path: ['searchTimeoutMs'], value: 45000 });
|
|
933
|
+
ops.push({ op: 'set', path: ['fetchTimeoutMs'], value: 35000 });
|
|
934
|
+
ops.push({ op: 'set', path: ['nativeTimeoutMs'], value: 60000 });
|
|
935
|
+
}
|
|
936
|
+
const envLines = Object.keys(map)
|
|
937
|
+
.filter((name) => ENV_NAMES.has(name))
|
|
938
|
+
.map((name) => `${name}=${map[name]}`);
|
|
939
|
+
ops.push({ op: 'set', path: ['argoEnv'], value: envLines });
|
|
940
|
+
const ok = await props.mutate(ops);
|
|
941
|
+
const presetName = type === 'fast' ? t('presetFast') : (type === 'standard' ? t('presetStandard') : t('presetDeep'));
|
|
942
|
+
report(ok, ok ? `${t('presetApplied')} ${presetName}` : t('refused'));
|
|
943
|
+
} catch (cause) {
|
|
944
|
+
report(false, cause && cause.message ? cause.message : String(cause));
|
|
945
|
+
} finally {
|
|
946
|
+
setBusy('');
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
|
|
798
950
|
if (props.view === 'summary') {
|
|
799
951
|
const summary = t('summary')
|
|
800
952
|
.replace('{search}', value.searchProviderEnabled !== false ? t('on') : t('off'))
|
|
@@ -913,6 +1065,95 @@ window.__ModuleLoader__.load({
|
|
|
913
1065
|
|
|
914
1066
|
const panel = [];
|
|
915
1067
|
|
|
1068
|
+
if (tab === 'dashboard') {
|
|
1069
|
+
const configuredKeyCount = Object.values(credentials).filter((c) => c && c.configured).length;
|
|
1070
|
+
const keylessEngineCount = CORE_ENGINES.filter((e) => e.keyless).length;
|
|
1071
|
+
const seamSearchOn = value.searchProviderEnabled !== false;
|
|
1072
|
+
const seamFetchOn = value.fetchProviderEnabled !== false;
|
|
1073
|
+
const activeToolCount = asArray(value.nativeTools).length;
|
|
1074
|
+
const poolConcurrency = asNumber(value.searchConcurrency, DEFAULTS.searchConcurrency);
|
|
1075
|
+
const poolIdleMs = asNumber(value.searchIdleMs, DEFAULTS.searchIdleMs);
|
|
1076
|
+
|
|
1077
|
+
panel.push(h('div', { key: 'metrics', style: S.metricGrid }, [
|
|
1078
|
+
h('div', { key: 'pool', style: S.metricCard }, [
|
|
1079
|
+
h('span', { key: 'l', style: S.metricLabel }, t('poolTitle')),
|
|
1080
|
+
h('div', { key: 'v', style: { display: 'flex', alignItems: 'center', gap: 6 } }, [
|
|
1081
|
+
h('span', { key: 'dot', style: S.dot(true) }),
|
|
1082
|
+
h('span', { key: 'val', style: S.metricValue }, `${poolConcurrency} 并发`),
|
|
1083
|
+
]),
|
|
1084
|
+
h('span', { key: 's', style: S.metricSub }, `${t('poolStatusHealthy')} · 保活 ${poolIdleMs / 1000}s`),
|
|
1085
|
+
]),
|
|
1086
|
+
h('div', { key: 'seam', style: S.metricCard }, [
|
|
1087
|
+
h('span', { key: 'l', style: S.metricLabel }, t('accessTitle')),
|
|
1088
|
+
h('span', { key: 'val', style: S.metricValue }, seamSearchOn && seamFetchOn ? '双路接管' : (seamSearchOn || seamFetchOn ? '单路接管' : '未接管')),
|
|
1089
|
+
h('span', { key: 's', style: S.metricSub }, `Search: ${seamSearchOn ? 'ON' : 'OFF'} · Fetch: ${seamFetchOn ? 'ON' : 'OFF'}`),
|
|
1090
|
+
]),
|
|
1091
|
+
h('div', { key: 'tools', style: S.metricCard }, [
|
|
1092
|
+
h('span', { key: 'l', style: S.metricLabel }, t('toolsTitle')),
|
|
1093
|
+
h('span', { key: 'val', style: S.metricValue }, `${activeToolCount} / ${TOOLS.length}`),
|
|
1094
|
+
h('span', { key: 's', style: S.metricSub }, '已就绪原生 MCP 工具'),
|
|
1095
|
+
]),
|
|
1096
|
+
h('div', { key: 'keys', style: S.metricCard }, [
|
|
1097
|
+
h('span', { key: 'l', style: S.metricLabel }, t('keysTitle')),
|
|
1098
|
+
h('span', { key: 'val', style: S.metricValue }, `${configuredKeyCount} 配置 · ${keylessEngineCount} 免密`),
|
|
1099
|
+
h('span', { key: 's', style: S.metricSub }, '覆盖全局多源引擎'),
|
|
1100
|
+
]),
|
|
1101
|
+
]));
|
|
1102
|
+
|
|
1103
|
+
panel.push(h(Card, { key: 'presets', title: t('presetTitle'), hint: t('presetHint') }, [
|
|
1104
|
+
h('div', { key: 'grid', style: S.presetGrid }, [
|
|
1105
|
+
h('div', {
|
|
1106
|
+
key: 'fast',
|
|
1107
|
+
style: S.presetCard(false),
|
|
1108
|
+
onClick: () => { void applyPreset('fast'); },
|
|
1109
|
+
}, [
|
|
1110
|
+
h('div', { key: 't', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } }, [
|
|
1111
|
+
h('span', { key: 'title', style: S.label }, t('presetFast')),
|
|
1112
|
+
h('span', { key: 'badge', style: S.badge('blue') }, '15s 超时'),
|
|
1113
|
+
]),
|
|
1114
|
+
h('span', { key: 'd', style: S.sub }, t('presetFastDesc')),
|
|
1115
|
+
]),
|
|
1116
|
+
h('div', {
|
|
1117
|
+
key: 'standard',
|
|
1118
|
+
style: S.presetCard(true),
|
|
1119
|
+
onClick: () => { void applyPreset('standard'); },
|
|
1120
|
+
}, [
|
|
1121
|
+
h('div', { key: 't', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } }, [
|
|
1122
|
+
h('span', { key: 'title', style: S.label }, t('presetStandard')),
|
|
1123
|
+
h('span', { key: 'badge', style: S.badge('green') }, '标准生产'),
|
|
1124
|
+
]),
|
|
1125
|
+
h('span', { key: 'd', style: S.sub }, t('presetStandardDesc')),
|
|
1126
|
+
]),
|
|
1127
|
+
h('div', {
|
|
1128
|
+
key: 'deep',
|
|
1129
|
+
style: S.presetCard(false),
|
|
1130
|
+
onClick: () => { void applyPreset('deep'); },
|
|
1131
|
+
}, [
|
|
1132
|
+
h('div', { key: 't', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } }, [
|
|
1133
|
+
h('span', { key: 'title', style: S.label }, t('presetDeep')),
|
|
1134
|
+
h('span', { key: 'badge', style: S.badge('blue') }, '深度核验'),
|
|
1135
|
+
]),
|
|
1136
|
+
h('span', { key: 'd', style: S.sub }, t('presetDeepDesc')),
|
|
1137
|
+
]),
|
|
1138
|
+
]),
|
|
1139
|
+
]));
|
|
1140
|
+
|
|
1141
|
+
panel.push(h(Card, { key: 'matrix', title: t('matrixTitle'), hint: t('matrixHint') }, [
|
|
1142
|
+
h('div', { key: 'grid', style: S.matrixGrid }, CORE_ENGINES.map((engine) => {
|
|
1143
|
+
const hasKey = engine.ref && credentials[engine.ref] && credentials[engine.ref].configured;
|
|
1144
|
+
const badgeType = hasKey ? 'green' : (engine.keyless ? 'blue' : 'gray');
|
|
1145
|
+
const badgeText = hasKey ? t('matrixConfigured') : (engine.keyless ? t('matrixKeyless') : t('matrixPending'));
|
|
1146
|
+
return h('div', { key: engine.id, style: S.matrixCard }, [
|
|
1147
|
+
h('div', { key: 'h', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 6 } }, [
|
|
1148
|
+
h('span', { key: 'n', style: S.label }, lang === 'en' ? engine.nameEn : engine.nameZh),
|
|
1149
|
+
h('span', { key: 'b', style: S.badge(badgeType) }, badgeText),
|
|
1150
|
+
]),
|
|
1151
|
+
h('span', { key: 'r', style: S.sub }, lang === 'en' ? engine.roleEn : engine.roleZh),
|
|
1152
|
+
]);
|
|
1153
|
+
})),
|
|
1154
|
+
]));
|
|
1155
|
+
}
|
|
1156
|
+
|
|
916
1157
|
if (tab === 'access') {
|
|
917
1158
|
panel.push(h(Card, { key: 'seam', title: t('accessTitle') }, [
|
|
918
1159
|
h(Switch, {
|
|
@@ -1007,15 +1248,50 @@ window.__ModuleLoader__.load({
|
|
|
1007
1248
|
|
|
1008
1249
|
if (tab === 'engines') {
|
|
1009
1250
|
const map = envMap();
|
|
1010
|
-
|
|
1251
|
+
// 渲染顺序即面板顺序。**不是**从 ENGINE_TOGGLES 去重推导——那样分组
|
|
1252
|
+
// 顺序会随白名单增删而漂移,用户每次升级都看到重排的面板。
|
|
1253
|
+
//
|
|
1254
|
+
// 历史 bug(2026-10-21 定位):`paid` 一度不在这个清单里,于是
|
|
1255
|
+
// ARGO_PAID_SEARCH 与 ARGO_BUDGET_DEEPSEEK_SEARCH 两条开关在
|
|
1256
|
+
// index.js 白名单、client.js ENGINE_TOGGLES、双语文案三处都齐全,
|
|
1257
|
+
// 却在面板上完全不显示——用户配好了预算却找不到入口,还以为是插件
|
|
1258
|
+
// 没装。漂移门禁只核对「两边名单同集合、字段一致」,核对不到「这个
|
|
1259
|
+
// 分组到底渲不渲染」;已由 test/client.test.js 的「engines tab renders
|
|
1260
|
+
// a control for every ENGINE_TOGGLES switch」补上(见该测试注释)。
|
|
1261
|
+
const groups = ['cache', 'fetch', 'semantic', 'engines', 'net', 'local', 'paid', 'misc'];
|
|
1011
1262
|
const groupTitle = (group) => t('group' + group.charAt(0).toUpperCase() + group.slice(1));
|
|
1012
1263
|
panel.push(h('p', { key: 'hint', style: S.hint }, t('enginesHint')));
|
|
1264
|
+
panel.push(h('input', {
|
|
1265
|
+
key: 'filter',
|
|
1266
|
+
type: 'text',
|
|
1267
|
+
style: S.filterInput,
|
|
1268
|
+
placeholder: t('searchFilterPlaceholder'),
|
|
1269
|
+
value: engineFilter,
|
|
1270
|
+
onChange: (event) => setEngineFilter(event.target.value),
|
|
1271
|
+
}));
|
|
1272
|
+
const query = engineFilter.trim().toLowerCase();
|
|
1013
1273
|
for (const group of groups) {
|
|
1014
|
-
|
|
1274
|
+
let specs = ENGINE_TOGGLES.filter((spec) => spec.group === group);
|
|
1275
|
+
if (query) {
|
|
1276
|
+
const groupNameMatch = group.toLowerCase().includes(query) || groupTitle(group).toLowerCase().includes(query);
|
|
1277
|
+
if (!groupNameMatch) {
|
|
1278
|
+
specs = specs.filter((spec) => {
|
|
1279
|
+
const name = spec.name.toLowerCase();
|
|
1280
|
+
const zh = (spec.zh || '').toLowerCase();
|
|
1281
|
+
const en = (spec.en || '').toLowerCase();
|
|
1282
|
+
const hintZh = (spec.hintZh || '').toLowerCase();
|
|
1283
|
+
const hintEn = (spec.hintEn || '').toLowerCase();
|
|
1284
|
+
return name.includes(query) || zh.includes(query) || en.includes(query) || hintZh.includes(query) || hintEn.includes(query);
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1015
1288
|
if (specs.length === 0) continue;
|
|
1016
1289
|
const bools = specs.filter((spec) => spec.kind === 'bool');
|
|
1017
1290
|
const fields = specs.filter((spec) => spec.kind !== 'bool');
|
|
1018
1291
|
const children = [];
|
|
1292
|
+
if (group === 'paid') {
|
|
1293
|
+
children.push(h('p', { key: 'groupHint', style: S.hint }, t('groupPaidHint')));
|
|
1294
|
+
}
|
|
1019
1295
|
for (const spec of bools) {
|
|
1020
1296
|
// 未配置时显示 argo 默认态并标注「默认」,配置过的显示自定义值,
|
|
1021
1297
|
// 让「这一行到底生不生效」在面板上一眼可辨。
|
|
@@ -1078,10 +1354,22 @@ window.__ModuleLoader__.load({
|
|
|
1078
1354
|
}
|
|
1079
1355
|
|
|
1080
1356
|
if (tab === 'keys') {
|
|
1357
|
+
const query = keyFilter.trim().toLowerCase();
|
|
1358
|
+
const displayKeys = query
|
|
1359
|
+
? KEYS.filter((entry) => entry.ref.toLowerCase().includes(query) || (entry.zh && entry.zh.toLowerCase().includes(query)) || (entry.en && entry.en.toLowerCase().includes(query)))
|
|
1360
|
+
: visibleKeys;
|
|
1081
1361
|
panel.push(h(Card, { key: 'keys', title: t('keysTitle'), hint: t('keysHint') }, [
|
|
1082
|
-
h('
|
|
1083
|
-
|
|
1084
|
-
|
|
1362
|
+
h('input', {
|
|
1363
|
+
key: 'filter',
|
|
1364
|
+
type: 'text',
|
|
1365
|
+
style: S.filterInput,
|
|
1366
|
+
placeholder: t('searchFilterPlaceholder'),
|
|
1367
|
+
value: keyFilter,
|
|
1368
|
+
onChange: (event) => setKeyFilter(event.target.value),
|
|
1369
|
+
}),
|
|
1370
|
+
h('span', { key: 'l', style: S.label }, query ? t('keysTitle') : t('commonKeys')),
|
|
1371
|
+
...displayKeys.map(keyRow),
|
|
1372
|
+
query ? null : h('div', { key: 'a', style: S.actions }, [
|
|
1085
1373
|
h('button', {
|
|
1086
1374
|
key: 'more',
|
|
1087
1375
|
type: 'button',
|
|
@@ -1178,6 +1466,7 @@ window.__ModuleLoader__.load({
|
|
|
1178
1466
|
h('p', { key: 'i', style: S.hint }, t('intro')),
|
|
1179
1467
|
]),
|
|
1180
1468
|
h('div', { key: 'tabs', style: S.tabs }, [
|
|
1469
|
+
tabButton('dashboard', t('tabDashboard')),
|
|
1181
1470
|
tabButton('access', t('tabAccess')),
|
|
1182
1471
|
tabButton('tools', t('tabTools')),
|
|
1183
1472
|
tabButton('engines', t('tabEngines')),
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* diagnostics.js — argo-dsh 运行时监控与连接池只读诊断
|
|
3
|
+
*
|
|
4
|
+
* 为设置面板、外部插件或排查脚本提供只读状态自省能力,
|
|
5
|
+
* 避免状态探查逻辑与核心检索逻辑耦合。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 获取当前常驻 MCP 连接池的运行时指标。
|
|
10
|
+
*
|
|
11
|
+
* @param {Array} pool - MCP 连接池实例数组
|
|
12
|
+
* @returns {Object} 包含连接数、当前负载以及各连接健康状态的快照
|
|
13
|
+
*/
|
|
14
|
+
export function getMcpPoolStats(pool = []) {
|
|
15
|
+
const connections = pool.map((conn, index) => ({
|
|
16
|
+
index,
|
|
17
|
+
load: typeof conn.load === 'function' ? conn.load() : 0,
|
|
18
|
+
hasProcess: Boolean(conn.proc ?? true),
|
|
19
|
+
}))
|
|
20
|
+
|
|
21
|
+
const totalLoad = connections.reduce((sum, item) => sum + item.load, 0)
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
poolSize: pool.length,
|
|
25
|
+
activeConnections: pool.filter(conn => typeof conn.load === 'function' && conn.load() > 0).length,
|
|
26
|
+
totalLoad,
|
|
27
|
+
connections,
|
|
28
|
+
timestamp: Date.now(),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 诊断并重置 MCP 连接池。
|
|
34
|
+
*
|
|
35
|
+
* @param {Array} pool - MCP 连接池实例数组
|
|
36
|
+
* @param {Function} disposeFn - 释放函数
|
|
37
|
+
* @returns {Object} 重置结果摘要
|
|
38
|
+
*/
|
|
39
|
+
export function resetMcpPool(pool = [], disposeFn) {
|
|
40
|
+
const beforeCount = pool.length
|
|
41
|
+
if (typeof disposeFn === 'function') {
|
|
42
|
+
disposeFn()
|
|
43
|
+
} else {
|
|
44
|
+
for (const conn of [...pool]) {
|
|
45
|
+
try { conn.close?.() } catch { /* ignore */ }
|
|
46
|
+
}
|
|
47
|
+
pool.length = 0
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
evictedCount: beforeCount,
|
|
51
|
+
poolSizeNow: pool.length,
|
|
52
|
+
success: true,
|
|
53
|
+
}
|
|
54
|
+
}
|