jarvis-agent-factory 3.45.4 → 3.45.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/package.json +2 -2
- package/dist/src/web/views/agents.html +579 -0
- package/dist/src/web/views/pipeline.html +1347 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Jarvis Agent Factory · 贾维斯智能体工厂
|
|
2
2
|
|
|
3
3
|
[](./LICENSE)
|
|
4
|
-
[](https://github.com/Wjl1224734792/Jarvis-Agent-Factory/releases)
|
|
5
5
|
[](https://www.npmjs.com/package/jarvis-agent-factory)
|
|
6
6
|
[](https://github.com/Wjl1224734792/visual-primitives-mcp)
|
|
7
7
|
<br>💡 **纯文本模型(如 DeepSeek)主力用户** → 搭配 [Visual Primitives MCP](https://github.com/Wjl1224734792/visual-primitives-mcp) 获得视觉理解能力
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jarvis-agent-factory",
|
|
3
|
-
"version": "3.45.
|
|
3
|
+
"version": "3.45.5",
|
|
4
4
|
"description": "Jarvis Agent Factory CLI — Claude Code 多智能体 AI 编程助手配置安装器 | Multi-agent AI coding assistant config installer for Claude Code",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jarvis",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"test": "vitest run",
|
|
39
39
|
"test:watch": "vitest",
|
|
40
40
|
"typecheck": "tsc --noEmit",
|
|
41
|
-
"build": "tsc && node -
|
|
41
|
+
"build": "tsc && node scripts/copy-assets.js",
|
|
42
42
|
"build:web": "cd web && npm run build",
|
|
43
43
|
"dev": "tsx src/cli/index.ts",
|
|
44
44
|
"jarvis": "node dist/src/cli/index.js",
|
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>智能体配置 · Jarvis Engine</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<script>tailwind.config={theme:{extend:{colors:{primary:'#6366F1',primary2:'#818CF8',cta:'#10B981',surface:'#FFFFFF',text:'#1E1B4B',muted:'#64748B'},fontFamily:{sans:['Inter','system-ui','sans-serif'],mono:['JetBrains Mono','Fira Code','monospace']}}}}</script>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
10
|
+
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
|
11
|
+
<style>
|
|
12
|
+
* { scrollbar-width: thin; scrollbar-color: #CBD5E1 transparent; }
|
|
13
|
+
.toast-in { animation:slideIn .25s ease-out; }
|
|
14
|
+
@keyframes slideIn { from{opacity:0;transform:translateY(12px);} to{opacity:1;transform:translateY(0);} }
|
|
15
|
+
@keyframes pulse-dot { 0%,100%{opacity:1;} 50%{opacity:.5;} }
|
|
16
|
+
.live-dot { animation:pulse-dot 2s ease-in-out infinite; }
|
|
17
|
+
.agent-card { transition: all .2s ease; }
|
|
18
|
+
.agent-card:hover { transform: translateY(-2px); }
|
|
19
|
+
.avatar-8 { display:grid; grid-template-columns:repeat(8,1fr); grid-template-rows:repeat(8,1fr); width:36px; height:36px; gap:1px; margin:6px auto; }
|
|
20
|
+
.avatar-8 span { width:100%; height:100%; border-radius:1px; }
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body class="bg-slate-50 text-slate-800 font-sans flex h-screen overflow-hidden">
|
|
24
|
+
|
|
25
|
+
<!-- ===== 控制台式左侧边栏 ===== -->
|
|
26
|
+
<aside class="w-72 min-w-[288px] bg-white text-slate-800 flex flex-col h-full z-10 shadow-sm border-r border-slate-200">
|
|
27
|
+
<!-- Logo 区域 -->
|
|
28
|
+
<div class="px-5 py-5 border-b border-slate-100">
|
|
29
|
+
<div class="flex items-center gap-3">
|
|
30
|
+
<div class="w-9 h-9 bg-indigo-600 rounded-lg flex items-center justify-center shadow-lg shadow-indigo-500/25">
|
|
31
|
+
<i data-lucide="zap" class="w-5 h-5 text-white"></i>
|
|
32
|
+
</div>
|
|
33
|
+
<div>
|
|
34
|
+
<h1 class="text-[15px] font-bold text-slate-800 leading-tight font-mono tracking-tight">Jarvis Engine</h1>
|
|
35
|
+
<p class="text-[11px] text-slate-400 font-mono" id="verNum">3.45.5</p>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- 导航 -->
|
|
41
|
+
<nav class="px-3 py-4 flex flex-col gap-0.5">
|
|
42
|
+
<a href="/dashboard" class="flex items-center gap-2.5 px-3 py-2.5 rounded-lg text-sm font-medium text-slate-500 hover:bg-indigo-50 hover:text-indigo-600 border-l-[3px] border-transparent transition-colors">
|
|
43
|
+
<i data-lucide="layout-dashboard" class="w-4 h-4"></i>
|
|
44
|
+
流水线看板
|
|
45
|
+
</a>
|
|
46
|
+
<a href="/dashboard#/archive" class="flex items-center gap-2.5 px-3 py-2.5 rounded-lg text-sm font-medium text-slate-500 hover:bg-indigo-50 hover:text-indigo-600 border-l-[3px] border-transparent transition-colors">
|
|
47
|
+
<i data-lucide="archive" class="w-4 h-4"></i>
|
|
48
|
+
归档记录
|
|
49
|
+
</a>
|
|
50
|
+
<a href="/agents" class="flex items-center gap-2.5 px-3 py-2.5 rounded-lg text-sm font-medium bg-indigo-50 text-indigo-600 border-l-[3px] border-indigo-500 transition-colors">
|
|
51
|
+
<i data-lucide="bot" class="w-4 h-4"></i>
|
|
52
|
+
智能体配置
|
|
53
|
+
</a>
|
|
54
|
+
</nav>
|
|
55
|
+
|
|
56
|
+
<!-- 统计概览 -->
|
|
57
|
+
<div class="px-4 py-3">
|
|
58
|
+
<p class="text-[10px] font-semibold text-slate-400 uppercase tracking-widest mb-2.5">智能体概览</p>
|
|
59
|
+
<div class="grid grid-cols-2 gap-2">
|
|
60
|
+
<div class="bg-slate-50 rounded-lg p-2.5 text-center">
|
|
61
|
+
<div class="text-lg font-bold text-slate-800 font-mono" id="statTotal">--</div>
|
|
62
|
+
<div class="text-[10px] text-slate-400 mt-0.5">全部智能体</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="bg-slate-50 rounded-lg p-2.5 text-center">
|
|
65
|
+
<div class="text-lg font-bold text-indigo-600 font-mono" id="statCustom">--</div>
|
|
66
|
+
<div class="text-[10px] text-slate-400 mt-0.5">已自定义</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<!-- 操作提示 -->
|
|
72
|
+
<div class="flex-1"></div>
|
|
73
|
+
<div class="px-4 py-3 border-t border-slate-100">
|
|
74
|
+
<p class="text-[10px] font-semibold text-slate-400 uppercase tracking-widest mb-2.5">MCP 接入状态</p>
|
|
75
|
+
<div class="flex flex-col gap-1.5 mb-2.5" id="mcpStatusSidebar">
|
|
76
|
+
<div class="flex items-center gap-2 text-[11px] text-slate-400"><span class="w-1.5 h-1.5 rounded-full bg-slate-400 inline-block flex-shrink-0"></span> 检测中...</div>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="text-[10px] text-slate-400 pt-2 border-t border-slate-100 leading-relaxed">
|
|
79
|
+
<p>点击卡片配置模型与思考等级</p>
|
|
80
|
+
<p class="mt-1"><span class="text-indigo-600">●</span> 紫色边框 = 已自定义配置</p>
|
|
81
|
+
<p><span class="text-slate-400">●</span> 灰色边框 = 使用默认值</p>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</aside>
|
|
85
|
+
|
|
86
|
+
<!-- ===== 主内容区域 ===== -->
|
|
87
|
+
<main class="flex-1 overflow-y-auto bg-slate-50">
|
|
88
|
+
<div class="max-w-6xl mx-auto px-8 py-8">
|
|
89
|
+
|
|
90
|
+
<!-- 页面标题 -->
|
|
91
|
+
<div class="mb-8">
|
|
92
|
+
<div class="flex items-center gap-3 mb-2">
|
|
93
|
+
<h2 class="text-2xl font-bold text-slate-800">智能体配置</h2>
|
|
94
|
+
<span class="px-2.5 py-0.5 text-[11px] font-medium bg-indigo-100 text-indigo-700 rounded-full">独立模型设置</span>
|
|
95
|
+
</div>
|
|
96
|
+
<p class="text-sm text-slate-500" id="agentCount">动态扫描模板目录 · 加载中...</p>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<!-- 工具栏 -->
|
|
100
|
+
<div class="flex flex-wrap items-center justify-between gap-4 mb-4">
|
|
101
|
+
<div class="flex gap-1.5" id="platformTabs">
|
|
102
|
+
<button class="px-3.5 py-1.5 text-xs font-medium rounded-lg bg-indigo-600 text-white transition-colors shadow-sm" onclick="setPlatform('all')">全部平台</button>
|
|
103
|
+
<button class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-500 hover:bg-slate-200 transition-colors" onclick="setPlatform('claude')">Claude Code</button>
|
|
104
|
+
<button class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-500 hover:bg-slate-200 transition-colors" onclick="setPlatform('opencode')">OpenCode</button>
|
|
105
|
+
<button class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-500 hover:bg-slate-200 transition-colors" onclick="setPlatform('codex')">Codex</button>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="flex items-center gap-2 bg-white border border-slate-200 rounded-lg px-3 py-2 shadow-sm">
|
|
108
|
+
<i data-lucide="search" class="w-4 h-4 text-slate-400"></i>
|
|
109
|
+
<input id="searchInput" class="text-sm bg-transparent outline-none w-48 text-slate-700 placeholder-slate-400" placeholder="搜索 agent 名称..." oninput="onSearch()">
|
|
110
|
+
<span id="clearSearch" class="text-xs text-slate-400 cursor-pointer hover:text-slate-600 hidden" onclick="clearSearch()">✕</span>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<!-- 来源筛选(模板/全局/项目) -->
|
|
115
|
+
<div class="flex gap-1.5 flex-wrap mb-3" id="sourceTabs">
|
|
116
|
+
<button class="px-3 py-1 text-xs font-medium rounded-lg bg-indigo-600 text-white transition-colors shadow-sm" onclick="setSource('all')">全部来源</button>
|
|
117
|
+
<button class="px-3 py-1 text-xs font-medium rounded-lg text-slate-500 hover:bg-slate-200 transition-colors" onclick="setSource('template')">模板默认</button>
|
|
118
|
+
<button class="px-3 py-1 text-xs font-medium rounded-lg text-slate-500 hover:bg-slate-200 transition-colors" onclick="setSource('global')">全局配置</button>
|
|
119
|
+
<button class="px-3 py-1 text-xs font-medium rounded-lg text-slate-500 hover:bg-slate-200 transition-colors" onclick="setSource('project')">项目配置</button>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<!-- 分类筛选 -->
|
|
123
|
+
<div class="flex gap-1.5 flex-wrap mb-6" id="categoryTabs">
|
|
124
|
+
<button class="px-3 py-1 text-xs font-medium rounded-lg bg-indigo-600 text-white transition-colors shadow-sm" onclick="setCategory('全部')">全部</button>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<!-- 智能体网格 -->
|
|
128
|
+
<div class="grid grid-cols-[repeat(auto-fill,minmax(185px,1fr))] gap-3" id="agentsGrid">
|
|
129
|
+
<div class="text-center py-16 text-slate-400 col-span-full">
|
|
130
|
+
<i data-lucide="loader" class="w-8 h-8 mx-auto mb-3 text-slate-300 animate-spin"></i>
|
|
131
|
+
<p class="text-sm">加载中...</p>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<!-- Footer -->
|
|
136
|
+
<footer class="mt-8 pt-6 border-t border-slate-200 text-xs text-slate-400 flex items-center justify-between">
|
|
137
|
+
<span class="font-mono">Jarvis Engine · MCP :3456</span>
|
|
138
|
+
<span>
|
|
139
|
+
<a href="/dashboard" class="text-indigo-500 hover:text-indigo-600 transition-colors font-medium">流水线看板</a>
|
|
140
|
+
<span class="mx-2 text-slate-300">·</span>
|
|
141
|
+
<span>会话隔离模式</span>
|
|
142
|
+
</span>
|
|
143
|
+
</footer>
|
|
144
|
+
</div>
|
|
145
|
+
</main>
|
|
146
|
+
|
|
147
|
+
<!-- ===== 配置弹窗 ===== -->
|
|
148
|
+
<div class="fixed inset-0 bg-black/30 z-50 hidden items-center justify-center backdrop-blur-sm" id="modal" onclick="if(event.target===this)closeModal()">
|
|
149
|
+
<div class="bg-white rounded-2xl p-6 min-w-[380px] max-w-[440px] shadow-2xl border border-slate-200">
|
|
150
|
+
<div class="flex items-center gap-3 mb-1">
|
|
151
|
+
<div class="w-9 h-9 bg-indigo-100 rounded-xl flex items-center justify-center">
|
|
152
|
+
<i data-lucide="settings" class="w-5 h-5 text-indigo-600"></i>
|
|
153
|
+
</div>
|
|
154
|
+
<div>
|
|
155
|
+
<h3 class="text-lg font-bold text-slate-800" id="modalName">---</h3>
|
|
156
|
+
<p class="text-[11px] text-slate-500 font-mono" id="modalRole">---</p>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div class="mt-4 space-y-4">
|
|
161
|
+
<!-- 模型选择 -->
|
|
162
|
+
<div>
|
|
163
|
+
<label class="block text-xs font-semibold text-slate-600 mb-1.5">模型选择</label>
|
|
164
|
+
<select id="modalSelect" class="w-full px-3 py-2.5 bg-slate-50 text-slate-700 border border-slate-200 rounded-lg text-sm cursor-pointer outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-100 transition-all" onchange="onModelChange()"></select>
|
|
165
|
+
<input id="modalCustom" type="text" placeholder="输入自定义模型名称..." class="w-full px-3 py-2.5 bg-slate-50 text-slate-700 border border-slate-200 rounded-lg text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-100 transition-all mt-2 hidden font-mono" oninput="onCustomInput()">
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<!-- 思考等级 -->
|
|
169
|
+
<div>
|
|
170
|
+
<label class="block text-xs font-semibold text-slate-600 mb-1.5">思考等级(Effort)</label>
|
|
171
|
+
<select id="modalEffort" class="w-full px-3 py-2.5 bg-slate-50 text-slate-700 border border-slate-200 rounded-lg text-sm cursor-pointer outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-100 transition-all"></select>
|
|
172
|
+
<p class="text-[10px] text-slate-400 mt-1">等级越高推理越深入,响应时间越长</p>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="flex gap-2 justify-end mt-5 pt-4 border-t border-slate-100">
|
|
177
|
+
<button class="px-4 py-2 text-xs font-medium text-slate-500 border border-slate-200 rounded-lg hover:border-indigo-300 hover:text-indigo-500 transition-colors mr-auto" id="resetBtn" onclick="resetModel()">
|
|
178
|
+
<i data-lucide="rotate-ccw" class="w-3.5 h-3.5 inline mr-1"></i>恢复默认
|
|
179
|
+
</button>
|
|
180
|
+
<button class="px-5 py-2 text-xs font-semibold bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition-colors shadow-sm" id="saveBtn" onclick="saveModel()">保存配置</button>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<!-- Toast 容器 -->
|
|
186
|
+
<div id="toastContainer" class="fixed bottom-6 right-6 z-[300] flex flex-col gap-2"></div>
|
|
187
|
+
|
|
188
|
+
<script>
|
|
189
|
+
/** 平台中文名映射 */
|
|
190
|
+
const PLATFORM_NAMES = {
|
|
191
|
+
claude: 'Claude Code',
|
|
192
|
+
opencode: 'OpenCode',
|
|
193
|
+
codex: 'Codex',
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/** 角色中文说明 */
|
|
197
|
+
const ROLE_INFO = {
|
|
198
|
+
'全栈': { desc: '前后端全栈开发', icon: 'code' },
|
|
199
|
+
'前端': { desc: '页面、组件、交互实现', icon: 'layout' },
|
|
200
|
+
'后端': { desc: '服务端逻辑与 API', icon: 'server' },
|
|
201
|
+
'移动端': { desc: 'iOS/Android/跨端', icon: 'smartphone' },
|
|
202
|
+
'测试': { desc: '单元/集成/E2E 测试', icon: 'test-tube' },
|
|
203
|
+
'审查': { desc: '代码审查与质量审计', icon: 'search' },
|
|
204
|
+
'架构': { desc: '系统架构与设计', icon: 'blueprint' },
|
|
205
|
+
'文档': { desc: 'API 契约与文档', icon: 'file-text' },
|
|
206
|
+
'基础设施': { desc: 'CI/CD 与部署', icon: 'container' },
|
|
207
|
+
'探索': { desc: '代码库探索与研究', icon: 'compass' },
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/** 努力等级中文说明 */
|
|
211
|
+
const EFFORT_NAMES = {
|
|
212
|
+
low: { label: '低', desc: '快速响应,适合简单任务' },
|
|
213
|
+
medium: { label: '中', desc: '平衡速度与质量' },
|
|
214
|
+
high: { label: '高', desc: '深入推理,默认推荐' },
|
|
215
|
+
xhigh: { label: '极高', desc: '最大推理深度' },
|
|
216
|
+
max: { label: '最大', desc: '极限推理,实验性' },
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// ---- 像素头像数据 ----
|
|
220
|
+
const PIXEL = {
|
|
221
|
+
brain:[0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0],
|
|
222
|
+
layout:[0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,1,0,0,1,1,0,1,1,1,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
|
|
223
|
+
palette:[0,0,1,0,0,1,0,0,0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0],
|
|
224
|
+
db:[0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1],
|
|
225
|
+
test:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0],
|
|
226
|
+
server:[0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,1,0,1,1,1,0,0,1,1,0,1,0,1,0,0,1,1,0,1,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,1,1,1,1,1,1,0],
|
|
227
|
+
route:[0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0],
|
|
228
|
+
cog:[0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0],
|
|
229
|
+
table:[1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1],
|
|
230
|
+
globe:[0,0,1,1,1,1,0,0,0,1,0,1,1,0,1,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,1,0,0,1,0,1,0,1,0,0,0,0,1,0,0,0,1,1,1,1,0,0],
|
|
231
|
+
play:[0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
|
232
|
+
file:[1,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0],
|
|
233
|
+
map:[0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0],
|
|
234
|
+
list:[1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,0],
|
|
235
|
+
shield:[0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,1,1,1,0,0],
|
|
236
|
+
eye:[0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,0,1,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const PIXEL_COLORS = {
|
|
240
|
+
brain:'#B4A7FF', layout:'#7DE2D1', palette:'#FF7B9C', db:'#4ECB71', test:'#FFE17B',
|
|
241
|
+
server:'#7DE2D1', route:'#FF7B9C', cog:'#8B869B', table:'#4ECB71', globe:'#7DE2D1',
|
|
242
|
+
play:'#4ECB71', file:'#8B869B', map:'#B4A7FF', list:'#FFE17B', shield:'#FF7B9C', eye:'#B4A7FF',
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
function px(icon) {
|
|
246
|
+
const p = PIXEL[icon] || PIXEL.brain;
|
|
247
|
+
const c = PIXEL_COLORS[icon] || PIXEL_COLORS.brain;
|
|
248
|
+
return `<div class="avatar-8">${p.map(v => `<span style="background:${v ? c : 'transparent'}"></span>`).join('')}</div>`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ---- 状态 ----
|
|
252
|
+
let agents = [], cur = null, platform = 'all', category = '全部', source = 'all', d = null, customModel = '', saving = false, searchText = '', projectName = 'project';
|
|
253
|
+
|
|
254
|
+
async function load(p) {
|
|
255
|
+
if (p !== undefined) platform = p;
|
|
256
|
+
const params = new URLSearchParams();
|
|
257
|
+
if (platform !== 'all') params.set('platform', platform);
|
|
258
|
+
if (category !== '全部') params.set('category', category);
|
|
259
|
+
if (source !== 'all') params.set('source', source);
|
|
260
|
+
if (searchText) params.set('search', searchText);
|
|
261
|
+
const qs = params.toString() ? '?' + params.toString() : '';
|
|
262
|
+
const r = await fetch('/api/agents' + qs);
|
|
263
|
+
d = await r.json();
|
|
264
|
+
agents = d.agents;
|
|
265
|
+
|
|
266
|
+
// 更新统计
|
|
267
|
+
document.getElementById('statTotal').textContent = d.total_count || agents.length;
|
|
268
|
+
document.getElementById('statCustom').textContent = agents.filter(a => a.is_custom).length;
|
|
269
|
+
|
|
270
|
+
// 懒初始化分类按钮
|
|
271
|
+
if (d.categories && document.getElementById('categoryTabs').children.length <= 1) {
|
|
272
|
+
document.getElementById('categoryTabs').innerHTML = d.categories.map(c => {
|
|
273
|
+
const isSel = c === category;
|
|
274
|
+
return `<button class="px-3 py-1 text-xs font-medium rounded-lg transition-colors ${isSel ? 'bg-indigo-600 text-white shadow-sm' : 'text-slate-500 hover:bg-slate-200'}" onclick="setCategory('${c}')">${c === '全部' ? '全部' : c}</button>`;
|
|
275
|
+
}).join('');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 更新来源统计到标签
|
|
279
|
+
if (d.source_counts) {
|
|
280
|
+
const sc = d.source_counts;
|
|
281
|
+
document.querySelectorAll('#sourceTabs button').forEach(b => {
|
|
282
|
+
const t = b.textContent.trim();
|
|
283
|
+
projectName = d.project_name || 'project';
|
|
284
|
+
if (t.includes('模板')) b.textContent = `模板默认 (${sc.template || 0})`;
|
|
285
|
+
else if (t.includes('全局')) b.textContent = `全局配置 (${sc.global || 0})`;
|
|
286
|
+
else if (t.includes('项目') || t === projectName) b.textContent = `${projectName} (${sc.project || 0})`;
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
document.getElementById('agentsGrid').innerHTML = render();
|
|
291
|
+
|
|
292
|
+
const scopeParts = [];
|
|
293
|
+
if (platform !== 'all') scopeParts.push(PLATFORM_NAMES[platform] || platform);
|
|
294
|
+
if (category !== '全部') scopeParts.push(category);
|
|
295
|
+
if (source !== 'all') scopeParts.push({template:'模板默认',global:'全局配置',project: (d.project_name || '项目配置')}[source] || source);
|
|
296
|
+
scopeParts.push(agents.length + ' 个智能体');
|
|
297
|
+
scopeParts.push('共 ' + d.total_count + ' 个');
|
|
298
|
+
document.getElementById('agentCount').textContent = scopeParts.join(' · ');
|
|
299
|
+
|
|
300
|
+
lucide.createIcons();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function setPlatform(p) {
|
|
304
|
+
platform = p;
|
|
305
|
+
document.querySelectorAll('#platformTabs button').forEach(b => {
|
|
306
|
+
const txt = b.textContent.trim().toLowerCase();
|
|
307
|
+
const isActive = txt.includes(p) || (p === 'all' && txt.includes('全部'));
|
|
308
|
+
b.className = isActive
|
|
309
|
+
? 'px-3.5 py-1.5 text-xs font-medium rounded-lg bg-indigo-600 text-white transition-colors shadow-sm'
|
|
310
|
+
: 'px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-500 hover:bg-slate-200 transition-colors';
|
|
311
|
+
});
|
|
312
|
+
load();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function setCategory(c) {
|
|
316
|
+
category = c;
|
|
317
|
+
document.querySelectorAll('#categoryTabs button').forEach(b => {
|
|
318
|
+
b.className = b.textContent.trim() === c
|
|
319
|
+
? 'px-3 py-1 text-xs font-medium rounded-lg bg-indigo-600 text-white transition-colors shadow-sm'
|
|
320
|
+
: 'px-3 py-1 text-xs font-medium rounded-lg text-slate-500 hover:bg-slate-200 transition-colors';
|
|
321
|
+
});
|
|
322
|
+
load();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function setSource(s) {
|
|
326
|
+
source = s;
|
|
327
|
+
document.querySelectorAll('#sourceTabs button').forEach(b => {
|
|
328
|
+
const t = b.textContent.trim().replace(/\s*\(\d+\)\s*/, '');
|
|
329
|
+
const isActive = t === (s === 'all' ? '全部来源' : s === 'template' ? '模板默认' : s === 'global' ? '全局配置' : projectName);
|
|
330
|
+
b.className = isActive
|
|
331
|
+
? 'px-3 py-1 text-xs font-medium rounded-lg bg-indigo-600 text-white transition-colors shadow-sm'
|
|
332
|
+
: 'px-3 py-1 text-xs font-medium rounded-lg text-slate-500 hover:bg-slate-200 transition-colors';
|
|
333
|
+
});
|
|
334
|
+
load();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function onSearch() {
|
|
338
|
+
searchText = document.getElementById('searchInput').value.trim();
|
|
339
|
+
document.getElementById('clearSearch').classList.toggle('hidden', searchText.length === 0);
|
|
340
|
+
load();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function clearSearch() {
|
|
344
|
+
document.getElementById('searchInput').value = '';
|
|
345
|
+
searchText = '';
|
|
346
|
+
document.getElementById('clearSearch').classList.add('hidden');
|
|
347
|
+
load();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** 获取角色图标(Lucide 图标名) */
|
|
351
|
+
function roleIcon(role) {
|
|
352
|
+
const map = {
|
|
353
|
+
'全栈': 'code-2', '前端': 'palette', '后端': 'server', '移动端': 'smartphone',
|
|
354
|
+
'测试': 'beaker', '审查': 'search-check', '架构': 'boxes', '文档': 'file-code',
|
|
355
|
+
'基础设施': 'container', '探索': 'compass',
|
|
356
|
+
};
|
|
357
|
+
return map[role] || 'bot';
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function render() {
|
|
361
|
+
if (agents.length === 0) {
|
|
362
|
+
return `<div class="text-center py-16 text-slate-400 col-span-full">
|
|
363
|
+
<i data-lucide="bot" class="w-10 h-10 mx-auto mb-3 text-slate-300"></i>
|
|
364
|
+
<p class="text-sm font-medium">未找到匹配的智能体</p>
|
|
365
|
+
<p class="text-xs mt-1 text-slate-400">尝试调整搜索条件或平台筛选</p>
|
|
366
|
+
</div>`;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return agents.map(a => {
|
|
370
|
+
const roleInfo = ROLE_INFO[a.role] || { desc: a.role || '', icon: 'bot' };
|
|
371
|
+
const effortInfo = EFFORT_NAMES[a.effort] || { label: a.effort || 'high' };
|
|
372
|
+
const platformName = PLATFORM_NAMES[a.platform] || a.platform || '';
|
|
373
|
+
|
|
374
|
+
// 来源/自定义标记
|
|
375
|
+
const borderClass = a.is_custom
|
|
376
|
+
? 'ring-2 ring-indigo-400 border-indigo-400 shadow-md shadow-indigo-100'
|
|
377
|
+
: a.source === 'global'
|
|
378
|
+
? 'ring-1 ring-emerald-300 border-emerald-300'
|
|
379
|
+
: a.source === 'project'
|
|
380
|
+
? 'ring-1 ring-amber-300 border-amber-300'
|
|
381
|
+
: 'border-slate-200 hover:border-slate-300';
|
|
382
|
+
|
|
383
|
+
const sourceBadge = a.source === 'global'
|
|
384
|
+
? '<span class="inline-flex items-center gap-0.5 text-[10px] text-emerald-600 bg-emerald-50 px-1.5 py-0.5 rounded-full font-medium">全局</span>'
|
|
385
|
+
: a.source === 'project'
|
|
386
|
+
? '<span class="inline-flex items-center gap-0.5 text-[10px] text-amber-600 bg-amber-50 px-1.5 py-0.5 rounded-full font-medium">项目</span>'
|
|
387
|
+
: '';
|
|
388
|
+
|
|
389
|
+
const pluginBadge = a.subdir === 'plugins'
|
|
390
|
+
? '<span class="inline-flex items-center gap-0.5 text-[10px] text-purple-600 bg-purple-50 px-1.5 py-0.5 rounded-full font-medium">插件</span>'
|
|
391
|
+
: '';
|
|
392
|
+
|
|
393
|
+
const customBadge = a.is_custom
|
|
394
|
+
? '<span class="inline-flex items-center gap-0.5 text-[10px] text-indigo-600 bg-indigo-50 px-1.5 py-0.5 rounded-full font-medium"><span class="w-1.5 h-1.5 rounded-full bg-indigo-500"></span>已配置</span>'
|
|
395
|
+
: '';
|
|
396
|
+
|
|
397
|
+
return `<div class="agent-card bg-white rounded-xl p-4 border ${borderClass} cursor-pointer group" onclick="openM('${a.id}')">
|
|
398
|
+
${px(a.icon)}
|
|
399
|
+
<div class="flex items-center gap-1.5 justify-center mt-1 flex-wrap">
|
|
400
|
+
<span class="text-[10px] text-slate-400 uppercase tracking-wide font-medium">${platformName}</span>
|
|
401
|
+
${sourceBadge}${pluginBadge}${customBadge}
|
|
402
|
+
</div>
|
|
403
|
+
<div class="text-sm font-semibold text-slate-800 mt-1.5 text-center truncate" title="${a.name}">${a.name}</div>
|
|
404
|
+
<div class="flex items-center justify-center gap-1 mt-1">
|
|
405
|
+
<i data-lucide="${roleIcon(a.role)}" class="w-3 h-3 text-slate-400"></i>
|
|
406
|
+
<span class="text-[11px] text-slate-500" title="${roleInfo.desc}">${a.role || '未知'}</span>
|
|
407
|
+
</div>
|
|
408
|
+
<div class="flex items-center justify-center gap-1.5 mt-2">
|
|
409
|
+
<span class="text-[10px] text-slate-500 bg-slate-100 px-2 py-0.5 rounded-full font-mono max-w-[120px] truncate" title="${a.model || '未配置'}">${a.model || '(未配置)'}</span>
|
|
410
|
+
<span class="text-[10px] font-medium ${a.is_custom ? 'text-indigo-500 bg-indigo-50' : 'text-slate-400 bg-slate-100'} px-1.5 py-0.5 rounded-full">${effortInfo.label}</span>
|
|
411
|
+
</div>
|
|
412
|
+
</div>`;
|
|
413
|
+
}).join('');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function platformModels(p) { return d && d.platform_models ? d.platform_models[p] || d.available_models : d.available_models; }
|
|
417
|
+
|
|
418
|
+
function onModelChange() {
|
|
419
|
+
document.getElementById('modalCustom').classList.toggle('hidden', document.getElementById('modalSelect').value !== '__custom__');
|
|
420
|
+
if (document.getElementById('modalSelect').value === '__custom__') document.getElementById('modalCustom').focus();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function onCustomInput() { customModel = document.getElementById('modalCustom').value; }
|
|
424
|
+
|
|
425
|
+
function openM(id) {
|
|
426
|
+
cur = agents.find(a => a.id === id);
|
|
427
|
+
if (!cur) return;
|
|
428
|
+
|
|
429
|
+
customModel = '';
|
|
430
|
+
document.getElementById('modalName').textContent = cur.name;
|
|
431
|
+
const platformName = PLATFORM_NAMES[cur.platform] || cur.platform;
|
|
432
|
+
document.getElementById('modalRole').textContent =
|
|
433
|
+
platformName + ' · ' + (cur.role || '未知') + ' · 默认: ' + (cur.defaultModel || '?') + ' @' + (cur.defaultEffort || 'high');
|
|
434
|
+
|
|
435
|
+
const pModels = platformModels(cur.platform);
|
|
436
|
+
const curInList = pModels.includes(cur.model);
|
|
437
|
+
document.getElementById('modalSelect').innerHTML =
|
|
438
|
+
pModels.map(m => `<option value="${m}" ${m === cur.model ? 'selected' : ''}>${m}</option>`).join('') +
|
|
439
|
+
(curInList ? '' : `<option value="${cur.model}" selected>${cur.model}</option>`) +
|
|
440
|
+
'<option value="__custom__">✏️ 自定义输入...</option>';
|
|
441
|
+
document.getElementById('modalCustom').classList.add('hidden');
|
|
442
|
+
document.getElementById('modalCustom').value = '';
|
|
443
|
+
|
|
444
|
+
const efforts = d.available_efforts || ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
445
|
+
document.getElementById('modalEffort').innerHTML = efforts.map(e => {
|
|
446
|
+
const ei = EFFORT_NAMES[e] || { label: e };
|
|
447
|
+
return `<option value="${e}" ${e === (cur.effort || 'high') ? 'selected' : ''}>${e} — ${ei.label}${ei.desc ? ' · ' + ei.desc : ''}</option>`;
|
|
448
|
+
}).join('');
|
|
449
|
+
|
|
450
|
+
const isReadonly = cur.source === 'template';
|
|
451
|
+
document.getElementById('modalSelect').disabled = isReadonly;
|
|
452
|
+
document.getElementById('modalEffort').disabled = isReadonly;
|
|
453
|
+
document.getElementById('resetBtn').classList.toggle('hidden', isReadonly);
|
|
454
|
+
if (isReadonly) {
|
|
455
|
+
document.getElementById('saveBtn').textContent = '模板默认不可编辑';
|
|
456
|
+
document.getElementById('saveBtn').className = 'px-4 py-2 text-xs font-medium rounded-lg bg-slate-100 text-slate-400 cursor-not-allowed';
|
|
457
|
+
} else {
|
|
458
|
+
document.getElementById('saveBtn').textContent = '保存配置';
|
|
459
|
+
document.getElementById('saveBtn').className = 'px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg shadow-sm transition-colors';
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
document.getElementById('modal').classList.remove('hidden');
|
|
463
|
+
document.getElementById('modal').classList.add('flex');
|
|
464
|
+
document.getElementById('saveBtn').disabled = isReadonly;
|
|
465
|
+
lucide.createIcons();
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function closeModal() {
|
|
469
|
+
document.getElementById('modal').classList.add('hidden');
|
|
470
|
+
document.getElementById('modal').classList.remove('flex');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function setSaveLoading(loading) {
|
|
474
|
+
saving = loading;
|
|
475
|
+
const btn = document.getElementById('saveBtn');
|
|
476
|
+
const reset = document.getElementById('resetBtn');
|
|
477
|
+
btn.disabled = loading;
|
|
478
|
+
reset.disabled = loading;
|
|
479
|
+
btn.textContent = loading ? '保存中...' : '保存配置';
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async function saveModel() {
|
|
483
|
+
if (saving) return;
|
|
484
|
+
if (cur && cur.source === 'template') { toast('模板默认智能体不可编辑', 'warn'); return; }
|
|
485
|
+
setSaveLoading(true);
|
|
486
|
+
try {
|
|
487
|
+
const sel = document.getElementById('modalSelect').value;
|
|
488
|
+
const m = sel === '__custom__' ? customModel || cur.model : sel;
|
|
489
|
+
const e = document.getElementById('modalEffort').value;
|
|
490
|
+
const resp = await fetch('/api/agents', {
|
|
491
|
+
method: 'POST',
|
|
492
|
+
headers: { 'Content-Type': 'application/json' },
|
|
493
|
+
body: JSON.stringify({ agent_id: cur.id, model: m, effort: e }),
|
|
494
|
+
});
|
|
495
|
+
const r = await resp.json();
|
|
496
|
+
closeModal();
|
|
497
|
+
cur.model = m;
|
|
498
|
+
cur.effort = e;
|
|
499
|
+
cur.is_custom = true;
|
|
500
|
+
document.getElementById('agentsGrid').innerHTML = render();
|
|
501
|
+
document.getElementById('statCustom').textContent = agents.filter(a => a.is_custom).length;
|
|
502
|
+
lucide.createIcons();
|
|
503
|
+
toast(r.file_synced
|
|
504
|
+
? '已保存并同步到源文件 · ' + cur.name + ' → ' + m + ' @' + e
|
|
505
|
+
: '已保存到数据库(源文件同步未成功)', r.file_synced ? 'success' : 'warn');
|
|
506
|
+
} catch (err) { toast('保存失败: ' + err.message, 'error'); }
|
|
507
|
+
finally { setSaveLoading(false); }
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
async function resetModel() {
|
|
511
|
+
if (saving) return;
|
|
512
|
+
if (cur && cur.source === 'template') { toast('模板默认智能体不可编辑', 'warn'); return; }
|
|
513
|
+
setSaveLoading(true);
|
|
514
|
+
try {
|
|
515
|
+
const resp = await fetch('/api/agents', {
|
|
516
|
+
method: 'POST',
|
|
517
|
+
headers: { 'Content-Type': 'application/json' },
|
|
518
|
+
body: JSON.stringify({ agent_id: cur.id, model: cur.defaultModel, effort: cur.defaultEffort || 'high' }),
|
|
519
|
+
});
|
|
520
|
+
const r = await resp.json();
|
|
521
|
+
closeModal();
|
|
522
|
+
cur.model = cur.defaultModel;
|
|
523
|
+
cur.effort = cur.defaultEffort || 'high';
|
|
524
|
+
cur.is_custom = false;
|
|
525
|
+
document.getElementById('agentsGrid').innerHTML = render();
|
|
526
|
+
document.getElementById('statCustom').textContent = agents.filter(a => a.is_custom).length;
|
|
527
|
+
lucide.createIcons();
|
|
528
|
+
toast(r.file_synced ? '已恢复默认 · ' + cur.name : '已恢复默认(文件同步未成功)', 'success');
|
|
529
|
+
} catch (err) { toast('重置失败: ' + err.message, 'error'); }
|
|
530
|
+
finally { setSaveLoading(false); }
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function toast(msg, type) {
|
|
534
|
+
const t = document.createElement('div');
|
|
535
|
+
const bg = type === 'error' ? 'bg-red-500' : type === 'warn' ? 'bg-amber-500' : 'bg-emerald-600';
|
|
536
|
+
t.className = 'toast-in px-4 py-3 rounded-xl text-sm font-medium shadow-lg text-white ' + bg;
|
|
537
|
+
t.textContent = msg;
|
|
538
|
+
document.getElementById('toastContainer').appendChild(t);
|
|
539
|
+
setTimeout(() => { t.style.opacity = '0'; t.style.transition = 'opacity .3s'; setTimeout(() => t.remove(), 300); }, 3500);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// MCP 平台接入状态轮询
|
|
543
|
+
async function loadMcpStatus() {
|
|
544
|
+
try {
|
|
545
|
+
const r = await fetch('/api/status');
|
|
546
|
+
const d = await r.json();
|
|
547
|
+
const platforms = d.connected_platforms || {};
|
|
548
|
+
const names = { claude: 'Claude Code', opencode: 'OpenCode', codex: 'Codex' };
|
|
549
|
+
let html = '';
|
|
550
|
+
for (const [key, label] of Object.entries(names)) {
|
|
551
|
+
const p = platforms[key];
|
|
552
|
+
const connected = p && p.connected;
|
|
553
|
+
const sessions = p ? p.active_sessions : 0;
|
|
554
|
+
html += `<div class="flex items-center gap-2 text-[11px] ${connected ? 'text-emerald-600' : 'text-slate-400'}">
|
|
555
|
+
<span class="w-1.5 h-1.5 rounded-full ${connected ? 'bg-emerald-500 live-dot' : 'bg-slate-600'} inline-block flex-shrink-0"></span>
|
|
556
|
+
${label} ${connected ? '<span class="text-emerald-500 font-mono">' + sessions + '</span> 会话' : ''}
|
|
557
|
+
</div>`;
|
|
558
|
+
}
|
|
559
|
+
document.getElementById('mcpStatusSidebar').innerHTML = html;
|
|
560
|
+
} catch {}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// 读取版本号
|
|
564
|
+
(async () => {
|
|
565
|
+
try {
|
|
566
|
+
const r = await fetch('/health');
|
|
567
|
+
const h = await r.json();
|
|
568
|
+
if (h && h.version) document.getElementById('verNum').textContent = 'v' + h.version;
|
|
569
|
+
} catch {}
|
|
570
|
+
})();
|
|
571
|
+
|
|
572
|
+
// 初始加载
|
|
573
|
+
load();
|
|
574
|
+
setInterval(loadMcpStatus, 5000);
|
|
575
|
+
loadMcpStatus();
|
|
576
|
+
lucide.createIcons();
|
|
577
|
+
</script>
|
|
578
|
+
</body>
|
|
579
|
+
</html>
|