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