argo-search 2.9.7 → 2.9.9
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 +73 -150
- package/README.es.md +73 -150
- package/README.ja.md +79 -156
- package/README.ko.md +78 -155
- package/README.md +80 -159
- 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 +78 -5
- package/bin/argo.js +33 -2
- package/config.yaml +269 -63
- package/docs/ARGO_INTRO.md +5 -3
- 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/docs/RELEASE_NOTES_v2.9.9.md +78 -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 +7 -2
- package/packages/dsh-plugin/README.md +25 -6
- package/packages/dsh-plugin/dsh/client.js +402 -47
- package/packages/dsh-plugin/dsh/diagnostics.js +97 -0
- package/packages/dsh-plugin/dsh/index.js +135 -1876
- 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 +3 -3
- package/packages/dsh-plugin/scripts/sync-local.sh +70 -0
- package/packages/dsh-plugin/test/client.test.js +177 -2
- package/packages/dsh-plugin/test/diagnostics.test.js +28 -0
- package/packages/dsh-plugin/test/index.test.js +44 -0
- package/scripts/adaptive.py +126 -43
- package/scripts/article_meta.py +131 -0
- package/scripts/bench_tail_ab.py +281 -0
- package/scripts/bounded_run.py +91 -49
- 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/clock_bridge.py +22 -0
- package/scripts/content_boundary.py +46 -0
- package/scripts/content_security.py +119 -4
- package/scripts/content_signals.py +26 -5
- package/scripts/cross_lingual_trace.py +293 -0
- package/scripts/dispatch_result.py +105 -0
- package/scripts/engine_add.py +602 -0
- package/scripts/engine_dispatch.py +58 -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 +58 -22
- package/scripts/fetch_quality.py +13 -0
- package/scripts/fetch_v3.py +28 -3
- package/scripts/gen_routing_snapshot.py +11 -0
- package/scripts/http_client.py +24 -38
- 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 +192 -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/net_proxy.py +27 -0
- package/scripts/output_transparency.py +200 -0
- package/scripts/paid_policy.py +98 -0
- package/scripts/quota.py +158 -13
- package/scripts/recovery.py +7 -3
- 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 +26 -5
- package/scripts/route_domains.py +170 -31
- package/scripts/safe_conn.py +52 -0
- 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 +8 -1
- package/scripts/search_output.py +99 -15
- package/scripts/search_pipeline.py +62 -195
- package/scripts/search_rank.py +6 -0
- package/scripts/search_types.py +107 -9
- package/scripts/semantic_evidence.py +179 -3
- package/scripts/serp_guard.py +138 -7
- package/scripts/serp_kill_log.py +25 -2
- package/scripts/stats_cli.py +13 -1
- package/scripts/tail_completion.py +212 -0
- package/scripts/url_safety.py +126 -3
- package/scripts/usage_log.py +45 -0
- 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/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,29 @@ window.__ModuleLoader__.load({
|
|
|
46
46
|
tabKeys: '密钥',
|
|
47
47
|
tabResearch: '研究',
|
|
48
48
|
tabAdvanced: '高级',
|
|
49
|
+
tabDashboard: '看板',
|
|
50
|
+
dashboardTitle: '运行监控与健康态看板',
|
|
51
|
+
routeHealthTitle: '路由观测',
|
|
52
|
+
routeHealthHint: '最近的路由决策与引擎状态(宿主提供时显示)。',
|
|
53
|
+
routeHealthFallback: '在终端运行 `argo stats` 或 `argo engine list` 查看路由健康、引擎成功率与配额消耗。',
|
|
54
|
+
dashboardHint: '实时监控常驻 MCP 连接池、上游搜索源拓扑与额度状态,提供一键预设与快速调优。',
|
|
55
|
+
poolTitle: '常驻 MCP 连接池监控',
|
|
56
|
+
poolStatusHealthy: '就绪(正常)',
|
|
57
|
+
presetTitle: '工作流快速预设',
|
|
58
|
+
presetHint: '一键对齐常用工作场景的并发与引擎策略,免去逐项手动调优。',
|
|
59
|
+
presetFast: '极速日常',
|
|
60
|
+
presetFastDesc: '免密免费源优先,关闭 TF-IDF 重排与数值交叉比对,超低延迟',
|
|
61
|
+
presetStandard: '平衡生产(推荐)',
|
|
62
|
+
presetStandardDesc: '开启 TF-IDF 重排、数值交叉核验、主流付费源,标准超时',
|
|
63
|
+
presetDeep: '深度调研',
|
|
64
|
+
presetDeepDesc: '激活更多取证工具、对冲自适应宽限、语义证据层、更大并发',
|
|
65
|
+
presetApplied: '已应用预设:',
|
|
66
|
+
matrixTitle: '核心引擎拓扑与覆盖全景',
|
|
67
|
+
matrixHint: '当前可用引擎的接入层级与角色分工。带免密标识的引擎无需密钥即可直接使用。',
|
|
68
|
+
matrixConfigured: '密钥已就绪',
|
|
69
|
+
matrixKeyless: '免密层可用',
|
|
70
|
+
matrixPending: '待配密钥',
|
|
71
|
+
searchFilterPlaceholder: '输入关键词快速筛选...',
|
|
49
72
|
summary: 'Argo · web_search {search} · web_fetch {fetch} · 原生工具 {tools} 个 · 研究 {workers}/{max}',
|
|
50
73
|
accessTitle: '接入方式',
|
|
51
74
|
searchSeam: '宿主 web_search 走 argo',
|
|
@@ -63,6 +86,8 @@ window.__ModuleLoader__.load({
|
|
|
63
86
|
concurrency: '并发连接数',
|
|
64
87
|
toolsTitle: '原生工具',
|
|
65
88
|
toolsHint: '勾选即注册为原生一等工具(CLI 单发,不依赖 MCP)。长尾工具只在需要时开,避免每轮工具面膨胀。',
|
|
89
|
+
toolPresetTitle: '推荐组合',
|
|
90
|
+
toolPresetHint: '按场景一键套用(会覆盖当前勾选)。工具分区见下方。',
|
|
66
91
|
coreTools: '高频路径',
|
|
67
92
|
longTools: '长尾取证',
|
|
68
93
|
nativeTimeout: '单次调用超时 (ms)',
|
|
@@ -109,6 +134,8 @@ window.__ModuleLoader__.load({
|
|
|
109
134
|
groupNet: '网络出口',
|
|
110
135
|
groupLocal: '本机检索',
|
|
111
136
|
groupMisc: '其他',
|
|
137
|
+
groupPaid: '额度与预算',
|
|
138
|
+
groupPaidHint: '免费与付费源统一管理:金额计费的源设月度预算,按次数限额的源设日/月次数上限;0=不限制。额度或预算用尽后该源自动跳过并回落免费源。改动对下一次新建连接生效。',
|
|
112
139
|
envDefault: '默认',
|
|
113
140
|
envRange: '需在 {min}–{max} 之间',
|
|
114
141
|
keySourceFile: '凭证库',
|
|
@@ -139,6 +166,29 @@ window.__ModuleLoader__.load({
|
|
|
139
166
|
tabKeys: 'Keys',
|
|
140
167
|
tabResearch: 'Research',
|
|
141
168
|
tabAdvanced: 'Advanced',
|
|
169
|
+
tabDashboard: 'Dashboard',
|
|
170
|
+
dashboardTitle: 'Runtime Monitoring & Health Dashboard',
|
|
171
|
+
routeHealthTitle: 'Routing telemetry',
|
|
172
|
+
routeHealthHint: 'Recent routing decisions and engine status (when provided by the host).',
|
|
173
|
+
routeHealthFallback: 'Run `argo stats` or `argo engine list` in a terminal to inspect routing health, engine success rates and quota usage.',
|
|
174
|
+
dashboardHint: 'Real-time overview of the resident MCP connection pool, upstream search engine matrix, and quick workflow presets.',
|
|
175
|
+
poolTitle: 'Resident MCP Pool Monitoring',
|
|
176
|
+
poolStatusHealthy: 'Ready (Healthy)',
|
|
177
|
+
presetTitle: 'Workflow Presets',
|
|
178
|
+
presetHint: 'One-click presets tailored for common research and search workflows.',
|
|
179
|
+
presetFast: 'Fast & Free',
|
|
180
|
+
presetFastDesc: 'Keyless & free sources, disable rerank & crosscheck for lowest latency',
|
|
181
|
+
presetStandard: 'Balanced (Recommended)',
|
|
182
|
+
presetStandardDesc: 'TF-IDF rerank, numeric crosscheck, mainstream paid sources, standard timeouts',
|
|
183
|
+
presetDeep: 'Deep Research',
|
|
184
|
+
presetDeepDesc: 'Full forensic tools, adaptive hedge grace, semantic evidence layer, max concurrency',
|
|
185
|
+
presetApplied: 'Preset applied:',
|
|
186
|
+
matrixTitle: 'Core Engine Topology & Coverage',
|
|
187
|
+
matrixHint: 'Access tiers and roles of available engines. Keyless engines work without an API key.',
|
|
188
|
+
matrixConfigured: 'Key Ready',
|
|
189
|
+
matrixKeyless: 'Keyless Ready',
|
|
190
|
+
matrixPending: 'Key Required',
|
|
191
|
+
searchFilterPlaceholder: 'Filter by keyword or engine...',
|
|
142
192
|
summary: 'Argo · web_search {search} · web_fetch {fetch} · {tools} native tools · research {workers}/{max}',
|
|
143
193
|
accessTitle: 'Access',
|
|
144
194
|
searchSeam: 'Route host web_search to argo',
|
|
@@ -156,6 +206,8 @@ window.__ModuleLoader__.load({
|
|
|
156
206
|
concurrency: 'Concurrent connections',
|
|
157
207
|
toolsTitle: 'Native tools',
|
|
158
208
|
toolsHint: 'Checked tools register as native first-class tools (single-shot CLI, no MCP). Enable long-tail tools only when needed.',
|
|
209
|
+
toolPresetTitle: 'Recommended sets',
|
|
210
|
+
toolPresetHint: 'Apply a scenario preset in one click (replaces current selection). See the grouped tools below.',
|
|
159
211
|
coreTools: 'Hot path',
|
|
160
212
|
longTools: 'Long tail',
|
|
161
213
|
nativeTimeout: 'Per-call timeout (ms)',
|
|
@@ -202,6 +254,8 @@ window.__ModuleLoader__.load({
|
|
|
202
254
|
groupNet: 'Network',
|
|
203
255
|
groupLocal: 'Local search',
|
|
204
256
|
groupMisc: 'Misc',
|
|
257
|
+
groupPaid: 'Quotas & budgets',
|
|
258
|
+
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
259
|
envDefault: 'default',
|
|
206
260
|
envRange: 'must be between {min} and {max}',
|
|
207
261
|
keySourceFile: 'credential store',
|
|
@@ -215,29 +269,46 @@ window.__ModuleLoader__.load({
|
|
|
215
269
|
},
|
|
216
270
|
};
|
|
217
271
|
|
|
272
|
+
// 工具分区(场景化):核心 / 取证 / 本地 / 长尾。
|
|
273
|
+
// 旧版只有 core/long 两组,16 个长尾工具平铺,新用户不知道该开哪些;
|
|
274
|
+
// 按使用场景分组后,配合下方「推荐组合」卡可以一键套用。
|
|
218
275
|
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: '
|
|
222
|
-
{ id: '
|
|
223
|
-
{ id: '
|
|
224
|
-
{ id: '
|
|
225
|
-
{ id: '
|
|
226
|
-
{ id: '
|
|
227
|
-
{ id: '
|
|
228
|
-
{ id: '
|
|
229
|
-
{ id: '
|
|
230
|
-
{ id: '
|
|
231
|
-
{ id: '
|
|
232
|
-
{ id: '
|
|
233
|
-
{ id: '
|
|
234
|
-
{ id: 'argo_answer', group: '
|
|
235
|
-
{ id: 'argo_watch', group: '
|
|
236
|
-
{ id: 'argo_cite', group: '
|
|
276
|
+
{ id: 'argo_search', group: 'core', zh: '统一搜索', en: 'Unified search', sceneZh: '日常一问的首选', sceneEn: 'Default for everyday queries' },
|
|
277
|
+
{ id: 'argo_fetch', group: 'core', zh: '智能抓取', en: 'Smart fetch', sceneZh: '读某个具体页面', sceneEn: 'Read a specific page' },
|
|
278
|
+
{ id: 'argo_evidence', group: 'forensics', zh: '来源可信度评估', en: 'Source credibility', sceneZh: '高后果问题的证据分级', sceneEn: 'Grade evidence for high-stakes answers' },
|
|
279
|
+
{ id: 'argo_clarify', group: 'forensics', zh: '意图消歧', en: 'Intent clarification', sceneZh: '查询有歧义时先消歧', sceneEn: 'Disambiguate an ambiguous query' },
|
|
280
|
+
{ id: 'argo_crawl', group: 'forensics', zh: '站点级爬取', en: 'Site crawl', sceneZh: '整站/多页采集', sceneEn: 'Site-level or multi-page collection' },
|
|
281
|
+
{ id: 'argo_extract', group: 'forensics', zh: '结构化提取', en: 'Structured extract', sceneZh: '从页面抽表格/字段', sceneEn: 'Extract tables/fields from a page' },
|
|
282
|
+
{ id: 'argo_pdf', group: 'forensics', zh: 'PDF 提取', en: 'PDF extract', sceneZh: '读 PDF 正文', sceneEn: 'Read PDF text' },
|
|
283
|
+
{ id: 'argo_screenshot', group: 'forensics', zh: '网页截图', en: 'Screenshot', sceneZh: '取证留档/看渲染结果', sceneEn: 'Archive or inspect a rendered page' },
|
|
284
|
+
{ id: 'argo_article', group: 'forensics', zh: '公众号全文', en: 'WeChat article', sceneZh: '读公众号文章正文', sceneEn: 'Read a WeChat article' },
|
|
285
|
+
{ id: 'argo_local_search', group: 'local', zh: '本机文件搜索', en: 'Local file search', sceneZh: '搜本机笔记/代码', sceneEn: 'Search local notes/code' },
|
|
286
|
+
{ id: 'argo_local_read', group: 'local', zh: '本机文件读取', en: 'Local file read', sceneZh: '读本机文件', sceneEn: 'Read a local file' },
|
|
287
|
+
{ id: 'argo_recompute', group: 'local', zh: '可复算执行器', en: 'Recompute', sceneZh: '本地可复算任务(需授权)', sceneEn: 'Local recompute (needs authorization)' },
|
|
288
|
+
{ id: 'argo_social_search', group: 'longtail', zh: '社交平台搜索', en: 'Social search', sceneZh: '舆情/UGC 采集', sceneEn: 'Sentiment / UGC collection' },
|
|
289
|
+
{ id: 'argo_job', group: 'longtail', zh: '招聘聚合', en: 'Job aggregation', sceneZh: '招聘/岗位聚合', sceneEn: 'Job aggregation' },
|
|
290
|
+
{ id: 'argo_preflight', group: 'longtail', zh: '批量 URL 预检', en: 'URL preflight', sceneZh: '批量检查 URL 可达性', sceneEn: 'Batch-check URL reachability' },
|
|
291
|
+
{ id: 'argo_answer', group: 'longtail', zh: '带引用直答', en: 'Cited answer', sceneZh: '在给定语料里直答', sceneEn: 'Answer from a given corpus' },
|
|
292
|
+
{ id: 'argo_watch', group: 'longtail', zh: '网页变化监控', en: 'Page watch', sceneZh: '盯页面变化', sceneEn: 'Watch a page for changes' },
|
|
293
|
+
{ id: 'argo_cite', group: 'longtail', zh: 'DOI 引用生成', en: 'DOI citation', sceneZh: '生成引用条目', sceneEn: 'Generate a citation' },
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
// 分区展示名 + 推荐组合(场景化,让用户一键套用而不是逐个勾选)
|
|
297
|
+
const TOOL_GROUPS = [
|
|
298
|
+
{ id: 'core', zh: '高频路径', en: 'Hot path' },
|
|
299
|
+
{ id: 'forensics', zh: '取证与核验', en: 'Forensics' },
|
|
300
|
+
{ id: 'local', zh: '本机', en: 'Local' },
|
|
301
|
+
{ id: 'longtail', zh: '长尾', en: 'Long tail' },
|
|
302
|
+
];
|
|
303
|
+
const TOOL_PRESETS = [
|
|
304
|
+
{ id: 'daily', zh: '日常', en: 'Daily', tools: ['argo_search', 'argo_fetch'], descZh: '一问一搜,最快', descEn: 'Fastest for simple queries' },
|
|
305
|
+
{ id: 'research', zh: '研究', en: 'Research', tools: ['argo_search', 'argo_fetch', 'argo_evidence', 'argo_clarify'], descZh: '加证据分级与消歧', descEn: 'Add evidence grading + disambiguation' },
|
|
306
|
+
{ id: 'forensics', zh: '取证', en: 'Forensics', tools: ['argo_search', 'argo_fetch', 'argo_crawl', 'argo_pdf', 'argo_screenshot', 'argo_extract'], descZh: '整站/多页/PDF 留档', descEn: 'Site/multi-page/PDF archiving' },
|
|
237
307
|
];
|
|
238
308
|
|
|
239
309
|
const KEYS = [
|
|
240
310
|
{ ref: 'DEEPSEEK_API_KEY', common: true, zh: 'DeepSeek 官方搜索', en: 'DeepSeek official search' },
|
|
311
|
+
{ ref: 'ARGO_YUANBAO_API_KEY', common: true, zh: '腾讯元宝搜索', en: 'Tencent Yuanbao' },
|
|
241
312
|
{ ref: 'TAVILY_API_KEY', common: true, zh: 'Tavily', en: 'Tavily' },
|
|
242
313
|
{ ref: 'EXA_API_KEY', common: true, zh: 'Exa', en: 'Exa' },
|
|
243
314
|
{ ref: 'BOCHA_API_KEY', common: true, zh: '博查', en: 'Bocha' },
|
|
@@ -261,12 +332,25 @@ window.__ModuleLoader__.load({
|
|
|
261
332
|
{ ref: 'UNPAYWALL_EMAIL', zh: 'Unpaywall 邮箱(非密钥)', en: 'Unpaywall email (not secret)' },
|
|
262
333
|
];
|
|
263
334
|
|
|
335
|
+
const CORE_ENGINES = [
|
|
336
|
+
{ id: 'deepseek', nameZh: 'DeepSeek 搜索', nameEn: 'DeepSeek Search', roleZh: '通用搜索 / 官方对齐', roleEn: 'General / Official Seam', ref: 'DEEPSEEK_API_KEY', keyless: false },
|
|
337
|
+
{ id: 'yuanbao', nameZh: '腾讯元宝', nameEn: 'Tencent Yuanbao', roleZh: '中文对冲主力', roleEn: 'Chinese General Primary', ref: 'ARGO_YUANBAO_API_KEY', keyless: false },
|
|
338
|
+
{ id: 'tavily', nameZh: 'Tavily Search', nameEn: 'Tavily Search', roleZh: 'AI 知识增强检索', roleEn: 'AI Knowledge Retrieval', ref: 'TAVILY_API_KEY', keyless: false },
|
|
339
|
+
{ id: 'exa', nameZh: 'Exa Neural', nameEn: 'Exa Neural', roleZh: '神经语义精准搜索', roleEn: 'Neural Semantic Search', ref: 'EXA_API_KEY', keyless: false },
|
|
340
|
+
{ id: 'brave', nameZh: 'Brave Search', nameEn: 'Brave Search', roleZh: '独立全球 Web 索引', roleEn: 'Independent Global Index', ref: 'BRAVE_API_KEY', keyless: true },
|
|
341
|
+
{ id: 'bocha', nameZh: '博查 Bocha', nameEn: 'Bocha', roleZh: '垂直多源聚合', roleEn: 'Vertical Multi-source', ref: 'BOCHA_API_KEY', keyless: false },
|
|
342
|
+
{ id: 'jina', nameZh: 'Jina Reader', nameEn: 'Jina Reader', roleZh: '正文高保真提取', roleEn: 'Article Text Extraction', ref: '', keyless: true },
|
|
343
|
+
{ id: 'tinyfish', nameZh: 'TinyFish', nameEn: 'TinyFish', roleZh: '无头渲染抓取兜底', roleEn: 'Headless Render Fallback', ref: 'TINYFISH_API_KEY', keyless: true },
|
|
344
|
+
{ id: 'zhihu', nameZh: '知乎 Zhihu', nameEn: 'Zhihu', roleZh: '中文专业问答舆情', roleEn: 'Professional Q&A / Social', ref: 'ZHIHU_ACCESS_SECRET', keyless: true },
|
|
345
|
+
];
|
|
346
|
+
|
|
264
347
|
// argo 引擎开关(argoEnv 的 KEY=VALUE 行)。与 index.js 的 ARGO_ENV_SPECS
|
|
265
348
|
// 同一份名单,由 test/client.test.js 的漂移门禁核对;kind 决定控件:
|
|
266
|
-
// bool
|
|
267
|
-
//
|
|
349
|
+
// bool=开关、num=数字、str=文本、list=名单。def 仅作「未配置时显示什么」,
|
|
350
|
+
// 缺省即不写该行。开关文案写正向行为或明确的关闭动作,不再用 invert 反转
|
|
351
|
+
// (历史 bug 见 index.js 注释)。
|
|
268
352
|
const ENGINE_TOGGLES = [
|
|
269
|
-
{ name: 'ARGO_NO_CACHE', kind: 'bool',
|
|
353
|
+
{ 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
354
|
{ 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
355
|
{ 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
356
|
{ 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 +373,14 @@ window.__ModuleLoader__.load({
|
|
|
289
373
|
{ 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
374
|
{ 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
375
|
{ 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: '
|
|
376
|
+
{ 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' },
|
|
377
|
+
{ 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)' },
|
|
378
|
+
{ 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' },
|
|
379
|
+
{ 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' },
|
|
380
|
+
{ 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' },
|
|
381
|
+
{ 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' },
|
|
382
|
+
{ 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' },
|
|
383
|
+
{ 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
384
|
{ 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
385
|
{ 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
386
|
{ 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 +482,43 @@ window.__ModuleLoader__.load({
|
|
|
391
482
|
actions: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' },
|
|
392
483
|
switchWrap: { display: 'flex', alignItems: 'center', gap: 8 },
|
|
393
484
|
divider: { height: '.5px', background: 'var(--dsw-alias-border-l2, #eee)', margin: '2px 0' },
|
|
485
|
+
metricGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: 10 },
|
|
486
|
+
metricCard: {
|
|
487
|
+
display: 'flex', flexDirection: 'column', gap: 6, padding: '12px 14px',
|
|
488
|
+
borderRadius: 10, border: '.5px solid var(--dsw-alias-border-l3, #e5e7eb)',
|
|
489
|
+
background: 'var(--dsw-alias-bg-layer-2, #f8fafc)',
|
|
490
|
+
},
|
|
491
|
+
metricLabel: { fontSize: 11, fontWeight: 500, color: 'var(--dsw-alias-label-tertiary, #7b818b)', textTransform: 'uppercase', letterSpacing: '0.04em' },
|
|
492
|
+
metricValue: { fontSize: 18, fontWeight: 600, lineHeight: '22px', color: 'var(--dsw-alias-label-primary, #1f2328)' },
|
|
493
|
+
metricSub: { fontSize: 11, lineHeight: '16px', color: 'var(--dsw-alias-label-secondary, #4b5563)' },
|
|
494
|
+
presetGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 },
|
|
495
|
+
presetCard: (active) => ({
|
|
496
|
+
display: 'flex', flexDirection: 'column', gap: 6, padding: '12px 14px',
|
|
497
|
+
borderRadius: 10, cursor: 'pointer', textAlign: 'left',
|
|
498
|
+
border: active ? '1.5px solid var(--dsw-alias-state-business-primary, #3b6ef6)' : '.5px solid var(--dsw-alias-border-l3, #e5e7eb)',
|
|
499
|
+
background: active ? 'var(--dsw-alias-bg-layer-1, #fff)' : 'var(--dsw-alias-settings-card-fill, #fff)',
|
|
500
|
+
boxShadow: active ? '0 1px 3px rgba(59, 110, 246, 0.12)' : 'none',
|
|
501
|
+
}),
|
|
502
|
+
matrixGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 8 },
|
|
503
|
+
matrixCard: {
|
|
504
|
+
display: 'flex', flexDirection: 'column', gap: 4, padding: '10px 12px',
|
|
505
|
+
borderRadius: 8, border: '.5px solid var(--dsw-alias-border-l3, #e5e7eb)',
|
|
506
|
+
background: 'var(--dsw-alias-bg-layer-1, #fff)',
|
|
507
|
+
},
|
|
508
|
+
badge: (color) => ({
|
|
509
|
+
display: 'inline-flex', alignItems: 'center', gap: 4, padding: '2px 8px',
|
|
510
|
+
borderRadius: 999, fontSize: 10, fontWeight: 500, lineHeight: '14px',
|
|
511
|
+
background: color === 'green' ? 'rgba(46, 160, 67, 0.12)' : (color === 'blue' ? 'rgba(59, 110, 246, 0.12)' : 'rgba(100, 116, 139, 0.12)'),
|
|
512
|
+
color: color === 'green' ? '#2da44e' : (color === 'blue' ? '#3b6ef6' : '#64748b'),
|
|
513
|
+
border: '.5px solid ' + (color === 'green' ? '#2da44e' : (color === 'blue' ? '#3b6ef6' : '#64748b')),
|
|
514
|
+
}),
|
|
515
|
+
filterInput: {
|
|
516
|
+
width: '100%', boxSizing: 'border-box', font: 'inherit', fontSize: 12, lineHeight: '18px',
|
|
517
|
+
padding: '6px 10px', borderRadius: 8,
|
|
518
|
+
border: '.5px solid var(--dsw-alias-border-l4, #d0d7de)',
|
|
519
|
+
background: 'var(--dsw-alias-bg-layer-1, #fff)', color: 'inherit',
|
|
520
|
+
marginBottom: 8,
|
|
521
|
+
},
|
|
394
522
|
};
|
|
395
523
|
|
|
396
524
|
function asString(value, fallback) {
|
|
@@ -499,7 +627,7 @@ window.__ModuleLoader__.load({
|
|
|
499
627
|
const initialLang = locale && locale.getSnapshot && locale.getSnapshot().active === 'en' ? 'en' : 'zh';
|
|
500
628
|
const [lang, setLang] = useState(initialLang);
|
|
501
629
|
|
|
502
|
-
const [tab, setTab] = useState('
|
|
630
|
+
const [tab, setTab] = useState('dashboard');
|
|
503
631
|
const [draft, setDraft] = useState(() => toDraft(value));
|
|
504
632
|
const [busy, setBusy] = useState('');
|
|
505
633
|
const [message, setMessage] = useState('');
|
|
@@ -513,6 +641,10 @@ window.__ModuleLoader__.load({
|
|
|
513
641
|
// argoEnv 引擎开关:bool 类即时写;num/str/list 类走草稿 + 分组保存。
|
|
514
642
|
const [envDraft, setEnvDraft] = useState({});
|
|
515
643
|
const [envBusy, setEnvBusy] = useState('');
|
|
644
|
+
const [engineFilter, setEngineFilter] = useState('');
|
|
645
|
+
const [keyFilter, setKeyFilter] = useState('');
|
|
646
|
+
// 路由观测(宿主经 remote 事件推送;无推送时卡片显示终端命令提示)
|
|
647
|
+
const [routeHealth, setRouteHealth] = useState(null);
|
|
516
648
|
|
|
517
649
|
const dirty = useRef(new Set());
|
|
518
650
|
// 读-改-写共享配置字段(keyRefs / nativeTools / argoEnv)时读实时快照,
|
|
@@ -568,10 +700,14 @@ window.__ModuleLoader__.load({
|
|
|
568
700
|
useEffect(() => {
|
|
569
701
|
if (props.view === 'summary') return undefined;
|
|
570
702
|
void refreshCredentials();
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
703
|
+
const offs = [];
|
|
704
|
+
if (props.remote && typeof props.remote.$on === 'function') {
|
|
705
|
+
const offCreds = props.remote.$on('credentials/reference-updated', () => { void refreshCredentials(); });
|
|
706
|
+
const offRoute = props.remote.$on('route-health/updated', (data) => { setRouteHealth(data); });
|
|
707
|
+
if (typeof offCreds === 'function') offs.push(offCreds);
|
|
708
|
+
if (typeof offRoute === 'function') offs.push(offRoute);
|
|
709
|
+
}
|
|
710
|
+
return () => { for (const off of offs) off(); };
|
|
575
711
|
}, [state]);
|
|
576
712
|
|
|
577
713
|
const edit = (key, text) => {
|
|
@@ -674,6 +810,14 @@ window.__ModuleLoader__.load({
|
|
|
674
810
|
void setField('nativeTools', ordered);
|
|
675
811
|
};
|
|
676
812
|
|
|
813
|
+
// 推荐组合:一键套用一组工具(场景化分区之上的快捷入口)。
|
|
814
|
+
// 只增不减会覆盖用户已有勾选,所以是「设为这组」而非「追加这组」。
|
|
815
|
+
const applyToolPreset = async (tools) => {
|
|
816
|
+
const ordered = TOOLS.map((tool) => tool.id).filter((toolId) => tools.includes(toolId));
|
|
817
|
+
for (const extra of tools) if (!ordered.includes(extra)) ordered.push(extra);
|
|
818
|
+
await props.set('nativeTools', ordered);
|
|
819
|
+
};
|
|
820
|
+
|
|
677
821
|
const saveKey = async (ref) => {
|
|
678
822
|
const key = asString(keyDraft[ref]).trim();
|
|
679
823
|
if (!key || !props.credentials) return;
|
|
@@ -737,8 +881,9 @@ window.__ModuleLoader__.load({
|
|
|
737
881
|
const ENV_FALSY = ['', '0', 'false', 'no', 'off', 'n', 'disable', 'disabled', 'none'];
|
|
738
882
|
const envBoolState = (spec, map) => {
|
|
739
883
|
const raw = map[spec.name];
|
|
740
|
-
|
|
741
|
-
|
|
884
|
+
return raw === undefined
|
|
885
|
+
? spec.def === true
|
|
886
|
+
: !ENV_FALSY.includes(String(raw).trim().toLowerCase());
|
|
742
887
|
};
|
|
743
888
|
const writeEnvLines = async (updates) => {
|
|
744
889
|
const map = envMap();
|
|
@@ -754,9 +899,8 @@ window.__ModuleLoader__.load({
|
|
|
754
899
|
};
|
|
755
900
|
const toggleEnv = (spec, on) => {
|
|
756
901
|
if (disabled) return;
|
|
757
|
-
//
|
|
758
|
-
|
|
759
|
-
const value = on === (spec.invert === true) ? '0' : '1';
|
|
902
|
+
// 开关状态直接落 0/1:文案与 env 同向(开=1,关=0),不做反转。
|
|
903
|
+
const value = on ? '1' : '0';
|
|
760
904
|
void writeEnvLines({ [spec.name]: value })
|
|
761
905
|
.then((ok) => report(ok, ok ? t('saved') : t('refused')))
|
|
762
906
|
.catch((cause) => report(false, cause && cause.message ? cause.message : String(cause)));
|
|
@@ -795,6 +939,54 @@ window.__ModuleLoader__.load({
|
|
|
795
939
|
}
|
|
796
940
|
};
|
|
797
941
|
|
|
942
|
+
const applyPreset = async (type) => {
|
|
943
|
+
if (disabled) return;
|
|
944
|
+
setBusy('preset');
|
|
945
|
+
try {
|
|
946
|
+
const map = envMap();
|
|
947
|
+
const ops = [];
|
|
948
|
+
if (type === 'fast') {
|
|
949
|
+
map.ARGO_TFIDF_RERANK = '0';
|
|
950
|
+
map.ARGO_NUMBER_CROSSCHECK = '0';
|
|
951
|
+
map.ARGO_PAID_SEARCH = 'off';
|
|
952
|
+
map.ARGO_ADAPTIVE_GRACE = '1';
|
|
953
|
+
map.ARGO_SERIAL_STAGGER_S = '0.5';
|
|
954
|
+
ops.push({ op: 'set', path: ['searchTimeoutMs'], value: 15000 });
|
|
955
|
+
ops.push({ op: 'set', path: ['fetchTimeoutMs'], value: 15000 });
|
|
956
|
+
} else if (type === 'standard') {
|
|
957
|
+
map.ARGO_TFIDF_RERANK = '1';
|
|
958
|
+
map.ARGO_NUMBER_CROSSCHECK = '1';
|
|
959
|
+
map.ARGO_MINHASH_DEDUPE = '1';
|
|
960
|
+
map.ARGO_PAID_SEARCH = 'auto';
|
|
961
|
+
map.ARGO_ADAPTIVE_GRACE = '1';
|
|
962
|
+
map.ARGO_SERIAL_STAGGER_S = '0.8';
|
|
963
|
+
ops.push({ op: 'set', path: ['searchTimeoutMs'], value: 30000 });
|
|
964
|
+
ops.push({ op: 'set', path: ['fetchTimeoutMs'], value: 25000 });
|
|
965
|
+
} else if (type === 'deep') {
|
|
966
|
+
map.ARGO_TFIDF_RERANK = '1';
|
|
967
|
+
map.ARGO_NUMBER_CROSSCHECK = '1';
|
|
968
|
+
map.ARGO_MINHASH_DEDUPE = '1';
|
|
969
|
+
map.ARGO_SEMANTIC_EVIDENCE = '1';
|
|
970
|
+
map.ARGO_ADAPTIVE_GRACE = '1';
|
|
971
|
+
map.ARGO_PAID_SEARCH = 'auto';
|
|
972
|
+
ops.push({ op: 'set', path: ['searchTimeoutMs'], value: 45000 });
|
|
973
|
+
ops.push({ op: 'set', path: ['fetchTimeoutMs'], value: 35000 });
|
|
974
|
+
ops.push({ op: 'set', path: ['nativeTimeoutMs'], value: 60000 });
|
|
975
|
+
}
|
|
976
|
+
const envLines = Object.keys(map)
|
|
977
|
+
.filter((name) => ENV_NAMES.has(name))
|
|
978
|
+
.map((name) => `${name}=${map[name]}`);
|
|
979
|
+
ops.push({ op: 'set', path: ['argoEnv'], value: envLines });
|
|
980
|
+
const ok = await props.mutate(ops);
|
|
981
|
+
const presetName = type === 'fast' ? t('presetFast') : (type === 'standard' ? t('presetStandard') : t('presetDeep'));
|
|
982
|
+
report(ok, ok ? `${t('presetApplied')} ${presetName}` : t('refused'));
|
|
983
|
+
} catch (cause) {
|
|
984
|
+
report(false, cause && cause.message ? cause.message : String(cause));
|
|
985
|
+
} finally {
|
|
986
|
+
setBusy('');
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
|
|
798
990
|
if (props.view === 'summary') {
|
|
799
991
|
const summary = t('summary')
|
|
800
992
|
.replace('{search}', value.searchProviderEnabled !== false ? t('on') : t('off'))
|
|
@@ -823,8 +1015,8 @@ window.__ModuleLoader__.load({
|
|
|
823
1015
|
? h('p', { style: error ? S.error : S.notice, role: 'status' }, message)
|
|
824
1016
|
: h('p', { style: S.status }, disabled ? t('readonly') : '');
|
|
825
1017
|
|
|
826
|
-
|
|
827
|
-
const
|
|
1018
|
+
// 按 TOOL_GROUPS 顺序分组渲染(场景化分区,替代旧的 core/long 两分)
|
|
1019
|
+
const toolsByGroup = (gid) => TOOLS.filter((tool) => tool.group === gid);
|
|
828
1020
|
const toolCheckbox = (tool) => h('label', { key: tool.id, style: S.check }, [
|
|
829
1021
|
h('input', {
|
|
830
1022
|
key: 'i',
|
|
@@ -913,6 +1105,108 @@ window.__ModuleLoader__.load({
|
|
|
913
1105
|
|
|
914
1106
|
const panel = [];
|
|
915
1107
|
|
|
1108
|
+
if (tab === 'dashboard') {
|
|
1109
|
+
const configuredKeyCount = Object.values(credentials).filter((c) => c && c.configured).length;
|
|
1110
|
+
const keylessEngineCount = CORE_ENGINES.filter((e) => e.keyless).length;
|
|
1111
|
+
const seamSearchOn = value.searchProviderEnabled !== false;
|
|
1112
|
+
const seamFetchOn = value.fetchProviderEnabled !== false;
|
|
1113
|
+
const activeToolCount = asArray(value.nativeTools).length;
|
|
1114
|
+
const poolConcurrency = asNumber(value.searchConcurrency, DEFAULTS.searchConcurrency);
|
|
1115
|
+
const poolIdleMs = asNumber(value.searchIdleMs, DEFAULTS.searchIdleMs);
|
|
1116
|
+
|
|
1117
|
+
panel.push(h('div', { key: 'metrics', style: S.metricGrid }, [
|
|
1118
|
+
h('div', { key: 'pool', style: S.metricCard }, [
|
|
1119
|
+
h('span', { key: 'l', style: S.metricLabel }, t('poolTitle')),
|
|
1120
|
+
h('div', { key: 'v', style: { display: 'flex', alignItems: 'center', gap: 6 } }, [
|
|
1121
|
+
h('span', { key: 'dot', style: S.dot(true) }),
|
|
1122
|
+
h('span', { key: 'val', style: S.metricValue }, `${poolConcurrency} 并发`),
|
|
1123
|
+
]),
|
|
1124
|
+
h('span', { key: 's', style: S.metricSub }, `${t('poolStatusHealthy')} · 保活 ${poolIdleMs / 1000}s`),
|
|
1125
|
+
]),
|
|
1126
|
+
h('div', { key: 'seam', style: S.metricCard }, [
|
|
1127
|
+
h('span', { key: 'l', style: S.metricLabel }, t('accessTitle')),
|
|
1128
|
+
h('span', { key: 'val', style: S.metricValue }, seamSearchOn && seamFetchOn ? '双路接管' : (seamSearchOn || seamFetchOn ? '单路接管' : '未接管')),
|
|
1129
|
+
h('span', { key: 's', style: S.metricSub }, `Search: ${seamSearchOn ? 'ON' : 'OFF'} · Fetch: ${seamFetchOn ? 'ON' : 'OFF'}`),
|
|
1130
|
+
]),
|
|
1131
|
+
h('div', { key: 'tools', style: S.metricCard }, [
|
|
1132
|
+
h('span', { key: 'l', style: S.metricLabel }, t('toolsTitle')),
|
|
1133
|
+
h('span', { key: 'val', style: S.metricValue }, `${activeToolCount} / ${TOOLS.length}`),
|
|
1134
|
+
h('span', { key: 's', style: S.metricSub }, '已就绪原生 MCP 工具'),
|
|
1135
|
+
]),
|
|
1136
|
+
h('div', { key: 'keys', style: S.metricCard }, [
|
|
1137
|
+
h('span', { key: 'l', style: S.metricLabel }, t('keysTitle')),
|
|
1138
|
+
h('span', { key: 'val', style: S.metricValue }, `${configuredKeyCount} 配置 · ${keylessEngineCount} 免密`),
|
|
1139
|
+
h('span', { key: 's', style: S.metricSub }, '覆盖全局多源引擎'),
|
|
1140
|
+
]),
|
|
1141
|
+
]));
|
|
1142
|
+
|
|
1143
|
+
// 路由观测卡:宿主经 remote 事件推送 routeHealth 时展示最近路由决策;
|
|
1144
|
+
// 未推送时给出终端命令提示——不让面板空白,也不假装有数据。
|
|
1145
|
+
panel.push(h(Card, { key: 'route', title: t('routeHealthTitle'), hint: t('routeHealthHint') }, [
|
|
1146
|
+
routeHealth && Array.isArray(routeHealth.samples) && routeHealth.samples.length > 0
|
|
1147
|
+
? h('div', { key: 'rows', style: { display: 'flex', flexDirection: 'column', gap: 6 } },
|
|
1148
|
+
routeHealth.samples.slice(0, 5).map((row, i) =>
|
|
1149
|
+
h('div', { key: 'r' + i, style: S.keyRow }, [
|
|
1150
|
+
h('span', { key: 'q', style: S.label }, String(row.query || '').slice(0, 40)),
|
|
1151
|
+
h('span', { key: 'd', style: S.sub }, `${row.domain || '-'} · ${row.engine || '-'}`),
|
|
1152
|
+
])))
|
|
1153
|
+
: h('p', { key: 'hint', style: S.hint }, t('routeHealthFallback')),
|
|
1154
|
+
]));
|
|
1155
|
+
|
|
1156
|
+
panel.push(h(Card, { key: 'presets', title: t('presetTitle'), hint: t('presetHint') }, [
|
|
1157
|
+
h('div', { key: 'grid', style: S.presetGrid }, [
|
|
1158
|
+
h('div', {
|
|
1159
|
+
key: 'fast',
|
|
1160
|
+
style: S.presetCard(false),
|
|
1161
|
+
onClick: () => { void applyPreset('fast'); },
|
|
1162
|
+
}, [
|
|
1163
|
+
h('div', { key: 't', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } }, [
|
|
1164
|
+
h('span', { key: 'title', style: S.label }, t('presetFast')),
|
|
1165
|
+
h('span', { key: 'badge', style: S.badge('blue') }, '15s 超时'),
|
|
1166
|
+
]),
|
|
1167
|
+
h('span', { key: 'd', style: S.sub }, t('presetFastDesc')),
|
|
1168
|
+
]),
|
|
1169
|
+
h('div', {
|
|
1170
|
+
key: 'standard',
|
|
1171
|
+
style: S.presetCard(true),
|
|
1172
|
+
onClick: () => { void applyPreset('standard'); },
|
|
1173
|
+
}, [
|
|
1174
|
+
h('div', { key: 't', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } }, [
|
|
1175
|
+
h('span', { key: 'title', style: S.label }, t('presetStandard')),
|
|
1176
|
+
h('span', { key: 'badge', style: S.badge('green') }, '标准生产'),
|
|
1177
|
+
]),
|
|
1178
|
+
h('span', { key: 'd', style: S.sub }, t('presetStandardDesc')),
|
|
1179
|
+
]),
|
|
1180
|
+
h('div', {
|
|
1181
|
+
key: 'deep',
|
|
1182
|
+
style: S.presetCard(false),
|
|
1183
|
+
onClick: () => { void applyPreset('deep'); },
|
|
1184
|
+
}, [
|
|
1185
|
+
h('div', { key: 't', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } }, [
|
|
1186
|
+
h('span', { key: 'title', style: S.label }, t('presetDeep')),
|
|
1187
|
+
h('span', { key: 'badge', style: S.badge('blue') }, '深度核验'),
|
|
1188
|
+
]),
|
|
1189
|
+
h('span', { key: 'd', style: S.sub }, t('presetDeepDesc')),
|
|
1190
|
+
]),
|
|
1191
|
+
]),
|
|
1192
|
+
]));
|
|
1193
|
+
|
|
1194
|
+
panel.push(h(Card, { key: 'matrix', title: t('matrixTitle'), hint: t('matrixHint') }, [
|
|
1195
|
+
h('div', { key: 'grid', style: S.matrixGrid }, CORE_ENGINES.map((engine) => {
|
|
1196
|
+
const hasKey = engine.ref && credentials[engine.ref] && credentials[engine.ref].configured;
|
|
1197
|
+
const badgeType = hasKey ? 'green' : (engine.keyless ? 'blue' : 'gray');
|
|
1198
|
+
const badgeText = hasKey ? t('matrixConfigured') : (engine.keyless ? t('matrixKeyless') : t('matrixPending'));
|
|
1199
|
+
return h('div', { key: engine.id, style: S.matrixCard }, [
|
|
1200
|
+
h('div', { key: 'h', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 6 } }, [
|
|
1201
|
+
h('span', { key: 'n', style: S.label }, lang === 'en' ? engine.nameEn : engine.nameZh),
|
|
1202
|
+
h('span', { key: 'b', style: S.badge(badgeType) }, badgeText),
|
|
1203
|
+
]),
|
|
1204
|
+
h('span', { key: 'r', style: S.sub }, lang === 'en' ? engine.roleEn : engine.roleZh),
|
|
1205
|
+
]);
|
|
1206
|
+
})),
|
|
1207
|
+
]));
|
|
1208
|
+
}
|
|
1209
|
+
|
|
916
1210
|
if (tab === 'access') {
|
|
917
1211
|
panel.push(h(Card, { key: 'seam', title: t('accessTitle') }, [
|
|
918
1212
|
h(Switch, {
|
|
@@ -978,15 +1272,27 @@ window.__ModuleLoader__.load({
|
|
|
978
1272
|
}
|
|
979
1273
|
|
|
980
1274
|
if (tab === 'tools') {
|
|
1275
|
+
panel.push(h(Card, { key: 'presets', title: t('toolPresetTitle'), hint: t('toolPresetHint') }, [
|
|
1276
|
+
h('div', { key: 'grid', style: S.presetGrid }, TOOL_PRESETS.map((preset) =>
|
|
1277
|
+
h('button', {
|
|
1278
|
+
key: preset.id, type: 'button', disabled,
|
|
1279
|
+
style: S.presetCard(false),
|
|
1280
|
+
onClick: () => { void applyToolPreset(preset.tools); },
|
|
1281
|
+
}, [
|
|
1282
|
+
h('div', { key: 't', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } }, [
|
|
1283
|
+
h('span', { key: 'title', style: S.label }, lang === 'en' ? preset.en : preset.zh),
|
|
1284
|
+
h('span', { key: 'badge', style: S.badge('blue') }, String(preset.tools.length) + ' 工具'),
|
|
1285
|
+
]),
|
|
1286
|
+
h('span', { key: 'd', style: S.sub }, lang === 'en' ? preset.descEn : preset.descZh),
|
|
1287
|
+
]))),
|
|
1288
|
+
]));
|
|
981
1289
|
panel.push(h(Card, { key: 'tools', title: t('toolsTitle'), hint: t('toolsHint') }, [
|
|
982
|
-
|
|
983
|
-
h('
|
|
984
|
-
h('div', { key: '
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
h('span', { key: 'l', style: S.label }, t('longTools')),
|
|
989
|
-
h('div', { key: 'g', style: S.toolGrid }, longTools.map(toolCheckbox)),
|
|
1290
|
+
...TOOL_GROUPS.map((grp, gi) => [
|
|
1291
|
+
gi > 0 ? h('div', { key: 'div' + gi, style: S.divider }) : null,
|
|
1292
|
+
h('div', { key: grp.id, style: { display: 'flex', flexDirection: 'column', gap: 4 } }, [
|
|
1293
|
+
h('span', { key: 'l', style: S.label }, lang === 'en' ? grp.en : grp.zh),
|
|
1294
|
+
h('div', { key: 'g', style: S.toolGrid }, toolsByGroup(grp.id).map(toolCheckbox)),
|
|
1295
|
+
]),
|
|
990
1296
|
]),
|
|
991
1297
|
h('div', { key: 'g2', style: S.grid }, [
|
|
992
1298
|
h(NumberField, {
|
|
@@ -1007,15 +1313,50 @@ window.__ModuleLoader__.load({
|
|
|
1007
1313
|
|
|
1008
1314
|
if (tab === 'engines') {
|
|
1009
1315
|
const map = envMap();
|
|
1010
|
-
|
|
1316
|
+
// 渲染顺序即面板顺序。**不是**从 ENGINE_TOGGLES 去重推导——那样分组
|
|
1317
|
+
// 顺序会随白名单增删而漂移,用户每次升级都看到重排的面板。
|
|
1318
|
+
//
|
|
1319
|
+
// 历史 bug(2026-10-21 定位):`paid` 一度不在这个清单里,于是
|
|
1320
|
+
// ARGO_PAID_SEARCH 与 ARGO_BUDGET_DEEPSEEK_SEARCH 两条开关在
|
|
1321
|
+
// index.js 白名单、client.js ENGINE_TOGGLES、双语文案三处都齐全,
|
|
1322
|
+
// 却在面板上完全不显示——用户配好了预算却找不到入口,还以为是插件
|
|
1323
|
+
// 没装。漂移门禁只核对「两边名单同集合、字段一致」,核对不到「这个
|
|
1324
|
+
// 分组到底渲不渲染」;已由 test/client.test.js 的「engines tab renders
|
|
1325
|
+
// a control for every ENGINE_TOGGLES switch」补上(见该测试注释)。
|
|
1326
|
+
const groups = ['cache', 'fetch', 'semantic', 'engines', 'net', 'local', 'paid', 'misc'];
|
|
1011
1327
|
const groupTitle = (group) => t('group' + group.charAt(0).toUpperCase() + group.slice(1));
|
|
1012
1328
|
panel.push(h('p', { key: 'hint', style: S.hint }, t('enginesHint')));
|
|
1329
|
+
panel.push(h('input', {
|
|
1330
|
+
key: 'filter',
|
|
1331
|
+
type: 'text',
|
|
1332
|
+
style: S.filterInput,
|
|
1333
|
+
placeholder: t('searchFilterPlaceholder'),
|
|
1334
|
+
value: engineFilter,
|
|
1335
|
+
onChange: (event) => setEngineFilter(event.target.value),
|
|
1336
|
+
}));
|
|
1337
|
+
const query = engineFilter.trim().toLowerCase();
|
|
1013
1338
|
for (const group of groups) {
|
|
1014
|
-
|
|
1339
|
+
let specs = ENGINE_TOGGLES.filter((spec) => spec.group === group);
|
|
1340
|
+
if (query) {
|
|
1341
|
+
const groupNameMatch = group.toLowerCase().includes(query) || groupTitle(group).toLowerCase().includes(query);
|
|
1342
|
+
if (!groupNameMatch) {
|
|
1343
|
+
specs = specs.filter((spec) => {
|
|
1344
|
+
const name = spec.name.toLowerCase();
|
|
1345
|
+
const zh = (spec.zh || '').toLowerCase();
|
|
1346
|
+
const en = (spec.en || '').toLowerCase();
|
|
1347
|
+
const hintZh = (spec.hintZh || '').toLowerCase();
|
|
1348
|
+
const hintEn = (spec.hintEn || '').toLowerCase();
|
|
1349
|
+
return name.includes(query) || zh.includes(query) || en.includes(query) || hintZh.includes(query) || hintEn.includes(query);
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1015
1353
|
if (specs.length === 0) continue;
|
|
1016
1354
|
const bools = specs.filter((spec) => spec.kind === 'bool');
|
|
1017
1355
|
const fields = specs.filter((spec) => spec.kind !== 'bool');
|
|
1018
1356
|
const children = [];
|
|
1357
|
+
if (group === 'paid') {
|
|
1358
|
+
children.push(h('p', { key: 'groupHint', style: S.hint }, t('groupPaidHint')));
|
|
1359
|
+
}
|
|
1019
1360
|
for (const spec of bools) {
|
|
1020
1361
|
// 未配置时显示 argo 默认态并标注「默认」,配置过的显示自定义值,
|
|
1021
1362
|
// 让「这一行到底生不生效」在面板上一眼可辨。
|
|
@@ -1078,10 +1419,22 @@ window.__ModuleLoader__.load({
|
|
|
1078
1419
|
}
|
|
1079
1420
|
|
|
1080
1421
|
if (tab === 'keys') {
|
|
1422
|
+
const query = keyFilter.trim().toLowerCase();
|
|
1423
|
+
const displayKeys = query
|
|
1424
|
+
? KEYS.filter((entry) => entry.ref.toLowerCase().includes(query) || (entry.zh && entry.zh.toLowerCase().includes(query)) || (entry.en && entry.en.toLowerCase().includes(query)))
|
|
1425
|
+
: visibleKeys;
|
|
1081
1426
|
panel.push(h(Card, { key: 'keys', title: t('keysTitle'), hint: t('keysHint') }, [
|
|
1082
|
-
h('
|
|
1083
|
-
|
|
1084
|
-
|
|
1427
|
+
h('input', {
|
|
1428
|
+
key: 'filter',
|
|
1429
|
+
type: 'text',
|
|
1430
|
+
style: S.filterInput,
|
|
1431
|
+
placeholder: t('searchFilterPlaceholder'),
|
|
1432
|
+
value: keyFilter,
|
|
1433
|
+
onChange: (event) => setKeyFilter(event.target.value),
|
|
1434
|
+
}),
|
|
1435
|
+
h('span', { key: 'l', style: S.label }, query ? t('keysTitle') : t('commonKeys')),
|
|
1436
|
+
...displayKeys.map(keyRow),
|
|
1437
|
+
query ? null : h('div', { key: 'a', style: S.actions }, [
|
|
1085
1438
|
h('button', {
|
|
1086
1439
|
key: 'more',
|
|
1087
1440
|
type: 'button',
|
|
@@ -1178,6 +1531,7 @@ window.__ModuleLoader__.load({
|
|
|
1178
1531
|
h('p', { key: 'i', style: S.hint }, t('intro')),
|
|
1179
1532
|
]),
|
|
1180
1533
|
h('div', { key: 'tabs', style: S.tabs }, [
|
|
1534
|
+
tabButton('dashboard', t('tabDashboard')),
|
|
1181
1535
|
tabButton('access', t('tabAccess')),
|
|
1182
1536
|
tabButton('tools', t('tabTools')),
|
|
1183
1537
|
tabButton('engines', t('tabEngines')),
|
|
@@ -1237,6 +1591,7 @@ window.__ModuleLoader__.load({
|
|
|
1237
1591
|
// 生成产物)同名同集合,ENGINE_TOGGLES 必须与 index.js 的 ARGO_ENV_SPECS
|
|
1238
1592
|
// 同名同集合——两边任何一边增删而另一边没跟上,测试即红。
|
|
1239
1593
|
exports.TOOLS = TOOLS;
|
|
1594
|
+
exports.TOOL_GROUPS = TOOL_GROUPS;
|
|
1240
1595
|
exports.ENGINE_TOGGLES = ENGINE_TOGGLES;
|
|
1241
1596
|
|
|
1242
1597
|
return module.exports;
|