mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-05-15 12:58:01 +02:00
Add files via upload
This commit is contained in:
@@ -330,9 +330,19 @@ func (h *AgentHandler) AgentLoop(c *gin.Context) {
|
||||
if remark == "" {
|
||||
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)
|
||||
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
|
||||
} else if req.Role != "" && req.Role != "默认" {
|
||||
if h.config.Roles != nil {
|
||||
@@ -805,9 +815,19 @@ func (h *AgentHandler) AgentLoopStream(c *gin.Context) {
|
||||
if remark == "" {
|
||||
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)
|
||||
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 != "默认" {
|
||||
if h.config.Roles != nil {
|
||||
if role, exists := h.config.Roles[req.Role]; exists && role.Enabled {
|
||||
|
||||
@@ -9285,6 +9285,69 @@ header {
|
||||
background: var(--bg-secondary);
|
||||
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 {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
|
||||
+88
-16
@@ -351,7 +351,15 @@ function webshellAiConvListSelect(conn, convId, messagesContainer, listEl) {
|
||||
if (!content && role !== 'assistant') return;
|
||||
var div = document.createElement('div');
|
||||
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);
|
||||
});
|
||||
if (list.length === 0) {
|
||||
@@ -546,7 +554,15 @@ function loadWebshellAiHistory(conn, messagesContainer) {
|
||||
if (!content && role !== 'assistant') return;
|
||||
var div = document.createElement('div');
|
||||
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);
|
||||
});
|
||||
if (list.length === 0) {
|
||||
@@ -598,11 +614,51 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
||||
messagesContainer.appendChild(assistantDiv);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
|
||||
function appendTimelineItem(type, title, message) {
|
||||
function appendTimelineItem(type, title, message, data) {
|
||||
var item = document.createElement('div');
|
||||
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.classList.add('has-items');
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
@@ -663,38 +719,54 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
||||
}
|
||||
} else if (eventData.type === 'error' && eventData.message) {
|
||||
streamingTypingId += 1;
|
||||
appendTimelineItem('error', '❌ 错误', eventData.message);
|
||||
assistantDiv.textContent = '错误: ' + eventData.message;
|
||||
var errLabel = (typeof window.t === 'function') ? window.t('chat.error') : '错误';
|
||||
appendTimelineItem('error', '❌ ' + errLabel, eventData.message, eventData.data);
|
||||
assistantDiv.textContent = errLabel + ': ' + 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 = '…';
|
||||
} else if (eventData.type === 'iteration') {
|
||||
var iterN = (eventData.data && eventData.data.iteration) || 0;
|
||||
var iterTitle = iterN ? '🔍 第 ' + iterN + ' 轮迭代' : ('🔍 ' + (eventData.message || '迭代'));
|
||||
appendTimelineItem('iteration', iterTitle, eventData.message || '');
|
||||
var iterTitle = (typeof window.t === 'function')
|
||||
? window.t('chat.iterationRound', { n: iterN || 1 })
|
||||
: (iterN ? ('第 ' + iterN + ' 轮迭代') : (eventData.message || '迭代'));
|
||||
appendTimelineItem('iteration', '🔍 ' + iterTitle, eventData.message || '', eventData.data);
|
||||
if (!streamingTarget) assistantDiv.textContent = '…';
|
||||
} 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 = '…';
|
||||
} else if (eventData.type === 'tool_calls_detected' && eventData.data) {
|
||||
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 = '…';
|
||||
} else if (eventData.type === 'tool_call' && eventData.data) {
|
||||
var d = eventData.data;
|
||||
var tn = d.toolName || '未知工具';
|
||||
var idx = d.index || 0;
|
||||
var total = d.total || 0;
|
||||
var title = '🔧 调用: ' + tn + (total ? ' (' + idx + '/' + total + ')' : '');
|
||||
appendTimelineItem('tool_call', title, eventData.message || '');
|
||||
var callTitle = (typeof window.t === 'function')
|
||||
? 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 = '…';
|
||||
} else if (eventData.type === 'tool_result' && eventData.data) {
|
||||
var dr = eventData.data;
|
||||
var success = dr.success !== false;
|
||||
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) : '');
|
||||
appendTimelineItem('tool_result', title, sub);
|
||||
appendTimelineItem('tool_result', title, sub, eventData.data);
|
||||
if (!streamingTarget) assistantDiv.textContent = '…';
|
||||
}
|
||||
} catch (e) { /* ignore parse error */ }
|
||||
|
||||
Reference in New Issue
Block a user