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: '刪除中...',