mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-23 19:32:39 +02:00
Add files via upload
This commit is contained in:
@@ -330,9 +330,19 @@ func (h *AgentHandler) AgentLoop(c *gin.Context) {
|
|||||||
if remark == "" {
|
if remark == "" {
|
||||||
remark = conn.URL
|
remark = conn.URL
|
||||||
}
|
}
|
||||||
finalMessage = fmt.Sprintf("[WebShell 助手上下文] 当前连接 ID:%s,备注:%s。可用工具(仅在该连接上操作时使用,connection_id 填 \"%s\"):webshell_exec、webshell_file_list、webshell_file_read、webshell_file_write。请根据用户输入决定下一步:若仅为问候、闲聊或简单问题,直接简短回复即可,不必调用工具;当用户明确需要执行命令、列目录、读写字件等操作时再调用上述工具。\n\n用户请求:%s",
|
finalMessage = fmt.Sprintf("[WebShell 助手上下文] 当前连接 ID:%s,备注:%s。可用工具(仅在该连接上操作时使用,connection_id 填 \"%s\"):webshell_exec、webshell_file_list、webshell_file_read、webshell_file_write、record_vulnerability、list_knowledge_risk_types、search_knowledge_base、list_skills、read_skill。请根据用户输入决定下一步:若仅为问候、闲聊或简单问题,直接简短回复即可,不必调用工具;当用户明确需要执行命令、列目录、读写文件、记录漏洞或检索知识库/查看 Skills 等操作时再调用上述工具。\n\n用户请求:%s",
|
||||||
conn.ID, remark, conn.ID, req.Message)
|
conn.ID, remark, conn.ID, req.Message)
|
||||||
roleTools = []string{builtin.ToolWebshellExec, builtin.ToolWebshellFileList, builtin.ToolWebshellFileRead, builtin.ToolWebshellFileWrite}
|
roleTools = []string{
|
||||||
|
builtin.ToolWebshellExec,
|
||||||
|
builtin.ToolWebshellFileList,
|
||||||
|
builtin.ToolWebshellFileRead,
|
||||||
|
builtin.ToolWebshellFileWrite,
|
||||||
|
builtin.ToolRecordVulnerability,
|
||||||
|
builtin.ToolListKnowledgeRiskTypes,
|
||||||
|
builtin.ToolSearchKnowledgeBase,
|
||||||
|
builtin.ToolListSkills,
|
||||||
|
builtin.ToolReadSkill,
|
||||||
|
}
|
||||||
roleSkills = nil
|
roleSkills = nil
|
||||||
} else if req.Role != "" && req.Role != "默认" {
|
} else if req.Role != "" && req.Role != "默认" {
|
||||||
if h.config.Roles != nil {
|
if h.config.Roles != nil {
|
||||||
@@ -805,9 +815,19 @@ func (h *AgentHandler) AgentLoopStream(c *gin.Context) {
|
|||||||
if remark == "" {
|
if remark == "" {
|
||||||
remark = conn.URL
|
remark = conn.URL
|
||||||
}
|
}
|
||||||
finalMessage = fmt.Sprintf("[WebShell 助手上下文] 当前连接 ID:%s,备注:%s。可用工具(仅在该连接上操作时使用,connection_id 填 \"%s\"):webshell_exec、webshell_file_list、webshell_file_read、webshell_file_write。请根据用户输入决定下一步:若仅为问候、闲聊或简单问题,直接简短回复即可,不必调用工具;当用户明确需要执行命令、列目录、读写字件等操作时再调用上述工具。\n\n用户请求:%s",
|
finalMessage = fmt.Sprintf("[WebShell 助手上下文] 当前连接 ID:%s,备注:%s。可用工具(仅在该连接上操作时使用,connection_id 填 \"%s\"):webshell_exec、webshell_file_list、webshell_file_read、webshell_file_write、record_vulnerability、list_knowledge_risk_types、search_knowledge_base、list_skills、read_skill。请根据用户输入决定下一步:若仅为问候、闲聊或简单问题,直接简短回复即可,不必调用工具;当用户明确需要执行命令、列目录、读写文件、记录漏洞或检索知识库/查看 Skills 等操作时再调用上述工具。\n\n用户请求:%s",
|
||||||
conn.ID, remark, conn.ID, req.Message)
|
conn.ID, remark, conn.ID, req.Message)
|
||||||
roleTools = []string{builtin.ToolWebshellExec, builtin.ToolWebshellFileList, builtin.ToolWebshellFileRead, builtin.ToolWebshellFileWrite}
|
roleTools = []string{
|
||||||
|
builtin.ToolWebshellExec,
|
||||||
|
builtin.ToolWebshellFileList,
|
||||||
|
builtin.ToolWebshellFileRead,
|
||||||
|
builtin.ToolWebshellFileWrite,
|
||||||
|
builtin.ToolRecordVulnerability,
|
||||||
|
builtin.ToolListKnowledgeRiskTypes,
|
||||||
|
builtin.ToolSearchKnowledgeBase,
|
||||||
|
builtin.ToolListSkills,
|
||||||
|
builtin.ToolReadSkill,
|
||||||
|
}
|
||||||
} else if req.Role != "" && req.Role != "默认" {
|
} else if req.Role != "" && req.Role != "默认" {
|
||||||
if h.config.Roles != nil {
|
if h.config.Roles != nil {
|
||||||
if role, exists := h.config.Roles[req.Role]; exists && role.Enabled {
|
if role, exists := h.config.Roles[req.Role]; exists && role.Enabled {
|
||||||
|
|||||||
@@ -9285,6 +9285,69 @@ header {
|
|||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
/* AI 助手 markdown 渲染优化:避免行间距离过大和内容横向溢出 */
|
||||||
|
.webshell-ai-msg.assistant {
|
||||||
|
/* markdown 里已经有块级元素,不需要再整体 pre-wrap,否则容易在块之间产生“空行”感 */
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
.webshell-ai-msg.assistant p,
|
||||||
|
.webshell-ai-msg.assistant ul,
|
||||||
|
.webshell-ai-msg.assistant ol,
|
||||||
|
.webshell-ai-msg.assistant pre,
|
||||||
|
.webshell-ai-msg.assistant blockquote {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.webshell-ai-msg.assistant p:last-child,
|
||||||
|
.webshell-ai-msg.assistant ul:last-child,
|
||||||
|
.webshell-ai-msg.assistant ol:last-child,
|
||||||
|
.webshell-ai-msg.assistant pre:last-child,
|
||||||
|
.webshell-ai-msg.assistant blockquote:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.webshell-ai-msg pre {
|
||||||
|
font-family: var(--font-mono, Menlo, Monaco, Consolas, "Courier New", monospace);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
background: #020617;
|
||||||
|
color: #e5e7eb;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: pre;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.webshell-ai-msg pre code {
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.webshell-ai-msg code {
|
||||||
|
font-family: var(--font-mono, Menlo, Monaco, Consolas, "Courier New", monospace);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
background: rgba(15, 23, 42, 0.06);
|
||||||
|
color: inherit;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.1em 0.4em;
|
||||||
|
white-space: normal;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.webshell-ai-msg table {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
overflow-x: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.webshell-ai-msg table th,
|
||||||
|
.webshell-ai-msg table td {
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
padding: 4px 6px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
.webshell-ai-msg ul,
|
||||||
|
.webshell-ai-msg ol {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
.webshell-ai-input-row {
|
.webshell-ai-input-row {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
+88
-16
@@ -351,7 +351,15 @@ function webshellAiConvListSelect(conn, convId, messagesContainer, listEl) {
|
|||||||
if (!content && role !== 'assistant') return;
|
if (!content && role !== 'assistant') return;
|
||||||
var div = document.createElement('div');
|
var div = document.createElement('div');
|
||||||
div.className = 'webshell-ai-msg ' + (role === 'user' ? 'user' : 'assistant');
|
div.className = 'webshell-ai-msg ' + (role === 'user' ? 'user' : 'assistant');
|
||||||
div.textContent = content;
|
if (role === 'user') {
|
||||||
|
div.textContent = content;
|
||||||
|
} else {
|
||||||
|
if (typeof formatMarkdown === 'function') {
|
||||||
|
div.innerHTML = formatMarkdown(content);
|
||||||
|
} else {
|
||||||
|
div.textContent = content;
|
||||||
|
}
|
||||||
|
}
|
||||||
messagesContainer.appendChild(div);
|
messagesContainer.appendChild(div);
|
||||||
});
|
});
|
||||||
if (list.length === 0) {
|
if (list.length === 0) {
|
||||||
@@ -546,7 +554,15 @@ function loadWebshellAiHistory(conn, messagesContainer) {
|
|||||||
if (!content && role !== 'assistant') return;
|
if (!content && role !== 'assistant') return;
|
||||||
var div = document.createElement('div');
|
var div = document.createElement('div');
|
||||||
div.className = 'webshell-ai-msg ' + (role === 'user' ? 'user' : 'assistant');
|
div.className = 'webshell-ai-msg ' + (role === 'user' ? 'user' : 'assistant');
|
||||||
div.textContent = content;
|
if (role === 'user') {
|
||||||
|
div.textContent = content;
|
||||||
|
} else {
|
||||||
|
if (typeof formatMarkdown === 'function') {
|
||||||
|
div.innerHTML = formatMarkdown(content);
|
||||||
|
} else {
|
||||||
|
div.textContent = content;
|
||||||
|
}
|
||||||
|
}
|
||||||
messagesContainer.appendChild(div);
|
messagesContainer.appendChild(div);
|
||||||
});
|
});
|
||||||
if (list.length === 0) {
|
if (list.length === 0) {
|
||||||
@@ -598,11 +614,51 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
|||||||
messagesContainer.appendChild(assistantDiv);
|
messagesContainer.appendChild(assistantDiv);
|
||||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||||
|
|
||||||
function appendTimelineItem(type, title, message) {
|
function appendTimelineItem(type, title, message, data) {
|
||||||
var item = document.createElement('div');
|
var item = document.createElement('div');
|
||||||
item.className = 'webshell-ai-timeline-item webshell-ai-timeline-' + type;
|
item.className = 'webshell-ai-timeline-item webshell-ai-timeline-' + type;
|
||||||
item.innerHTML = '<span class="webshell-ai-timeline-title">' + escapeHtml(title || message || '') + '</span>';
|
|
||||||
if (message && message !== title) item.innerHTML += '<div class="webshell-ai-timeline-msg">' + escapeHtml(message) + '</div>';
|
var html = '<span class="webshell-ai-timeline-title">' + escapeHtml(title || message || '') + '</span>';
|
||||||
|
|
||||||
|
// 工具调用入参
|
||||||
|
if (type === 'tool_call' && data) {
|
||||||
|
try {
|
||||||
|
var args = data.argumentsObj || (data.arguments ? JSON.parse(data.arguments) : null);
|
||||||
|
if (args && typeof args === 'object') {
|
||||||
|
var paramsLabel = (typeof window.t === 'function') ? window.t('timeline.params') : '参数:';
|
||||||
|
html += '<div class="webshell-ai-timeline-msg"><div class="tool-arg-section"><strong>' +
|
||||||
|
escapeHtml(paramsLabel) +
|
||||||
|
'</strong><pre class="tool-args">' +
|
||||||
|
escapeHtml(JSON.stringify(args, null, 2)) +
|
||||||
|
'</pre></div></div>';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// JSON 解析失败时忽略参数详情,避免打断主流程
|
||||||
|
}
|
||||||
|
} else if (type === 'tool_result' && data) {
|
||||||
|
// 工具调用出参
|
||||||
|
var isError = data.isError || data.success === false;
|
||||||
|
var noResultText = (typeof window.t === 'function') ? window.t('timeline.noResult') : '无结果';
|
||||||
|
var result = data.result != null ? data.result : (data.error != null ? data.error : noResultText);
|
||||||
|
var resultStr = (typeof result === 'string') ? result : JSON.stringify(result);
|
||||||
|
var execResultLabel = (typeof window.t === 'function') ? window.t('timeline.executionResult') : '执行结果:';
|
||||||
|
var execIdLabel = (typeof window.t === 'function') ? window.t('timeline.executionId') : '执行ID:';
|
||||||
|
html += '<div class="webshell-ai-timeline-msg"><div class="tool-result-section ' +
|
||||||
|
(isError ? 'error' : 'success') +
|
||||||
|
'"><strong>' + escapeHtml(execResultLabel) + '</strong><pre class="tool-result">' +
|
||||||
|
escapeHtml(resultStr) +
|
||||||
|
'</pre>' +
|
||||||
|
(data.executionId ? '<div class="tool-execution-id"><span>' +
|
||||||
|
escapeHtml(execIdLabel) +
|
||||||
|
'</span> <code>' +
|
||||||
|
escapeHtml(String(data.executionId)) +
|
||||||
|
'</code></div>' : '') +
|
||||||
|
'</div></div>';
|
||||||
|
} else if (message && message !== title) {
|
||||||
|
html += '<div class="webshell-ai-timeline-msg">' + escapeHtml(message) + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
item.innerHTML = html;
|
||||||
timelineContainer.appendChild(item);
|
timelineContainer.appendChild(item);
|
||||||
timelineContainer.classList.add('has-items');
|
timelineContainer.classList.add('has-items');
|
||||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||||
@@ -663,38 +719,54 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
|||||||
}
|
}
|
||||||
} else if (eventData.type === 'error' && eventData.message) {
|
} else if (eventData.type === 'error' && eventData.message) {
|
||||||
streamingTypingId += 1;
|
streamingTypingId += 1;
|
||||||
appendTimelineItem('error', '❌ 错误', eventData.message);
|
var errLabel = (typeof window.t === 'function') ? window.t('chat.error') : '错误';
|
||||||
assistantDiv.textContent = '错误: ' + eventData.message;
|
appendTimelineItem('error', '❌ ' + errLabel, eventData.message, eventData.data);
|
||||||
|
assistantDiv.textContent = errLabel + ': ' + eventData.message;
|
||||||
} else if (eventData.type === 'progress' && eventData.message) {
|
} else if (eventData.type === 'progress' && eventData.message) {
|
||||||
appendTimelineItem('progress', '🔍 ' + eventData.message, '');
|
var progressMsg = (typeof window.translateProgressMessage === 'function')
|
||||||
|
? window.translateProgressMessage(eventData.message)
|
||||||
|
: eventData.message;
|
||||||
|
appendTimelineItem('progress', '🔍 ' + progressMsg, '', eventData.data);
|
||||||
if (!streamingTarget) assistantDiv.textContent = '…';
|
if (!streamingTarget) assistantDiv.textContent = '…';
|
||||||
} else if (eventData.type === 'iteration') {
|
} else if (eventData.type === 'iteration') {
|
||||||
var iterN = (eventData.data && eventData.data.iteration) || 0;
|
var iterN = (eventData.data && eventData.data.iteration) || 0;
|
||||||
var iterTitle = iterN ? '🔍 第 ' + iterN + ' 轮迭代' : ('🔍 ' + (eventData.message || '迭代'));
|
var iterTitle = (typeof window.t === 'function')
|
||||||
appendTimelineItem('iteration', iterTitle, eventData.message || '');
|
? window.t('chat.iterationRound', { n: iterN || 1 })
|
||||||
|
: (iterN ? ('第 ' + iterN + ' 轮迭代') : (eventData.message || '迭代'));
|
||||||
|
appendTimelineItem('iteration', '🔍 ' + iterTitle, eventData.message || '', eventData.data);
|
||||||
if (!streamingTarget) assistantDiv.textContent = '…';
|
if (!streamingTarget) assistantDiv.textContent = '…';
|
||||||
} else if (eventData.type === 'thinking' && eventData.message) {
|
} else if (eventData.type === 'thinking' && eventData.message) {
|
||||||
appendTimelineItem('thinking', '🤔 AI 思考', eventData.message);
|
var thinkLabel = (typeof window.t === 'function') ? window.t('chat.aiThinking') : 'AI 思考';
|
||||||
|
appendTimelineItem('thinking', '🤔 ' + thinkLabel, eventData.message, eventData.data);
|
||||||
if (!streamingTarget) assistantDiv.textContent = '…';
|
if (!streamingTarget) assistantDiv.textContent = '…';
|
||||||
} else if (eventData.type === 'tool_calls_detected' && eventData.data) {
|
} else if (eventData.type === 'tool_calls_detected' && eventData.data) {
|
||||||
var count = eventData.data.count || 0;
|
var count = eventData.data.count || 0;
|
||||||
appendTimelineItem('tool_calls_detected', '🔧 检测到 ' + count + ' 个工具调用', eventData.message || '');
|
var detectedLabel = (typeof window.t === 'function')
|
||||||
|
? window.t('chat.toolCallsDetected', { count: count })
|
||||||
|
: ('检测到 ' + count + ' 个工具调用');
|
||||||
|
appendTimelineItem('tool_calls_detected', '🔧 ' + detectedLabel, eventData.message || '', eventData.data);
|
||||||
if (!streamingTarget) assistantDiv.textContent = '…';
|
if (!streamingTarget) assistantDiv.textContent = '…';
|
||||||
} else if (eventData.type === 'tool_call' && eventData.data) {
|
} else if (eventData.type === 'tool_call' && eventData.data) {
|
||||||
var d = eventData.data;
|
var d = eventData.data;
|
||||||
var tn = d.toolName || '未知工具';
|
var tn = d.toolName || '未知工具';
|
||||||
var idx = d.index || 0;
|
var idx = d.index || 0;
|
||||||
var total = d.total || 0;
|
var total = d.total || 0;
|
||||||
var title = '🔧 调用: ' + tn + (total ? ' (' + idx + '/' + total + ')' : '');
|
var callTitle = (typeof window.t === 'function')
|
||||||
appendTimelineItem('tool_call', title, eventData.message || '');
|
? window.t('chat.callTool', { name: tn, index: idx, total: total })
|
||||||
|
: ('调用: ' + tn + (total ? ' (' + idx + '/' + total + ')' : ''));
|
||||||
|
var title = '🔧 ' + callTitle;
|
||||||
|
appendTimelineItem('tool_call', title, eventData.message || '', eventData.data);
|
||||||
if (!streamingTarget) assistantDiv.textContent = '…';
|
if (!streamingTarget) assistantDiv.textContent = '…';
|
||||||
} else if (eventData.type === 'tool_result' && eventData.data) {
|
} else if (eventData.type === 'tool_result' && eventData.data) {
|
||||||
var dr = eventData.data;
|
var dr = eventData.data;
|
||||||
var success = dr.success !== false;
|
var success = dr.success !== false;
|
||||||
var tname = dr.toolName || '工具';
|
var tname = dr.toolName || '工具';
|
||||||
var title = (success ? '✅ ' : '❌ ') + tname + (success ? ' 执行完成' : ' 执行失败');
|
var titleText = (typeof window.t === 'function')
|
||||||
|
? (success ? window.t('chat.toolExecComplete', { name: tname }) : window.t('chat.toolExecFailed', { name: tname }))
|
||||||
|
: (tname + (success ? ' 执行完成' : ' 执行失败'));
|
||||||
|
var title = (success ? '✅ ' : '❌ ') + titleText;
|
||||||
var sub = eventData.message || (dr.result ? String(dr.result).slice(0, 300) : '');
|
var sub = eventData.message || (dr.result ? String(dr.result).slice(0, 300) : '');
|
||||||
appendTimelineItem('tool_result', title, sub);
|
appendTimelineItem('tool_result', title, sub, eventData.data);
|
||||||
if (!streamingTarget) assistantDiv.textContent = '…';
|
if (!streamingTarget) assistantDiv.textContent = '…';
|
||||||
}
|
}
|
||||||
} catch (e) { /* ignore parse error */ }
|
} catch (e) { /* ignore parse error */ }
|
||||||
|
|||||||
Reference in New Issue
Block a user