From 89d8c454bb62af56ec806d588d9bfe71139b60a7 Mon Sep 17 00:00:00 2001 From: OoO Date: Sun, 31 May 2026 20:53:00 +0800 Subject: [PATCH] =?UTF-8?q?V10.513=20=E6=A8=A1=E7=B5=84=E5=8C=96=E5=A4=96?= =?UTF-8?q?=E9=83=A8=E5=B7=A5=E5=85=B7=E8=A8=BA=E6=96=B7=20payload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO_NEXT_STEPS.txt | 1 + config.py | 2 +- docs/memory/history_logs.md | 1 + routes/system_public_routes.py | 165 +---------------- services/external_tool_payload_service.py | 209 ++++++++++++++++++++++ tests/test_external_tool_entrypoints.py | 30 ++-- 6 files changed, 233 insertions(+), 175 deletions(-) create mode 100644 services/external_tool_payload_service.py diff --git a/TODO_NEXT_STEPS.txt b/TODO_NEXT_STEPS.txt index b42f220..52c171a 100644 --- a/TODO_NEXT_STEPS.txt +++ b/TODO_NEXT_STEPS.txt @@ -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 初始化。 diff --git a/config.py b/config.py index 9789a51..f259a50 100644 --- a/config.py +++ b/config.py @@ -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 # 用於模板顯示 diff --git a/docs/memory/history_logs.md b/docs/memory/history_logs.md index 25861b2..6b3c662 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -13,6 +13,7 @@ ## 📅 詳細更新日誌 (考古存檔) ### 2026-05-31:Webcrumbs 共用 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 內直接驗收。 diff --git a/routes/system_public_routes.py b/routes/system_public_routes.py index 1e20647..f3bbe58 100644 --- a/routes/system_public_routes.py +++ b/routes/system_public_routes.py @@ -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'), ) diff --git a/services/external_tool_payload_service.py b/services/external_tool_payload_service.py new file mode 100644 index 0000000..a682229 --- /dev/null +++ b/services/external_tool_payload_service.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""外部工具診斷頁 payload 組裝服務。 + +Route 只負責 HTTP glue;Metabase、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() diff --git a/tests/test_external_tool_entrypoints.py b/tests/test_external_tool_entrypoints.py index 583be2f..23b0198 100644 --- a/tests/test_external_tool_entrypoints.py +++ b/tests/test_external_tool_entrypoints.py @@ -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