mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-03-31 16:20:28 +02:00
2648 lines
224 KiB
HTML
2648 lines
224 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>CyberStrikeAI</title>
|
||
<link rel="icon" type="image/png" href="/static/logo.png">
|
||
<link rel="shortcut icon" type="image/png" href="/static/favicon.ico">
|
||
<link rel="stylesheet" href="/static/css/style.css">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@4.19.0/css/xterm.css">
|
||
</head>
|
||
<body>
|
||
<div id="login-overlay" class="login-overlay" style="display: none;">
|
||
<div class="login-card">
|
||
<div class="login-brand">
|
||
<h2 data-i18n="login.title">登录 CyberStrikeAI</h2>
|
||
<p class="login-subtitle" data-i18n="login.subtitle">请输入配置中的访问密码</p>
|
||
</div>
|
||
<form id="login-form" class="login-form">
|
||
<div class="form-group">
|
||
<label for="login-password" data-i18n="login.passwordLabel">密码</label>
|
||
<input type="password" id="login-password" placeholder="输入登录密码" required autocomplete="current-password" data-i18n="login.passwordPlaceholder" data-i18n-attr="placeholder" />
|
||
</div>
|
||
<div id="login-error" class="login-error" role="alert" style="display: none;"></div>
|
||
<button type="submit" class="btn-primary login-submit">
|
||
<span data-i18n="login.submit">登录</span>
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="container">
|
||
<header>
|
||
<div class="header-content">
|
||
<div class="logo header-logo-link" onclick="switchPage('dashboard')" role="button" data-i18n="header.backToDashboard" data-i18n-attr="title" data-i18n-skip-text="true" title="返回仪表盘">
|
||
<img src="/static/logo.png" alt="CyberStrikeAI Logo" style="width: 32px; height: 32px; margin-right: 8px;">
|
||
<h1>CyberStrikeAI</h1>
|
||
<span class="version-badge" data-i18n="header.version" data-i18n-attr="title" data-i18n-skip-text="true" title="当前版本">{{.Version}}</span>
|
||
</div>
|
||
<div class="header-right">
|
||
<div class="header-actions">
|
||
<button class="openapi-doc-btn" onclick="window.open('/api-docs', '_blank')" data-i18n="header.apiDocs" data-i18n-attr="title" data-i18n-skip-text="true" title="API 文档">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<polyline points="14 2 14 8 20 8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<path d="M16 13H8M16 17H8M10 9H8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="header.apiDocs">API 文档</span>
|
||
</button>
|
||
<button class="openapi-doc-btn" onclick="window.open('https://github.com/Ed1s0nZ/CyberStrikeAI', '_blank')" data-i18n="header.github" data-i18n-attr="title" data-i18n-skip-text="true" title="GitHub">
|
||
<svg width="16" height="16" viewBox="0 0 98 96" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"/>
|
||
</svg>
|
||
<span data-i18n="header.github">GitHub</span>
|
||
</button>
|
||
<div class="lang-switcher">
|
||
<button class="btn-secondary lang-switcher-btn" onclick="toggleLangDropdown()" data-i18n="header.language" data-i18n-attr="title" data-i18n-skip-text="true" title="界面语言">
|
||
<span class="lang-switcher-icon">🌐</span>
|
||
<span id="current-lang-label">中文</span>
|
||
</button>
|
||
<div id="lang-dropdown" class="lang-dropdown" style="display: none;">
|
||
<div class="lang-option" data-lang="zh-CN" onclick="onLanguageSelect('zh-CN')">中文</div>
|
||
<div class="lang-option" data-lang="en-US" onclick="onLanguageSelect('en-US')">English</div>
|
||
</div>
|
||
</div>
|
||
<div class="user-menu-container">
|
||
<button class="user-avatar-btn" onclick="toggleUserMenu()" data-i18n="header.userMenu" data-i18n-attr="title" data-i18n-skip-text="true" title="用户菜单">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<circle cx="12" cy="7" r="4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
<div id="user-menu-dropdown" class="user-menu-dropdown" style="display: none;">
|
||
<div class="user-menu-item" onclick="logout()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<polyline points="16 17 21 12 16 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<line x1="21" y1="12" x2="9" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="header.logout">退出登录</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="main-layout">
|
||
<!-- 主侧边栏 -->
|
||
<aside class="main-sidebar" id="main-sidebar">
|
||
<div class="sidebar-collapse-btn" onclick="toggleSidebar()" data-i18n="header.toggleSidebar" data-i18n-attr="title" data-i18n-skip-text="true" title="折叠/展开侧边栏">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M15 18l-6-6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<nav class="main-sidebar-nav">
|
||
<div class="nav-item" data-page="dashboard">
|
||
<div class="nav-item-content" data-title="仪表盘" onclick="switchPage('dashboard')" data-i18n="nav.dashboard" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="3" y="3" width="7" height="9"></rect>
|
||
<rect x="14" y="3" width="7" height="5"></rect>
|
||
<rect x="14" y="12" width="7" height="9"></rect>
|
||
<rect x="3" y="16" width="7" height="5"></rect>
|
||
</svg>
|
||
<span data-i18n="nav.dashboard">仪表盘</span>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item" data-page="chat">
|
||
<div class="nav-item-content" data-title="对话" onclick="switchPage('chat')" data-i18n="nav.chat" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
||
</svg>
|
||
<span data-i18n="nav.chat">对话</span>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item" data-page="info-collect">
|
||
<div class="nav-item-content" data-title="信息收集" onclick="switchPage('info-collect')" data-i18n="nav.infoCollect" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="11" cy="11" r="8"></circle>
|
||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||
</svg>
|
||
<span data-i18n="nav.infoCollect">信息收集</span>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item" data-page="tasks">
|
||
<div class="nav-item-content" data-title="任务管理" onclick="switchPage('tasks')" data-i18n="nav.tasks" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M9 11l3 3L22 4"></path>
|
||
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
|
||
</svg>
|
||
<span data-i18n="nav.tasks">任务管理</span>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item" data-page="vulnerabilities">
|
||
<div class="nav-item-content" data-title="漏洞管理" onclick="switchPage('vulnerabilities')" data-i18n="nav.vulnerabilities" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
|
||
<path d="M9 12l2 2 4-4"></path>
|
||
</svg>
|
||
<span data-i18n="nav.vulnerabilities">漏洞管理</span>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item" data-page="webshell">
|
||
<div class="nav-item-content" data-title="WebShell管理" onclick="switchPage('webshell')" data-i18n="nav.webshell" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polyline points="4 17 10 11 4 5"></polyline>
|
||
<line x1="12" y1="19" x2="20" y2="19"></line>
|
||
</svg>
|
||
<span data-i18n="nav.webshell">WebShell管理</span>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item" data-page="chat-files">
|
||
<div class="nav-item-content" data-title="文件管理" onclick="switchPage('chat-files')" data-i18n="nav.chatFiles" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
|
||
</svg>
|
||
<span data-i18n="nav.chatFiles">文件管理</span>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item nav-item-has-submenu" data-page="mcp">
|
||
<div class="nav-item-content" data-title="MCP" onclick="toggleSubmenu('mcp')" data-i18n="nav.mcp" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path>
|
||
</svg>
|
||
<span data-i18n="nav.mcp">MCP</span>
|
||
<svg class="submenu-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<div class="nav-submenu">
|
||
<div class="nav-submenu-item" data-page="mcp-monitor" onclick="switchPage('mcp-monitor')">
|
||
<span data-i18n="nav.mcpMonitor">MCP状态监控</span>
|
||
</div>
|
||
<div class="nav-submenu-item" data-page="mcp-management" onclick="switchPage('mcp-management')">
|
||
<span data-i18n="nav.mcpManagement">MCP管理</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item nav-item-has-submenu" data-page="knowledge">
|
||
<div class="nav-item-content" data-title="知识" onclick="toggleSubmenu('knowledge')" data-i18n="nav.knowledge" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
|
||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
|
||
</svg>
|
||
<span data-i18n="nav.knowledge">知识</span>
|
||
<svg class="submenu-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<div class="nav-submenu">
|
||
<div class="nav-submenu-item" data-page="knowledge-retrieval-logs" onclick="switchPage('knowledge-retrieval-logs')">
|
||
<span data-i18n="nav.knowledgeRetrievalLogs">检索历史</span>
|
||
</div>
|
||
<div class="nav-submenu-item" data-page="knowledge-management" onclick="switchPage('knowledge-management')">
|
||
<span data-i18n="nav.knowledgeManagement">知识管理</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item nav-item-has-submenu" data-page="skills">
|
||
<div class="nav-item-content" data-title="Skills" onclick="toggleSubmenu('skills')" data-i18n="nav.skills" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
|
||
<polyline points="14 2 14 8 20 8"></polyline>
|
||
<line x1="16" y1="13" x2="8" y2="13"></line>
|
||
<line x1="16" y1="17" x2="8" y2="17"></line>
|
||
<polyline points="10 9 9 9 8 9"></polyline>
|
||
</svg>
|
||
<span data-i18n="nav.skills">Skills</span>
|
||
<svg class="submenu-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<div class="nav-submenu">
|
||
<div class="nav-submenu-item" data-page="skills-monitor" onclick="switchPage('skills-monitor')">
|
||
<span data-i18n="nav.skillsMonitor">Skills状态监控</span>
|
||
</div>
|
||
<div class="nav-submenu-item" data-page="skills-management" onclick="switchPage('skills-management')">
|
||
<span data-i18n="nav.skillsManagement">Skills管理</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item nav-item-has-submenu" data-page="agents">
|
||
<div class="nav-item-content" data-title="Agents" onclick="toggleSubmenu('agents')" data-i18n="nav.agents" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
|
||
<polyline points="2 17 12 22 22 17"></polyline>
|
||
<polyline points="2 12 12 17 22 12"></polyline>
|
||
</svg>
|
||
<span data-i18n="nav.agents">Agents</span>
|
||
<svg class="submenu-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<div class="nav-submenu">
|
||
<div class="nav-submenu-item" data-page="agents-management" onclick="switchPage('agents-management')">
|
||
<span data-i18n="nav.agentsManagement">Agent管理</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item nav-item-has-submenu" data-page="roles">
|
||
<div class="nav-item-content" data-title="角色" onclick="toggleSubmenu('roles')" data-i18n="nav.roles" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
||
<circle cx="9" cy="7" r="4"></circle>
|
||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
|
||
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
||
</svg>
|
||
<span data-i18n="nav.roles">角色</span>
|
||
<svg class="submenu-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<div class="nav-submenu">
|
||
<div class="nav-submenu-item" data-page="roles-management" onclick="switchPage('roles-management')">
|
||
<span data-i18n="nav.rolesManagement">角色管理</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item" data-page="settings">
|
||
<div class="nav-item-content" data-title="系统设置" onclick="switchPage('settings')" data-i18n="nav.settings" data-i18n-attr="data-title" data-i18n-skip-text="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="3"></circle>
|
||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
||
</svg>
|
||
<span data-i18n="nav.settings">系统设置</span>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
</aside>
|
||
|
||
<!-- 内容区域 -->
|
||
<div class="content-area">
|
||
<!-- 仪表盘页面 -->
|
||
<div id="page-dashboard" class="page active">
|
||
<div class="dashboard-page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="dashboard.title">仪表盘</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-secondary" onclick="refreshDashboard()" data-i18n="dashboard.refreshData" data-i18n-attr="title" title="刷新数据"><span data-i18n="common.refresh">刷新</span></button>
|
||
</div>
|
||
</div>
|
||
<div class="dashboard-content">
|
||
<!-- 第一行:核心 KPI(仪表盘最佳实践:关键指标置顶) -->
|
||
<div class="dashboard-kpi-row" id="dashboard-cards">
|
||
<div class="dashboard-kpi-card" role="button" tabindex="0" onclick="switchPage('tasks')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('tasks'); }" data-i18n="dashboard.clickToViewTasks" data-i18n-attr="title" title="点击查看任务管理"> <div class="dashboard-kpi-value" id="dashboard-running-tasks">-</div><div class="dashboard-kpi-label" data-i18n="dashboard.runningTasks">运行中任务</div></div>
|
||
<div class="dashboard-kpi-card" role="button" tabindex="0" onclick="switchPage('vulnerabilities')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('vulnerabilities'); }" data-i18n="dashboard.clickToViewVuln" data-i18n-attr="title" title="点击查看漏洞管理"><div class="dashboard-kpi-value" id="dashboard-vuln-total">-</div><div class="dashboard-kpi-label" data-i18n="dashboard.vulnTotal">漏洞总数</div></div>
|
||
<div class="dashboard-kpi-card" role="button" tabindex="0" onclick="switchPage('mcp-monitor')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('mcp-monitor'); }" data-i18n="dashboard.clickToViewMCP" data-i18n-attr="title" title="点击查看 MCP 监控"><div class="dashboard-kpi-value" id="dashboard-kpi-tools-calls">-</div><div class="dashboard-kpi-label" data-i18n="dashboard.toolCalls">工具调用次数</div></div>
|
||
<div class="dashboard-kpi-card" role="button" tabindex="0" onclick="switchPage('mcp-monitor')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('mcp-monitor'); }" data-i18n="dashboard.clickToViewMCP" data-i18n-attr="title" title="点击查看 MCP 监控"><div class="dashboard-kpi-value" id="dashboard-kpi-success-rate">-</div><div class="dashboard-kpi-label" data-i18n="dashboard.successRate">工具执行成功率</div></div>
|
||
</div>
|
||
<!-- 两列主内容区 -->
|
||
<div class="dashboard-grid">
|
||
<div class="dashboard-main">
|
||
<section class="dashboard-section dashboard-section-chart">
|
||
<h3 class="dashboard-section-title" data-i18n="dashboard.severityDistribution">漏洞严重程度分布</h3>
|
||
<div class="dashboard-chart-wrap">
|
||
<div class="dashboard-stacked-bar" id="dashboard-stacked-bar">
|
||
<span class="dashboard-bar-seg seg-critical" id="dashboard-bar-critical" style="width: 0%"></span>
|
||
<span class="dashboard-bar-seg seg-high" id="dashboard-bar-high" style="width: 0%"></span>
|
||
<span class="dashboard-bar-seg seg-medium" id="dashboard-bar-medium" style="width: 0%"></span>
|
||
<span class="dashboard-bar-seg seg-low" id="dashboard-bar-low" style="width: 0%"></span>
|
||
<span class="dashboard-bar-seg seg-info" id="dashboard-bar-info" style="width: 0%"></span>
|
||
</div>
|
||
<div class="dashboard-legend" id="dashboard-vuln-bars">
|
||
<div class="dashboard-legend-item"><span class="dashboard-legend-dot critical"></span><span class="dashboard-legend-label" data-i18n="dashboard.severityCritical">严重</span><span class="dashboard-legend-value" id="dashboard-severity-critical">0</span></div>
|
||
<div class="dashboard-legend-item"><span class="dashboard-legend-dot high"></span><span class="dashboard-legend-label" data-i18n="dashboard.severityHigh">高危</span><span class="dashboard-legend-value" id="dashboard-severity-high">0</span></div>
|
||
<div class="dashboard-legend-item"><span class="dashboard-legend-dot medium"></span><span class="dashboard-legend-label" data-i18n="dashboard.severityMedium">中危</span><span class="dashboard-legend-value" id="dashboard-severity-medium">0</span></div>
|
||
<div class="dashboard-legend-item"><span class="dashboard-legend-dot low"></span><span class="dashboard-legend-label" data-i18n="dashboard.severityLow">低危</span><span class="dashboard-legend-value" id="dashboard-severity-low">0</span></div>
|
||
<div class="dashboard-legend-item"><span class="dashboard-legend-dot info"></span><span class="dashboard-legend-label" data-i18n="dashboard.severityInfo">信息</span><span class="dashboard-legend-value" id="dashboard-severity-info">0</span></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section class="dashboard-section dashboard-section-overview">
|
||
<h3 class="dashboard-section-title" data-i18n="dashboard.runOverview">运行概览</h3>
|
||
<div class="dashboard-overview-list">
|
||
<div class="dashboard-overview-item dashboard-overview-item-batch" role="button" tabindex="0" onclick="switchPage('tasks')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('tasks'); }">
|
||
<span class="dashboard-overview-icon dashboard-overview-icon-batch" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg></span>
|
||
<div class="dashboard-overview-content">
|
||
<div class="dashboard-overview-header">
|
||
<span class="dashboard-overview-label" data-i18n="dashboard.batchQueues">批量任务队列</span>
|
||
<span class="dashboard-overview-total" id="dashboard-batch-total">-</span>
|
||
</div>
|
||
<div class="dashboard-overview-stats">
|
||
<span class="dashboard-overview-stat dashboard-overview-stat-pending">
|
||
<span class="dashboard-overview-stat-badge badge-pending"></span>
|
||
<span class="dashboard-overview-stat-value" id="dashboard-batch-pending">-</span>
|
||
<span class="dashboard-overview-stat-label" data-i18n="dashboard.pending">待执行</span>
|
||
</span>
|
||
<span class="dashboard-overview-stat dashboard-overview-stat-running">
|
||
<span class="dashboard-overview-stat-badge badge-running"></span>
|
||
<span class="dashboard-overview-stat-value" id="dashboard-batch-running">-</span>
|
||
<span class="dashboard-overview-stat-label" data-i18n="dashboard.executing">执行中</span>
|
||
</span>
|
||
<span class="dashboard-overview-stat dashboard-overview-stat-done">
|
||
<span class="dashboard-overview-stat-badge badge-done"></span>
|
||
<span class="dashboard-overview-stat-value" id="dashboard-batch-done">-</span>
|
||
<span class="dashboard-overview-stat-label" data-i18n="dashboard.completed">已完成</span>
|
||
</span>
|
||
</div>
|
||
<div class="dashboard-overview-progress">
|
||
<div class="dashboard-overview-progress-bar">
|
||
<div class="dashboard-overview-progress-segment dashboard-overview-progress-pending" id="dashboard-batch-progress-pending" style="width: 0%"></div>
|
||
<div class="dashboard-overview-progress-segment dashboard-overview-progress-running" id="dashboard-batch-progress-running" style="width: 0%"></div>
|
||
<div class="dashboard-overview-progress-segment dashboard-overview-progress-done" id="dashboard-batch-progress-done" style="width: 0%"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="dashboard-overview-item dashboard-overview-item-tools" role="button" tabindex="0" onclick="switchPage('mcp-monitor')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('mcp-monitor'); }">
|
||
<span class="dashboard-overview-icon dashboard-overview-icon-tools" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg></span>
|
||
<div class="dashboard-overview-content">
|
||
<div class="dashboard-overview-header">
|
||
<span class="dashboard-overview-label" data-i18n="dashboard.toolInvocations">工具调用</span>
|
||
<span class="dashboard-overview-success-rate" id="dashboard-tools-success-rate">-</span>
|
||
</div>
|
||
<div class="dashboard-overview-value-group">
|
||
<span class="dashboard-overview-value-large" id="dashboard-tools-calls">-</span>
|
||
<span class="dashboard-overview-value-unit" data-i18n="dashboard.callsUnit">次调用</span>
|
||
<span class="dashboard-overview-value-separator">·</span>
|
||
<span class="dashboard-overview-value-normal" id="dashboard-tools-count">-</span>
|
||
<span class="dashboard-overview-value-unit" data-i18n="dashboard.toolsUnit">个工具</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="dashboard-overview-item dashboard-overview-item-knowledge" role="button" tabindex="0" onclick="switchPage('knowledge-management')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('knowledge-management'); }">
|
||
<span class="dashboard-overview-icon dashboard-overview-icon-knowledge" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg></span>
|
||
<div class="dashboard-overview-content">
|
||
<div class="dashboard-overview-header">
|
||
<span class="dashboard-overview-label" data-i18n="dashboard.knowledgeLabel">知识</span>
|
||
<span class="dashboard-overview-status" id="dashboard-knowledge-status">-</span>
|
||
</div>
|
||
<div class="dashboard-overview-value-group">
|
||
<span class="dashboard-overview-value-large" id="dashboard-knowledge-items">-</span>
|
||
<span class="dashboard-overview-value-unit" data-i18n="dashboard.knowledgeItems">项知识</span>
|
||
<span class="dashboard-overview-value-separator">·</span>
|
||
<span class="dashboard-overview-value-normal" id="dashboard-knowledge-categories">-</span>
|
||
<span class="dashboard-overview-value-unit" data-i18n="dashboard.categoriesUnit">个分类</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="dashboard-overview-item dashboard-overview-item-skills" role="button" tabindex="0" onclick="switchPage('skills-monitor')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('skills-monitor'); }">
|
||
<span class="dashboard-overview-icon dashboard-overview-icon-skills" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg></span>
|
||
<div class="dashboard-overview-content">
|
||
<div class="dashboard-overview-header">
|
||
<span class="dashboard-overview-label" data-i18n="dashboard.skillsLabel">Skills</span>
|
||
<span class="dashboard-overview-status" id="dashboard-skills-status">-</span>
|
||
</div>
|
||
<div class="dashboard-overview-value-group">
|
||
<span class="dashboard-overview-value-large" id="dashboard-skills-calls">-</span>
|
||
<span class="dashboard-overview-value-unit" data-i18n="dashboard.callsUnit">次调用</span>
|
||
<span class="dashboard-overview-value-separator">·</span>
|
||
<span class="dashboard-overview-value-normal" id="dashboard-skills-count">-</span>
|
||
<span class="dashboard-overview-value-unit" data-i18n="dashboard.skillUnit">个 Skill</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section class="dashboard-section dashboard-section-quick dashboard-quick-inline">
|
||
<h3 class="dashboard-section-title" data-i18n="dashboard.quickLinks">快捷入口</h3>
|
||
<div class="dashboard-quick-links dashboard-quick-links-row">
|
||
<a class="dashboard-quick-link" onclick="switchPage('chat')"><span class="dashboard-quick-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg></span><span data-i18n="nav.chat">对话</span></a>
|
||
<a class="dashboard-quick-link" onclick="switchPage('tasks')"><span class="dashboard-quick-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"></path><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path></svg></span><span data-i18n="nav.tasks">任务管理</span></a>
|
||
<a class="dashboard-quick-link" onclick="switchPage('vulnerabilities')"><span class="dashboard-quick-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg></span><span data-i18n="nav.vulnerabilities">漏洞管理</span></a>
|
||
<a class="dashboard-quick-link" onclick="switchPage('mcp-management')"><span class="dashboard-quick-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path></svg></span><span data-i18n="nav.mcpManagement">MCP 管理</span></a>
|
||
<a class="dashboard-quick-link" onclick="switchPage('knowledge-management')"><span class="dashboard-quick-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path></svg></span><span data-i18n="nav.knowledgeManagement">知识管理</span></a>
|
||
<a class="dashboard-quick-link" onclick="switchPage('skills-management')"><span class="dashboard-quick-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline></svg></span><span data-i18n="nav.skillsManagement">Skills 管理</span></a>
|
||
<a class="dashboard-quick-link" onclick="switchPage('roles-management')"><span class="dashboard-quick-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg></span><span data-i18n="nav.rolesManagement">角色管理</span></a>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
<div class="dashboard-side">
|
||
<section class="dashboard-section dashboard-section-tools">
|
||
<h3 class="dashboard-section-title" data-i18n="dashboard.toolsExecCount">工具执行次数</h3>
|
||
<div class="dashboard-tools-chart-wrap">
|
||
<div class="dashboard-tools-chart-placeholder" id="dashboard-tools-pie-placeholder" data-i18n="common.noData">暂无数据</div>
|
||
<div class="dashboard-tools-bar-chart" id="dashboard-tools-bar-chart"></div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
<div class="dashboard-cta-block">
|
||
<div class="dashboard-cta-content">
|
||
<div class="dashboard-cta-icon" aria-hidden="true">
|
||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
|
||
</div>
|
||
<div class="dashboard-cta-copy">
|
||
<p class="dashboard-cta-text" data-i18n="dashboard.ctaTitle">开始你的安全之旅</p>
|
||
<p class="dashboard-cta-sub" data-i18n="dashboard.ctaSub">在对话中描述目标,AI 将协助执行扫描与漏洞分析</p>
|
||
</div>
|
||
</div>
|
||
<button class="dashboard-cta-btn" onclick="switchPage('chat')">
|
||
<span data-i18n="dashboard.goToChat">前往对话</span>
|
||
<span class="dashboard-cta-btn-arrow" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 12h14M12 5l7 7-7 7"/></svg></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 对话页面 -->
|
||
<div id="page-chat" class="page">
|
||
<div class="chat-page-layout">
|
||
<!-- 历史对话侧边栏(可折叠,与主导航侧边栏类似) -->
|
||
<aside class="conversation-sidebar" id="conversation-sidebar">
|
||
<!-- 头部一行:折叠与「新对话」并排,避免绝对定位重叠(flex 为最佳实践) -->
|
||
<div class="sidebar-header conversation-sidebar-header">
|
||
<button type="button" class="new-chat-btn" onclick="startNewConversation()">
|
||
<span>+</span> <span data-i18n="chat.newChat">新对话</span>
|
||
</button>
|
||
<button type="button" class="conversation-sidebar-collapse-btn" onclick="toggleConversationSidebar()" data-i18n="chat.toggleConversationPanel" data-i18n-attr="title" data-i18n-skip-text="true" title="折叠/展开对话列表" aria-label="折叠/展开对话列表">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||
<path d="M15 18l-6-6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="sidebar-content">
|
||
<!-- 全局搜索 -->
|
||
<div class="conversation-search-box" style="margin-bottom: 16px; margin-top: 16px;">
|
||
<input type="text" id="conversation-search-input" data-i18n="chat.searchHistory" data-i18n-attr="placeholder" placeholder="搜索历史记录..."
|
||
oninput="handleConversationSearch(this.value)"
|
||
onkeypress="if(event.key === 'Enter') handleConversationSearch(this.value)" />
|
||
<button class="conversation-search-clear" id="conversation-search-clear"
|
||
onclick="clearConversationSearch()" style="display: none;" data-i18n="common.clearSearch" data-i18n-attr="title" title="清除搜索">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
||
<path d="M15 9l-6 6M9 9l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 对话分组 -->
|
||
<div class="conversation-groups-section">
|
||
<div class="section-header">
|
||
<span class="section-title" data-i18n="chat.conversationGroups">对话分组</span>
|
||
<button class="add-group-btn" onclick="showCreateGroupModal()" data-i18n="chat.addGroup" data-i18n-attr="title" data-i18n-skip-text="true" title="新建分组">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div id="conversation-groups-list" class="conversation-groups-list"></div>
|
||
</div>
|
||
|
||
<!-- 最近对话 -->
|
||
<div class="recent-conversations-section">
|
||
<div class="section-header">
|
||
<span class="section-title" data-i18n="chat.recentConversations">最近对话</span>
|
||
<button class="batch-manage-btn" onclick="showBatchManageModal()" data-i18n="chat.batchManage" data-i18n-attr="title" data-i18n-skip-text="true" title="批量管理">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<line x1="3" y1="12" x2="21" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
<line x1="3" y1="6" x2="21" y2="6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
<line x1="3" y1="18" x2="21" y2="18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
<circle cx="8" cy="6" r="1" fill="currentColor"/>
|
||
<circle cx="8" cy="12" r="1" fill="currentColor"/>
|
||
<circle cx="8" cy="18" r="1" fill="currentColor"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div id="conversations-list" class="conversations-list"></div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- 分组详情页面 -->
|
||
<div id="group-detail-page" class="group-detail-page" style="display: none;">
|
||
<div class="group-detail-header">
|
||
<button class="back-btn" onclick="exitGroupDetail()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M19 12H5M12 19l-7-7 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
<h2 id="group-detail-title" class="group-detail-title"></h2>
|
||
<div class="group-detail-actions">
|
||
<button class="group-action-btn" onclick="toggleGroupSearch()" data-i18n="chatGroup.search" data-i18n-attr="title" title="搜索" id="group-search-toggle-btn">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="11" cy="11" r="8" stroke="currentColor" stroke-width="2"/>
|
||
<path d="m21 21-4.35-4.35" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
<button class="group-action-btn" onclick="editGroup()" data-i18n="chatGroup.edit" data-i18n-attr="title" title="编辑">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
<button class="group-action-btn delete-btn" onclick="deleteGroup()" data-i18n="chatGroup.delete" data-i18n-attr="title" title="删除">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="group-search-container" class="group-search-container" style="display: none;">
|
||
<div class="group-search-input-wrapper">
|
||
<input type="text" id="group-search-input" class="group-search-input" data-i18n="chat.searchInGroup" data-i18n-attr="placeholder" placeholder="搜索分组中的对话..." onkeyup="handleGroupSearchInput(event)" oninput="handleGroupSearchInput(event)">
|
||
<button class="group-search-clear-btn" onclick="clearGroupSearch()" data-i18n="common.clearSearch" data-i18n-attr="title" title="清除搜索" id="group-search-clear-btn" style="display: none;">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
||
<path d="m8 8 8 8M16 8l-8 8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="group-detail-content">
|
||
<div id="group-conversations-list" class="group-conversations-list"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 对话界面 -->
|
||
<div class="chat-container">
|
||
<!-- 会话顶部栏(只在有会话选中时显示) -->
|
||
<div id="conversation-header" class="conversation-header" style="display: none;">
|
||
<div class="conversation-header-content">
|
||
<button id="attack-chain-btn" class="attack-chain-btn" data-i18n="chat.viewAttackChain" data-i18n-attr="title" data-i18n-skip-text="true" title="查看攻击链" disabled>
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M10.5 13.5l3-3M8 8H5a4 4 0 1 0 0 8h3m8-8h3a4 4 0 0 1 0 8h-3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="chat.attackChain">攻击链</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="active-tasks-bar" class="active-tasks-bar"></div>
|
||
<div id="chat-messages" class="chat-messages"></div>
|
||
<div id="chat-input-container" class="chat-input-container">
|
||
<div class="role-selector-wrapper">
|
||
<button id="role-selector-btn" class="role-selector-btn" onclick="toggleRoleSelectionPanel()" data-i18n="chat.selectRole" data-i18n-attr="title" title="选择角色">
|
||
<span id="role-selector-icon" class="role-selector-icon">🔵</span>
|
||
<span id="role-selector-text" class="role-selector-text" data-i18n="chat.defaultRole">默认</span>
|
||
<svg class="role-selector-arrow" width="10" height="10" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
<!-- 角色选择下拉面板 -->
|
||
<div id="role-selection-panel" class="role-selection-panel" style="display: none;">
|
||
<div class="role-selection-panel-header">
|
||
<h3 class="role-selection-panel-title" data-i18n="chatGroup.rolePanelTitle">选择角色</h3>
|
||
<button class="role-selection-panel-close" onclick="closeRoleSelectionPanel()" data-i18n="common.close" data-i18n-attr="title" title="关闭">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div id="role-selection-list" class="role-selection-list-main"></div>
|
||
</div>
|
||
</div>
|
||
<div id="agent-mode-wrapper" class="agent-mode-wrapper" style="display: none;">
|
||
<div class="agent-mode-inner">
|
||
<button type="button" id="agent-mode-btn" class="role-selector-btn agent-mode-btn" onclick="toggleAgentModePanel()" data-i18n="chat.agentModeSelectAria" data-i18n-attr="aria-label,title" data-i18n-skip-text="true" aria-label="选择单代理或多代理" aria-haspopup="listbox" aria-expanded="false" title="选择单代理或多代理">
|
||
<span id="agent-mode-icon" class="role-selector-icon" aria-hidden="true">🤖</span>
|
||
<span id="agent-mode-text" class="role-selector-text">单代理</span>
|
||
<svg class="role-selector-arrow" width="10" height="10" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||
<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
<div id="agent-mode-panel" class="agent-mode-panel" style="display: none;" role="listbox" aria-labelledby="agent-mode-panel-title">
|
||
<div class="role-selection-panel-header agent-mode-panel-header">
|
||
<h3 id="agent-mode-panel-title" class="role-selection-panel-title" data-i18n="chat.agentModePanelTitle">对话模式</h3>
|
||
<button type="button" class="role-selection-panel-close" onclick="closeAgentModePanel()" data-i18n="common.close" data-i18n-attr="title" title="关闭">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="agent-mode-options">
|
||
<button type="button" class="role-selection-item-main agent-mode-option" data-value="single" role="option" onclick="selectAgentMode('single')">
|
||
<div class="role-selection-item-icon-main" aria-hidden="true">🤖</div>
|
||
<div class="role-selection-item-content-main">
|
||
<div class="role-selection-item-name-main" data-i18n="chat.agentModeSingle">单代理</div>
|
||
<div class="role-selection-item-description-main" data-i18n="chat.agentModeSingleHint">单模型 ReAct 循环,适合常规对话与工具调用</div>
|
||
</div>
|
||
<div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="single">✓</div>
|
||
</button>
|
||
<button type="button" class="role-selection-item-main agent-mode-option" data-value="multi" role="option" onclick="selectAgentMode('multi')">
|
||
<div class="role-selection-item-icon-main" aria-hidden="true">🧩</div>
|
||
<div class="role-selection-item-content-main">
|
||
<div class="role-selection-item-name-main" data-i18n="chat.agentModeMulti">多代理</div>
|
||
<div class="role-selection-item-description-main" data-i18n="chat.agentModeMultiHint">Eino DeepAgent 编排子代理,适合复杂任务</div>
|
||
</div>
|
||
<div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="multi">✓</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<input type="hidden" id="agent-mode-select" value="single" autocomplete="off">
|
||
</div>
|
||
<div class="chat-input-with-files">
|
||
<div id="chat-file-list" class="chat-file-list" aria-label="已选文件列表"></div>
|
||
<div class="chat-input-field">
|
||
<textarea id="chat-input" data-i18n="chat.inputPlaceholder" data-i18n-attr="placeholder" data-i18n-skip-text="true" placeholder="输入测试目标或命令... (输入 @ 选择工具 | Shift+Enter 换行,Enter 发送)" rows="1"></textarea>
|
||
<div id="mention-suggestions" class="mention-suggestions" role="listbox" aria-label="工具提及候选"></div>
|
||
</div>
|
||
</div>
|
||
<input type="file" id="chat-file-input" class="chat-file-input-hidden" multiple accept="*" data-i18n="chat.selectFile" data-i18n-attr="title" title="选择文件">
|
||
<button type="button" class="chat-upload-btn" onclick="document.getElementById('chat-file-input').click()" data-i18n="chat.uploadFile" data-i18n-attr="title" title="上传文件(可多选或拖拽到此处)">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
<button class="send-btn" onclick="sendMessage()">
|
||
<span data-i18n="chat.send">发送</span>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M5 12h14M12 5l7 7-7 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MCP状态监控页面 -->
|
||
<div id="page-mcp-monitor" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="mcp.monitorTitle">MCP 状态监控</h2>
|
||
<button class="btn-secondary" onclick="refreshMonitorPanel()"><span data-i18n="common.refresh">刷新</span></button>
|
||
</div>
|
||
<div class="page-content">
|
||
<div class="monitor-sections">
|
||
<section class="monitor-section monitor-overview">
|
||
<div class="section-header">
|
||
<h3 data-i18n="mcp.execStats">执行统计</h3>
|
||
</div>
|
||
<div id="monitor-stats" class="monitor-stats-grid">
|
||
<div class="monitor-empty" data-i18n="mcpMonitor.loading">加载中...</div>
|
||
</div>
|
||
</section>
|
||
<section class="monitor-section monitor-executions">
|
||
<div class="section-header">
|
||
<h3 data-i18n="mcp.latestExecutions">最新执行记录</h3>
|
||
<div class="section-actions">
|
||
<label>
|
||
<span data-i18n="mcp.toolSearch">工具搜索</span>
|
||
<input type="text" id="monitor-tool-filter" data-i18n="mcp.toolSearchPlaceholder" data-i18n-attr="placeholder" placeholder="输入工具名称..." oninput="handleToolFilterInput()" onkeydown="if(event.key==='Enter') applyMonitorFilters()" />
|
||
</label>
|
||
<label>
|
||
<span data-i18n="mcp.statusFilter">状态筛选</span>
|
||
<select id="monitor-status-filter" onchange="applyMonitorFilters()">
|
||
<option value="all" data-i18n="mcp.filterAll">全部</option>
|
||
<option value="completed" data-i18n="mcpMonitor.statusCompleted">已完成</option>
|
||
<option value="running" data-i18n="mcpMonitor.statusRunning">执行中</option>
|
||
<option value="failed" data-i18n="mcpMonitor.statusFailed">失败</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div id="monitor-batch-actions" class="monitor-batch-actions" style="display: none;">
|
||
<div class="batch-actions-info">
|
||
<span id="monitor-selected-count">已选择 0 项</span>
|
||
</div>
|
||
<div class="batch-actions-buttons">
|
||
<button class="btn-secondary" onclick="selectAllExecutions()" data-i18n="mcp.selectAll">全选</button>
|
||
<button class="btn-secondary" onclick="deselectAllExecutions()" data-i18n="mcpMonitor.deselectAll">取消全选</button>
|
||
<button class="btn-secondary btn-delete" onclick="batchDeleteExecutions()" data-i18n="mcp.deleteSelected">批量删除</button>
|
||
</div>
|
||
</div>
|
||
<div id="monitor-executions" class="monitor-table-container">
|
||
<div class="monitor-empty" data-i18n="mcpMonitor.loading">加载中...</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MCP管理页面 -->
|
||
<div id="page-mcp-management" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="mcp.managementTitle">MCP 管理</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-secondary" onclick="loadExternalMCPs()"><span data-i18n="common.refresh">刷新</span></button>
|
||
<button class="btn-primary" onclick="showAddExternalMCPModal()" data-i18n="mcp.addExternalMCP">添加外部MCP</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content">
|
||
<!-- MCP工具配置 -->
|
||
<div class="settings-section" style="margin-bottom: 32px;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
||
<h3 style="margin: 0;" data-i18n="mcp.toolConfig">MCP 工具配置</h3>
|
||
<button class="btn-primary" onclick="saveToolsConfig()" data-i18n="mcp.saveToolConfig">保存工具配置</button>
|
||
</div>
|
||
<div class="tools-controls">
|
||
<div class="tools-actions">
|
||
<button class="btn-secondary" onclick="selectAllTools()" data-i18n="mcp.selectAll">全选</button>
|
||
<button class="btn-secondary" onclick="deselectAllTools()" data-i18n="mcp.deselectAll">全不选</button>
|
||
<div class="search-box">
|
||
<input type="text" id="tools-search" data-i18n="mcp.toolSearchPlaceholder" data-i18n-attr="placeholder" placeholder="搜索工具..." onkeypress="handleSearchKeyPress(event)" oninput="if(this.value.trim() === '') clearSearch()" />
|
||
<button class="btn-search" onclick="searchTools()" data-i18n="common.search" data-i18n-attr="title" title="搜索">🔍</button>
|
||
</div>
|
||
<div class="tools-stats" id="tools-stats"></div>
|
||
</div>
|
||
<div id="tools-list" class="tools-list"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 外部MCP配置 -->
|
||
<div class="settings-section">
|
||
<h3 data-i18n="mcp.externalConfig">外部 MCP 配置</h3>
|
||
<div class="external-mcp-controls">
|
||
<div class="external-mcp-actions">
|
||
<div class="external-mcp-stats" id="external-mcp-stats"></div>
|
||
</div>
|
||
<div id="external-mcp-list" class="external-mcp-list"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 知识管理页面 -->
|
||
<div id="page-knowledge-management" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="knowledge.title">知识管理</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-secondary" onclick="refreshKnowledgeBase()" data-i18n="common.refresh">刷新</button>
|
||
<button class="btn-secondary" onclick="rebuildKnowledgeIndex()" data-i18n="knowledge.rebuildIndex">重建索引</button>
|
||
<button class="btn-primary" onclick="showAddKnowledgeItemModal()" data-i18n="knowledge.addKnowledge">添加知识</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content">
|
||
<div class="knowledge-controls">
|
||
<div class="knowledge-stats-bar" id="knowledge-stats">
|
||
<div class="knowledge-stat-item">
|
||
<span class="knowledge-stat-label" data-i18n="knowledge.totalItems">总知识项</span>
|
||
<span class="knowledge-stat-value">-</span>
|
||
</div>
|
||
<div class="knowledge-stat-item">
|
||
<span class="knowledge-stat-label" data-i18n="knowledge.categories">分类数</span>
|
||
<span class="knowledge-stat-value">-</span>
|
||
</div>
|
||
<div class="knowledge-stat-item">
|
||
<span class="knowledge-stat-label" data-i18n="knowledgePage.totalContent">总内容</span>
|
||
<span class="knowledge-stat-value">-</span>
|
||
</div>
|
||
</div>
|
||
<div id="knowledge-index-progress" style="display: none; margin-bottom: 16px;"></div>
|
||
<div class="knowledge-filters">
|
||
<label>
|
||
<span data-i18n="knowledgePage.categoryFilter">分类筛选</span>
|
||
<div class="custom-select-wrapper">
|
||
<div class="custom-select" id="knowledge-category-filter-wrapper">
|
||
<div class="custom-select-trigger" id="knowledge-category-filter-trigger">
|
||
<span data-i18n="knowledgePage.all">全部</span>
|
||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<div class="custom-select-dropdown" id="knowledge-category-filter-dropdown">
|
||
<div class="custom-select-option" data-value="" onclick="selectKnowledgeCategory('')" data-i18n="knowledgePage.all">全部</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</label>
|
||
<div class="search-box">
|
||
<input type="text" id="knowledge-search" data-i18n="knowledgePage.searchPlaceholder" data-i18n-attr="placeholder" placeholder="搜索知识..." oninput="handleKnowledgeSearchInput()" onkeydown="if(event.key==='Enter') searchKnowledgeItems()" />
|
||
<button class="btn-search" onclick="searchKnowledgeItems()" data-i18n="common.search" data-i18n-attr="title" title="搜索">🔍</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="knowledge-items-list" class="knowledge-items-list">
|
||
<div class="loading-spinner" data-i18n="knowledgePage.loading">加载中...</div>
|
||
</div>
|
||
<div id="knowledge-pagination"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 知识检索历史页面 -->
|
||
<div id="page-knowledge-retrieval-logs" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="knowledge.retrievalLogs">检索历史</h2>
|
||
<button class="btn-secondary" onclick="refreshRetrievalLogs()" data-i18n="common.refresh">刷新</button>
|
||
</div>
|
||
<div class="page-content">
|
||
<div class="retrieval-logs-controls">
|
||
<div class="retrieval-stats-bar" id="retrieval-stats">
|
||
<div class="retrieval-stat-item">
|
||
<span class="retrieval-stat-label" data-i18n="retrievalLogs.totalRetrievals">总检索次数</span>
|
||
<span class="retrieval-stat-value">-</span>
|
||
</div>
|
||
<div class="retrieval-stat-item">
|
||
<span class="retrieval-stat-label" data-i18n="retrievalLogs.successRetrievals">成功检索</span>
|
||
<span class="retrieval-stat-value">-</span>
|
||
</div>
|
||
<div class="retrieval-stat-item">
|
||
<span class="retrieval-stat-label" data-i18n="retrievalLogs.successRate">成功率</span>
|
||
<span class="retrieval-stat-value">-</span>
|
||
</div>
|
||
<div class="retrieval-stat-item">
|
||
<span class="retrieval-stat-label" data-i18n="retrievalLogs.retrievedItems">检索到知识项</span>
|
||
<span class="retrieval-stat-value">-</span>
|
||
</div>
|
||
</div>
|
||
<div class="retrieval-logs-filters">
|
||
<label>
|
||
<span data-i18n="retrievalLogs.conversationId">对话ID</span>
|
||
<input type="text" id="retrieval-logs-conversation-id" data-i18n="retrievalLogs.optionalConversation" data-i18n-attr="placeholder" placeholder="可选:筛选特定对话" />
|
||
</label>
|
||
<label>
|
||
<span data-i18n="retrievalLogs.messageId">消息ID</span>
|
||
<input type="text" id="retrieval-logs-message-id" data-i18n="retrievalLogs.optionalMessage" data-i18n-attr="placeholder" placeholder="可选:筛选特定消息" />
|
||
</label>
|
||
<button class="btn-secondary" onclick="filterRetrievalLogs()" data-i18n="retrievalLogs.filter">筛选</button>
|
||
</div>
|
||
</div>
|
||
<div id="retrieval-logs-list" class="retrieval-logs-list">
|
||
<div class="loading-spinner" data-i18n="retrievalLogs.loading">加载中...</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 信息收集页面 -->
|
||
<div id="page-info-collect" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="infoCollectPage.title">信息收集</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-secondary" onclick="resetFofaForm()" data-i18n="infoCollectPage.reset">重置</button>
|
||
<button class="btn-primary" onclick="submitFofaSearch()" data-i18n="infoCollectPage.confirm">确定</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content">
|
||
<div class="info-collect-panel">
|
||
<div class="info-collect-form">
|
||
<div class="form-group">
|
||
<label for="fofa-query" data-i18n="infoCollectPage.fofaQuerySyntax">FOFA 查询语法</label>
|
||
<textarea id="fofa-query" class="info-collect-query-input" rows="1" data-i18n="infoCollect.queryPlaceholder" data-i18n-attr="placeholder" placeholder='例如:app="Apache" && country="CN"'></textarea>
|
||
<small class="form-hint" data-i18n="infoCollectPage.formHint">查询语法参考 FOFA 文档,支持 && / || / () 等。</small>
|
||
<div class="info-collect-presets" aria-label="FOFA 查询示例" data-i18n="infoCollectPage.queryPresetsAria" data-i18n-attr="aria-label" data-i18n-skip-text="true">
|
||
<button class="preset-chip" type="button" onclick="applyFofaQueryPreset('app="Apache" && country="CN"')" data-i18n="infoCollectPage.presetApache" data-i18n-attr="title" data-i18n-title="infoCollectPage.fillExample" title="填入示例">Apache + 中国</button>
|
||
<button class="preset-chip" type="button" onclick="applyFofaQueryPreset('title="登录" && country="CN"')" data-i18n="infoCollectPage.presetLogin" data-i18n-attr="title" data-i18n-title="infoCollectPage.fillExample" title="填入示例">登录页 + 中国</button>
|
||
<button class="preset-chip" type="button" onclick="applyFofaQueryPreset('domain="example.com"')" data-i18n="infoCollectPage.presetDomain" data-i18n-attr="title" data-i18n-title="infoCollectPage.fillExample" title="填入示例">指定域名</button>
|
||
<button class="preset-chip" type="button" onclick="applyFofaQueryPreset('ip="1.1.1.1"')" data-i18n="infoCollectPage.presetIp" data-i18n-attr="title" data-i18n-title="infoCollectPage.fillExample" title="填入示例">指定 IP</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="fofa-nl" data-i18n="infoCollectPage.naturalLanguage">自然语言(AI 解析为 FOFA 语法)</label>
|
||
<div class="info-collect-nl-row">
|
||
<textarea id="fofa-nl" class="info-collect-query-input" rows="1" data-i18n="infoCollectPage.nlPlaceholder" data-i18n-attr="placeholder" placeholder="例如:找美国 Missouri 的 Apache 站点,标题包含 Home"></textarea>
|
||
<button id="fofa-nl-parse-btn" class="btn-secondary" type="button" onclick="parseFofaNaturalLanguage()" data-i18n="infoCollectPage.parseBtn" data-i18n-attr="title" data-i18n-title="infoCollectPage.parseBtnTitle" title="将自然语言解析为 FOFA 查询语法">AI 解析</button>
|
||
</div>
|
||
<div id="fofa-nl-status" class="fofa-nl-status muted" style="display: none;" aria-live="polite"></div>
|
||
<small class="form-hint" data-i18n="infoCollectPage.parseHint">解析后会弹窗展示 FOFA 语法(可编辑),确认无误后再填入查询框并执行查询。</small>
|
||
</div>
|
||
<div class="info-collect-form-row">
|
||
<div class="form-group">
|
||
<label for="fofa-size" data-i18n="infoCollectPage.returnCount">返回数量</label>
|
||
<input type="number" id="fofa-size" min="1" max="10000" value="100" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="fofa-page" data-i18n="infoCollectPage.pageNum">页码</label>
|
||
<input type="number" id="fofa-page" min="1" value="1" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="checkbox-label" style="margin-top: 24px;">
|
||
<input type="checkbox" id="fofa-full" class="modern-checkbox" />
|
||
<span class="checkbox-custom"></span>
|
||
<span class="checkbox-text" data-i18n="infoCollectPage.fullLabel">full</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="fofa-fields" data-i18n="infoCollectPage.returnFields">返回字段名(逗号分隔)</label>
|
||
<input type="text" id="fofa-fields" value="host,ip,port,domain,title,protocol,country,province,city,server" />
|
||
<div class="info-collect-presets" aria-label="FOFA 字段模板" data-i18n="infoCollectPage.fieldsPresetsAria" data-i18n-attr="aria-label" data-i18n-skip-text="true">
|
||
<button class="preset-chip" type="button" onclick="applyFofaFieldsPreset('host,ip,port,domain')" data-i18n="infoCollectPage.minFields" data-i18n-attr="title" data-i18n-title="infoCollectPage.minFieldsTitle" title="适合快速导出目标">最小字段</button>
|
||
<button class="preset-chip" type="button" onclick="applyFofaFieldsPreset('host,title,ip,port,domain,protocol,server,icp,country,province,city')" data-i18n="infoCollectPage.webCommon" data-i18n-attr="title" data-i18n-title="infoCollectPage.webCommonTitle" title="适合浏览和筛选">Web 常用</button>
|
||
<button class="preset-chip" type="button" onclick="applyFofaFieldsPreset('host,ip,port,domain,title,protocol,country,province,city,server,as_number,as_organization,icp,header,banner')" data-i18n="infoCollectPage.intelEnhanced" data-i18n-attr="title" data-i18n-title="infoCollectPage.intelEnhancedTitle" title="更偏指纹/情报">情报增强</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info-collect-results">
|
||
<div class="info-collect-results-header">
|
||
<div class="info-collect-results-header-left">
|
||
<div class="info-collect-results-title" data-i18n="infoCollectPage.queryResults">查询结果</div>
|
||
<div class="info-collect-results-meta" id="fofa-results-meta">-</div>
|
||
</div>
|
||
<div class="info-collect-results-toolbar" aria-label="结果工具条" data-i18n="infoCollectPage.resultsToolbarAria" data-i18n-attr="aria-label" data-i18n-skip-text="true">
|
||
<div class="info-collect-selected" id="fofa-selected-meta" data-i18n="infoCollectPage.selectedRowsZero">已选择 0 条</div>
|
||
<button class="btn-secondary btn-small" type="button" onclick="toggleFofaColumnsPanel()" data-i18n="infoCollectPage.showHideColumns" data-i18n-attr="title" title="显示/隐藏字段"><span data-i18n="infoCollectPage.columns">列</span></button>
|
||
<button class="btn-secondary btn-small" type="button" onclick="exportFofaResults('csv')" data-i18n="infoCollectPage.exportCsvTitle" data-i18n-attr="title" title="导出当前结果为 CSV(UTF-8,兼容中文)"><span data-i18n="infoCollectPage.exportCsv">导出 CSV</span></button>
|
||
<button class="btn-secondary btn-small" type="button" onclick="exportFofaResults('json')" data-i18n="infoCollectPage.exportJsonTitle" data-i18n-attr="title" title="导出当前结果为 JSON"><span data-i18n="infoCollectPage.exportJson">导出 JSON</span></button>
|
||
<button class="btn-secondary btn-small" type="button" onclick="exportFofaResults('xlsx')" data-i18n="infoCollectPage.exportXlsxTitle" data-i18n-attr="title" title="导出当前结果为 Excel"><span data-i18n="infoCollectPage.exportXlsx">导出 XLSX</span></button>
|
||
<button class="btn-primary btn-small" type="button" onclick="batchScanSelectedFofaRows()" data-i18n="infoCollectPage.batchScanTitle" data-i18n-attr="title" title="将所选行创建为批量任务队列"><span data-i18n="infoCollectPage.batchScan">批量扫描</span></button>
|
||
</div>
|
||
</div>
|
||
<div class="info-collect-results-table-wrap">
|
||
<!-- 字段显示/隐藏面板 -->
|
||
<div id="fofa-columns-panel" class="info-collect-columns-panel" style="display: none;">
|
||
<div class="info-collect-columns-panel-header">
|
||
<div class="info-collect-columns-title" data-i18n="infoCollectPage.showColumns">显示字段</div>
|
||
<div class="info-collect-columns-actions">
|
||
<button class="btn-secondary btn-small" type="button" onclick="showAllFofaColumns()" data-i18n="infoCollectPage.columnsPanelAll">全选</button>
|
||
<button class="btn-secondary btn-small" type="button" onclick="hideAllFofaColumns()" data-i18n="infoCollectPage.columnsPanelNone">全不选</button>
|
||
<button class="btn-secondary btn-small" type="button" onclick="closeFofaColumnsPanel()" data-i18n="infoCollectPage.columnsPanelClose">关闭</button>
|
||
</div>
|
||
</div>
|
||
<div id="fofa-columns-list" class="info-collect-columns-list"></div>
|
||
</div>
|
||
<table class="info-collect-table">
|
||
<thead id="fofa-results-thead"></thead>
|
||
<tbody id="fofa-results-tbody">
|
||
<tr><td class="muted" style="padding: 16px;" data-i18n="common.noData">暂无数据</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 漏洞管理页面 -->
|
||
<div id="page-vulnerabilities" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="vulnerability.title">漏洞管理</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-secondary" onclick="refreshVulnerabilities()" data-i18n="common.refresh">刷新</button>
|
||
<button class="btn-primary" onclick="showAddVulnerabilityModal()" data-i18n="vulnerability.addVuln">添加漏洞</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content">
|
||
<!-- 统计看板 -->
|
||
<div class="vulnerability-dashboard" id="vulnerability-dashboard">
|
||
<div class="dashboard-stats">
|
||
<div class="stat-card">
|
||
<div class="stat-label" data-i18n="vulnerabilityPage.statTotal">总漏洞数</div>
|
||
<div class="stat-value" id="stat-total">-</div>
|
||
</div>
|
||
<div class="stat-card stat-critical">
|
||
<div class="stat-label" data-i18n="dashboard.severityCritical">严重</div>
|
||
<div class="stat-value" id="stat-critical">-</div>
|
||
</div>
|
||
<div class="stat-card stat-high">
|
||
<div class="stat-label" data-i18n="dashboard.severityHigh">高危</div>
|
||
<div class="stat-value" id="stat-high">-</div>
|
||
</div>
|
||
<div class="stat-card stat-medium">
|
||
<div class="stat-label" data-i18n="dashboard.severityMedium">中危</div>
|
||
<div class="stat-value" id="stat-medium">-</div>
|
||
</div>
|
||
<div class="stat-card stat-low">
|
||
<div class="stat-label" data-i18n="dashboard.severityLow">低危</div>
|
||
<div class="stat-value" id="stat-low">-</div>
|
||
</div>
|
||
<div class="stat-card stat-info">
|
||
<div class="stat-label" data-i18n="dashboard.severityInfo">信息</div>
|
||
<div class="stat-value" id="stat-info">-</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 筛选和搜索 -->
|
||
<div class="vulnerability-controls">
|
||
<div class="vulnerability-filters">
|
||
<label>
|
||
<span data-i18n="vulnerabilityPage.vulnId">漏洞ID</span>
|
||
<input type="text" id="vulnerability-id-filter" data-i18n="vulnerabilityPage.searchVulnId" data-i18n-attr="placeholder" placeholder="搜索漏洞ID" />
|
||
</label>
|
||
<label>
|
||
<span data-i18n="vulnerabilityPage.conversationId">会话ID</span>
|
||
<input type="text" id="vulnerability-conversation-filter" data-i18n="vulnerabilityPage.filterConversation" data-i18n-attr="placeholder" placeholder="筛选特定会话" />
|
||
</label>
|
||
<label>
|
||
<span data-i18n="vulnerabilityPage.severity">严重程度</span>
|
||
<select id="vulnerability-severity-filter">
|
||
<option value="" data-i18n="knowledgePage.all">全部</option>
|
||
<option value="critical" data-i18n="dashboard.severityCritical">严重</option>
|
||
<option value="high" data-i18n="dashboard.severityHigh">高危</option>
|
||
<option value="medium" data-i18n="dashboard.severityMedium">中危</option>
|
||
<option value="low" data-i18n="dashboard.severityLow">低危</option>
|
||
<option value="info" data-i18n="dashboard.severityInfo">信息</option>
|
||
</select>
|
||
</label>
|
||
<label>
|
||
<span data-i18n="vulnerabilityPage.status">状态</span>
|
||
<select id="vulnerability-status-filter">
|
||
<option value="" data-i18n="knowledgePage.all">全部</option>
|
||
<option value="open" data-i18n="vulnerabilityPage.statusOpen">待处理</option>
|
||
<option value="confirmed" data-i18n="vulnerabilityPage.statusConfirmed">已确认</option>
|
||
<option value="fixed" data-i18n="vulnerabilityPage.statusFixed">已修复</option>
|
||
<option value="false_positive" data-i18n="vulnerabilityPage.statusFalsePositive">误报</option>
|
||
</select>
|
||
</label>
|
||
<button class="btn-secondary" onclick="filterVulnerabilities()" data-i18n="vulnerabilityPage.filter">筛选</button>
|
||
<button class="btn-secondary" onclick="clearVulnerabilityFilters()" data-i18n="vulnerabilityPage.clear">清除</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 漏洞列表 -->
|
||
<div id="vulnerabilities-list" class="vulnerabilities-list">
|
||
<div class="loading-spinner" data-i18n="vulnerabilityPage.loading">加载中...</div>
|
||
</div>
|
||
|
||
<!-- 分页控件 -->
|
||
<div id="vulnerability-pagination" class="pagination-container pagination-fixed"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- WebShell 管理页面 -->
|
||
<div id="page-webshell" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="webshell.title">WebShell 管理</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-primary" onclick="showAddWebshellModal()" data-i18n="webshell.addConnection">添加连接</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content webshell-page-content">
|
||
<div class="webshell-layout">
|
||
<div id="webshell-sidebar" class="webshell-sidebar">
|
||
<div class="webshell-sidebar-header" data-i18n="webshell.connections">连接列表</div>
|
||
<div class="webshell-conn-search">
|
||
<input type="text"
|
||
id="webshell-conn-search"
|
||
class="form-control webshell-conn-search-input"
|
||
data-i18n="webshell.searchPlaceholder"
|
||
data-i18n-attr="placeholder"
|
||
placeholder="搜索连接..." />
|
||
</div>
|
||
<div class="webshell-sidebar-tools">
|
||
<button type="button" class="btn-ghost btn-sm" id="webshell-batch-probe-btn" data-i18n="webshell.batchProbe">一键批量探活</button>
|
||
</div>
|
||
<div id="webshell-list" class="webshell-list">
|
||
<div class="webshell-empty" data-i18n="webshell.noConnections">暂无连接,请点击「添加连接」</div>
|
||
</div>
|
||
</div>
|
||
<div id="webshell-resize-handle" class="webshell-resize-handle" title="拖拽调整宽度"></div>
|
||
<div class="webshell-main">
|
||
<div id="webshell-workspace" class="webshell-workspace">
|
||
<div class="webshell-workspace-placeholder" data-i18n="webshell.selectOrAdd">请从左侧选择连接,或添加新的 WebShell 连接</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 对话附件 / 文件管理 -->
|
||
<div id="page-chat-files" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="chatFilesPage.title">文件管理</h2>
|
||
<div class="page-header-actions">
|
||
<button type="button" class="btn-primary" onclick="chatFilesOpenUploadPicker()" data-i18n="chatFilesPage.upload">上传文件</button>
|
||
<input type="file" id="chat-files-upload-input" style="display:none" onchange="onChatFilesUploadPick(event)" />
|
||
<button class="btn-secondary" onclick="loadChatFilesPage()" data-i18n="common.refresh">刷新</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content">
|
||
<p class="chat-files-intro" data-i18n="chatFilesPage.intro">管理在对话中上传的文件。需要让 AI 引用某文件时,在列表中点击「复制路径」,到对话里粘贴即可。</p>
|
||
<div class="tasks-filters chat-files-filters">
|
||
<label>
|
||
<span data-i18n="chatFilesPage.conversationFilter">会话 ID</span>
|
||
<input type="text" id="chat-files-filter-conv" class="form-control" data-i18n="chatFilesPage.conversationPlaceholder" data-i18n-attr="placeholder" placeholder="留空表示全部" onkeydown="if(event.key==='Enter') loadChatFilesPage()" />
|
||
</label>
|
||
<label style="flex:1;min-width:180px;max-width:360px;">
|
||
<span data-i18n="chatFilesPage.searchName">文件名</span>
|
||
<input type="text" id="chat-files-filter-name" class="form-control" data-i18n="chatFilesPage.searchNamePlaceholder" data-i18n-attr="placeholder" placeholder="筛选文件名" oninput="chatFilesFilterNameOnInput()" onkeydown="if(event.key==='Enter') loadChatFilesPage()" />
|
||
</label>
|
||
<label>
|
||
<span data-i18n="chatFilesPage.groupBy">分组</span>
|
||
<select id="chat-files-group-by" class="form-control" onchange="chatFilesGroupByChange()">
|
||
<option value="none" data-i18n="chatFilesPage.groupNone">不分组</option>
|
||
<option value="date" data-i18n="chatFilesPage.groupByDate">按日期</option>
|
||
<option value="conversation" data-i18n="chatFilesPage.groupByConversation">按会话</option>
|
||
<option value="folder" data-i18n="chatFilesPage.groupByFolder">按文件夹</option>
|
||
</select>
|
||
</label>
|
||
<button class="btn-secondary" type="button" onclick="loadChatFilesPage()" data-i18n="common.search">搜索</button>
|
||
</div>
|
||
<div id="chat-files-list-wrap" class="chat-files-table-wrap">
|
||
<div class="loading-spinner" data-i18n="common.loading">加载中…</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务管理页面 -->
|
||
<div id="page-tasks" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="tasks.title">任务管理</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-primary" onclick="showBatchImportModal()" data-i18n="tasks.newTask">新建任务</button>
|
||
<label class="auto-refresh-toggle">
|
||
<input type="checkbox" id="tasks-auto-refresh" checked onchange="toggleTasksAutoRefresh(this.checked)">
|
||
<span data-i18n="tasks.autoRefresh">自动刷新</span>
|
||
</label>
|
||
<button class="btn-secondary" onclick="refreshBatchQueues()" data-i18n="common.refresh">刷新</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content">
|
||
<!-- 批量任务队列列表 -->
|
||
<div class="batch-queues-section" id="batch-queues-section" style="display: none;">
|
||
<!-- 筛选控件 -->
|
||
<div class="batch-queues-filters tasks-filters">
|
||
<label>
|
||
<span data-i18n="tasksPage.statusFilter">状态筛选</span>
|
||
<select id="batch-queues-status-filter" onchange="filterBatchQueues()">
|
||
<option value="all" data-i18n="knowledgePage.all">全部</option>
|
||
<option value="pending" data-i18n="tasksPage.statusPending">待执行</option>
|
||
<option value="running" data-i18n="dashboard.executing">执行中</option>
|
||
<option value="paused" data-i18n="tasksPage.statusPaused">已暂停</option>
|
||
<option value="completed" data-i18n="dashboard.completed">已完成</option>
|
||
<option value="cancelled" data-i18n="tasksPage.statusCancelled">已取消</option>
|
||
</select>
|
||
</label>
|
||
<label style="flex: 1; max-width: 300px;">
|
||
<span data-i18n="tasksPage.searchQueuePlaceholder">搜索队列ID、标题或创建时间</span>
|
||
<input type="text" id="batch-queues-search" data-i18n="tasksPage.searchKeywordPlaceholder" data-i18n-attr="placeholder" placeholder="输入关键字搜索..."
|
||
oninput="filterBatchQueues()">
|
||
</label>
|
||
</div>
|
||
<div id="batch-queues-list" class="batch-queues-list"></div>
|
||
<!-- 分页控件 -->
|
||
<div id="batch-queues-pagination" class="pagination-container pagination-fixed"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 角色管理页面 -->
|
||
<div id="page-roles-management" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="roles.title">角色管理</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-secondary" onclick="refreshRoles()" data-i18n="common.refresh">刷新</button>
|
||
<button class="btn-primary" onclick="showAddRoleModal()" data-i18n="roles.createRole">创建角色</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content">
|
||
<div class="roles-controls">
|
||
<div class="roles-search-box">
|
||
<input type="text" id="roles-search" data-i18n="roles.searchPlaceholder" data-i18n-attr="placeholder" placeholder="搜索角色..." oninput="handleRolesSearchInput()" onkeydown="if(event.key==='Enter') searchRoles()" />
|
||
<button class="roles-search-clear" id="roles-search-clear" onclick="clearRolesSearch()" style="display: none;" data-i18n="common.clearSearch" data-i18n-attr="title" title="清除搜索">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
||
<path d="M15 9l-6 6M9 9l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="roles-list" class="roles-grid">
|
||
<div class="loading-spinner" data-i18n="knowledgePage.loading">加载中...</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Skills状态监控页面 -->
|
||
<div id="page-skills-monitor" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="skills.monitorTitle">Skills状态监控</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-secondary" onclick="refreshSkillsMonitor()" data-i18n="common.refresh">刷新</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content">
|
||
<div class="monitor-sections">
|
||
<section class="monitor-section monitor-overview">
|
||
<div class="section-header">
|
||
<h3 data-i18n="skills.callStats">调用统计</h3>
|
||
</div>
|
||
<div id="skills-stats" class="monitor-stats-grid">
|
||
<div class="monitor-empty" data-i18n="skillsPage.loading">加载中...</div>
|
||
</div>
|
||
</section>
|
||
<section class="monitor-section monitor-executions">
|
||
<div class="section-header">
|
||
<h3 data-i18n="skillsPage.skillsCallStats">Skills调用统计</h3>
|
||
<div class="section-actions">
|
||
<button class="btn-secondary btn-small" onclick="clearSkillsStats()" data-i18n="skillsPage.clearStatsTitle" data-i18n-attr="title" data-i18n-skip-text="true" title="清空所有统计数据"><span data-i18n="skillsPage.clearStats">清空统计</span></button>
|
||
</div>
|
||
</div>
|
||
<div id="skills-monitor-list" class="monitor-table-container">
|
||
<div class="monitor-empty" data-i18n="skillsPage.loading">加载中...</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Skills管理页面 -->
|
||
<div id="page-skills-management" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="skills.title">Skills管理</h2>
|
||
<div class="page-header-actions">
|
||
<button class="btn-secondary" onclick="refreshSkills()" data-i18n="common.refresh">刷新</button>
|
||
<button class="btn-primary" onclick="showAddSkillModal()" data-i18n="skills.createSkill">创建Skill</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content page-content-with-pagination">
|
||
<div class="skills-controls">
|
||
<div class="skills-search-box">
|
||
<input type="text" id="skills-search" data-i18n="skillsPage.searchPlaceholder" data-i18n-attr="placeholder" placeholder="搜索Skills..." oninput="handleSkillsSearchInput()" onkeydown="if(event.key==='Enter') searchSkills()" />
|
||
<button class="skills-search-clear" id="skills-search-clear" onclick="clearSkillsSearch()" style="display: none;" data-i18n="common.clearSearch" data-i18n-attr="title" title="清除搜索">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
||
<path d="M15 9l-6 6M9 9l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="skills-list" class="skills-grid">
|
||
<div class="loading-spinner" data-i18n="skillsPage.loading">加载中...</div>
|
||
</div>
|
||
<div id="skills-pagination" class="pagination-container pagination-fixed"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 多代理子 Agent(Markdown)管理 -->
|
||
<div id="page-agents-management" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="agentsPage.title">Agent 管理</h2>
|
||
<div class="page-header-actions">
|
||
<button type="button" class="btn-secondary" onclick="loadMarkdownAgents()" data-i18n="common.refresh">刷新</button>
|
||
<button type="button" class="btn-primary" onclick="showAddMarkdownAgentModal()" data-i18n="agentsPage.create">新建 Agent</button>
|
||
</div>
|
||
</div>
|
||
<div class="page-content">
|
||
<p class="agents-page-hint" data-i18n="agentsPage.hint">子 Agent 仅在 agents 目录下 .md 维护。</p>
|
||
<div id="agents-md-dir" class="agents-dir-label"></div>
|
||
<div id="agents-md-list" class="skills-grid">
|
||
<div class="loading-spinner" data-i18n="agentsPage.loading">加载中...</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Agent Markdown 编辑弹窗 -->
|
||
<div id="agent-md-modal" class="modal" style="display: none;">
|
||
<div class="modal-content" style="max-width: 720px; max-height: 92vh;">
|
||
<div class="modal-header">
|
||
<h2 id="agent-md-modal-title" data-i18n="agentsPage.editTitle">编辑 Agent</h2>
|
||
<span class="modal-close" onclick="closeMarkdownAgentModal()">×</span>
|
||
</div>
|
||
<div class="modal-body" style="overflow-y: auto; max-height: calc(92vh - 130px);">
|
||
<input type="hidden" id="agent-md-filename-current" value="">
|
||
<div class="form-group" id="agent-md-filename-row">
|
||
<label data-i18n="agentsPage.filename">文件名(.md)</label>
|
||
<input type="text" id="agent-md-filename" data-i18n="agentsPage.filenamePlaceholder" data-i18n-attr="placeholder" placeholder="例如 code-reviewer.md" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label data-i18n="agentsPage.fieldRole">类型</label>
|
||
<select id="agent-md-role" class="form-select">
|
||
<option value="sub" data-i18n="agentsPage.roleSub">子代理</option>
|
||
<option value="orchestrator" data-i18n="agentsPage.roleOrchestrator">主代理(Deep 协调者)</option>
|
||
</select>
|
||
<p class="form-hint muted" data-i18n="agentsPage.roleHint">主代理也可使用固定文件名 orchestrator.md;全目录仅允许一个主代理。</p>
|
||
</div>
|
||
<div class="form-group">
|
||
<label data-i18n="agentsPage.fieldId">Agent ID(留空则从名称生成)</label>
|
||
<input type="text" id="agent-md-id" placeholder="code-reviewer" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label data-i18n="agentsPage.fieldName">显示名称</label>
|
||
<input type="text" id="agent-md-name" data-i18n="agentsPage.namePlaceholder" data-i18n-attr="placeholder" placeholder="Code Reviewer" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label data-i18n="agentsPage.fieldDesc">描述</label>
|
||
<textarea id="agent-md-description" rows="2" data-i18n="agentsPage.descPlaceholder" data-i18n-attr="placeholder" placeholder="何时调用该子代理"></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label data-i18n="agentsPage.fieldTools">可用工具(逗号分隔,与角色工具 key 一致)</label>
|
||
<input type="text" id="agent-md-tools" placeholder="tool_a, tool_b" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label data-i18n="agentsPage.fieldBindRole">绑定角色(可选)</label>
|
||
<input type="text" id="agent-md-bind-role" placeholder="" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label data-i18n="agentsPage.fieldMaxIter">子代理最大迭代(0=使用全局默认)</label>
|
||
<input type="number" id="agent-md-max-iter" min="0" value="0" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label data-i18n="agentsPage.fieldInstruction">系统提示词(Markdown 正文)</label>
|
||
<textarea id="agent-md-instruction" rows="14" data-i18n="agentsPage.instructionPlaceholder" data-i18n-attr="placeholder" placeholder="You are a specialist agent..."></textarea>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn-secondary" onclick="closeMarkdownAgentModal()" data-i18n="common.cancel">取消</button>
|
||
<button type="button" class="btn-primary" onclick="saveMarkdownAgent()" data-i18n="common.save">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 系统设置页面 -->
|
||
<div id="page-settings" class="page">
|
||
<div class="page-header">
|
||
<h2 data-i18n="settings.title">系统设置</h2>
|
||
</div>
|
||
<div class="page-content settings-layout">
|
||
<!-- 左侧导航栏 -->
|
||
<aside class="settings-sidebar">
|
||
<nav class="settings-nav">
|
||
<div class="settings-nav-item active" data-section="basic" onclick="switchSettingsSection('basic')">
|
||
<span data-i18n="settings.nav.basic">基本设置</span>
|
||
</div>
|
||
<div class="settings-nav-item" data-section="knowledge" onclick="switchSettingsSection('knowledge')">
|
||
<span data-i18n="settings.nav.knowledge">知识库</span>
|
||
</div>
|
||
<div class="settings-nav-item" data-section="robots" onclick="switchSettingsSection('robots')">
|
||
<span data-i18n="settings.nav.robots">机器人设置</span>
|
||
</div>
|
||
<div class="settings-nav-item" data-section="terminal" onclick="switchSettingsSection('terminal')">
|
||
<span data-i18n="settings.nav.terminal">终端</span>
|
||
</div>
|
||
<div class="settings-nav-item" data-section="security" onclick="switchSettingsSection('security')">
|
||
<span data-i18n="settings.nav.security">安全设置</span>
|
||
</div>
|
||
</nav>
|
||
</aside>
|
||
|
||
<!-- 右侧内容区域 -->
|
||
<div class="settings-content">
|
||
<!-- 基本设置 -->
|
||
<div id="settings-section-basic" class="settings-section-content active">
|
||
<div class="settings-section-header">
|
||
<h3 data-i18n="settingsBasic.basicTitle">基本设置</h3>
|
||
</div>
|
||
|
||
<!-- OpenAI配置 -->
|
||
<div class="settings-subsection">
|
||
<h4 data-i18n="settingsBasic.openaiConfig">OpenAI 配置</h4>
|
||
<div class="settings-form">
|
||
<div class="form-group">
|
||
<label for="openai-base-url">Base URL <span style="color: red;">*</span></label>
|
||
<input type="text" id="openai-base-url" data-i18n="settingsBasic.openaiBaseUrlPlaceholder" data-i18n-attr="placeholder" placeholder="https://api.openai.com/v1" required />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="openai-api-key">API Key <span style="color: red;">*</span></label>
|
||
<input type="password" id="openai-api-key" data-i18n="settingsBasic.openaiApiKeyPlaceholder" data-i18n-attr="placeholder" placeholder="输入OpenAI API Key" required />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="openai-model"><span data-i18n="settingsBasic.model">模型</span> <span style="color: red;">*</span></label>
|
||
<input type="text" id="openai-model" data-i18n="settingsBasic.modelPlaceholder" data-i18n-attr="placeholder" placeholder="gpt-4" required />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- FOFA配置 -->
|
||
<div class="settings-subsection">
|
||
<h4 data-i18n="settingsBasic.fofaConfig">FOFA 配置</h4>
|
||
<div class="settings-form">
|
||
<div class="form-group">
|
||
<label for="fofa-base-url">Base URL</label>
|
||
<input type="text" id="fofa-base-url" data-i18n="settingsBasic.fofaBaseUrlPlaceholder" data-i18n-attr="placeholder" placeholder="https://fofa.info/api/v1/search/all(可选)" />
|
||
<small class="form-hint" data-i18n="settingsBasic.fofaBaseUrlHint">留空则使用默认地址。</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="fofa-email" data-i18n="settingsBasic.email">Email</label>
|
||
<input type="text" id="fofa-email" data-i18n="settingsBasic.fofaEmailPlaceholder" data-i18n-attr="placeholder" placeholder="输入 FOFA 账号邮箱" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="fofa-api-key">API Key</label>
|
||
<input type="password" id="fofa-api-key" data-i18n="settingsBasic.fofaApiKeyPlaceholder" data-i18n-attr="placeholder" placeholder="输入 FOFA API Key" autocomplete="off" />
|
||
<small class="form-hint" data-i18n="settingsBasic.fofaApiKeyHint">仅保存在服务器配置中(`config.yaml`)。</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Agent配置 -->
|
||
<div class="settings-subsection">
|
||
<h4 data-i18n="settingsBasic.agentConfig">Agent 配置</h4>
|
||
<div class="settings-form">
|
||
<div class="form-group">
|
||
<label for="agent-max-iterations" data-i18n="settingsBasic.maxIterations">最大迭代次数</label>
|
||
<input type="number" id="agent-max-iterations" min="1" max="100" data-i18n="settingsBasic.iterationsPlaceholder" data-i18n-attr="placeholder" placeholder="30" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" id="multi-agent-enabled" class="modern-checkbox" />
|
||
<span class="checkbox-custom"></span>
|
||
<span class="checkbox-text" data-i18n="settingsBasic.enableMultiAgent">启用 Eino 多代理(DeepAgent)</span>
|
||
</label>
|
||
<small class="form-hint" data-i18n="settingsBasic.enableMultiAgentHint">开启后对话页可选「多代理」模式;子代理在 config.yaml 的 multi_agent.sub_agents 中配置。</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="multi-agent-default-mode" data-i18n="settingsBasic.multiAgentDefaultMode">对话页默认模式</label>
|
||
<select id="multi-agent-default-mode">
|
||
<option value="single" data-i18n="settingsBasic.multiAgentModeSingle">单代理(ReAct)</option>
|
||
<option value="multi" data-i18n="settingsBasic.multiAgentModeMulti">多代理(Eino)</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" id="multi-agent-robot-use" class="modern-checkbox" />
|
||
<span class="checkbox-custom"></span>
|
||
<span class="checkbox-text" data-i18n="settingsBasic.multiAgentRobotUse">企业微信 / 钉钉 / 飞书机器人也使用多代理</span>
|
||
</label>
|
||
<small class="form-hint" data-i18n="settingsBasic.multiAgentRobotUseHint">需同时勾选「启用多代理」;调用量与成本更高。</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" id="multi-agent-batch-use" class="modern-checkbox" />
|
||
<span class="checkbox-custom"></span>
|
||
<span class="checkbox-text" data-i18n="settingsBasic.multiAgentBatchUse">批量任务队列也使用多代理</span>
|
||
</label>
|
||
<small class="form-hint" data-i18n="settingsBasic.multiAgentBatchUseHint">开启后,任务管理中按队列执行的每个子任务将走 Eino DeepAgent(需启用多代理)。</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-actions">
|
||
<button class="btn-primary" onclick="applySettings()" data-i18n="settings.apply.button">应用配置</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 知识库设置 -->
|
||
<div id="settings-section-knowledge" class="settings-section-content">
|
||
<div class="settings-section-header">
|
||
<h3 data-i18n="settings.knowledge.title">知识库设置</h3>
|
||
</div>
|
||
|
||
<div class="settings-subsection">
|
||
<h4 data-i18n="settingsBasic.knowledgeConfig">知识库配置</h4>
|
||
<div class="settings-form">
|
||
<div class="form-group">
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" id="knowledge-enabled" class="modern-checkbox" />
|
||
<span class="checkbox-custom"></span>
|
||
<span class="checkbox-text" data-i18n="settingsBasic.enableKnowledge">启用知识检索功能</span>
|
||
</label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-base-path" data-i18n="settingsBasic.knowledgeBasePath">知识库路径</label>
|
||
<input type="text" id="knowledge-base-path" data-i18n="settingsBasic.knowledgeBasePathPlaceholder" data-i18n-attr="placeholder" placeholder="knowledge_base" />
|
||
<small class="form-hint" data-i18n="settingsBasic.knowledgeBasePathHint">相对于配置文件所在目录的路径</small>
|
||
</div>
|
||
|
||
<div class="settings-subsection-header">
|
||
<h5 data-i18n="settingsBasic.embeddingConfig">嵌入模型配置</h5>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-embedding-provider" data-i18n="settingsBasic.provider">提供商</label>
|
||
<select id="knowledge-embedding-provider">
|
||
<option value="openai">OpenAI</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-embedding-base-url">Base URL</label>
|
||
<input type="text" id="knowledge-embedding-base-url" data-i18n="settingsBasic.embeddingBaseUrlPlaceholder" data-i18n-attr="placeholder" placeholder="留空则使用OpenAI配置的base_url" />
|
||
<small class="form-hint" data-i18n="settingsBasic.embeddingBaseUrlPlaceholder">留空则使用OpenAI配置的base_url</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-embedding-api-key">API Key</label>
|
||
<input type="password" id="knowledge-embedding-api-key" data-i18n="settingsBasic.embeddingApiKeyPlaceholder" data-i18n-attr="placeholder" placeholder="留空则使用OpenAI配置的api_key" />
|
||
<small class="form-hint" data-i18n="settingsBasic.embeddingApiKeyPlaceholder">留空则使用OpenAI配置的api_key</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-embedding-model" data-i18n="settingsBasic.modelName">模型名称</label>
|
||
<input type="text" id="knowledge-embedding-model" data-i18n="settingsBasic.embeddingModelPlaceholder" data-i18n-attr="placeholder" placeholder="text-embedding-v4" />
|
||
</div>
|
||
|
||
<div class="settings-subsection-header">
|
||
<h5 data-i18n="settingsBasic.retrievalConfig">检索配置</h5>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-retrieval-top-k" data-i18n="settingsBasic.topK">Top-K 结果数量</label>
|
||
<input type="number" id="knowledge-retrieval-top-k" min="1" max="20" data-i18n="settingsBasic.topKPlaceholder" data-i18n-attr="placeholder" placeholder="5" />
|
||
<small class="form-hint" data-i18n="settingsBasic.topKHint">检索返回的Top-K结果数量</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-retrieval-similarity-threshold" data-i18n="settingsBasic.similarityThreshold">相似度阈值</label>
|
||
<input type="number" id="knowledge-retrieval-similarity-threshold" min="0" max="1" step="0.1" data-i18n="settingsBasic.similarityPlaceholder" data-i18n-attr="placeholder" placeholder="0.7" />
|
||
<small class="form-hint" data-i18n="settingsBasic.similarityHint">相似度阈值(0-1),低于此值的结果将被过滤</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-retrieval-hybrid-weight" data-i18n="settingsBasic.hybridWeight">混合检索权重</label>
|
||
<input type="number" id="knowledge-retrieval-hybrid-weight" min="0" max="1" step="0.1" data-i18n="settingsBasic.hybridPlaceholder" data-i18n-attr="placeholder" placeholder="0.7" />
|
||
<small class="form-hint" data-i18n="settingsBasic.hybridHint">向量检索的权重(0-1),1.0表示纯向量检索,0.0表示纯关键词检索</small>
|
||
</div>
|
||
|
||
<div class="settings-subsection-header">
|
||
<h5 data-i18n="settingsBasic.indexConfig">索引配置</h5>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-indexing-chunk-size" data-i18n="settingsBasic.chunkSize">分块大小(Chunk Size)</label>
|
||
<input type="number" id="knowledge-indexing-chunk-size" min="128" max="4096" data-i18n="settingsBasic.chunkSizePlaceholder" data-i18n-attr="placeholder" placeholder="512" />
|
||
<small class="form-hint" data-i18n="settingsBasic.chunkSizeHint">每个块的最大 token 数(默认 512),长文本会被分割成多个块</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-indexing-chunk-overlap" data-i18n="settingsBasic.chunkOverlap">分块重叠(Chunk Overlap)</label>
|
||
<input type="number" id="knowledge-indexing-chunk-overlap" min="0" max="512" data-i18n="settingsBasic.chunkOverlapPlaceholder" data-i18n-attr="placeholder" placeholder="50" />
|
||
<small class="form-hint" data-i18n="settingsBasic.chunkOverlapHint">块之间的重叠 token 数(默认 50),保持上下文连贯性</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-indexing-max-chunks-per-item" data-i18n="settingsBasic.maxChunksPerItem">单个知识项最大块数</label>
|
||
<input type="number" id="knowledge-indexing-max-chunks-per-item" min="0" max="1000" data-i18n="settingsBasic.maxChunksPlaceholder" data-i18n-attr="placeholder" placeholder="0" />
|
||
<small class="form-hint" data-i18n="settingsBasic.maxChunksHint">单个知识项的最大块数量(0 表示不限制),防止单个文件消耗过多 API 配额</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-indexing-max-rpm" data-i18n="settingsBasic.maxRpm">每分钟最大请求数(Max RPM)</label>
|
||
<input type="number" id="knowledge-indexing-max-rpm" min="0" max="1000" data-i18n="settingsBasic.maxRpmPlaceholder" data-i18n-attr="placeholder" placeholder="0" />
|
||
<small class="form-hint" data-i18n="settingsBasic.maxRpmHint">每分钟最大请求数(默认 0 表示不限制),如 OpenAI 默认 200 RPM</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-indexing-rate-limit-delay-ms" data-i18n="settingsBasic.rateLimitDelay">请求间隔延迟(毫秒)</label>
|
||
<input type="number" id="knowledge-indexing-rate-limit-delay-ms" min="0" max="10000" data-i18n="settingsBasic.rateLimitPlaceholder" data-i18n-attr="placeholder" placeholder="300" />
|
||
<small class="form-hint" data-i18n="settingsBasic.rateLimitHint">请求间隔毫秒数(默认 300),用于避免 API 速率限制,设为 0 不限制</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-indexing-max-retries" data-i18n="settingsBasic.maxRetries">最大重试次数</label>
|
||
<input type="number" id="knowledge-indexing-max-retries" min="0" max="10" data-i18n="settingsBasic.maxRetriesPlaceholder" data-i18n-attr="placeholder" placeholder="3" />
|
||
<small class="form-hint" data-i18n="settingsBasic.maxRetriesHint">最大重试次数(默认 3),遇到速率限制或服务器错误时自动重试</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-indexing-retry-delay-ms" data-i18n="settingsBasic.retryDelay">重试间隔(毫秒)</label>
|
||
<input type="number" id="knowledge-indexing-retry-delay-ms" min="0" max="10000" data-i18n="settingsBasic.retryDelayPlaceholder" data-i18n-attr="placeholder" placeholder="1000" />
|
||
<small class="form-hint" data-i18n="settingsBasic.retryDelayHint">重试间隔毫秒数(默认 1000),每次重试会递增延迟</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-actions">
|
||
<button class="btn-primary" onclick="applySettings()" data-i18n="settings.apply.button">应用配置</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 机器人设置 -->
|
||
<div id="settings-section-robots" class="settings-section-content">
|
||
<div class="settings-section-header">
|
||
<h3 data-i18n="settings.robots.title">机器人设置</h3>
|
||
<p class="settings-description" data-i18n="settings.robots.description">配置企业微信、钉钉、飞书等机器人,在手机端直接与 CyberStrikeAI 对话,无需在服务器上打开网页。</p>
|
||
</div>
|
||
|
||
<!-- 企业微信 -->
|
||
<div class="settings-subsection">
|
||
<h4 data-i18n="settings.robots.wecom.title">企业微信</h4>
|
||
<div class="settings-form">
|
||
<div class="form-group">
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" id="robot-wecom-enabled" class="modern-checkbox" />
|
||
<span class="checkbox-custom"></span>
|
||
<span class="checkbox-text" data-i18n="settings.robots.wecom.enabled">启用企业微信机器人</span>
|
||
</label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-wecom-token" data-i18n="settings.robots.wecom.token">Token</label>
|
||
<input type="text" id="robot-wecom-token" data-i18n="settings.robots.wecom.tokenPlaceholder" data-i18n-attr="placeholder" placeholder="Token" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-wecom-encoding-aes-key" data-i18n="settings.robots.wecom.encodingAesKey">EncodingAESKey</label>
|
||
<input type="text" id="robot-wecom-encoding-aes-key" data-i18n="settings.robots.wecom.encodingAesKeyPlaceholder" data-i18n-attr="placeholder" placeholder="EncodingAESKey(明文模式可留空)" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-wecom-corp-id" data-i18n="settings.robots.wecom.corpId">CorpID</label>
|
||
<input type="text" id="robot-wecom-corp-id" data-i18n="settings.robots.wecom.corpIdPlaceholder" data-i18n-attr="placeholder" placeholder="企业 ID" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-wecom-secret" data-i18n="settings.robots.wecom.secret">Secret</label>
|
||
<input type="password" id="robot-wecom-secret" data-i18n="settings.robots.wecom.secretPlaceholder" data-i18n-attr="placeholder" placeholder="应用 Secret" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-wecom-agent-id" data-i18n="settings.robots.wecom.agentId">AgentID</label>
|
||
<input type="number" id="robot-wecom-agent-id" data-i18n="settings.robots.wecom.agentIdPlaceholder" data-i18n-attr="placeholder" placeholder="应用 AgentId" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 钉钉 -->
|
||
<div class="settings-subsection">
|
||
<h4 data-i18n="settings.robots.dingtalk.title">钉钉</h4>
|
||
<div class="settings-form">
|
||
<div class="form-group">
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" id="robot-dingtalk-enabled" class="modern-checkbox" />
|
||
<span class="checkbox-custom"></span>
|
||
<span class="checkbox-text" data-i18n="settings.robots.dingtalk.enabled">启用钉钉机器人</span>
|
||
</label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-dingtalk-client-id" data-i18n="settings.robots.dingtalk.clientIdLabel">Client ID (AppKey)</label>
|
||
<input type="text" id="robot-dingtalk-client-id" data-i18n="settings.robots.dingtalk.clientIdPlaceholder" data-i18n-attr="placeholder" placeholder="钉钉应用 AppKey" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-dingtalk-client-secret" data-i18n="settings.robots.dingtalk.clientSecretLabel">Client Secret</label>
|
||
<input type="password" id="robot-dingtalk-client-secret" data-i18n="settings.robots.dingtalk.clientSecretPlaceholder" data-i18n-attr="placeholder" placeholder="钉钉应用 Secret" autocomplete="off" />
|
||
<small class="form-hint" data-i18n="settings.robots.dingtalk.streamHint">需开启机器人能力并配置流式接入</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 飞书 -->
|
||
<div class="settings-subsection">
|
||
<h4 data-i18n="settings.robots.lark.title">飞书 (Lark)</h4>
|
||
<div class="settings-form">
|
||
<div class="form-group">
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" id="robot-lark-enabled" class="modern-checkbox" />
|
||
<span class="checkbox-custom"></span>
|
||
<span class="checkbox-text" data-i18n="settings.robots.lark.enabled">启用飞书机器人</span>
|
||
</label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-lark-app-id" data-i18n="settings.robots.lark.appIdLabel">App ID</label>
|
||
<input type="text" id="robot-lark-app-id" data-i18n="settings.robots.lark.appIdPlaceholder" data-i18n-attr="placeholder" placeholder="飞书应用 App ID" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-lark-app-secret" data-i18n="settings.robots.lark.appSecretLabel">App Secret</label>
|
||
<input type="password" id="robot-lark-app-secret" data-i18n="settings.robots.lark.appSecretPlaceholder" data-i18n-attr="placeholder" placeholder="飞书应用 App Secret" autocomplete="off" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="robot-lark-verify-token" data-i18n="settings.robots.lark.verifyTokenLabel">Verify Token(可选)</label>
|
||
<input type="text" id="robot-lark-verify-token" data-i18n="settings.robots.lark.verifyTokenPlaceholder" data-i18n-attr="placeholder" placeholder="事件订阅 Verification Token" autocomplete="off" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-subsection">
|
||
<h4 data-i18n="settingsRobotsExtra.botCommandsTitle">机器人命令说明</h4>
|
||
<p class="settings-description" data-i18n="settingsRobotsExtra.botCommandsDesc">在对话中可发送以下命令(支持中英文):</p>
|
||
<ul style="color: var(--text-muted); font-size: 13px; line-height: 1.8; margin: 8px 0 0 16px;">
|
||
<li><code>帮助</code> <code>help</code> — <span data-i18n="settingsRobotsExtra.botCmdHelp">显示本帮助 | Show this help</span></li>
|
||
<li><code>列表</code> <code>list</code> — <span data-i18n="settingsRobotsExtra.botCmdList">列出所有对话标题与 ID | List conversations</span></li>
|
||
<li><code>切换 <ID></code> <code>switch <ID></code> — <span data-i18n="settingsRobotsExtra.botCmdSwitch">指定对话继续 | Switch to conversation</span></li>
|
||
<li><code>新对话</code> <code>new</code> — <span data-i18n="settingsRobotsExtra.botCmdNew">开启新对话 | Start new conversation</span></li>
|
||
<li><code>清空</code> <code>clear</code> — <span data-i18n="settingsRobotsExtra.botCmdClear">清空当前上下文 | Clear context</span></li>
|
||
<li><code>当前</code> <code>current</code> — <span data-i18n="settingsRobotsExtra.botCmdCurrent">显示当前对话 ID 与标题 | Show current conversation</span></li>
|
||
<li><code>停止</code> <code>stop</code> — <span data-i18n="settingsRobotsExtra.botCmdStop">中断当前任务 | Stop running task</span></li>
|
||
<li><code>角色</code> <code>roles</code> — <span data-i18n="settingsRobotsExtra.botCmdRoles">列出所有可用角色 | List roles</span></li>
|
||
<li><code>角色 <名></code> <code>role <name></code> — <span data-i18n="settingsRobotsExtra.botCmdRole">切换当前角色 | Switch role</span></li>
|
||
<li><code>删除 <ID></code> <code>delete <ID></code> — <span data-i18n="settingsRobotsExtra.botCmdDelete">删除指定对话 | Delete conversation</span></li>
|
||
<li><code>版本</code> <code>version</code> — <span data-i18n="settingsRobotsExtra.botCmdVersion">显示当前版本号 | Show version</span></li>
|
||
</ul>
|
||
<p class="settings-description" style="margin-top: 8px;" data-i18n="settingsRobotsExtra.botCommandsFooter">除以上命令外,直接输入内容将发送给 AI 进行渗透测试/安全分析。Otherwise, send any text for AI penetration testing / security analysis.</p>
|
||
</div>
|
||
|
||
<div class="settings-actions">
|
||
<button class="btn-primary" onclick="applySettings()" data-i18n="settings.apply.button">应用配置</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 终端 -->
|
||
<div id="settings-section-terminal" class="settings-section-content">
|
||
<div class="settings-section-header">
|
||
<h3 data-i18n="settingsTerminal.title">终端</h3>
|
||
<p class="settings-description" data-i18n="settingsTerminal.description">在服务器上执行命令,便于运维与调试。命令在服务端执行,请勿执行敏感或破坏性操作。</p>
|
||
</div>
|
||
<div class="terminal-wrapper">
|
||
<div class="terminal-tabs">
|
||
<div class="terminal-tab active" data-tab-id="1"><span class="terminal-tab-label" onclick="switchTerminalTab(1)">终端 1</span><button type="button" class="terminal-tab-close" onclick="removeTerminalTab(1); event.stopPropagation();" data-i18n="settingsTerminal.close" data-i18n-attr="title" title="关闭">×</button></div>
|
||
<button type="button" class="terminal-tab-new" onclick="addTerminalTab()" data-i18n="settingsTerminal.newTerminal" data-i18n-attr="title" title="新终端">+</button>
|
||
</div>
|
||
<div class="terminal-panes">
|
||
<div id="terminal-pane-1" class="terminal-pane active">
|
||
<div id="terminal-container-1" class="terminal-container"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 安全设置 -->
|
||
<div id="settings-section-security" class="settings-section-content">
|
||
<div class="settings-section-header">
|
||
<h3 data-i18n="settings.nav.security">安全设置</h3>
|
||
</div>
|
||
|
||
<div class="settings-subsection">
|
||
<h4 data-i18n="settingsSecurity.changePasswordTitle">修改密码</h4>
|
||
<p class="settings-description" data-i18n="settingsSecurity.changePasswordDesc">修改登录密码后,需要使用新密码重新登录。</p>
|
||
<div class="settings-form">
|
||
<div class="form-group">
|
||
<label for="auth-current-password" data-i18n="settingsSecurity.currentPassword">当前密码</label>
|
||
<input type="password" id="auth-current-password" data-i18n="settingsSecurity.currentPasswordPlaceholder" data-i18n-attr="placeholder" placeholder="输入当前登录密码" autocomplete="current-password" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="auth-new-password" data-i18n="settingsSecurity.newPassword">新密码</label>
|
||
<input type="password" id="auth-new-password" data-i18n="settingsSecurity.newPasswordPlaceholder" data-i18n-attr="placeholder" placeholder="设置新密码(至少 8 位)" autocomplete="new-password" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="auth-confirm-password" data-i18n="settingsSecurity.confirmPassword">确认新密码</label>
|
||
<input type="password" id="auth-confirm-password" data-i18n="settingsSecurity.confirmPasswordPlaceholder" data-i18n-attr="placeholder" placeholder="再次输入新密码" autocomplete="new-password" />
|
||
</div>
|
||
<div class="form-actions">
|
||
<button class="btn-secondary" type="button" onclick="resetPasswordForm()" data-i18n="settingsSecurity.clear">清空</button>
|
||
<button class="btn-primary change-password-submit" type="button" onclick="changePassword()" data-i18n="settingsSecurity.changePasswordBtn">修改密码</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- MCP调用详情模态框 -->
|
||
<div id="mcp-detail-modal" class="modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="mcpDetailModal.title">工具调用详情</h2>
|
||
<span class="modal-close" onclick="closeMCPDetail()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="detail-section detail-section-overview">
|
||
<div class="detail-section-header">
|
||
<h3 data-i18n="mcpDetailModal.execInfo">执行信息</h3>
|
||
</div>
|
||
<div class="detail-info-grid">
|
||
<div class="detail-item">
|
||
<strong data-i18n="mcpDetailModal.tool">工具</strong>
|
||
<span id="detail-tool-name"></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<strong data-i18n="mcpDetailModal.status">状态</strong>
|
||
<span id="detail-status" class="status-chip status-unknown"></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<strong data-i18n="mcpDetailModal.time">时间</strong>
|
||
<span id="detail-time"></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<strong data-i18n="mcpDetailModal.executionId">执行 ID</strong>
|
||
<span id="detail-execution-id" class="mono-text"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<div class="detail-section-header">
|
||
<h3 data-i18n="mcpDetailModal.requestParams">请求参数</h3>
|
||
<button class="btn-ghost" type="button" onclick="copyDetailBlock('detail-request', this)" data-i18n="mcpDetailModal.copyJson">复制 JSON</button>
|
||
</div>
|
||
<div class="detail-code-card">
|
||
<pre id="detail-request" class="code-block"></pre>
|
||
</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<div class="detail-section-header">
|
||
<h3 data-i18n="mcpDetailModal.responseResult">响应结果</h3>
|
||
<button class="btn-ghost" type="button" onclick="copyDetailBlock('detail-response', this)" data-i18n="mcpDetailModal.copyContent">复制内容</button>
|
||
</div>
|
||
<div class="detail-code-card">
|
||
<pre id="detail-response" class="code-block"></pre>
|
||
</div>
|
||
</div>
|
||
<div class="detail-section detail-success-wrapper" id="detail-success-section" style="display: none;">
|
||
<div class="detail-section-header">
|
||
<h3 data-i18n="mcpDetailModal.correctInfo">正确信息</h3>
|
||
<button class="btn-ghost" type="button" onclick="copyDetailBlock('detail-success', this)" data-i18n="mcpDetailModal.copyContent">复制内容</button>
|
||
</div>
|
||
<div class="detail-code-card">
|
||
<pre id="detail-success" class="code-block"></pre>
|
||
</div>
|
||
</div>
|
||
<div class="detail-section detail-error-wrapper" id="detail-error-section" style="display: none;">
|
||
<div class="detail-section-header">
|
||
<h3 data-i18n="mcpDetailModal.errorInfo">错误信息</h3>
|
||
<button class="btn-ghost" type="button" onclick="copyDetailBlock('detail-error', this)" data-i18n="mcpDetailModal.copyError">复制错误</button>
|
||
</div>
|
||
<div class="detail-code-card">
|
||
<pre id="detail-error" class="code-block error"></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 外部MCP配置模态框 -->
|
||
<div id="external-mcp-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 900px;">
|
||
<div class="modal-header">
|
||
<h2 id="external-mcp-modal-title" data-i18n="mcp.addExternalMCP">添加外部MCP</h2>
|
||
<span class="modal-close" onclick="closeExternalMCPModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="external-mcp-json"><span data-i18n="externalMcpModal.configJson">配置JSON</span> <span style="color: red;">*</span></label>
|
||
<textarea id="external-mcp-json" rows="15" data-i18n="externalMcpModal.placeholder" data-i18n-attr="placeholder" style="font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.875rem; line-height: 1.5;"></textarea>
|
||
<div class="password-hint">
|
||
<strong data-i18n="externalMcpModal.formatLabel">配置格式:</strong><span data-i18n="externalMcpModal.formatDesc">JSON对象,key为配置名称,value为配置内容。状态通过"启动/停止"按钮控制,无需在JSON中配置。</span><br>
|
||
<strong data-i18n="externalMcpModal.configExample">配置示例:</strong><br>
|
||
<strong data-i18n="externalMcpModal.stdioMode">stdio模式:</strong><br>
|
||
<code data-i18n="externalMcpModal.exampleStdio" style="display: block; margin: 8px 0; padding: 8px; background: var(--bg-secondary); border-radius: 4px; white-space: pre-wrap;">{
|
||
"hexstrike-ai": {
|
||
"command": "python3",
|
||
"args": ["/path/to/script.py", "--server", "http://example.com"],
|
||
"description": "描述",
|
||
"timeout": 300
|
||
}
|
||
}</code>
|
||
<strong data-i18n="externalMcpModal.httpMode">HTTP模式:</strong><br>
|
||
<code data-i18n="externalMcpModal.exampleHttp" style="display: block; margin: 8px 0; padding: 8px; background: var(--bg-secondary); border-radius: 4px; white-space: pre-wrap;">{
|
||
"cyberstrike-ai-http": {
|
||
"transport": "http",
|
||
"url": "http://127.0.0.1:8081/mcp"
|
||
}
|
||
}</code>
|
||
<strong data-i18n="externalMcpModal.sseMode">SSE模式:</strong><br>
|
||
<code data-i18n="externalMcpModal.exampleSse" style="display: block; margin: 8px 0; padding: 8px; background: var(--bg-secondary); border-radius: 4px; white-space: pre-wrap;">{
|
||
"cyberstrike-ai-sse": {
|
||
"transport": "sse",
|
||
"url": "http://127.0.0.1:8081/mcp/sse"
|
||
}
|
||
}</code>
|
||
</div>
|
||
<div id="external-mcp-json-error" class="error-message" style="display: none; margin-top: 8px; padding: 8px; background: rgba(220, 53, 69, 0.1); border: 1px solid rgba(220, 53, 69, 0.3); border-radius: 4px; color: var(--error-color); font-size: 0.875rem;"></div>
|
||
</div>
|
||
<div class="form-group">
|
||
<button type="button" class="btn-secondary" onclick="formatExternalMCPJSON()" style="margin-top: 8px;" data-i18n="externalMcpModal.formatJson">格式化JSON</button>
|
||
<button type="button" class="btn-secondary" onclick="loadExternalMCPExample()" style="margin-top: 8px; margin-left: 8px;" data-i18n="externalMcpModal.loadExample">加载示例</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn-secondary" onclick="closeExternalMCPModal()" data-i18n="common.cancel">取消</button>
|
||
<button class="btn-primary" onclick="saveExternalMCP()" data-i18n="common.save">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 攻击链可视化模态框 -->
|
||
<div id="attack-chain-modal" class="modal">
|
||
<div class="modal-content attack-chain-modal-content">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="attackChainModal.title">攻击链可视化</h2>
|
||
<div class="modal-header-actions">
|
||
<button class="btn-primary attack-chain-action-btn" onclick="regenerateAttackChain()" data-i18n="attackChainModal.regenerateTitle" data-i18n-attr="title" data-i18n-skip-text="true" title="重新生成攻击链(包含最新对话内容)">
|
||
🔄 <span data-i18n="attackChainModal.regenerate">重新生成</span>
|
||
</button>
|
||
<button class="btn-secondary attack-chain-action-btn" onclick="exportAttackChain('png')" data-i18n="attackChainModal.exportPng" data-i18n-attr="title" title="导出为PNG">
|
||
📥 PNG
|
||
</button>
|
||
<button class="btn-secondary attack-chain-action-btn" onclick="exportAttackChain('svg')" data-i18n="attackChainModal.exportSvg" data-i18n-attr="title" title="导出为SVG">
|
||
📥 SVG
|
||
</button>
|
||
<button class="btn-secondary attack-chain-action-btn" onclick="refreshAttackChain()" data-i18n="attackChainModal.refreshTitle" data-i18n-attr="title" title="刷新当前攻击链(不重新生成)">
|
||
↻ <span data-i18n="common.refresh">刷新</span>
|
||
</button>
|
||
<span class="modal-close" onclick="closeAttackChainModal()">×</span>
|
||
</div>
|
||
</div>
|
||
<div class="modal-body attack-chain-body">
|
||
<div class="attack-chain-main-layout">
|
||
<div class="attack-chain-visualization-area">
|
||
<div class="attack-chain-toolbar">
|
||
<div class="attack-chain-info">
|
||
<span id="attack-chain-stats">Nodes: 0 | Edges: 0</span>
|
||
</div>
|
||
<div class="attack-chain-filters">
|
||
<input type="text" id="attack-chain-search" data-i18n="attackChainModal.searchPlaceholder" data-i18n-attr="placeholder" placeholder="搜索节点..."
|
||
oninput="filterAttackChainNodes(this.value)">
|
||
<select id="attack-chain-type-filter"
|
||
onchange="filterAttackChainByType(this.value)">
|
||
<option value="all" data-i18n="attackChainModal.allTypes">所有类型</option>
|
||
<option value="target" data-i18n="attackChainModal.target">目标</option>
|
||
<option value="action" data-i18n="attackChainModal.action">行动</option>
|
||
<option value="vulnerability" data-i18n="attackChainModal.vulnerability">漏洞</option>
|
||
</select>
|
||
<select id="attack-chain-risk-filter"
|
||
onchange="filterAttackChainByRisk(this.value)">
|
||
<option value="all" data-i18n="attackChainModal.allRisks">所有风险</option>
|
||
<option value="high" data-i18n="attackChainModal.highRisk">高风险 (80-100)</option>
|
||
<option value="medium-high" data-i18n="attackChainModal.mediumHighRisk">中高风险 (60-79)</option>
|
||
<option value="medium" data-i18n="attackChainModal.mediumRisk">中风险 (40-59)</option>
|
||
<option value="low" data-i18n="attackChainModal.lowRisk">低风险 (0-39)</option>
|
||
</select>
|
||
<button class="btn-secondary" onclick="resetAttackChainFilters()" data-i18n="attackChainModal.resetFilter">
|
||
重置筛选
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="attack-chain-container" class="attack-chain-container">
|
||
<div class="loading-spinner" data-i18n="attackChainModal.loading">加载中...</div>
|
||
</div>
|
||
</div>
|
||
<div class="attack-chain-sidebar">
|
||
<div class="attack-chain-sidebar-content">
|
||
<div class="attack-chain-legend">
|
||
<div class="legend-section">
|
||
<div class="legend-title" data-i18n="attackChainModal.riskLevel">风险等级</div>
|
||
<div class="legend-item">
|
||
<span class="legend-color" style="background: #ff4444;"></span>
|
||
<span data-i18n="attackChainModal.highRisk">高风险 (80-100)</span>
|
||
</div>
|
||
<div class="legend-item">
|
||
<span class="legend-color" style="background: #ff8800;"></span>
|
||
<span data-i18n="attackChainModal.mediumHighRisk">中高风险 (60-79)</span>
|
||
</div>
|
||
<div class="legend-item">
|
||
<span class="legend-color" style="background: #ffbb00;"></span>
|
||
<span data-i18n="attackChainModal.mediumRisk">中风险 (40-59)</span>
|
||
</div>
|
||
<div class="legend-item">
|
||
<span class="legend-color" style="background: #88cc00;"></span>
|
||
<span data-i18n="attackChainModal.lowRisk">低风险 (0-39)</span>
|
||
</div>
|
||
</div>
|
||
<div class="legend-section">
|
||
<div class="legend-title" data-i18n="attackChainModal.lineMeaning">连接线含义</div>
|
||
<div class="legend-item">
|
||
<span class="legend-line" style="border-top: 2px solid #42a5f5;"></span>
|
||
<span data-i18n="attackChainModal.blueLine">蓝色线:行动发现漏洞</span>
|
||
</div>
|
||
<div class="legend-item">
|
||
<span class="legend-line" style="border-top: 2px solid #e53935;"></span>
|
||
<span data-i18n="attackChainModal.redLine">红色线:使能/促成关系</span>
|
||
</div>
|
||
<div class="legend-item">
|
||
<span class="legend-line" style="border-top: 2px solid #616161;"></span>
|
||
<span data-i18n="attackChainModal.grayLine">灰色线:逻辑顺序</span>
|
||
</div>
|
||
</div>
|
||
<div id="attack-chain-details" class="legend-section attack-chain-details" style="display: none;">
|
||
<div class="legend-title attack-chain-details-title">
|
||
<span data-i18n="attackChainModal.nodeDetails">节点详情</span>
|
||
<button class="attack-chain-details-close" onclick="closeNodeDetails()" data-i18n="attackChainModal.closeDetails" data-i18n-attr="title" title="关闭详情">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div id="attack-chain-details-content" class="attack-chain-details-content"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="chat-files-edit-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 720px;">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="chatFilesPage.editTitle">编辑文件</h2>
|
||
<span class="modal-close" onclick="closeChatFilesEditModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p class="chat-files-modal-path"><code id="chat-files-edit-path"></code></p>
|
||
<textarea id="chat-files-edit-textarea" class="form-control chat-files-edit-textarea" rows="18"></textarea>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn-secondary" onclick="closeChatFilesEditModal()" data-i18n="common.cancel">取消</button>
|
||
<button type="button" class="btn-primary" onclick="saveChatFilesEdit()" data-i18n="common.save">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="chat-files-rename-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 480px;">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="chatFilesPage.renameTitle">重命名</h2>
|
||
<span class="modal-close" onclick="closeChatFilesRenameModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<label class="chat-files-rename-label">
|
||
<span data-i18n="chatFilesPage.newFileName">新文件名</span>
|
||
<input type="text" id="chat-files-rename-input" class="form-control" />
|
||
</label>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn-secondary" onclick="closeChatFilesRenameModal()" data-i18n="common.cancel">取消</button>
|
||
<button type="button" class="btn-primary" onclick="submitChatFilesRename()" data-i18n="common.ok">确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="chat-files-mkdir-modal" class="modal">
|
||
<div class="modal-content chat-files-mkdir-modal-content">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="chatFilesPage.newFolderTitle">新建文件夹</h2>
|
||
<span class="modal-close" onclick="closeChatFilesMkdirModal()">×</span>
|
||
</div>
|
||
<div class="modal-body chat-files-mkdir-body">
|
||
<div class="chat-files-mkdir-location" aria-live="polite">
|
||
<div class="chat-files-mkdir-location-caption" data-i18n="chatFilesPage.newFolderLocation">位置</div>
|
||
<div class="chat-files-mkdir-path-box">
|
||
<code class="chat-files-mkdir-path" id="chat-files-mkdir-parent-hint">chat_uploads</code>
|
||
</div>
|
||
</div>
|
||
<label class="chat-files-rename-label chat-files-mkdir-label">
|
||
<span class="chat-files-mkdir-field-name">
|
||
<svg class="chat-files-mkdir-field-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
||
<span data-i18n="chatFilesPage.newFolderNameLabel">文件夹名称</span>
|
||
</span>
|
||
<input type="text" id="chat-files-mkdir-input" class="form-control chat-files-mkdir-input" data-i18n="chatFilesPage.newFolderNamePlaceholder" data-i18n-attr="placeholder" placeholder="仅名称,不含 /" autocomplete="off" />
|
||
</label>
|
||
</div>
|
||
<div class="modal-footer chat-files-mkdir-footer">
|
||
<button type="button" class="btn-secondary chat-files-mkdir-btn-cancel" onclick="closeChatFilesMkdirModal()" data-i18n="common.cancel">取消</button>
|
||
<button type="button" class="btn-primary chat-files-mkdir-btn-submit" onclick="submitChatFilesMkdir()" data-i18n="common.ok">确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Marked.js for Markdown parsing -->
|
||
<script src="https://cdn.jsdelivr.net/npm/marked@11.1.1/marked.min.js"></script>
|
||
<!-- DOMPurify for HTML sanitization to prevent XSS -->
|
||
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.8/dist/purify.min.js"></script>
|
||
<!-- Cytoscape.js for attack chain visualization -->
|
||
<script src="https://cdn.jsdelivr.net/npm/cytoscape@3.27.0/dist/cytoscape.min.js"></script>
|
||
<!-- ELK.js for high-quality DAG layout (reduces edge crossings) -->
|
||
<script src="https://cdn.jsdelivr.net/npm/elkjs@0.9.2/lib/elk.bundled.js"></script>
|
||
<!-- SheetJS for XLSX export (info-collect) -->
|
||
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
|
||
<script>
|
||
// 确保ELK对象全局可用
|
||
if (typeof ELK === 'undefined' && typeof elk !== 'undefined') {
|
||
window.ELK = elk;
|
||
}
|
||
</script>
|
||
<!-- 知识项编辑模态框 -->
|
||
<!-- Skill模态框 -->
|
||
<div id="skill-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 900px;">
|
||
<div class="modal-header">
|
||
<h2 id="skill-modal-title" data-i18n="skillModal.addSkill">添加Skill</h2>
|
||
<span class="modal-close" onclick="closeSkillModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="skill-name"><span data-i18n="skillModal.skillName">Skill名称</span> <span style="color: red;">*</span></label>
|
||
<input type="text" id="skill-name" data-i18n="skillModal.skillNamePlaceholder" data-i18n-attr="placeholder" placeholder="例如: sql-injection-testing" required />
|
||
<small class="form-hint" data-i18n="skillModal.skillNameHint">只能包含字母、数字、连字符和下划线</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="skill-description" data-i18n="skillModal.description">描述</label>
|
||
<input type="text" id="skill-description" data-i18n="skillModal.descriptionPlaceholder" data-i18n-attr="placeholder" placeholder="Skill的简短描述" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="skill-content"><span data-i18n="skillModal.contentLabel">内容(Markdown格式)</span> <span style="color: red;">*</span></label>
|
||
<textarea id="skill-content" rows="20" data-i18n="skillModal.contentPlaceholder" data-i18n-attr="placeholder" placeholder="输入skill内容,支持Markdown格式..." style="font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.875rem; line-height: 1.5;" required></textarea>
|
||
<small class="form-hint"><span data-i18n="skillModal.contentHint">支持YAML front matter格式(可选),例如:</span><br>
|
||
---<br>
|
||
name: skill-name<br>
|
||
description: Skill描述<br>
|
||
version: 1.0.0<br>
|
||
---<br><br>
|
||
# Skill标题<br>
|
||
这里是skill内容...</small>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn-secondary" onclick="closeSkillModal()" data-i18n="common.cancel">取消</button>
|
||
<button class="btn-primary" onclick="saveSkill()" data-i18n="common.save">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="knowledge-item-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 900px;">
|
||
<div class="modal-header">
|
||
<h2 id="knowledge-item-modal-title">添加知识</h2>
|
||
<span class="modal-close" onclick="closeKnowledgeItemModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="knowledge-item-category">分类(风险类型)<span style="color: red;">*</span></label>
|
||
<input type="text" id="knowledge-item-category" placeholder="例如:SQL注入" required />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-item-title">标题<span style="color: red;">*</span></label>
|
||
<input type="text" id="knowledge-item-title" placeholder="知识项标题" required />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="knowledge-item-content">内容(Markdown格式)<span style="color: red;">*</span></label>
|
||
<textarea id="knowledge-item-content" rows="20" placeholder="输入知识内容,支持Markdown格式..." style="font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.875rem; line-height: 1.5;" required></textarea>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn-secondary" onclick="closeKnowledgeItemModal()">取消</button>
|
||
<button class="btn-primary" onclick="saveKnowledgeItem()">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 批量管理对话模态框 -->
|
||
<div id="batch-manage-modal" class="modal">
|
||
<div class="modal-content batch-manage-modal-content">
|
||
<div class="modal-header">
|
||
<h2 id="batch-manage-title">管理对话记录·共<span id="batch-manage-count">0</span>条</h2>
|
||
<div class="batch-manage-header-actions">
|
||
<div class="batch-search-box">
|
||
<input type="text" id="batch-search-input" data-i18n="batchManageModal.searchPlaceholder" data-i18n-attr="placeholder" placeholder="搜索历史记录" oninput="filterBatchConversations(this.value)" />
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="11" cy="11" r="8" stroke="currentColor" stroke-width="2"/>
|
||
<path d="m21 21-4.35-4.35" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</div>
|
||
<span class="modal-close" onclick="closeBatchManageModal()">×</span>
|
||
</div>
|
||
</div>
|
||
<div class="modal-body batch-manage-body">
|
||
<div class="batch-conversations-table">
|
||
<div class="batch-table-header">
|
||
<div class="batch-table-col-checkbox"></div>
|
||
<div class="batch-table-col-name" data-i18n="batchManageModal.conversationName">对话名称</div>
|
||
<div class="batch-table-col-time" data-i18n="batchManageModal.lastTime">最近一次对话时间</div>
|
||
<div class="batch-table-col-action" data-i18n="batchManageModal.action">操作</div>
|
||
</div>
|
||
<div id="batch-conversations-list" class="batch-conversations-list"></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer batch-manage-footer">
|
||
<label class="select-all-checkbox">
|
||
<input type="checkbox" id="batch-select-all" onchange="toggleSelectAllBatch()" />
|
||
<span data-i18n="batchManageModal.selectAll">全选</span>
|
||
</label>
|
||
<div class="batch-footer-actions">
|
||
<button class="btn-secondary" onclick="closeBatchManageModal()" data-i18n="common.cancel">取消</button>
|
||
<button class="btn-primary" onclick="deleteSelectedConversations()" data-i18n="batchManageModal.deleteSelected">删除所选</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 创建分组模态框 -->
|
||
<div id="create-group-modal" class="modal">
|
||
<div class="modal-content create-group-modal-content">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="createGroupModal.title">创建分组</h2>
|
||
<span class="modal-close" onclick="closeCreateGroupModal()">×</span>
|
||
</div>
|
||
<div class="modal-body create-group-body">
|
||
<p class="create-group-description" data-i18n="createGroupModal.description">分组功能可将对话集中归类管理,让对话更加井然有序。</p>
|
||
<div class="create-group-input-wrapper">
|
||
<button type="button" class="group-icon-input" id="create-group-icon-btn" onclick="toggleGroupIconPicker()" data-i18n="createGroupModal.selectIcon" data-i18n-attr="title" title="点击选择图标">📁</button>
|
||
<input type="text" id="create-group-name-input" data-i18n="createGroupModal.groupNamePlaceholder" data-i18n-attr="placeholder" placeholder="请输入分组名称" />
|
||
<!-- Emoji选择器面板 -->
|
||
<div id="group-icon-picker" class="group-icon-picker" style="display: none;">
|
||
<div class="icon-picker-header">
|
||
<span data-i18n="createGroupModal.pickIcon">选择图标</span>
|
||
<div class="icon-picker-custom">
|
||
<input type="text" id="custom-icon-input" class="custom-icon-input" data-i18n="createGroupModal.customIcon" data-i18n-attr="placeholder" placeholder="自定义" maxlength="2" />
|
||
<button type="button" class="custom-icon-btn" onclick="applyCustomIcon()" data-i18n="createGroupModal.confirmIcon">确定</button>
|
||
</div>
|
||
</div>
|
||
<div class="icon-picker-grid">
|
||
<span class="icon-option" onclick="selectGroupIcon('📁')">📁</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🔒')">🔒</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🛡️')">🛡️</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('⚔️')">⚔️</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🎯')">🎯</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🔍')">🔍</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('💻')">💻</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🐛')">🐛</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🚀')">🚀</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('⚡')">⚡</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🔥')">🔥</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('💡')">💡</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🎮')">🎮</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🏴☠️')">🏴☠️</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🕵️')">🕵️</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🔑')">🔑</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('📡')">📡</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🌐')">🌐</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('📊')">📊</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('📝')">📝</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('🗂️')">🗂️</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('📌')">📌</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('⭐')">⭐</span>
|
||
<span class="icon-option" onclick="selectGroupIcon('💎')">💎</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="create-group-suggestions">
|
||
<div class="suggestion-tag" data-i18n="createGroupModal.suggestionPenetrationTest" onclick="selectSuggestionByKey('createGroupModal.suggestionPenetrationTest')">渗透测试</div>
|
||
<div class="suggestion-tag" data-i18n="createGroupModal.suggestionCtf" onclick="selectSuggestionByKey('createGroupModal.suggestionCtf')">CTF</div>
|
||
<div class="suggestion-tag" data-i18n="createGroupModal.suggestionRedTeam" onclick="selectSuggestionByKey('createGroupModal.suggestionRedTeam')">红队</div>
|
||
<div class="suggestion-tag" data-i18n="createGroupModal.suggestionVulnerabilityMining" onclick="selectSuggestionByKey('createGroupModal.suggestionVulnerabilityMining')">漏洞挖掘</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn-secondary" onclick="closeCreateGroupModal()" data-i18n="createGroupModal.cancel">取消</button>
|
||
<button class="btn-primary" onclick="createGroup(event)" data-i18n="createGroupModal.create">创建</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 上下文菜单 -->
|
||
<div id="conversation-context-menu" class="context-menu" style="display: none;">
|
||
<div id="attack-chain-menu-item" class="context-menu-item" onclick="showAttackChainFromContext()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M10.5 13.5l3-3M8 8H5a4 4 0 1 0 0 8h3m8-8h3a4 4 0 0 1 0 8h-3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="contextMenu.viewAttackChain">查看攻击链</span>
|
||
</div>
|
||
<div class="context-menu-item context-menu-item-has-submenu" onmouseenter="handleDownloadMarkdownSubmenuEnter()" onmouseleave="handleDownloadMarkdownSubmenuLeave(event)">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M12 3v12m0 0l-4-4m4 4l4-4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="contextMenu.downloadMarkdown">下载 Markdown</span>
|
||
<svg class="submenu-arrow" width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<div id="download-markdown-submenu" class="context-submenu" style="display: none;" onmouseenter="clearDownloadMarkdownSubmenuHideTimeout()" onmouseleave="hideDownloadMarkdownSubmenu()">
|
||
<div class="context-submenu-item" onclick="downloadConversationMarkdownFromContext(false)">
|
||
<span data-i18n="contextMenu.downloadMarkdownSummary">简版</span>
|
||
</div>
|
||
<div class="context-submenu-item" onclick="downloadConversationMarkdownFromContext(true)">
|
||
<span data-i18n="contextMenu.downloadMarkdownFull">完整版</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="context-menu-divider"></div>
|
||
<div class="context-menu-item" onclick="renameConversation()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="contextMenu.rename">重命名</span>
|
||
</div>
|
||
<div class="context-menu-item" onclick="pinConversation()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M12 17v5M5 17h14l-1-7H6l-1 7zM9 10V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span id="pin-conversation-menu-text" data-i18n="contextMenu.pinConversation">置顶此对话</span>
|
||
</div>
|
||
<div class="context-menu-item" onclick="showBatchManageModal()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<line x1="3" y1="12" x2="21" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
<line x1="3" y1="6" x2="21" y2="6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
<line x1="3" y1="18" x2="21" y2="18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
<circle cx="8" cy="6" r="1" fill="currentColor"/>
|
||
<circle cx="8" cy="12" r="1" fill="currentColor"/>
|
||
<circle cx="8" cy="18" r="1" fill="currentColor"/>
|
||
</svg>
|
||
<span data-i18n="contextMenu.batchManage">批量管理</span>
|
||
</div>
|
||
<div class="context-menu-item context-menu-item-has-submenu" onmouseenter="handleMoveToGroupSubmenuEnter()" onmouseleave="handleMoveToGroupSubmenuLeave(event)">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="contextMenu.moveToGroup">移动到分组</span>
|
||
<svg class="submenu-arrow" width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<div id="move-to-group-submenu" class="context-submenu" style="display: none;" onmouseenter="clearSubmenuHideTimeout()" onmouseleave="hideMoveToGroupSubmenu()"></div>
|
||
</div>
|
||
<div class="context-menu-divider"></div>
|
||
<div class="context-menu-item context-menu-item-danger" onclick="deleteConversationFromContext()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="contextMenu.deleteConversation">删除此对话</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分组上下文菜单 -->
|
||
<div id="group-context-menu" class="context-menu" style="display: none;">
|
||
<div class="context-menu-item" onclick="renameGroupFromContext()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="contextMenu.rename">重命名</span>
|
||
</div>
|
||
<div class="context-menu-item" onclick="pinGroupFromContext()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M12 17v5M5 17h14l-1-7H6l-1 7zM9 10V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span id="pin-group-menu-text" data-i18n="contextMenu.pinGroup">置顶此分组</span>
|
||
</div>
|
||
<div class="context-menu-item context-menu-item-danger" onclick="deleteGroupFromContext()">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="contextMenu.deleteGroup">删除此分组</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 新建任务模态框 -->
|
||
<div id="batch-import-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 800px;">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="batchImportModal.title">新建任务</h2>
|
||
<span class="modal-close" onclick="closeBatchImportModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="batch-queue-title" data-i18n="batchImportModal.queueTitle">任务标题</label>
|
||
<input type="text" id="batch-queue-title" data-i18n="batchImportModal.queueTitlePlaceholder" data-i18n-attr="placeholder" placeholder="请输入任务标题(可选,用于标识和筛选)" />
|
||
<div class="form-hint" style="margin-top: 4px;" data-i18n="batchImportModal.queueTitleHint">为批量任务队列设置一个标题,方便后续查找和管理。</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="batch-queue-role" data-i18n="batchImportModal.role">角色</label>
|
||
<select id="batch-queue-role" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 0.875rem;">
|
||
<option value="" data-i18n="batchImportModal.defaultRole">默认</option>
|
||
</select>
|
||
<div class="form-hint" style="margin-top: 4px;" data-i18n="batchImportModal.roleHint">选择一个角色,所有任务将使用该角色的配置(提示词和工具)执行。</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="batch-tasks-input"><span data-i18n="batchImportModal.tasksList">任务列表(每行一个任务)</span><span style="color: red;">*</span></label>
|
||
<textarea id="batch-tasks-input" rows="15" data-i18n="batchImportModal.tasksListPlaceholderExample" data-i18n-attr="placeholder" placeholder="请输入任务列表,每行一个任务,例如: 扫描 192.168.1.1 的开放端口 检查 https://example.com 是否存在SQL注入 枚举 example.com 的子域名" style="font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.875rem; line-height: 1.5;"></textarea>
|
||
<div class="form-hint" style="margin-top: 8px;" data-i18n="batchImportModal.tasksListHintFull">提示:每行输入一个任务指令,系统将依次执行这些任务。空行会被自动忽略。</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<div id="batch-import-stats" class="batch-import-stats"></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn-secondary" onclick="closeBatchImportModal()" data-i18n="common.cancel">取消</button>
|
||
<button class="btn-primary" onclick="createBatchQueue()" data-i18n="batchImportModal.createQueue">创建队列</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 批量任务队列详情模态框 -->
|
||
<div id="batch-queue-detail-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 900px;">
|
||
<div class="modal-header">
|
||
<h2 id="batch-queue-detail-title" data-i18n="batchQueueDetailModal.title">批量任务队列详情</h2>
|
||
<div style="display: flex; align-items: center; gap: 12px;">
|
||
<div class="modal-header-actions">
|
||
<button class="btn-secondary btn-small" id="batch-queue-add-task-btn" onclick="showAddBatchTaskModal()" style="display: none;" data-i18n="batchQueueDetailModal.addTask">添加任务</button>
|
||
<button class="btn-primary btn-small" id="batch-queue-start-btn" onclick="startBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.startExecute">开始执行</button>
|
||
<button class="btn-secondary btn-small" id="batch-queue-pause-btn" onclick="pauseBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.pauseQueue">暂停队列</button>
|
||
<button class="btn-secondary btn-small btn-danger" id="batch-queue-delete-btn" onclick="deleteBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.deleteQueue">删除队列</button>
|
||
</div>
|
||
<span class="modal-close" onclick="closeBatchQueueDetailModal()">×</span>
|
||
</div>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="batch-queue-detail-content"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 编辑批量任务模态框 -->
|
||
<div id="edit-batch-task-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 600px;">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="editBatchTaskModal.title">编辑任务</h2>
|
||
<span class="modal-close" onclick="closeEditBatchTaskModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="edit-task-message" data-i18n="editBatchTaskModal.taskMessage">任务消息</label>
|
||
<textarea id="edit-task-message" class="form-control" rows="5" data-i18n="editBatchTaskModal.taskMessagePlaceholder" data-i18n-attr="placeholder" placeholder="请输入任务消息"></textarea>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button class="btn-primary" onclick="saveBatchTask()" data-i18n="common.save">保存</button>
|
||
<button class="btn-secondary" onclick="closeEditBatchTaskModal()" data-i18n="common.cancel">取消</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 添加批量任务模态框 -->
|
||
<div id="add-batch-task-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 600px;">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="addBatchTaskModal.title">添加任务</h2>
|
||
<span class="modal-close" onclick="closeAddBatchTaskModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="add-task-message" data-i18n="addBatchTaskModal.taskMessage">任务消息</label>
|
||
<textarea id="add-task-message" class="form-control" rows="5" data-i18n="addBatchTaskModal.taskMessagePlaceholder" data-i18n-attr="placeholder" placeholder="请输入任务消息"></textarea>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button class="btn-primary" onclick="saveAddBatchTask()" data-i18n="addBatchTaskModal.add">添加</button>
|
||
<button class="btn-secondary" onclick="closeAddBatchTaskModal()" data-i18n="common.cancel">取消</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 漏洞编辑模态框 -->
|
||
<div id="vulnerability-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 900px;">
|
||
<div class="modal-header">
|
||
<h2 id="vulnerability-modal-title" data-i18n="vulnerability.addVuln">添加漏洞</h2>
|
||
<span class="modal-close" onclick="closeVulnerabilityModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="vulnerability-conversation-id"><span data-i18n="vulnerabilityModal.conversationId">会话ID</span> <span style="color: red;">*</span></label>
|
||
<input type="text" id="vulnerability-conversation-id" data-i18n="vulnerabilityModal.conversationIdPlaceholder" data-i18n-attr="placeholder" placeholder="输入会话ID" required />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vulnerability-title"><span data-i18n="vulnerabilityModal.title">标题</span> <span style="color: red;">*</span></label>
|
||
<input type="text" id="vulnerability-title" data-i18n="vulnerabilityModal.titlePlaceholder" data-i18n-attr="placeholder" placeholder="漏洞标题" required />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vulnerability-description" data-i18n="vulnerabilityModal.description">描述</label>
|
||
<textarea id="vulnerability-description" rows="5" data-i18n="vulnerabilityModal.descriptionPlaceholder" data-i18n-attr="placeholder" placeholder="漏洞详细描述"></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vulnerability-severity"><span data-i18n="vulnerabilityModal.severity">严重程度</span> <span style="color: red;">*</span></label>
|
||
<select id="vulnerability-severity" required>
|
||
<option value="" data-i18n="vulnerabilityModal.pleaseSelect">请选择</option>
|
||
<option value="critical" data-i18n="vulnerabilityModal.severityCritical">严重</option>
|
||
<option value="high" data-i18n="vulnerabilityModal.severityHigh">高危</option>
|
||
<option value="medium" data-i18n="vulnerabilityModal.severityMedium">中危</option>
|
||
<option value="low" data-i18n="vulnerabilityModal.severityLow">低危</option>
|
||
<option value="info" data-i18n="vulnerabilityModal.severityInfo">信息</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vulnerability-status" data-i18n="vulnerabilityModal.status">状态</label>
|
||
<select id="vulnerability-status">
|
||
<option value="open" data-i18n="vulnerabilityModal.statusOpen">待处理</option>
|
||
<option value="confirmed" data-i18n="vulnerabilityModal.statusConfirmed">已确认</option>
|
||
<option value="fixed" data-i18n="vulnerabilityModal.statusFixed">已修复</option>
|
||
<option value="false_positive" data-i18n="vulnerabilityModal.statusFalsePositive">误报</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vulnerability-type" data-i18n="vulnerabilityModal.type">漏洞类型</label>
|
||
<input type="text" id="vulnerability-type" data-i18n="vulnerabilityModal.typePlaceholder" data-i18n-attr="placeholder" placeholder="如:SQL注入、XSS、CSRF等" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vulnerability-target" data-i18n="vulnerabilityModal.target">目标</label>
|
||
<input type="text" id="vulnerability-target" data-i18n="vulnerabilityModal.targetPlaceholder" data-i18n-attr="placeholder" placeholder="受影响的目标(URL、IP地址等)" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vulnerability-proof" data-i18n="vulnerabilityModal.proof">证明(POC)</label>
|
||
<textarea id="vulnerability-proof" rows="5" data-i18n="vulnerabilityModal.proofPlaceholder" data-i18n-attr="placeholder" placeholder="漏洞证明,如请求/响应、截图等"></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vulnerability-impact" data-i18n="vulnerabilityModal.impact">影响</label>
|
||
<textarea id="vulnerability-impact" rows="3" data-i18n="vulnerabilityModal.impactPlaceholder" data-i18n-attr="placeholder" placeholder="漏洞影响说明"></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vulnerability-recommendation" data-i18n="vulnerabilityModal.recommendation">修复建议</label>
|
||
<textarea id="vulnerability-recommendation" rows="3" data-i18n="vulnerabilityModal.recommendationPlaceholder" data-i18n-attr="placeholder" placeholder="修复建议"></textarea>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn-secondary" onclick="closeVulnerabilityModal()" data-i18n="common.cancel">取消</button>
|
||
<button class="btn-primary" onclick="saveVulnerability()" data-i18n="common.save">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- WebShell 添加连接模态框 -->
|
||
<div id="webshell-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 560px;">
|
||
<div class="modal-header">
|
||
<h2 id="webshell-modal-title" data-i18n="webshell.addConnection">添加连接</h2>
|
||
<span class="modal-close" onclick="closeWebshellModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<input type="hidden" id="webshell-edit-id" value="" />
|
||
<div class="form-group">
|
||
<label for="webshell-url"><span data-i18n="webshell.url">Shell 地址</span> <span style="color: red;">*</span></label>
|
||
<input type="text" id="webshell-url" data-i18n="webshell.urlPlaceholder" data-i18n-attr="placeholder" placeholder="http(s)://target.com/shell.php" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="webshell-password" data-i18n="webshell.password">连接密码/密钥</label>
|
||
<input type="text" id="webshell-password" data-i18n="webshell.passwordPlaceholder" data-i18n-attr="placeholder" placeholder="如冰蝎/蚁剑的连接密码" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="webshell-type" data-i18n="webshell.type">Shell 类型</label>
|
||
<select id="webshell-type">
|
||
<option value="php" data-i18n="webshell.typePhp">PHP</option>
|
||
<option value="asp" data-i18n="webshell.typeAsp">ASP</option>
|
||
<option value="aspx" data-i18n="webshell.typeAspx">ASPX</option>
|
||
<option value="jsp" data-i18n="webshell.typeJsp">JSP</option>
|
||
<option value="custom" data-i18n="webshell.typeCustom">自定义</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="webshell-method" data-i18n="webshell.method">请求方式</label>
|
||
<select id="webshell-method">
|
||
<option value="post" data-i18n="webshell.methodPost">POST</option>
|
||
<option value="get" data-i18n="webshell.methodGet">GET</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="webshell-cmd-param" data-i18n="webshell.cmdParam">命令参数名</label>
|
||
<input type="text" id="webshell-cmd-param" data-i18n="webshell.cmdParamPlaceholder" data-i18n-attr="placeholder" placeholder="不填默认为 cmd,如 xxx 则请求为 xxx=命令" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="webshell-remark" data-i18n="webshell.remark">备注</label>
|
||
<input type="text" id="webshell-remark" data-i18n="webshell.remarkPlaceholder" data-i18n-attr="placeholder" placeholder="便于识别的备注名" />
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn-secondary" id="webshell-test-btn" onclick="testWebshellConnection()" data-i18n="webshell.testConnectivity">测试连通性</button>
|
||
<button class="btn-secondary" onclick="closeWebshellModal()" data-i18n="common.cancel">取消</button>
|
||
<button class="btn-primary" onclick="saveWebshellConnection()" data-i18n="common.save">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 角色选择弹窗 -->
|
||
<div id="role-select-modal" class="modal">
|
||
<div class="modal-content role-select-modal-content">
|
||
<div class="modal-header">
|
||
<h2 data-i18n="chatGroup.rolePanelTitle">选择角色</h2>
|
||
<span class="modal-close" onclick="closeRoleSelectModal()">×</span>
|
||
</div>
|
||
<div class="modal-body role-select-body">
|
||
<div id="role-select-list" class="role-select-list"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 角色编辑模态框 -->
|
||
<div id="role-modal" class="modal">
|
||
<div class="modal-content" style="max-width: 900px;">
|
||
<div class="modal-header">
|
||
<h2 id="role-modal-title" data-i18n="roleModal.addRole">添加角色</h2>
|
||
<span class="modal-close" onclick="closeRoleModal()">×</span>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="role-name"><span data-i18n="roleModal.roleName">角色名称</span> <span style="color: red;">*</span></label>
|
||
<input type="text" id="role-name" data-i18n="roleModal.roleNamePlaceholder" data-i18n-attr="placeholder" placeholder="输入角色名称" required />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="role-description" data-i18n="roleModal.roleDescription">角色描述</label>
|
||
<input type="text" id="role-description" data-i18n="roleModal.roleDescriptionPlaceholder" data-i18n-attr="placeholder" placeholder="输入角色描述" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="role-icon" data-i18n="roleModal.roleIcon">角色图标</label>
|
||
<input type="text" id="role-icon" data-i18n="roleModal.roleIconPlaceholder" data-i18n-attr="placeholder" placeholder="输入emoji图标,例如: 🏆" maxlength="10" />
|
||
<small class="form-hint" data-i18n="roleModal.roleIconHint">输入一个emoji作为角色的图标,将显示在角色选择器中。</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="role-user-prompt" data-i18n="roleModal.userPrompt">用户提示词</label>
|
||
<textarea id="role-user-prompt" rows="10" data-i18n="roleModal.userPromptPlaceholder" data-i18n-attr="placeholder" placeholder="输入用户提示词,会在用户消息前追加此提示词..."></textarea>
|
||
<small class="form-hint" data-i18n="roleModal.userPromptHint">此提示词会追加到用户消息前,用于指导AI的行为。注意:这不会修改系统提示词。</small>
|
||
</div>
|
||
<div class="form-group" id="role-tools-section">
|
||
<label data-i18n="roleModal.relatedTools">关联的工具(可选)</label>
|
||
<div id="role-tools-default-hint" class="role-tools-default-hint" style="display: none;">
|
||
<div class="role-tools-default-info">
|
||
<span class="role-tools-default-icon">ℹ️</span>
|
||
<div class="role-tools-default-content">
|
||
<div class="role-tools-default-title" data-i18n="roleModal.defaultRoleToolsTitle">默认角色使用所有工具</div>
|
||
<div class="role-tools-default-desc" data-i18n="roleModal.defaultRoleToolsDesc">默认角色会自动使用MCP管理中启用的所有工具,无需单独配置。</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="role-tools-controls">
|
||
<div class="role-tools-actions">
|
||
<button type="button" class="btn-secondary" onclick="selectAllRoleTools()" data-i18n="roleModal.selectAll">全选</button>
|
||
<button type="button" class="btn-secondary" onclick="deselectAllRoleTools()" data-i18n="roleModal.deselectAll">全不选</button>
|
||
<div class="role-tools-search-box">
|
||
<input type="text" id="role-tools-search" data-i18n="roleModal.searchToolsPlaceholder" data-i18n-attr="placeholder" placeholder="搜索工具..."
|
||
oninput="searchRoleTools(this.value)"
|
||
onkeypress="if(event.key === 'Enter') searchRoleTools(this.value)" />
|
||
<button class="role-tools-search-clear" id="role-tools-search-clear"
|
||
onclick="clearRoleToolsSearch()" style="display: none;" data-i18n="common.clearSearch" data-i18n-attr="title" title="清除搜索">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
||
<path d="M15 9l-6 6M9 9l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="role-tools-stats" class="role-tools-stats"></div>
|
||
</div>
|
||
<div id="role-tools-list" class="role-tools-list">
|
||
<div class="tools-loading" data-i18n="roleModal.loadingTools">正在加载工具列表...</div>
|
||
</div>
|
||
<small class="form-hint" data-i18n="roleModal.relatedToolsHint">勾选要关联的工具,留空则使用MCP管理中的全部工具配置。</small>
|
||
</div>
|
||
<div class="form-group" id="role-skills-section">
|
||
<label data-i18n="roleModal.relatedSkills">关联的Skills(可选)</label>
|
||
<div class="role-skills-controls">
|
||
<div class="role-skills-actions">
|
||
<button type="button" class="btn-secondary" onclick="selectAllRoleSkills()" data-i18n="roleModal.selectAll">全选</button>
|
||
<button type="button" class="btn-secondary" onclick="deselectAllRoleSkills()" data-i18n="roleModal.deselectAll">全不选</button>
|
||
<div class="role-skills-search-box">
|
||
<input type="text" id="role-skills-search" data-i18n="roleModal.searchSkillsPlaceholder" data-i18n-attr="placeholder" placeholder="搜索skill..."
|
||
oninput="searchRoleSkills(this.value)"
|
||
onkeypress="if(event.key === 'Enter') searchRoleSkills(this.value)" />
|
||
<button class="role-skills-search-clear" id="role-skills-search-clear"
|
||
onclick="clearRoleSkillsSearch()" style="display: none;" data-i18n="common.clearSearch" data-i18n-attr="title" title="清除搜索">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
||
<path d="M15 9l-6 6M9 9l6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div id="role-skills-stats" class="role-skills-stats"></div>
|
||
</div>
|
||
<div id="role-skills-list" class="role-skills-list">
|
||
<div class="skills-loading" data-i18n="roleModal.loadingSkills">正在加载skills列表...</div>
|
||
</div>
|
||
<small class="form-hint" data-i18n="roleModal.relatedSkillsHint">勾选要关联的skills,这些skills的内容会在执行任务前注入到系统提示词中,帮助AI更好地理解相关专业知识。</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" id="role-enabled" class="modern-checkbox" checked />
|
||
<span class="checkbox-custom"></span>
|
||
<span class="checkbox-text" data-i18n="roleModal.enableRole">启用此角色</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn-secondary" onclick="closeRoleModal()" data-i18n="common.cancel">取消</button>
|
||
<button class="btn-primary" onclick="saveRole()" data-i18n="common.save">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/i18next@23.11.5/i18next.min.js"></script>
|
||
<script src="/static/js/i18n.js"></script>
|
||
<script src="/static/js/builtin-tools.js"></script>
|
||
<script src="/static/js/auth.js"></script>
|
||
<script src="/static/js/info-collect.js"></script>
|
||
<script src="/static/js/router.js"></script>
|
||
<script src="/static/js/agents.js"></script>
|
||
<script src="/static/js/dashboard.js"></script>
|
||
<script src="/static/js/monitor.js"></script>
|
||
<script src="/static/js/chat.js"></script>
|
||
<script src="/static/js/settings.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/xterm@4.19.0/lib/xterm.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.js"></script>
|
||
<script src="/static/js/terminal.js"></script>
|
||
<script src="/static/js/knowledge.js"></script>
|
||
<script src="/static/js/skills.js"></script>
|
||
<script src="/static/js/vulnerability.js?v=4"></script>
|
||
<script src="/static/js/webshell.js"></script>
|
||
<script src="/static/js/chat-files.js"></script>
|
||
<script src="/static/js/tasks.js"></script>
|
||
<script src="/static/js/roles.js"></script>
|
||
</body>
|
||
</html>
|
||
|