From e2d555a9455d4eb48df354312f67544ba9c11c06 Mon Sep 17 00:00:00 2001
From: Junyan Qin
Date: Sat, 18 Apr 2026 00:03:18 +0800
Subject: [PATCH] feat(web): show Box runtime status in plugin debug info
popover
Add Box status section to the debug info popover on the plugin list page,
displaying connection status, backend info, profile, active sessions,
and recent error count. Fetched from GET /api/v1/box/status in parallel
with plugin debug info. Includes i18n for all 8 supported languages.
---
web/src/app/home/plugins/page.tsx | 92 ++++++++++++++++++++++++-
web/src/app/infra/entities/api/index.ts | 13 ++++
web/src/app/infra/http/BackendClient.ts | 5 ++
web/src/i18n/locales/en-US.ts | 9 +++
web/src/i18n/locales/es-ES.ts | 9 +++
web/src/i18n/locales/ja-JP.ts | 9 +++
web/src/i18n/locales/ru-RU.ts | 9 +++
web/src/i18n/locales/th-TH.ts | 9 +++
web/src/i18n/locales/vi-VN.ts | 9 +++
web/src/i18n/locales/zh-Hans.ts | 9 +++
web/src/i18n/locales/zh-Hant.ts | 9 +++
11 files changed, 180 insertions(+), 2 deletions(-)
diff --git a/web/src/app/home/plugins/page.tsx b/web/src/app/home/plugins/page.tsx
index 28b6585f..6f56795a 100644
--- a/web/src/app/home/plugins/page.tsx
+++ b/web/src/app/home/plugins/page.tsx
@@ -17,6 +17,10 @@ import {
Check,
Bug,
Unlink,
+ Box,
+ CircleCheck,
+ CircleX,
+ Activity,
} from 'lucide-react';
import {
DropdownMenu,
@@ -43,7 +47,10 @@ import { httpClient } from '@/app/infra/http/HttpClient';
import { toast } from 'sonner';
import { useTranslation } from 'react-i18next';
import { systemInfo } from '@/app/infra/http/HttpClient';
-import { ApiRespPluginSystemStatus } from '@/app/infra/entities/api';
+import {
+ ApiRespPluginSystemStatus,
+ ApiRespBoxStatus,
+} from '@/app/infra/entities/api';
import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext';
import {
PluginInstallTaskQueue,
@@ -133,6 +140,7 @@ function PluginListView() {
const [debugPopoverOpen, setDebugPopoverOpen] = useState(false);
const [copiedDebugUrl, setCopiedDebugUrl] = useState(false);
const [copiedDebugKey, setCopiedDebugKey] = useState(false);
+ const [boxStatus, setBoxStatus] = useState(null);
useEffect(() => {
const fetchPluginSystemStatus = async () => {
@@ -454,8 +462,12 @@ function PluginListView() {
const handleShowDebugInfo = async () => {
try {
- const info = await httpClient.getPluginDebugInfo();
+ const [info, box] = await Promise.all([
+ httpClient.getPluginDebugInfo(),
+ httpClient.getBoxStatus().catch(() => null),
+ ]);
setDebugInfo(info);
+ setBoxStatus(box);
setDebugPopoverOpen(true);
} catch (error) {
console.error('Failed to fetch debug info:', error);
@@ -645,6 +657,82 @@ function PluginListView() {
)}
+
+ {/* Box Runtime Status */}
+
+
+
+
+ {t('plugins.boxStatusTitle')}
+
+
+ {boxStatus ? (
+
+
+
+ {t('plugins.boxStatus')}:
+
+ {boxStatus.available ? (
+
+
+ {t('plugins.boxConnected')}
+
+ ) : (
+
+
+ {t('plugins.boxUnavailable')}
+
+ )}
+
+ {boxStatus.backend && (
+
+
+ {t('plugins.boxBackend')}:
+
+
+ {boxStatus.backend.name}
+ {boxStatus.backend.available
+ ? ''
+ : ` (${t('plugins.boxUnavailable')})`}
+
+
+ )}
+
+
+ {t('plugins.boxProfile')}:
+
+ {boxStatus.profile}
+
+ {boxStatus.available && (
+ <>
+
+
+ {t('plugins.boxSessions')}:
+
+
+
+ {boxStatus.active_sessions ?? 0}
+
+
+ {(boxStatus.recent_error_count ?? 0) > 0 && (
+
+
+ {t('plugins.boxErrors')}:
+
+
+ {boxStatus.recent_error_count}
+
+
+ )}
+ >
+ )}
+
+ ) : (
+
+ {t('plugins.boxStatusLoadFailed')}
+
+ )}
+
diff --git a/web/src/app/infra/entities/api/index.ts b/web/src/app/infra/entities/api/index.ts
index ab950612..24e360cd 100644
--- a/web/src/app/infra/entities/api/index.ts
+++ b/web/src/app/infra/entities/api/index.ts
@@ -351,6 +351,19 @@ export interface ApiRespPluginSystemStatus {
plugin_connector_error: string;
}
+export interface ApiRespBoxStatus {
+ available: boolean;
+ profile: string;
+ recent_error_count: number;
+ backend?: {
+ name: string;
+ available: boolean;
+ };
+ active_sessions?: number;
+ managed_processes?: number;
+ session_ttl_sec?: number;
+}
+
export interface ApiRespAsyncTasks {
tasks: AsyncTask[];
}
diff --git a/web/src/app/infra/http/BackendClient.ts b/web/src/app/infra/http/BackendClient.ts
index 2234f3ac..643f1bb1 100644
--- a/web/src/app/infra/http/BackendClient.ts
+++ b/web/src/app/infra/http/BackendClient.ts
@@ -35,6 +35,7 @@ import {
ApiRespProviderRerankModel,
RerankModel,
ApiRespPluginSystemStatus,
+ ApiRespBoxStatus,
ApiRespMCPServers,
ApiRespMCPServer,
MCPServer,
@@ -889,6 +890,10 @@ export class BackendClient extends BaseHttpClient {
return this.get('/api/v1/plugins/debug-info');
}
+ public getBoxStatus(): Promise {
+ return this.get('/api/v1/box/status');
+ }
+
// ============ User API ============
public checkIfInited(): Promise<{ initialized: boolean }> {
return this.get('/api/v1/user/init');
diff --git a/web/src/i18n/locales/en-US.ts b/web/src/i18n/locales/en-US.ts
index 4e7df75e..644d30e3 100644
--- a/web/src/i18n/locales/en-US.ts
+++ b/web/src/i18n/locales/en-US.ts
@@ -467,6 +467,15 @@ const enUS = {
noDebugKey: '(Not Set)',
debugKeyDisabled:
'Debug key is not set, plugin debugging does not require authentication',
+ boxStatusTitle: 'Box Runtime',
+ boxStatus: 'Status',
+ boxConnected: 'Connected',
+ boxUnavailable: 'Unavailable',
+ boxBackend: 'Backend',
+ boxProfile: 'Profile',
+ boxSessions: 'Sessions',
+ boxErrors: 'Errors',
+ boxStatusLoadFailed: 'Failed to load Box status',
failedToGetDebugInfo: 'Failed to get debug information',
copiedToClipboard: 'Copied to clipboard',
deleting: 'Deleting...',
diff --git a/web/src/i18n/locales/es-ES.ts b/web/src/i18n/locales/es-ES.ts
index 46e00c09..decd5fe7 100644
--- a/web/src/i18n/locales/es-ES.ts
+++ b/web/src/i18n/locales/es-ES.ts
@@ -479,6 +479,15 @@ const esES = {
noDebugKey: '(No establecida)',
debugKeyDisabled:
'La clave de depuración no está configurada, la depuración del plugin no requiere autenticación',
+ boxStatusTitle: 'Box Runtime',
+ boxStatus: 'Estado',
+ boxConnected: 'Conectado',
+ boxUnavailable: 'No disponible',
+ boxBackend: 'Backend',
+ boxProfile: 'Perfil',
+ boxSessions: 'Sesiones',
+ boxErrors: 'Errores',
+ boxStatusLoadFailed: 'Error al cargar el estado de Box',
failedToGetDebugInfo: 'Error al obtener la información de depuración',
copiedToClipboard: 'Copiado al portapapeles',
deleting: 'Eliminando...',
diff --git a/web/src/i18n/locales/ja-JP.ts b/web/src/i18n/locales/ja-JP.ts
index e491987c..75813ee3 100644
--- a/web/src/i18n/locales/ja-JP.ts
+++ b/web/src/i18n/locales/ja-JP.ts
@@ -472,6 +472,15 @@ const jaJP = {
noDebugKey: '(未設定)',
debugKeyDisabled:
'デバッグキーが設定されていません。プラグインデバッグには認証が不要です',
+ boxStatusTitle: 'Box ランタイム',
+ boxStatus: 'ステータス',
+ boxConnected: '接続済み',
+ boxUnavailable: '利用不可',
+ boxBackend: 'バックエンド',
+ boxProfile: 'プロファイル',
+ boxSessions: 'セッション',
+ boxErrors: 'エラー',
+ boxStatusLoadFailed: 'Box ステータスの読み込みに失敗しました',
failedToGetDebugInfo: 'デバッグ情報の取得に失敗しました',
copiedToClipboard: 'クリップボードにコピーしました',
deleting: '削除中...',
diff --git a/web/src/i18n/locales/ru-RU.ts b/web/src/i18n/locales/ru-RU.ts
index ed02bc86..69f538a9 100644
--- a/web/src/i18n/locales/ru-RU.ts
+++ b/web/src/i18n/locales/ru-RU.ts
@@ -475,6 +475,15 @@ const ruRU = {
noDebugKey: '(Не задан)',
debugKeyDisabled:
'Ключ отладки не задан, аутентификация при отладке плагина не требуется',
+ boxStatusTitle: 'Box Runtime',
+ boxStatus: 'Статус',
+ boxConnected: 'Подключено',
+ boxUnavailable: 'Недоступно',
+ boxBackend: 'Бэкенд',
+ boxProfile: 'Профиль',
+ boxSessions: 'Сессии',
+ boxErrors: 'Ошибки',
+ boxStatusLoadFailed: 'Не удалось загрузить статус Box',
failedToGetDebugInfo: 'Не удалось получить отладочную информацию',
copiedToClipboard: 'Скопировано в буфер обмена',
deleting: 'Удаление...',
diff --git a/web/src/i18n/locales/th-TH.ts b/web/src/i18n/locales/th-TH.ts
index 8baccbae..d3f1b8df 100644
--- a/web/src/i18n/locales/th-TH.ts
+++ b/web/src/i18n/locales/th-TH.ts
@@ -462,6 +462,15 @@ const thTH = {
noDebugKey: '(ไม่ได้ตั้งค่า)',
debugKeyDisabled:
'ไม่ได้ตั้งค่าคีย์ดีบัก การดีบักปลั๊กอินไม่ต้องยืนยันตัวตน',
+ boxStatusTitle: 'Box Runtime',
+ boxStatus: 'สถานะ',
+ boxConnected: 'เชื่อมต่อแล้ว',
+ boxUnavailable: 'ไม่พร้อมใช้งาน',
+ boxBackend: 'แบ็กเอนด์',
+ boxProfile: 'โปรไฟล์',
+ boxSessions: 'เซสชัน',
+ boxErrors: 'ข้อผิดพลาด',
+ boxStatusLoadFailed: 'โหลดสถานะ Box ล้มเหลว',
failedToGetDebugInfo: 'ไม่สามารถดึงข้อมูลดีบักได้',
copiedToClipboard: 'คัดลอกไปยังคลิปบอร์ดแล้ว',
deleting: 'กำลังลบ...',
diff --git a/web/src/i18n/locales/vi-VN.ts b/web/src/i18n/locales/vi-VN.ts
index cc5631b0..66363f19 100644
--- a/web/src/i18n/locales/vi-VN.ts
+++ b/web/src/i18n/locales/vi-VN.ts
@@ -472,6 +472,15 @@ const viVN = {
noDebugKey: '(Chưa đặt)',
debugKeyDisabled:
'Khóa gỡ lỗi chưa được đặt, gỡ lỗi plugin không yêu cầu xác thực',
+ boxStatusTitle: 'Box Runtime',
+ boxStatus: 'Trạng thái',
+ boxConnected: 'Đã kết nối',
+ boxUnavailable: 'Không khả dụng',
+ boxBackend: 'Backend',
+ boxProfile: 'Hồ sơ',
+ boxSessions: 'Phiên',
+ boxErrors: 'Lỗi',
+ boxStatusLoadFailed: 'Tải trạng thái Box thất bại',
failedToGetDebugInfo: 'Lấy thông tin gỡ lỗi thất bại',
copiedToClipboard: 'Đã sao chép vào clipboard',
deleting: 'Đang xóa...',
diff --git a/web/src/i18n/locales/zh-Hans.ts b/web/src/i18n/locales/zh-Hans.ts
index b644fabc..6c1821d2 100644
--- a/web/src/i18n/locales/zh-Hans.ts
+++ b/web/src/i18n/locales/zh-Hans.ts
@@ -446,6 +446,15 @@ const zhHans = {
debugKey: '调试密钥',
noDebugKey: '(未设置)',
debugKeyDisabled: '未设置调试密钥,插件调试无需认证',
+ boxStatusTitle: 'Box 运行时',
+ boxStatus: '状态',
+ boxConnected: '已连接',
+ boxUnavailable: '不可用',
+ boxBackend: '后端',
+ boxProfile: '配置',
+ boxSessions: '会话数',
+ boxErrors: '错误数',
+ boxStatusLoadFailed: '加载 Box 状态失败',
failedToGetDebugInfo: '获取调试信息失败',
copiedToClipboard: '已复制到剪贴板',
deleting: '删除中...',
diff --git a/web/src/i18n/locales/zh-Hant.ts b/web/src/i18n/locales/zh-Hant.ts
index ce98cbaf..78a0e944 100644
--- a/web/src/i18n/locales/zh-Hant.ts
+++ b/web/src/i18n/locales/zh-Hant.ts
@@ -447,6 +447,15 @@ const zhHant = {
debugKey: '偵錯金鑰',
noDebugKey: '(未設定)',
debugKeyDisabled: '未設定偵錯金鑰,外掛偵錯無需認證',
+ boxStatusTitle: 'Box 執行時',
+ boxStatus: '狀態',
+ boxConnected: '已連線',
+ boxUnavailable: '不可用',
+ boxBackend: '後端',
+ boxProfile: '設定檔',
+ boxSessions: '工作階段',
+ boxErrors: '錯誤數',
+ boxStatusLoadFailed: '載入 Box 狀態失敗',
failedToGetDebugInfo: '取得偵錯資訊失敗',
copiedToClipboard: '已複製到剪貼簿',
deleting: '刪除中...',