feat(data): 能力路由矩阵落地——五档入册、除权独立路由、插件源下限放宽

- 新增能力注册表 capabilities.py 与 /capability-matrix: 六能力(实时/日K/
  分钟/五档/除权/财务)统一 candidates/pending/usable, 各页门控以 usable 为准
- 五档盘口 (depth5) 进入矩阵: tf_tier=pro, 插件数据集白名单暂未开放
- 除权「跟随日K」(same_as_daily) 下线: 每能力独立路由, 四处跟随解析移除,
  存量旧值经 preferences getter 回退 tickflow 自愈; 矩阵显示与实际拉取口径一致
- 实时轮询下限: 路由到插件/自定义源时不受 TickFlow 档位限速, 通用下限 1s
  (默认间隔仍 6s, TickFlow 路由档位查表不变)
- 插件 manifest 新增 homepage 透传; loader 输出 api_key_masked (Key 脱敏
  与 TickFlow 同一展示契约); fuyao 声明官网
This commit is contained in:
shy3130
2026-08-30 19:05:21 +08:00
parent 578a531743
commit b4aa5918fc
14 changed files with 581 additions and 25 deletions
@@ -11,6 +11,7 @@ from pathlib import Path
import yaml
from app import secrets_store
from app.config import settings
from app.data_providers.custom.config import (
DEFAULT_TIMEOUT,
@@ -89,6 +90,19 @@ def list_plugins() -> list[dict]:
return list(_PLUGIN_STATUS.values())
def _plugin_key_masked(name: str, api_key_env: str) -> str:
"""插件当前生效 Key 的脱敏串 (secrets.json 优先, .env 兜底), 未配置返回空。
与 TickFlow Key 的展示契约一致 (settings API 的 tickflow_api_key_masked):
完整 Key 永不出后端, 只出 mask() 结果, 供设置页常驻显示。
"""
env = str(api_key_env or "").strip()
if not env:
return ""
key = secrets_store.get_env_backed_secret(f"{name.lower()}_api_key", env)
return secrets_store.mask(key) if key else ""
def plugin_manifest(name: str) -> dict | None:
"""读取指定插件的 plugin.yaml 清单。"""
plugin_dir = plugins_dir() / (name or "")
@@ -560,7 +574,9 @@ def _register_one_plugin(manifest: dict) -> None:
"status": reason,
"description": manifest.get("description", ""),
"install_hint": manifest.get("install_hint", ""),
"homepage": manifest.get("homepage", ""),
"api_key_env": manifest.get("api_key_env", ""),
"api_key_masked": _plugin_key_masked(name, manifest.get("api_key_env", "")),
}
if not available:
return # 依赖没装: 不注册, 但状态已记录供 UI 显示