V10.513 模組化外部工具診斷 payload
All checks were successful
CD Pipeline / deploy (push) Successful in 1m8s

This commit is contained in:
OoO
2026-05-31 20:53:00 +08:00
parent 73d2d863e5
commit 89d8c454bb
6 changed files with 233 additions and 175 deletions

View File

@@ -4,6 +4,7 @@
================================================================================
【已完成】
- V10.513 外部工具診斷頁 payload 模組化:新增 `services/external_tool_payload_service.py`,把 Metabase/Grist/Webcrumbs 的診斷 payload 與 Webcrumbs host data 組裝移出 `routes/system_public_routes.py`,讓 route 回到 HTTP glue`system_public_routes.py` 從 600+ 行降至 500 行內。
- V10.512 Webcrumbs live plugin 接上 MOMO/PChome 只讀 host data新增 `services/webcrumbs_host_data_service.py`,復用 `competitor_intel_repository.fetch_top_competitor_risks()` / coverage把 exact / total_price / price_alert_exact 價差摘要轉成 `StockPlatformSharedUI.marketSnapshot` / `aiCandidate`;不呼叫 LLM、不抓外站、不寫 DB無風險或失敗時仍輸出安全空狀態。
- V10.511 Webcrumbs live plugin 補 host data 安全空狀態:`/webcrumbs` 會注入 `StockPlatformSharedUI.marketSnapshot` / `aiCandidate` 的診斷空資料,避免 plugin fallback demo 數字被誤認成真實市場或 AI 建議。
- V10.510 Webcrumbs 從 runtime 接線推進到專案內 live plugin 試點:`/webcrumbs` 會設定 `StockPlatformSharedUI.allowedPluginUris` 並嵌入同源 `/webcrumbs-assets/plugins/finance.market-ticker-strip/0.1.0`、`finance.ai-candidate-card/0.1.0`,用同一頁同時驗 runtime、plugin proxy 與共享 UI loader 初始化。

View File

@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.512"
SYSTEM_VERSION = "V10.513"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -13,6 +13,7 @@
## 📅 詳細更新日誌 (考古存檔)
### 2026-05-31Webcrumbs 共用 UI Runtime 與市場情報 writer approval
- **V10.513 外部工具診斷 payload 模組化**: 新增 `services/external_tool_payload_service.py`,把 Metabase/Grist/Webcrumbs 診斷頁 payload 與 Webcrumbs host data 組裝從 `routes/system_public_routes.py` 移出route 保持 HTTP glue 與 asset proxy避免 Webcrumbs live plugin 與比價 host data 持續把公開系統 route 撐成大檔。
- **V10.512 Webcrumbs MOMO/PChome host data**: 新增 `services/webcrumbs_host_data_service.py`,讓 `/webcrumbs` live plugin preview 復用 `competitor_intel_repository.fetch_top_competitor_risks()` 與 coverage將 exact / total_price / price_alert_exact 的只讀價差摘要轉成 `StockPlatformSharedUI.marketSnapshot` / `aiCandidate`;不呼叫 LLM、不抓外站、不寫 DB無風險或讀取失敗時仍輸出安全空狀態。
- **V10.511 Webcrumbs host data 安全空狀態**: `/webcrumbs` 會注入 `StockPlatformSharedUI.marketSnapshot` / `aiCandidate` 的診斷空資料,避免 Shared UI Hub plugin 因資料源未接入而 fallback 顯示假市場數字、假 AI 候選或假信心分數。
- **V10.510 Webcrumbs live plugin 試點**: `/webcrumbs` 診斷頁不再只顯示 runtime 設定,新增 `StockPlatformSharedUI.allowedPluginUris` 與同源 `/webcrumbs-assets/plugins/finance.market-ticker-strip/0.1.0``finance.ai-candidate-card/0.1.0` live plugin embed讓 shared-ui loader、plugin asset proxy 與頁面初始化時序能在 momo-pro 內直接驗收。

View File

