920 lines
74 KiB
HTML
920 lines
74 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<meta name="theme-color" content="#FAF7F2">
|
||
<meta name="description" content="Metona Ollama Desktop - TypeScript + Electron AI 聊天客户端">
|
||
<link rel="icon" href="./assets/icons/llama.ico" type="image/x-icon">
|
||
<title>Metona Ollama</title>
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
|
||
<!-- ═══════════════ 日志面板(左侧)═══════════════ -->
|
||
<aside class="log-panel" id="logPanel">
|
||
<div class="log-panel-header">
|
||
<span class="log-panel-title">📋 执行日志</span>
|
||
<button class="btn btn-sm" id="btnExportLog" title="导出日志" style="padding:2px 8px;font-size:11px;opacity:0.7;">📥 导出</button>
|
||
</div>
|
||
<div class="log-panel-body" id="logPanelBody"></div>
|
||
</aside>
|
||
|
||
<!-- ═══════════════ 主内容区 ═══════════════ -->
|
||
<div class="main-wrap with-workspace" id="mainWrap">
|
||
|
||
<!-- ═══════════════ 顶部导航 ═══════════════ -->
|
||
<header class="app-header">
|
||
<div class="header-left">
|
||
<img class="logo" src="./assets/icons/llama.png" alt="logo" />
|
||
<span class="app-title">Metona Ollama</span>
|
||
<span class="app-version">v0.16.0</span>
|
||
<button class="icon-btn help-btn" id="btnHelp" title="使用帮助">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="header-right">
|
||
<div class="conn-status pending" id="connStatus" title="连接状态">
|
||
<span class="status-dot"></span>
|
||
<span class="status-label">未连接</span>
|
||
</div>
|
||
<button class="icon-btn" id="btnNewChat" title="新建会话">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnMemory" title="Agent 记忆">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/>
|
||
<line x1="9" y1="21" x2="15" y2="21"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnTools" title="工具面板">
|
||
<svg 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>
|
||
</button>
|
||
<button class="icon-btn" id="btnTokenDashboard" title="Token 监控">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="18" height="18" rx="2"/><line x1="7" y1="13" x2="7" y2="17"/><line x1="11" y1="9" x2="11" y2="17"/><line x1="15" y1="5" x2="15" y2="17"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnHistory" title="历史记录">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnSettings" title="设置">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="3"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn searxng-btn" id="btnSearxng" title="SearXNG 搜索配置">
|
||
<span class="searxng-icon-dot" id="searxngIconDot"></span>
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ═══════════════ 模型选择栏 ═══════════════ -->
|
||
<div class="model-bar">
|
||
<div class="model-bar-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="4" y="4" width="16" height="16" rx="2"/>
|
||
<path d="M9 9h6M9 12h6M9 15h4"/>
|
||
</svg>
|
||
</div>
|
||
<div class="model-select-wrap">
|
||
<select class="model-select" id="modelSelect">
|
||
<option value="">正在加载模型...</option>
|
||
</select>
|
||
<span class="model-select-arrow">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
|
||
</span>
|
||
</div>
|
||
<div class="model-badges" id="modelBadges">
|
||
<span class="model-badge think-badge" id="badgeThink" style="display:none;">🧠 Think</span>
|
||
<span class="model-badge vision-badge" id="badgeVision" style="display:none;">👁️ Vision</span>
|
||
<span class="model-badge tools-badge" id="badgeTools" style="display:none;">🔧 Tools</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 聊天区域 ═══════════════ -->
|
||
<main class="chat-area" id="chatArea">
|
||
<div class="empty-state" id="emptyState">
|
||
<div class="empty-icon">
|
||
<img src="./assets/icons/llama.png" alt="Metona Ollama" style="width:80px;height:80px;object-fit:contain;opacity:0.85;" />
|
||
</div>
|
||
<h2>开始对话</h2>
|
||
<p>选择一个模型,输入消息开始聊天</p>
|
||
</div>
|
||
<div class="messages" id="messagesContainer" style="display:none;"></div>
|
||
<button class="scroll-to-bottom" id="scrollToBottom" style="display:none;" title="回到最新">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="6 9 12 15 18 9"/>
|
||
</svg>
|
||
<span>回到底部</span>
|
||
</button>
|
||
</main>
|
||
|
||
<!-- ═══════════════ 历史只读提示栏 ═══════════════ -->
|
||
<div class="history-view-bar" id="historyBar" style="display:none;">
|
||
<span>📖 正在查看历史记录(只读模式)</span>
|
||
<button class="btn btn-sm btn-outline" id="btnExitHistory">返回新会话</button>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 输入区域 ═══════════════ -->
|
||
<div class="input-area" id="inputArea">
|
||
<div class="image-preview" id="imagePreview" style="display:none;"></div>
|
||
<div class="file-preview" id="filePreview" style="display:none;"></div>
|
||
<div class="video-preview" id="videoPreview" style="display:none;"></div>
|
||
<div class="input-row">
|
||
<button class="icon-btn attach-btn" id="btnAttachImg" title="上传图片">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/>
|
||
<polyline points="21 15 16 10 5 21"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn attach-btn" id="btnAttachVideo" title="上传视频(提取帧分析)">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polygon points="23 7 16 12 23 17 23 7"/>
|
||
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn attach-btn" id="btnAttachFile" title="上传文件(文本/代码)">
|
||
<svg 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"/>
|
||
<polyline points="10 9 9 9 8 9"/>
|
||
</svg>
|
||
</button>
|
||
<textarea class="chat-input" id="chatInput" placeholder="输入消息..." rows="1"></textarea>
|
||
<label class="think-toggle" id="thinkToggleWrap" title="Think 模式未可用">
|
||
<input type="checkbox" id="toggleThink" disabled>
|
||
<span class="think-btn">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/>
|
||
<line x1="9" y1="21" x2="15" y2="21"/>
|
||
</svg>
|
||
</span>
|
||
</label>
|
||
<label class="plan-toggle" id="planToggleWrap" title="Plan 模式:先规划后执行">
|
||
<input type="checkbox" id="togglePlan">
|
||
<span class="plan-btn">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="9" x2="15" y2="9"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="12" y2="17"/>
|
||
</svg>
|
||
</span>
|
||
</label>
|
||
<button class="send-btn" id="btnSend">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div><!-- /.main-wrap -->
|
||
|
||
<!-- ═══════════════ 工作空间面板(右侧,常驻显示,flex 子元素)═══════════════ -->
|
||
<aside class="workspace-panel" id="workspacePanel">
|
||
<div class="ws-header">
|
||
<span class="ws-title">🖥️ 工作空间</span>
|
||
<div class="ws-tabs">
|
||
<button class="ws-header-tab" id="wsTabTerminal">💻 命令行</button>
|
||
<button class="ws-header-tab active" id="wsTabTools">🔧 工具</button>
|
||
<button class="ws-header-tab" id="wsTabFiles">📁 文件</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 终端 Tab -->
|
||
<div class="ws-content" id="wsTerminalContent" style="display:none;">
|
||
<div class="ws-term-output" id="wsTermOutput"></div>
|
||
<div class="ws-term-toolbar">
|
||
<span class="ws-term-hint" id="wsTermHint">等待 AI 执行命令...</span>
|
||
<div class="ws-term-toolbar-btns">
|
||
<button class="ws-toolbar-btn ws-stop-btn" id="wsStopBtn" title="终止当前命令" disabled>
|
||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||
<rect x="6" y="6" width="12" height="12" rx="1"/>
|
||
</svg>
|
||
</button>
|
||
<button class="ws-toolbar-btn" id="wsClearBtn" title="清空">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="3 6 5 6 21 6"/>
|
||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 工具调用 Tab -->
|
||
<div class="ws-content" id="wsToolsContent">
|
||
<div class="ws-tools-output" id="wsToolsOutput"></div>
|
||
<div class="ws-term-toolbar">
|
||
<span class="ws-tools-hint" id="wsToolsHint">等待工具调用...</span>
|
||
<div class="ws-term-toolbar-btns">
|
||
<button class="ws-toolbar-btn" id="wsToolsClearBtn" title="清空">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="3 6 5 6 21 6"/>
|
||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文件 Tab -->
|
||
<div class="ws-content" id="wsFilesContent" style="display:none;">
|
||
<div class="ws-file-toolbar">
|
||
<button class="ws-toolbar-btn" id="wsFileUpBtn" title="上级目录">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="15 18 9 12 15 6"/>
|
||
</svg>
|
||
</button>
|
||
<span class="ws-file-path" id="wsFilePath">~</span>
|
||
<button class="ws-toolbar-btn" id="wsFileRefreshBtn" title="刷新">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="23 4 23 10 17 10"/>
|
||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="ws-file-list" id="wsFileList"></div>
|
||
<div class="ws-preview" id="wsPreview" style="display:none;">
|
||
<div class="ws-preview-header">
|
||
<span class="ws-preview-title" id="wsPreviewTitle"></span>
|
||
<button class="icon-btn" id="wsPreviewClose">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="ws-preview-content" id="wsPreviewContent"></div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ═══════════════ 设置模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="settingsModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>⚙️ 设置</h3>
|
||
<button class="icon-btn" id="btnCloseSettings">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="setting-group">
|
||
<label class="setting-label">Ollama 服务地址</label>
|
||
<input class="setting-input" id="inputServerUrl" type="url" placeholder="http://127.0.0.1:11434">
|
||
<div class="connection-info" id="connInfo">
|
||
<span class="status-badge" id="connBadge">检测中...</span>
|
||
<span class="server-version" id="serverVersion"></span>
|
||
</div>
|
||
<div class="cors-hint" id="corsHint" style="display:none;">
|
||
<p>⚠️ 连接失败,可能是 CORS 问题。请在启动 Ollama 时设置环境变量:</p>
|
||
<code>OLLAMA_ORIGINS="*" ollama serve</code>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label" style="margin-top:12px;">温度(Temperature):<span id="tempValue">0.7</span></label>
|
||
<input type="range" id="inputTemperature" class="temp-slider" min="0" max="2" step="0.1" value="0.7">
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">低值更精确稳定,高值更有创意多样(0 = 确定性,2 = 最大随机)</p>
|
||
<label class="setting-label" style="margin-top:12px;">Agent 最大轮数(maxTurns)</label>
|
||
<div style="display:flex;gap:8px;align-items:center;">
|
||
<input class="setting-input" id="inputMaxTurns" type="number" min="1" max="500" step="1" value="85" style="margin-bottom:0;">
|
||
<span class="text-muted" style="white-space:nowrap;">轮</span>
|
||
</div>
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">Agent Loop 最大工具调用轮数。默认 85 轮,复杂任务可能需要更多轮次。</p>
|
||
<label class="setting-label" style="margin-top:12px;">上下文长度(Context Length)</label>
|
||
<select class="setting-input" id="selectContextLength" style="margin-bottom:0;">
|
||
<option value="131072">128K</option>
|
||
<option value="262144">256K</option>
|
||
<option value="524288">512K</option>
|
||
<option value="1048576">1M</option>
|
||
</select>
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">发送给模型的最大上下文窗口大小。默认 128K,越大可容纳更长对话但消耗更多显存/内存。</p>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">⏱️ 超时设置(Timeout)</label>
|
||
<p class="text-muted" style="font-size:11px;margin-bottom:10px;">调整各通信层超时时长。留空则使用默认值(HTTP:30s / 流式:300s / MCP:60s)。设为 0 可禁用超时。</p>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||
<div>
|
||
<label style="font-size:12px;color:var(--text-secondary);" for="inputHttpTimeout">🌐 HTTP 请求超时(秒)</label>
|
||
<input class="setting-input" id="inputHttpTimeout" type="number" min="0" step="5" placeholder="30" style="margin-bottom:0;">
|
||
<p class="text-muted" style="font-size:10px;">web_search / web_fetch 等 HTTP 请求的超时</p>
|
||
</div>
|
||
<div>
|
||
<label style="font-size:12px;color:var(--text-secondary);" for="inputStreamTimeout">📡 流式调用超时(秒)</label>
|
||
<input class="setting-input" id="inputStreamTimeout" type="number" min="0" step="10" placeholder="300" style="margin-bottom:0;">
|
||
<p class="text-muted" style="font-size:10px;">Ollama 单轮流式响应的最大等待时间</p>
|
||
</div>
|
||
<div style="grid-column:1/-1;">
|
||
<label style="font-size:12px;color:var(--text-secondary);" for="inputMCPTimeout">🔌 MCP 请求超时(秒)</label>
|
||
<input class="setting-input" id="inputMCPTimeout" type="number" min="0" step="10" placeholder="60" style="margin-bottom:0;">
|
||
<p class="text-muted" style="font-size:10px;">MCP Server JSON-RPC 请求的超时(需重启 MCP 服务器生效)</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">运行中的模型</label>
|
||
<div class="running-models" id="runningModels">
|
||
<p class="text-muted">无运行中的模型</p>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🖥️ 工作空间目录</label>
|
||
<div style="display:flex;gap:8px;align-items:center;">
|
||
<input class="setting-input" id="inputWorkspaceDir" type="text" placeholder="默认: 应用数据目录/workspace" style="margin-bottom:0;flex:1;" readonly>
|
||
<button class="btn btn-sm btn-outline" id="btnBrowseWorkspace">📂 浏览</button>
|
||
</div>
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">工作空间用于存放 AI 执行命令时的工作文件。命令行默认在此目录执行。</p>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🧠 Agent 记忆系统</label>
|
||
<p class="text-muted" style="font-size:11px;">记忆存储在<strong>工作空间 MEMORY.md</strong> 文件中。新对话时自动检索注入上下文,对话结束时 AI 自动提取保存(多层质量过滤,宁缺毋滥)。受路径保护,仅 <code>memory</code> 工具可读写。</p>
|
||
<div class="tool-list" style="margin-top:8px;">
|
||
<div class="tool-item"><span>📌 事实</span><span class="text-muted">项目信息、身份背景</span></div>
|
||
<div class="tool-item"><span>⚙️ 偏好</span><span class="text-muted">语言风格、技术栈偏好</span></div>
|
||
<div class="tool-item"><span>📏 规则</span><span class="text-muted">编码规范、输出格式要求</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🤖 子代理默认模型</label>
|
||
<p class="text-muted" style="font-size:11px;margin-bottom:8px;">子代理(spawn_task)使用的默认模型。留空则使用当前选中的模型。可为简单任务选择更快的模型。</p>
|
||
<select class="setting-input" id="selectSubAgentModel">
|
||
<option value="">跟随当前模型</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🤖 子代理 & 看门狗参数</label>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||
<div>
|
||
<label style="font-size:12px;color:var(--text-secondary);" for="inputSubAgentMaxLoops">🔄 子代理最大轮次</label>
|
||
<input class="setting-input" id="inputSubAgentMaxLoops" type="number" min="1" max="100" step="1" placeholder="10" style="margin-bottom:0;">
|
||
<p class="text-muted" style="font-size:10px;">spawn_task 子代理的最大工具调用轮次</p>
|
||
</div>
|
||
<div>
|
||
<label style="font-size:12px;color:var(--text-secondary);" for="inputSubAgentTimeout">⏱️ 子代理超时(秒)</label>
|
||
<input class="setting-input" id="inputSubAgentTimeout" type="number" min="0" max="3600" step="30" placeholder="300" style="margin-bottom:0;">
|
||
<p class="text-muted" style="font-size:10px;">子代理单次任务最大执行时间,0=禁用</p>
|
||
</div>
|
||
<div style="grid-column:1/-1;">
|
||
<label style="font-size:12px;color:var(--text-secondary);" for="inputLoopWatchdog">🐕 看门狗超时(分钟)</label>
|
||
<input class="setting-input" id="inputLoopWatchdog" type="number" min="0" max="120" step="5" placeholder="30" style="margin-bottom:0;">
|
||
<p class="text-muted" style="font-size:10px;">Agent Loop 全局超时保护,超过后强制终止。0=禁用,默认30分钟</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">显存管理</label>
|
||
<button class="btn btn-danger" id="btnReleaseVRAM">释放显存(卸载模型)</button>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">数据管理</label>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||
<button class="btn btn-outline" id="btnExportAll">📦 导出全部会话</button>
|
||
<button class="btn btn-outline" id="btnImportSessions">📥 导入会话</button>
|
||
<button class="btn btn-danger" id="btnClearAllHistory">清空所有历史</button>
|
||
</div>
|
||
<p class="text-muted" style="margin-top:6px;font-size:11px;">导出格式为 JSON,可备份后在其他设备导入恢复。</p>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🔌 MCP 服务器</label>
|
||
<div style="display:flex;gap:8px;margin-bottom:8px;">
|
||
<input class="setting-input" id="inputMCPName" type="text" placeholder="名称" style="margin-bottom:0;width:100px;">
|
||
<input class="setting-input" id="inputMCPCommand" type="text" placeholder="命令 (如: npx)" style="margin-bottom:0;width:100px;">
|
||
<input class="setting-input" id="inputMCPArgs" type="text" placeholder="参数 (空格分隔)" style="margin-bottom:0;flex:1;">
|
||
<button class="btn btn-sm btn-outline" id="btnAddMCPServer">➕ 添加</button>
|
||
</div>
|
||
<div id="mcpServerList" style="font-size:12px;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 历史记录模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="historyModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>🕐 历史记录</h3>
|
||
<button class="icon-btn" id="btnCloseHistory">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="history-search-wrap">
|
||
<input class="history-search-input" id="historySearchInput" type="text" placeholder="搜索会话标题或内容...">
|
||
<button class="history-search-clear" id="historySearchClear" style="display:none;">✕</button>
|
||
</div>
|
||
<div class="history-list" id="historyList"></div>
|
||
<div class="history-pagination" id="historyPagination"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 帮助模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="helpModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>📖 使用帮助</h3>
|
||
<button class="icon-btn" id="btnCloseHelp">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="help-section"><h4>🚀 快速开始</h4><ol><li>确保 Ollama 已启动(默认 <code>http://127.0.0.1:11434</code>,可在设置中修改)</li><li>顶部模型栏选择一个模型,右侧会显示模型能力徽章(🧠 Think / 👁️ Vision / 🔧 Tools)</li><li>输入消息,按 <kbd>Enter</kbd> 发送,<kbd>Shift+Enter</kbd> 换行</li></ol></div>
|
||
<div class="help-section"><h4>💬 聊天功能</h4><ul><li><strong>流式回复</strong> — 实时打字效果,随时点 ■ 停止</li><li><strong>Think 推理</strong> — 下方 Think 按钮切换,让模型展示深度思考过程(需模型支持,如 Qwen3)</li><li><strong>上下文长度手动控制</strong> — 设置面板下拉选择(128K / 256K / 512K / 1M),默认 128K。模型栏显示当前配置值,下拉框中每个模型旁显示其自身的上下文长度</li><li><strong>Qwen3 兼容</strong> — 自动保证 system 消息唯一且位于首位,避免 Qwen3 等模型 400 错误</li><li><strong>多模态</strong> — 上传图片或视频(≤10MB),模型需支持 Vision。图片自动压缩,视频 1fps 提取帧序列带时序标注</li><li><strong>文件分析</strong> — 支持 50+ 种文本/代码格式,单文件 ≤500KB,自动剥离注释并按上下文预算智能截断</li><li><strong>AI 回复顶部</strong> — 每条 AI 回复上方展示 📋 系统提示词折叠卡片,可点击查看实际发送给模型的完整上下文</li></ul></div>
|
||
<div class="help-section"><h4>🔧 Tool Calling(始终开启)</h4><ul><li>所有消息均通过 <strong>Agent Loop 自主调用本地工具</strong>,像一个本地 Agent,无普通聊天模式</li><li><strong>40+1 个工具</strong>,分为 9 类:<ul><li><strong>文件系统</strong>(16 个):read_file / write_file / list_directory / search_files / create_directory / delete_file / move_file / copy_file / edit_file / get_file_info / tree / download_file / diff_files / replace_in_files / read_multiple_files / compress</li><li><strong>命令执行</strong>(1 个):run_command(实时流式输出,支持自动/需确认/禁用三种模式,可配置超时)</li><li><strong>联网搜索</strong>(2 个):web_search(支持 SearXNG 元搜索引擎 JSON API / 四引擎 HTML 解析,双模式可切换)/ web_fetch(反爬headers+UA自动切换+自动回退浏览器渲染)</li><li><strong>Git</strong>(1 个):git(17 个子操作,push/pull/clone 内置60-120s超时保护)</li><li><strong>浏览器控制</strong>(9 个):browser_open / browser_screenshot / browser_evaluate / browser_extract / browser_click / browser_type / browser_scroll / browser_wait / browser_close</li><li><strong>记忆 & 会话</strong>(5 个):memory(统一记忆管理,5 个 action)/ session_list / session_read / spawn_task / plan_track</li><li><strong>系统工具</strong>(6 个):datetime / calculator / random / uuid / json_format / hash</li></ul></li><li>read_file 支持 binary模式读取/base64解码/字节分页/2000行默认+截断hint自动续读</li><li>write_file 支持 base64写二进制文件/mode(overwrite+append)合并append_file功能/10MB</li><li>edit_file 支持 use_regex 正则替换</li><li>search_files 支持正则表达式搜索(use_regex=true)</li><li>browser_screenshot 支持全页面截图+元素截图</li><li>browser_extract 支持CSS选择器提取特定区域</li><li>new browser_wait 等待元素出现或定时等待</li><li>所有写操作工具均支持三档开关:自动/需确认/禁用(浏览器工具始终自动执行)</li><li>危险命令(<code>rm -rf</code>、<code>mkfs</code>、反弹 shell 等)和系统路径(<code>/etc</code>、<code>~/.ssh</code> 等)被自动拦截</li><li>工具调用以<strong>可视化卡片</strong>展示状态(pending → running → success/error),在工作空间🔧工具页签中显示</li><li>默认最大 <strong>85 轮</strong>工具调用循环(设置面板可调),上下文使用率>80%时自动缩减到3轮</li><li>独立工具自动<strong>并行执行</strong>(16个只读工具加入并行白名单),有依赖关系的工具串行执行</li></ul></div>
|
||
<div class="help-section"><h4>🧠 Agent 记忆系统</h4><ul><li>记忆存储在工作空间 <strong>MEMORY.md</strong> 文件,受严格路径保护,仅 <code>memory</code> 工具可读写</li><li>新对话时自动检索相关记忆注入 AI 上下文,让 AI "记住"你</li><li>对话结束时 AI 自动提取有价值的用户信息保存(多层质量过滤,宁缺毋滥)</li><li><strong>memory 工具</strong>(5 个 action):search(关键词搜索)/ add(添加)/ replace(替换)/ remove(删除)/ read_all(读取全部)</li><li>点击顶部 🧠 按钮打开记忆面板:查看、添加、删除记忆条目</li><li><strong>记忆容量上限 500 条</strong>,超限时自动清理低价值条目(规则类型受保护)</li><li>写入前自动安全扫描(prompt injection / 敏感信息 / 不可见字符检测)</li><li>应用启动时自动校验 MEMORY.md 格式,格式错误自动备份重建</li></ul></div>
|
||
<div class="help-section"><h4>📋 Plan Mode(计划模式)</h4><ul><li>点击输入框上方 📋 按钮开启 <strong>Plan Mode</strong>(开关式)</li><li>开启后,AI <strong>先生成执行计划</strong>(Markdown 渲染弹窗),用户批准后才开始执行</li><li>计划批准后自动初始化追踪器,每个步骤完成后调用 <code>plan_track</code> 工具标记进度</li><li>系统提示词自动注入当前进度状态,AI 始终知道还剩多少步未完成</li><li><strong>多步骤任务防遗忘</strong>:自动检测用户请求中的动作动词,对比已完成步骤,注入提醒</li><li>关闭 Plan Mode 后恢复正常 Agent Loop 模式</li><li><strong>仅 Plan 模式</strong> 下 plan_track 工具可见,避免污染普通模式的工具列表</li></ul></div>
|
||
<div class="help-section"><h4>🛡️ 抗幻觉 & 稳定性</h4><ul><li><strong>5 层防御体系</strong>:系统提示词加固 → 任务感知 → 中途幻觉检测(中英双语规则覆盖全部工具类别)→ 进度锚点 → 完成闸门(6 项检查,幻觉/注入 → 阻断,质量/效率 → 咨询)</li><li><strong>完成闸门</strong>:对话结束前自动审查 AI 回复,检测工具幻觉和 prompt injection,阻断有问题的回复</li><li><strong>中英双语检测规则</strong>:覆盖中英文模型输出,防止 AI 声称执行了未调用的工具</li><li><strong>智能重试机制</strong>:永久错误(文件不存在/权限拒绝)立即返回,瞬态错误(网络/超时)指数退避最多重试 2 次</li><li><strong>看门狗超时</strong>:设置面板可配置全局超时(默认 30 分钟),AI 卡死或无限循环时自动中止</li><li><strong>流式总超时</strong>:可配置超时(默认 300s),Ollama 卡死不再永久阻塞</li><li><strong>中止保护</strong>:所有状态处理器 + 重试循环均检查中止信号,点击 ■ 按钮立即生效</li><li><strong>上下文硬上限</strong>:200 条消息强制压缩 + 120 条增量压缩,防止 OOM</li></ul></div>
|
||
<div class="help-section"><h4>🤖 Agent Loop 增强</h4><ul><li><strong>🎬 视频上传</strong>:支持上传 .mp4/.avi/.mov/.mkv/.webm 等视频(≤10MB),自动 1fps 提取帧序列(带时间戳),多模态模型原生理解视频时序关系</li><li><strong>8 状态机</strong>:INIT→THINKING→PARSING→EXECUTING→OBSERVING→REFLECTING→COMPRESSING→TERMINATED,每个状态独立的中止检查和处理</li><li><strong>智能上下文压缩</strong>:滑动窗口 + LLM结构化JSON摘要,120条触发增量压缩 + 200条硬上限强制压缩</li><li><strong>流式总超时</strong>:可配置(默认300s),Ollama 假死不再永久阻塞</li><li><strong>HTTP/MCP 超时可配</strong>:设置面板可分别调整 HTTP(默认30s)和 MCP(默认60s)超时</li><li><strong>智能工具调度</strong>:路径依赖检测自动串行化(write→read/create→write),只读工具并行执行</li><li><strong>工具缓存 TTL</strong>:搜索5分钟/网页10分钟/文件30分钟/git 30秒,默认60秒过期,不再永久缓存</li><li><strong>Plan Mode 断点续传</strong>:中止后可恢复未完成的计划,进度自动保存</li><li><strong>Token 感知迭代预算</strong>:上下文使用率>80%时自动缩减剩余轮次到3轮</li><li><strong>跨会话工具上下文</strong>:新对话自动注入上一轮已执行的工具调用及结果(role:tool 消息),AI 不会重复执行已完成的操作</li><li><strong>旧工具结果智能截断</strong>:超过10轮后自动截断到2000字符,优先在JSON边界处截断</li></ul></div>
|
||
<div class="help-section"><h4>🔌 MCP(Model Context Protocol)</h4><ul><li>支持连接外部 MCP Server,动态扩展工具能力</li><li>设置面板可添加/启用/禁用/删除 MCP 服务器</li><li>MCP 工具以 <code>mcp_{server}__{tool}</code> 前缀注册,与内置工具统一调度</li><li>启动时自动连接已启用的 MCP 服务器</li></ul></div>
|
||
<div class="help-section"><h4>🔍 SearXNG 元搜索引擎</h4><ul><li>点击顶部 🔍 按钮打开配置面板,可接入自部署的 SearXNG 实例</li><li><strong>JSON 模式</strong>:调用 SearXNG JSON API,聚合 70+ 引擎结果,结构化解析</li><li><strong>HTML 模式</strong>:获取原始搜索结果页面,交由 AI 自行分析提取信息</li><li>支持认证 Key(HTTP Header Authorization),保护私有实例</li><li>启用后替代内置四引擎方案;关闭即回退,无缝切换</li><li>所有参数(引擎、语言、安全搜索、时间范围等)均可独立配置</li></ul></div>
|
||
<div class="help-section"><h4>📋 自定义文件(SOUL.md / AGENT.md / USER.md)</h4><ul><li>在工作空间目录创建以下文件即可自定义 AI 行为,修改后下一轮对话立即生效</li><li><strong>SOUL.md</strong> — AI 身份、性格、行为准则(<strong>永远不可被压缩</strong>,注入为最高优先级系统提示词)</li><li><strong>AGENT.md</strong> — 工具调用规则、链式调用模式、核心约束(内置精简默认版,可通过工作空间覆盖)</li><li><strong>USER.md</strong> — 用户画像:技术栈、偏好、习惯等个人信息,AI 在对话中自动参考(仅工作空间,无内置默认)</li><li>可在 AI 回复顶部的 📋 系统提示词卡片中查看实际注入的完整上下文</li><li>SOUL.md / AGENT.md 删除工作空间中文件可恢复为内置默认版本;USER.md 无内置版,不存在则不注入</li></ul></div>
|
||
<div class="help-section"><h4>📊 Token 实时监控</h4><ul><li>点击顶部 📊 按钮打开 Token 监控仪表盘</li><li><strong>全局统计</strong> — 跨会话累计 Token 消耗,柱状图按会话展示趋势</li><li><strong>当前会话</strong> — 实时显示本轮对话的 Token 消耗,按轮次展示明细</li><li>每 2 秒自动刷新数据,支持输入/输出分色显示</li></ul></div>
|
||
<div class="help-section"><h4>🖥️ 布局说明</h4><ul><li><strong>左侧面板</strong> — 执行日志,实时显示应用运行日志(连接、模型加载、工具调用等)</li><li><strong>中间区域</strong> — 聊天消息,顶部 Header + 模型栏,底部输入框</li><li><strong>右侧面板</strong> — 工作空间(常驻显示),包含 3 个页签:<ul><li><strong>💻 命令行 Tab</strong> — 终端界面,实时流式输出,支持长时间运行(无超时),单一终端进程</li><li><strong>🔧 工具 Tab</strong> — 展示本轮对话的工具调用卡片,含状态统计(总数 ✅ 成功 ❌ 失败),AI 执行命令时自动切到此页签</li><li><strong>📁 文件 Tab</strong> — 浏览工作空间目录,点击文件预览内容(带行号),支持上级目录导航</li></ul></li><li>工作空间目录可在设置中修改</li></ul></div>
|
||
<div class="help-section"><h4>🕐 历史记录</h4><ul><li>所有会话自动保存到本地 SQLite</li><li>点击顶部 🕐 按钮查看、搜索、恢复历史会话</li><li>支持导出 JSON / <code>.metona</code> 加密备份</li></ul></div>
|
||
<div class="help-section"><h4>⚡ 快捷键 & 命令</h4><table class="help-shortcuts"><tr><td><kbd>Enter</kbd></td><td>发送消息</td></tr><tr><td><kbd>Shift + Enter</kbd></td><td>换行</td></tr><tr><td><kbd>Ctrl + N</kbd></td><td>新建会话</td></tr><tr><td><kbd>Ctrl + M</kbd></td><td>记忆管理</td></tr><tr><td><kbd>Esc</kbd></td><td>关闭弹窗</td></tr></table><p style="margin-top:8px;font-size:12px;color:var(--text-secondary);">输入框命令(直接输入后发送):</p><table class="help-shortcuts"><tr><td><code>/retry</code></td><td>重试上一轮回复</td></tr><tr><td><code>/undo</code></td><td>撤销最后一条用户消息及回复</td></tr><tr><td><code>/compress</code></td><td>用 AI 摘要压缩长对话上下文</td></tr></table></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 工具面板模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="toolsModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>🔧 工具面板(40 个 + 1 Plan 专属)</h3>
|
||
<button class="icon-btn" id="btnCloseTools">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p class="text-muted" style="font-size:12px;margin-bottom:12px;">AI 始终可在对话中自主调用以下工具(Tool Calling 永久开启)。高风险操作需用户确认。</p>
|
||
<div class="tools-grid">
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📄</span><span class="tool-card-name">read_file</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">读取文件(文本/binary/base64),2000行默认,截断自动提示续读</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">✏️</span><span class="tool-card-name">write_file</span><span class="tool-card-badge confirm" id="badge_write_file">需确认</span></div>
|
||
<div class="tool-card-desc">写入/追加文件,支持base64二进制,最大10MB</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="write_file" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📁</span><span class="tool-card-name">list_directory</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">列出目录内容(2000条上限),支持递归/偏移分页/扩展名过滤</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔍</span><span class="tool-card-name">search_files</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">按文件名或内容搜索,支持正则表达式</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📂</span><span class="tool-card-name">create_directory</span><span class="tool-card-badge confirm" id="badge_create_directory">需确认</span></div>
|
||
<div class="tool-card-desc">创建目录,自动创建父级</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="create_directory" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🗑️</span><span class="tool-card-name">delete_file</span><span class="tool-card-badge confirm" id="badge_delete_file">需确认</span></div>
|
||
<div class="tool-card-desc">删除文件或目录,返回被删文件数和总大小</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="delete_file" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📦</span><span class="tool-card-name">move_file</span><span class="tool-card-badge confirm" id="badge_move_file">需确认</span></div>
|
||
<div class="tool-card-desc">移动或重命名文件/目录</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="move_file" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📋</span><span class="tool-card-name">copy_file</span><span class="tool-card-badge confirm" id="badge_copy_file">需确认</span></div>
|
||
<div class="tool-card-desc">复制文件或目录到新位置</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="copy_file" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🌐</span><span class="tool-card-name">web_fetch</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">抓取网页内容,反爬headers+UA切换+自动回退浏览器渲染</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">✂️</span><span class="tool-card-name">edit_file</span><span class="tool-card-badge confirm" id="badge_edit_file">需确认</span></div>
|
||
<div class="tool-card-desc">查找替换(支持正则use_regex),全部或单次替换</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="edit_file" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">💻</span><span class="tool-card-name">run_command</span><span class="tool-card-badge confirm" id="badge_run_command">需确认</span></div>
|
||
<div class="tool-card-desc">执行 shell 命令,无超时限制</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="run_command" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">ℹ️</span><span class="tool-card-name">get_file_info</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">获取文件/目录详细信息:大小、日期、权限</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🌳</span><span class="tool-card-name">tree</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">以树形结构展示目录内容</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">⬇️</span><span class="tool-card-name">download_file</span><span class="tool-card-badge confirm" id="badge_download_file">需确认</span></div>
|
||
<div class="tool-card-desc">从 URL 下载文件到本地</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="download_file" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔀</span><span class="tool-card-name">diff_files</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">对比两个文件,显示差异</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔄</span><span class="tool-card-name">replace_in_files</span><span class="tool-card-badge confirm" id="badge_replace_in_files">需确认</span></div>
|
||
<div class="tool-card-desc">按 glob 模式批量查找替换多个文件</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="replace_in_files" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📚</span><span class="tool-card-name">read_multiple_files</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">并行读取最多50个文件,每文件10KB</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔖</span><span class="tool-card-name">git</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">Git 全操作(17个子命令:status/log/diff/add/commit/push等)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🗜️</span><span class="tool-card-name">compress</span><span class="tool-card-badge confirm" id="badge_compress">需确认</span></div>
|
||
<div class="tool-card-desc">创建或解压 zip/tar.gz 归档</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select class="tool-mode-select" data-tool="compress" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔍</span><span class="tool-card-name">web_search</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">SearXNG JSON API / 四引擎并行,默认30条</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🧠</span><span class="tool-card-name">memory</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">统一记忆管理(5 action):search 关键词搜索 / add 添加 / replace 子串替换 / remove 子串删除 / read_all 读取全部。MEMORY.md 受路径保护</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📋</span><span class="tool-card-name">session_list</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">列出历史会话,支持按标题搜索</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📖</span><span class="tool-card-name">session_read</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">读取历史会话消息内容</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🤖</span><span class="tool-card-name">spawn_task</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">生成子代理独立执行任务,完成后回传结果</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🌐</span><span class="tool-card-name">browser_open</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">打开URL,支持等待特定CSS元素出现(SPA页面)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📸</span><span class="tool-card-name">browser_screenshot</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">截图(视口/全页面/指定元素),返回base64 PNG</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">⚡</span><span class="tool-card-name">browser_evaluate</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">在浏览器页面中执行 JavaScript</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📰</span><span class="tool-card-name">browser_extract</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">提取页面文本+链接,支持CSS选择器指定区域</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">👆</span><span class="tool-card-name">browser_click</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">点击元素,支持等待出现+自动滚动到视图</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">⌨️</span><span class="tool-card-name">browser_type</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">输入文本,支持追加模式(不先清空)+自动提交表单</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">↕️</span><span class="tool-card-name">browser_scroll</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">滚动页面(上/下/顶/底)或滚动到指定元素</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">⏳</span><span class="tool-card-name">browser_wait</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">等待指定元素出现或固定时间延迟</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">❌</span><span class="tool-card-name">browser_close</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">关闭 Agent 浏览器并释放资源</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🕐</span><span class="tool-card-name">datetime</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">获取系统精确时间(ISO/Unix/日期/时间/完整)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔢</span><span class="tool-card-name">calculator</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">安全数学计算(+ - * / ** % (),递归下降解析器)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🎲</span><span class="tool-card-name">random</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">随机数/随机选择(整数/浮点/抽取/字符串)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🆔</span><span class="tool-card-name">uuid</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">生成 UUID v4 唯一标识符(批量最多20个)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📋</span><span class="tool-card-name">json_format</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">JSON 格式化 + 语法验证 + 键排序</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔐</span><span class="tool-card-name">hash</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">哈希计算(MD5/SHA1/SHA256/SHA384/SHA512)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📋</span><span class="tool-card-name">plan_track</span><span class="tool-card-badge confirm" id="badge_plan_track">需确认</span></div>
|
||
<div class="tool-card-desc">Plan 模式专属:追踪执行进度,标记/查看步骤完成状态</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">计划确认:</label>
|
||
<select id="select_plan_confirm" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动确认</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ Token 监控仪表盘 ═══════════════ -->
|
||
<div class="modal-overlay" id="tokenDashboardModal" style="display:none;">
|
||
<div class="modal" style="max-width:860px;width:860px;">
|
||
<div class="modal-header">
|
||
<h3>📊 Token 实时监控</h3>
|
||
<button class="icon-btn" id="btnCloseTokenDashboard">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body" style="max-height:75vh;overflow-y:auto;">
|
||
<div class="td-overview" id="tdOverview"></div>
|
||
<div class="td-view-toggle" id="tdViewToggle">
|
||
<button class="td-view-btn active" data-view="global">🌍 全局统计</button>
|
||
<button class="td-view-btn" data-view="session">💬 当前会话</button>
|
||
</div>
|
||
<div class="td-ctx-ring-section" id="tdCtxRingSection" style="display:none;">
|
||
<div class="td-section-title">🎯 上下文窗口使用率</div>
|
||
<div class="td-ctx-ring-wrap" id="tdCtxRing"></div>
|
||
</div>
|
||
<div class="td-section">
|
||
<div class="td-section-title" id="tdChartTitle">📈 Token 消耗趋势 — 全局</div>
|
||
<div class="td-chart" id="tdChart"></div>
|
||
</div>
|
||
<div class="td-section">
|
||
<div class="td-section-title" id="tdTableTitle">📋 明细(按时间倒序)</div>
|
||
<div class="td-table-wrap">
|
||
<table class="td-table">
|
||
<thead>
|
||
<tr>
|
||
<th>轮次</th>
|
||
<th>角色</th>
|
||
<th>输入</th>
|
||
<th>输出</th>
|
||
<th>合计</th>
|
||
<th>耗时</th>
|
||
<th>时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tdTableBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 图片预览 Lightbox ═══════════════ -->
|
||
<div class="lightbox-overlay" id="lightbox" style="display:none;">
|
||
<button class="lightbox-close" id="lightboxClose">✕</button>
|
||
<img class="lightbox-img" id="lightboxImg" src="" alt="预览">
|
||
</div>
|
||
|
||
<!-- ═══════════════ 工具调用确认对话框 ═══════════════ -->
|
||
<div class="modal-overlay" id="toolConfirmModal" style="display:none;">
|
||
<div class="modal" style="max-width:520px;">
|
||
<div class="modal-header">
|
||
<h3 id="toolConfirmTitle">⚠️ 确认操作</h3>
|
||
</div>
|
||
<div class="modal-body" id="toolConfirmBody">
|
||
<p>AI 请求执行一个需要确认的操作。</p>
|
||
</div>
|
||
<div class="modal-actions" style="padding:12px 20px 16px;">
|
||
<div style="flex:1;"></div>
|
||
<button class="btn btn-outline" id="toolCancelBtn">❌ 取消</button>
|
||
<button class="btn btn-primary" id="toolConfirmBtn">✅ 确认执行</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ Toast 通知容器 ═══════════════ -->
|
||
<div class="toast-container" id="toastContainer"></div>
|
||
|
||
<!-- ═══════════════ SearXNG 配置模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="searxngModal" style="display:none;">
|
||
<div class="modal" style="max-width:640px;">
|
||
<div class="modal-header">
|
||
<h3>🔍 SearXNG 元搜索引擎</h3>
|
||
<button class="icon-btn" id="btnCloseSearxng">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="setting-group">
|
||
<label class="setting-label">
|
||
<span class="searxng-badge" id="searxngBadge">⚪ 未启用</span>
|
||
<label class="toggle-label" style="margin-left:auto;display:inline-flex;">
|
||
<input type="checkbox" id="toggleSearxngEnabled">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</label>
|
||
<p class="text-muted" style="font-size:11px;margin-top:4px;">
|
||
启用后,联网搜索将使用 SearXNG 元搜索引擎(隐私保护、70+ 引擎聚合),替代内置的四引擎 HTML 解析方案。
|
||
</p>
|
||
</div>
|
||
|
||
<div class="setting-group">
|
||
<label class="setting-label">SearXNG API 地址</label>
|
||
<input class="setting-input" id="searxngUrl" type="url" placeholder="请输入 SearXNG 实例地址" required style="margin-bottom:6px;">
|
||
<p class="text-muted" style="font-size:11px;">SearXNG 实例的根地址,无需带 /search 路径</p>
|
||
</div>
|
||
|
||
<div class="setting-group">
|
||
<label class="setting-label">搜索引擎</label>
|
||
<input class="setting-input" id="searxngEngines" type="text" placeholder="留空使用实例默认引擎,逗号分隔" style="margin-bottom:6px;">
|
||
<p class="text-muted" style="font-size:11px;">逗号分隔的引擎列表。常用: google, bing, duckduckgo, wikipedia, brave, qwant, startpage</p>
|
||
</div>
|
||
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;">
|
||
<div class="setting-group">
|
||
<label class="setting-label">语言</label>
|
||
<select class="setting-input" id="searxngLanguage" style="margin-bottom:0;">
|
||
<option value="zh-CN">简体中文</option>
|
||
<option value="zh-TW">繁體中文</option>
|
||
<option value="en">English</option>
|
||
<option value="ja">日本語</option>
|
||
<option value="ko">한국어</option>
|
||
<option value="">自动检测</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">安全搜索</label>
|
||
<select class="setting-input" id="searxngSafeSearch" style="margin-bottom:0;">
|
||
<option value="0">关闭</option>
|
||
<option value="1">中等</option>
|
||
<option value="2">严格</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">时间范围</label>
|
||
<select class="setting-input" id="searxngTimeRange" style="margin-bottom:0;">
|
||
<option value="">不限</option>
|
||
<option value="day">最近一天</option>
|
||
<option value="week">最近一周</option>
|
||
<option value="month">最近一月</option>
|
||
<option value="year">最近一年</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">单次结果数</label>
|
||
<input class="setting-input" id="searxngMaxResults" type="number" min="0" max="100" step="1" placeholder="0=使用默认值(30)" style="margin-bottom:0;">
|
||
<p class="text-muted" id="searxngMaxResultsHint" style="font-size:11px;margin-top:4px;display:none;color:var(--text-disabled);">HTML 模式下此选项不生效,返回整页文本</p>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">返回格式</label>
|
||
<select class="setting-input" id="searxngFormat" style="margin-bottom:0;">
|
||
<option value="json">JSON(结构化解析)</option>
|
||
<option value="html">HTML(原始网页,AI 自行分析)</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">自动抓取条数</label>
|
||
<input class="setting-input" id="searxngFetchCount" type="number" min="1" max="8" step="1" placeholder="留空=AI决定(最多8条)" style="margin-bottom:0;">
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">抓取类型</label>
|
||
<select class="setting-input" id="searxngFetchMode" style="margin-bottom:0;">
|
||
<option value="sequential">顺序抓取</option>
|
||
<option value="random">随机抓取</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="setting-group">
|
||
<label class="setting-label">🔑 认证</label>
|
||
<div style="display:flex;gap:8px;align-items:center;">
|
||
<select class="setting-input" id="searxngAuthType" style="width:110px;flex-shrink:0;margin-bottom:0;">
|
||
<option value="bearer">Bearer</option>
|
||
<option value="basic">Basic</option>
|
||
</select>
|
||
<input class="setting-input" id="searxngAuthKey" type="password" placeholder="留空则不发送认证" style="flex:1;margin-bottom:0;">
|
||
</div>
|
||
<p class="text-muted" style="font-size:11px;margin-top:4px;">Bearer: API Token · Basic: 账号:密码(自动 Base64 编码)。需配合 Nginx 反向代理验证</p>
|
||
</div>
|
||
|
||
<div class="modal-actions">
|
||
<div style="flex:1;"></div>
|
||
<button class="btn btn-primary" id="btnSaveSearxng">💾 保存配置</button>
|
||
</div>
|
||
|
||
<div style="margin-top:16px;padding:12px;background:var(--bg-layer);border-radius:var(--radius-md);">
|
||
<p style="font-size:11px;color:var(--text-secondary);margin:0;">
|
||
💡 <strong>提示:</strong>SearXNG 是开源元搜索引擎,不追踪用户、不记录隐私。
|
||
需要自行部署或使用公共实例。API 端点格式:<code>{{URL}}/search?q={{query}}&format=json</code>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 应用入口 ════════════════ -->
|
||
<script type="module" src="./main.ts"></script>
|
||
</body>
|
||
</html>
|