@@ -10,7 +10,7 @@ import mimetypes
import posixpath
import zipfile
from datetime import datetime, timezone, timedelta
from urllib.parse import quote, urlparse
from urllib.parse import quote
from flask import Blueprint, Response, jsonify, render_template, send_from_directory, url_for
import requests
@@ -20,19 +20,13 @@ from auth import login_required
from config import (
BASE_DIR,
DATABASE_TYPE,
GRIST_URL,
METABASE_URL,
SYSTEM_VERSION,
WEBCRUMBS_ASSET_UPSTREAM_URL,
WEBCRUMBS_BASE_URL,
WEBCRUMBS_ENABLED,
WEBCRUMBS_PLUGIN_BASE_URL,
WEBCRUMBS_RUNTIME_URL,
WEBCRUMBS_RUNTIME_VERSION,
)
from database.manager import DatabaseManager
from database.models import Product, PriceRecord
from services.webcrumbs_host_data_service import build_webcrumbs_marketplace_host_data
from services.external_tool_payload_service import build_external_tool_payload
from services.json_storage import load_categories
from services.logger_manager import SystemLogger
from utils.security import safe_join
@@ -48,155 +42,6 @@ STATIC_DIR = os.path.join(BASE_DIR, 'web/static')
WEBCRUMBS_ASSET_ALLOWED_PREFIXES = ('loader/', 'plugins/', 'demo/')
def _safe_launch_url(configured_url, bridge_path):
"""Return a browser-safe launch URL, keeping nav links inside momo-pro by default."""
candidate = (configured_url or '').strip()
if not candidate or candidate.rstrip('/') == bridge_path.rstrip('/'):
return ''
if candidate.startswith('/') and not candidate.startswith('//'):
return candidate
parsed = urlparse(candidate)
if parsed.scheme in {'http', 'https'} and parsed.hostname == 'mo.wooo.work':
if parsed.path.rstrip('/') == bridge_path.rstrip('/'):
return ''
return candidate
return ''
def _configured_url_label(configured_url):
candidate = (configured_url or '').strip()
if not candidate:
return '未設定'
parsed = urlparse(candidate)
if parsed.scheme in {'http', 'https'} and parsed.hostname != 'mo.wooo.work':
return f'{parsed.hostname}(已由入口攔截)'
return candidate
def _is_blocked_external_url(configured_url):
candidate = (configured_url or '').strip()
if not candidate or candidate.startswith('/') and not candidate.startswith('//'):
return False
parsed = urlparse(candidate)
return parsed.scheme in {'http', 'https'} and parsed.hostname != 'mo.wooo.work'
def _external_tool_payload(kind):
if kind == 'metabase':
launch_url = _safe_launch_url(METABASE_URL, '/metabase')
return {
'key': 'metabase',
'eyebrow': 'Analytics Bridge',
'title': '自訂圖表入口',
'status_label': '待接上 BI 服務' if not launch_url else '可開啟',
'summary': '這裡是 momo-pro 的 Metabase 安全入口。導覽先停在系統內部,不再出現空白頁或錯站外跳。',
'detail': '目前正式主機沒有啟用 Metabase / Grist 的 bi profile 容器Gateway 也沒有可用 proxy因此先提供可用的分析替代入口與設定診斷。',
'launch_href': launch_url,
'launch_label': '開啟 Metabase',
'configured_label': _configured_url_label(METABASE_URL),
'checks': [
{'label': '導覽路由', 'value': '/metabase', 'state': 'ok'},
{'label': '跨站保護', 'value': '已鎖在 momo-pro', 'state': 'ok'},
{'label': 'BI 服務', 'value': '尚未接入 proxy', 'state': 'warn'},
],
'actions': [
{'label': '月份總表', 'href': '/monthly_summary_analysis', 'icon': 'fas fa-table'},
{'label': '成長分析', 'href': '/growth_analysis', 'icon': 'fas fa-arrow-trend-up'},
{'label': '當日業績', 'href': '/daily_sales', 'icon': 'fas fa-calendar-day'},
],
}
if kind == 'webcrumbs':
runtime_ready = bool(WEBCRUMBS_ENABLED and WEBCRUMBS_RUNTIME_URL)
launch_url = WEBCRUMBS_BASE_URL if WEBCRUMBS_BASE_URL else ''
plugin_base = (WEBCRUMBS_PLUGIN_BASE_URL or '').rstrip('/')
plugin_previews = []
if plugin_base:
plugin_previews = [
{
'label': 'Market ticker strip',
'uri': f'{plugin_base}/finance.market-ticker-strip/0.1.0',
},
{
'label': 'AI candidate card',
'uri': f'{plugin_base}/finance.ai-candidate-card/0.1.0',
},
]
try:
plugin_seed_data = build_webcrumbs_marketplace_host_data(limit=5)
except Exception as exc:
sys_log.warning(f"[Webcrumbs] host data build skipped: {exc}")
plugin_seed_data = {
'marketSnapshot': [{
'name': 'MOMO/PChome data source unavailable',
'price': 'not_available',
'change_pct': 'not_available',
'freshness_status': 'diagnostic_unavailable',
}],
'aiCandidate': {
'ticker': '-',
'name': 'MOMO/PChome host data unavailable',
'thesis': 'Webcrumbs runtime 已接入,但只讀比價摘要暫時不可用;本頁不顯示 fallback demo 數字。',
'confidence_score': 'not_available',
'risk_level': 'source_unavailable',
'release_status': 'blocked',
'evidence_refs': ['competitor_intel_repository_unavailable'],
'updated_at': SYSTEM_VERSION,
},
}
return {
'key': 'webcrumbs',
'eyebrow': 'Shared UI Runtime',
'title': 'Webcrumbs 共用 UI Runtime',
'status_label': 'Runtime 已接入' if runtime_ready else 'Runtime 未啟用',
'summary': 'Webcrumbs 已接到 momo-pro 全站 shell正式頁面透過同源 asset proxy 載入 shared-ui loader 與版本化外掛。',
'detail': '工具入口可開啟 Webcrumbs/Open Design正式頁面載入則走 /webcrumbs-assets/,避免跨域憑證或官方 @latest 造成生產頁面不穩。',
'launch_href': launch_url,
'launch_label': '開啟 Webcrumbs',
'configured_label': WEBCRUMBS_RUNTIME_URL or '未設定',
'checks': [
{'label': '導覽路由', 'value': '/webcrumbs', 'state': 'ok'},
{'label': 'Runtime', 'value': '已載入全站 shell' if runtime_ready else '未啟用或 URL 無效', 'state': 'ok' if runtime_ready else 'warn'},
{'label': '版本', 'value': WEBCRUMBS_RUNTIME_VERSION or '未指定', 'state': 'ok' if WEBCRUMBS_RUNTIME_VERSION else 'warn'},
{'label': 'Plugin Base', 'value': WEBCRUMBS_PLUGIN_BASE_URL or '未設定', 'state': 'ok' if WEBCRUMBS_PLUGIN_BASE_URL else 'warn'},
{'label': 'Asset Proxy', 'value': WEBCRUMBS_ASSET_UPSTREAM_URL or '未設定', 'state': 'ok' if WEBCRUMBS_ASSET_UPSTREAM_URL else 'warn'},
],
'actions': [
{'label': '商品看板', 'href': '/', 'icon': 'fas fa-border-all'},
{'label': '比價覆核', 'href': '/?filter=pchome_review', 'icon': 'fas fa-scale-balanced'},
{'label': 'AI 觀測台', 'href': '/observability/overview', 'icon': 'fas fa-satellite-dish'},
],
'plugin_previews': plugin_previews,
'plugin_preview_uris': [preview['uri'] for preview in plugin_previews],
'plugin_seed_data': plugin_seed_data,
}
launch_url = _safe_launch_url(GRIST_URL, '/grist')
blocked_external = _is_blocked_external_url(GRIST_URL)
return {
'key': 'grist',
'eyebrow': 'Data Collaboration',
'title': '資料協作入口',
'status_label': '錯鏈已攔截' if blocked_external else ('可開啟' if launch_url else '待接上協作服務'),
'summary': '資料協作入口已回到 momo-pro 內部,不會再連到其他專案站台。',
'detail': 'Grist 的正式 momo-pro 專案隔離尚未接上 Gateway在完成專屬服務前本頁會提供資料作業入口與目前設定狀態。',
'launch_href': launch_url,
'launch_label': '開啟 Grist',
'configured_label': _configured_url_label(GRIST_URL),
'checks': [
{'label': '導覽路由', 'value': '/grist', 'state': 'ok'},
{'label': '錯站攔截', 'value': '已攔截外部錯鏈' if blocked_external else '未偵測錯鏈', 'state': 'ok'},
{'label': '協作服務', 'value': '尚未接入 proxy', 'state': 'warn'},
],
'actions': [
{'label': '月份總表', 'href': '/monthly_summary_analysis', 'icon': 'fas fa-table'},
{'label': '雲端匯入', 'href': '/auto_import', 'icon': 'fas fa-download'},
{'label': '業績分析', 'href': '/sales_analysis', 'icon': 'fas fa-chart-bar'},
],
}
@system_public_bp.route('/favicon.ico')
def favicon():
"""使用既有品牌圖示回應瀏覽器預設 favicon 探測,避免全站 404 噪音。"""
@@ -233,7 +78,7 @@ def metabase_status():
'external_tool_status.html',
active_page='metabase',
system_version=SYSTEM_VERSION,
tool=_external_tool_payload('metabase'),
tool=build_external_tool_payload('metabase'),
)
@@ -246,7 +91,7 @@ def grist_status():
'external_tool_status.html',
active_page='grist',
system_version=SYSTEM_VERSION,
tool=_external_tool_payload('grist'),
tool=build_external_tool_payload('grist'),
)
@@ -259,7 +104,7 @@ def webcrumbs_status():
'external_tool_status.html',
active_page='webcrumbs',
system_version=SYSTEM_VERSION,
tool=_external_tool_payload('webcrumbs'),
tool=build_external_tool_payload('webcrumbs'),
)

View File

@@ -0,0 +1,209 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""外部工具診斷頁 payload 組裝服務。
Route 只負責 HTTP glueMetabase、Grist、Webcrumbs 的狀態文案、
替代入口與 shared-ui host data 在這裡集中組裝。
"""
from __future__ import annotations
from urllib.parse import urlparse
from config import (
GRIST_URL,
METABASE_URL,
SYSTEM_VERSION,
WEBCRUMBS_ASSET_UPSTREAM_URL,
WEBCRUMBS_BASE_URL,
WEBCRUMBS_ENABLED,
WEBCRUMBS_PLUGIN_BASE_URL,
WEBCRUMBS_RUNTIME_URL,
WEBCRUMBS_RUNTIME_VERSION,
)
from services.logger_manager import SystemLogger
from services.webcrumbs_host_data_service import build_webcrumbs_marketplace_host_data
sys_log = SystemLogger("ExternalToolPayloadService").get_logger()
def _safe_launch_url(configured_url, bridge_path):
"""回傳安全的站內啟動 URL避免導覽跳到其他專案。"""
candidate = (configured_url or "").strip()
if not candidate or candidate.rstrip("/") == bridge_path.rstrip("/"):
return ""
if candidate.startswith("/") and not candidate.startswith("//"):
return candidate
parsed = urlparse(candidate)
if parsed.scheme in {"http", "https"} and parsed.hostname == "mo.wooo.work":
if parsed.path.rstrip("/") == bridge_path.rstrip("/"):
return ""
return candidate
return ""
def _configured_url_label(configured_url):
candidate = (configured_url or "").strip()
if not candidate:
return "未設定"
parsed = urlparse(candidate)
if parsed.scheme in {"http", "https"} and parsed.hostname != "mo.wooo.work":
return f"{parsed.hostname}(已由入口攔截)"
return candidate
def _is_blocked_external_url(configured_url):
candidate = (configured_url or "").strip()
if not candidate or candidate.startswith("/") and not candidate.startswith("//"):
return False
parsed = urlparse(candidate)
return parsed.scheme in {"http", "https"} and parsed.hostname != "mo.wooo.work"
def _webcrumbs_seed_data():
try:
return build_webcrumbs_marketplace_host_data(limit=5)
except Exception as exc:
sys_log.warning(f"[Webcrumbs] host data build skipped: {exc}")
return {
"marketSnapshot": [
{
"name": "MOMO/PChome data source unavailable",
"price": "not_available",
"change_pct": "not_available",
"freshness_status": "diagnostic_unavailable",
}
],
"aiCandidate": {
"ticker": "-",
"name": "MOMO/PChome host data unavailable",
"thesis": "Webcrumbs runtime 已接入,但只讀比價摘要暫時不可用;本頁不顯示 fallback demo 數字。",
"confidence_score": "not_available",
"risk_level": "source_unavailable",
"release_status": "blocked",
"evidence_refs": ["competitor_intel_repository_unavailable"],
"updated_at": SYSTEM_VERSION,
},
}
def _metabase_payload():
launch_url = _safe_launch_url(METABASE_URL, "/metabase")
return {
"key": "metabase",
"eyebrow": "Analytics Bridge",
"title": "自訂圖表入口",
"status_label": "待接上 BI 服務" if not launch_url else "可開啟",
"summary": "這裡是 momo-pro 的 Metabase 安全入口。導覽先停在系統內部,不再出現空白頁或錯站外跳。",
"detail": "目前正式主機沒有啟用 Metabase / Grist 的 bi profile 容器Gateway 也沒有可用 proxy因此先提供可用的分析替代入口與設定診斷。",
"launch_href": launch_url,
"launch_label": "開啟 Metabase",
"configured_label": _configured_url_label(METABASE_URL),
"checks": [
{"label": "導覽路由", "value": "/metabase", "state": "ok"},
{"label": "跨站保護", "value": "已鎖在 momo-pro", "state": "ok"},
{"label": "BI 服務", "value": "尚未接入 proxy", "state": "warn"},
],
"actions": [
{"label": "月份總表", "href": "/monthly_summary_analysis", "icon": "fas fa-table"},
{"label": "成長分析", "href": "/growth_analysis", "icon": "fas fa-arrow-trend-up"},
{"label": "當日業績", "href": "/daily_sales", "icon": "fas fa-calendar-day"},
],
}
def _webcrumbs_payload():
runtime_ready = bool(WEBCRUMBS_ENABLED and WEBCRUMBS_RUNTIME_URL)
launch_url = WEBCRUMBS_BASE_URL if WEBCRUMBS_BASE_URL else ""
plugin_base = (WEBCRUMBS_PLUGIN_BASE_URL or "").rstrip("/")
plugin_previews = []
if plugin_base:
plugin_previews = [
{
"label": "Market ticker strip",
"uri": f"{plugin_base}/finance.market-ticker-strip/0.1.0",
},
{
"label": "AI candidate card",
"uri": f"{plugin_base}/finance.ai-candidate-card/0.1.0",
},
]
return {
"key": "webcrumbs",
"eyebrow": "Shared UI Runtime",
"title": "Webcrumbs 共用 UI Runtime",
"status_label": "Runtime 已接入" if runtime_ready else "Runtime 未啟用",
"summary": "Webcrumbs 已接到 momo-pro 全站 shell正式頁面透過同源 asset proxy 載入 shared-ui loader 與版本化外掛。",
"detail": "工具入口可開啟 Webcrumbs/Open Design正式頁面載入則走 /webcrumbs-assets/,避免跨域憑證或官方 @latest 造成生產頁面不穩。",
"launch_href": launch_url,
"launch_label": "開啟 Webcrumbs",
"configured_label": WEBCRUMBS_RUNTIME_URL or "未設定",
"checks": [
{"label": "導覽路由", "value": "/webcrumbs", "state": "ok"},
{
"label": "Runtime",
"value": "已載入全站 shell" if runtime_ready else "未啟用或 URL 無效",
"state": "ok" if runtime_ready else "warn",
},
{
"label": "版本",
"value": WEBCRUMBS_RUNTIME_VERSION or "未指定",
"state": "ok" if WEBCRUMBS_RUNTIME_VERSION else "warn",
},
{
"label": "Plugin Base",
"value": WEBCRUMBS_PLUGIN_BASE_URL or "未設定",
"state": "ok" if WEBCRUMBS_PLUGIN_BASE_URL else "warn",
},
{
"label": "Asset Proxy",
"value": WEBCRUMBS_ASSET_UPSTREAM_URL or "未設定",
"state": "ok" if WEBCRUMBS_ASSET_UPSTREAM_URL else "warn",
},
],
"actions": [
{"label": "商品看板", "href": "/", "icon": "fas fa-border-all"},
{"label": "比價覆核", "href": "/?filter=pchome_review", "icon": "fas fa-scale-balanced"},
{"label": "AI 觀測台", "href": "/observability/overview", "icon": "fas fa-satellite-dish"},
],
"plugin_previews": plugin_previews,
"plugin_preview_uris": [preview["uri"] for preview in plugin_previews],
"plugin_seed_data": _webcrumbs_seed_data(),
}
def _grist_payload():
launch_url = _safe_launch_url(GRIST_URL, "/grist")
blocked_external = _is_blocked_external_url(GRIST_URL)
return {
"key": "grist",
"eyebrow": "Data Collaboration",
"title": "資料協作入口",
"status_label": "錯鏈已攔截" if blocked_external else ("可開啟" if launch_url else "待接上協作服務"),
"summary": "資料協作入口已回到 momo-pro 內部,不會再連到其他專案站台。",
"detail": "Grist 的正式 momo-pro 專案隔離尚未接上 Gateway在完成專屬服務前本頁會提供資料作業入口與目前設定狀態。",
"launch_href": launch_url,
"launch_label": "開啟 Grist",
"configured_label": _configured_url_label(GRIST_URL),
"checks": [
{"label": "導覽路由", "value": "/grist", "state": "ok"},
{"label": "錯站攔截", "value": "已攔截外部錯鏈" if blocked_external else "未偵測錯鏈", "state": "ok"},
{"label": "協作服務", "value": "尚未接入 proxy", "state": "warn"},
],
"actions": [
{"label": "月份總表", "href": "/monthly_summary_analysis", "icon": "fas fa-table"},
{"label": "雲端匯入", "href": "/auto_import", "icon": "fas fa-download"},
{"label": "業績分析", "href": "/sales_analysis", "icon": "fas fa-chart-bar"},
],
}
def build_external_tool_payload(kind):
"""建立外部工具診斷頁 payload。"""
if kind == "metabase":
return _metabase_payload()
if kind == "webcrumbs":
return _webcrumbs_payload()
return _grist_payload()

View File

@@ -27,34 +27,36 @@ def test_metabase_and_grist_navigation_stays_inside_momo_pro():
def test_external_tool_bridge_pages_are_diagnostic_not_blank():
route_source = (ROOT / "routes/system_public_routes.py").read_text(encoding="utf-8")
service_source = (ROOT / "services/external_tool_payload_service.py").read_text(encoding="utf-8")
template = (ROOT / "templates/external_tool_status.html").read_text(encoding="utf-8")
css = (ROOT / "web/static/css/page-external-tools.css").read_text(encoding="utf-8")
assert "def _external_tool_payload(kind)" in route_source
assert "parsed.path.rstrip('/') == bridge_path.rstrip('/')" in route_source
assert "def _is_blocked_external_url(configured_url)" in route_source
assert "build_external_tool_payload" in route_source
assert "def build_external_tool_payload(kind)" in service_source
assert 'parsed.path.rstrip("/") == bridge_path.rstrip("/")' in service_source
assert "def _is_blocked_external_url(configured_url)" in service_source
assert "external-tool-checks" in template
assert "external-tool-diagnostics" in template
assert "external-tool-action-grid" in template
assert "def webcrumbs_status()" in route_source
assert "def webcrumbs_asset_proxy(asset_path)" in route_source
assert "WEBCRUMBS_ASSET_ALLOWED_PREFIXES" in route_source
assert "Webcrumbs 共用 UI Runtime" in route_source
assert "build_webcrumbs_marketplace_host_data" in route_source
assert "plugin_previews" in route_source
assert "finance.market-ticker-strip/0.1.0" in route_source
assert "finance.ai-candidate-card/0.1.0" in route_source
assert "plugin_seed_data" in route_source
assert "diagnostic_unavailable" in route_source
assert "source_unavailable" in route_source
assert "Webcrumbs 共用 UI Runtime" in service_source
assert "build_webcrumbs_marketplace_host_data" in service_source
assert "plugin_previews" in service_source
assert "finance.market-ticker-strip/0.1.0" in service_source
assert "finance.ai-candidate-card/0.1.0" in service_source
assert "plugin_seed_data" in service_source
assert "diagnostic_unavailable" in service_source
assert "source_unavailable" in service_source
assert "StockPlatformSharedUI.allowedPluginUris" in template
assert "StockPlatformSharedUI.marketSnapshot" in template
assert "StockPlatformSharedUI.aiCandidate" in template
assert "stock-platform-plugin" in template
assert "external-tool-webcrumbs-grid" in css
assert "尚未接入 proxy" in route_source
assert "已由入口攔截" in route_source
assert "AwoooI" not in route_source
assert "尚未接入 proxy" in service_source
assert "已由入口攔截" in service_source
assert "AwoooI" not in service_source
assert "--momo-accent-rust" not in css
assert "--momo-accent-honey" not in css