opc-agent 4.2.3 → 4.2.4
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/dist/studio-ui/index.html +157 -157
- package/package.json +1 -1
|
@@ -177,11 +177,11 @@
|
|
|
177
177
|
<div class="app">
|
|
178
178
|
<!-- Sidebar -->
|
|
179
179
|
<nav class="sidebar" id="sidebar">
|
|
180
|
-
<div class="sidebar-logo"
|
|
180
|
+
<div class="sidebar-logo">⚡ OPC Studio</div>
|
|
181
181
|
|
|
182
182
|
<div class="sidebar-section">
|
|
183
183
|
<div class="sidebar-item active" onclick="navigate('assistant')" id="nav-assistant">
|
|
184
|
-
<span
|
|
184
|
+
<span>🧑💻</span><span>OPC 助手</span>
|
|
185
185
|
<span class="status-dot"></span>
|
|
186
186
|
</div>
|
|
187
187
|
</div>
|
|
@@ -189,32 +189,32 @@
|
|
|
189
189
|
<div class="sidebar-divider"></div>
|
|
190
190
|
|
|
191
191
|
<div class="sidebar-section">
|
|
192
|
-
<div class="sidebar-label"
|
|
192
|
+
<div class="sidebar-label">🤖 OPC Agent</div>
|
|
193
193
|
<div id="agent-list"></div>
|
|
194
|
-
<div class="sidebar-item action" onclick="navigate('new-agent')"><span
|
|
195
|
-
<div class="sidebar-item action" onclick="showToast('
|
|
196
|
-
<div class="sidebar-item" onclick="navigate('channels')"><span
|
|
194
|
+
<div class="sidebar-item action" onclick="navigate('new-agent')"><span>➕</span><span>新建 Agent</span></div>
|
|
195
|
+
<div class="sidebar-item action" onclick="showToast('群组功能即将推出')"><span>👥</span><span>新建群组</span></div>
|
|
196
|
+
<div class="sidebar-item" onclick="navigate('channels')"><span>📡</span><span>渠道配置</span></div>
|
|
197
197
|
</div>
|
|
198
198
|
|
|
199
199
|
<div class="sidebar-divider"></div>
|
|
200
200
|
|
|
201
201
|
<div class="sidebar-section">
|
|
202
|
-
<div class="sidebar-label"
|
|
203
|
-
<div class="sidebar-item" onclick="navigate('models')" id="nav-models"><span
|
|
202
|
+
<div class="sidebar-label">🧩 AgentKits</div>
|
|
203
|
+
<div class="sidebar-item" onclick="navigate('models')" id="nav-models"><span>🤖</span><span>模型配置</span></div>
|
|
204
204
|
</div>
|
|
205
205
|
|
|
206
206
|
<div class="sidebar-divider"></div>
|
|
207
207
|
|
|
208
208
|
<div class="sidebar-section">
|
|
209
|
-
<div class="sidebar-label"
|
|
210
|
-
<div class="sidebar-item" onclick="navigate('knowledge')" id="nav-knowledge"><span
|
|
209
|
+
<div class="sidebar-label">🧠 DeepBrain</div>
|
|
210
|
+
<div class="sidebar-item" onclick="navigate('knowledge')" id="nav-knowledge"><span>📖</span><span>知识库浏览</span></div>
|
|
211
211
|
</div>
|
|
212
212
|
|
|
213
213
|
<div class="sidebar-divider"></div>
|
|
214
214
|
|
|
215
215
|
<div class="sidebar-section">
|
|
216
|
-
<div class="sidebar-label"
|
|
217
|
-
<div class="sidebar-item" onclick="navigate('workstation')" id="nav-workstation"><span
|
|
216
|
+
<div class="sidebar-label">🖥️ Workstation</div>
|
|
217
|
+
<div class="sidebar-item" onclick="navigate('workstation')" id="nav-workstation"><span>📋</span><span>岗位模板库</span></div>
|
|
218
218
|
</div>
|
|
219
219
|
</nav>
|
|
220
220
|
|
|
@@ -224,30 +224,30 @@
|
|
|
224
224
|
<!-- Page: OPC Assistant Chat -->
|
|
225
225
|
<div class="page active" id="page-assistant">
|
|
226
226
|
<div class="page-header">
|
|
227
|
-
<span
|
|
227
|
+
<span>🧑💻</span> OPC 助手
|
|
228
228
|
<span class="status"></span>
|
|
229
|
-
<span class="status-text"
|
|
229
|
+
<span class="status-text">在线</span>
|
|
230
230
|
</div>
|
|
231
231
|
<div class="chat-container">
|
|
232
232
|
<div class="chat-main">
|
|
233
233
|
<div class="chat-messages" id="assistant-messages">
|
|
234
234
|
<div class="chat-msg agent">
|
|
235
235
|
<div class="bubble">
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
236
|
+
👋 Σ╜áσÑ╜!µêæµÿ» OPC 助手。<br><br>
|
|
237
|
+
我可以帮你:<br>
|
|
238
|
+
• 答疑解惑<br>
|
|
239
|
+
• 创建 AI Agent<br>
|
|
240
|
+
• Θàìτ╜«渠道(Telegram / 微信 / 飞书)<br>
|
|
241
|
+
• 处理关于 OPC 的任何问题<br><br>
|
|
242
|
+
Φ»òΦ»òΦ╖ƒµêæΦ»┤τé╣Σ╗ÇΣ╣êσɺ!
|
|
243
243
|
</div>
|
|
244
244
|
</div>
|
|
245
245
|
</div>
|
|
246
246
|
<div class="chat-input-bar">
|
|
247
|
-
<button class="icon-btn"
|
|
248
|
-
<button class="icon-btn"
|
|
249
|
-
<input type="text" id="assistant-input" placeholder="
|
|
250
|
-
<button class="send-btn" onclick="sendAssistantMsg()"
|
|
247
|
+
<button class="icon-btn">📎</button>
|
|
248
|
+
<button class="icon-btn">🎤</button>
|
|
249
|
+
<input type="text" id="assistant-input" placeholder="输入消息..." onkeydown="if(event.key==='Enter')sendAssistantMsg()">
|
|
250
|
+
<button class="send-btn" onclick="sendAssistantMsg()">发送</button>
|
|
251
251
|
</div>
|
|
252
252
|
</div>
|
|
253
253
|
</div>
|
|
@@ -256,29 +256,29 @@
|
|
|
256
256
|
<!-- Page: Agent Chat -->
|
|
257
257
|
<div class="page" id="page-agent-chat">
|
|
258
258
|
<div class="page-header">
|
|
259
|
-
<span id="agent-chat-icon"
|
|
259
|
+
<span id="agent-chat-icon">🤖</span>
|
|
260
260
|
<span id="agent-chat-name">Agent</span>
|
|
261
261
|
<span class="status"></span>
|
|
262
|
-
<span class="status-text"
|
|
263
|
-
<button class="settings-btn" onclick="toggleSettings()"
|
|
262
|
+
<span class="status-text">在线</span>
|
|
263
|
+
<button class="settings-btn" onclick="toggleSettings()">⚙️</button>
|
|
264
264
|
</div>
|
|
265
265
|
<div class="chat-container">
|
|
266
266
|
<div class="chat-main">
|
|
267
267
|
<div class="chat-messages" id="agent-messages"></div>
|
|
268
268
|
<div class="chat-input-bar">
|
|
269
|
-
<button class="icon-btn"
|
|
270
|
-
<button class="icon-btn"
|
|
271
|
-
<input type="text" id="agent-input" placeholder="
|
|
272
|
-
<button class="send-btn" onclick="sendAgentMsg()"
|
|
269
|
+
<button class="icon-btn">📎</button>
|
|
270
|
+
<button class="icon-btn">🎤</button>
|
|
271
|
+
<input type="text" id="agent-input" placeholder="输入消息..." onkeydown="if(event.key==='Enter')sendAgentMsg()">
|
|
272
|
+
<button class="send-btn" onclick="sendAgentMsg()">发送</button>
|
|
273
273
|
</div>
|
|
274
274
|
</div>
|
|
275
275
|
<div class="settings-panel" id="agent-settings">
|
|
276
276
|
<div class="settings-tabs" id="settings-tabs">
|
|
277
|
-
<div class="settings-tab active" onclick="switchSettingsTab('role')"
|
|
278
|
-
<div class="settings-tab" onclick="switchSettingsTab('model')"
|
|
279
|
-
<div class="settings-tab" onclick="switchSettingsTab('channel')"
|
|
280
|
-
<div class="settings-tab" onclick="switchSettingsTab('memory')"
|
|
281
|
-
<div class="settings-tab" onclick="switchSettingsTab('skills')"
|
|
277
|
+
<div class="settings-tab active" onclick="switchSettingsTab('role')">角色</div>
|
|
278
|
+
<div class="settings-tab" onclick="switchSettingsTab('model')">模型</div>
|
|
279
|
+
<div class="settings-tab" onclick="switchSettingsTab('channel')">渠道</div>
|
|
280
|
+
<div class="settings-tab" onclick="switchSettingsTab('memory')">记忆</div>
|
|
281
|
+
<div class="settings-tab" onclick="switchSettingsTab('skills')">技能</div>
|
|
282
282
|
</div>
|
|
283
283
|
<div class="settings-content" id="settings-content">
|
|
284
284
|
<!-- Filled by JS -->
|
|
@@ -289,106 +289,106 @@
|
|
|
289
289
|
|
|
290
290
|
<!-- Page: Models Config (AgentKits) -->
|
|
291
291
|
<div class="page" id="page-models">
|
|
292
|
-
<div class="page-header"><span
|
|
292
|
+
<div class="page-header"><span>🧩</span> AgentKits — 模型配置</div>
|
|
293
293
|
<div class="page-body" id="models-body">
|
|
294
294
|
<div style="background:var(--yellow-light);border:1px solid var(--yellow);border-radius:var(--radius-sm);padding:12px 16px;margin-bottom:16px;font-size:14px;">
|
|
295
|
-
<strong style="color:var(--yellow);"
|
|
295
|
+
<strong style="color:var(--yellow);">⚠️ ΦºäσêÖ:</strong>µ▓íµ£ë配通 API Key τÜä Provider,σà╢模型Σ╕ìΣ╝Üσç║τÄ░在 Agent τÜä模型ΘÇëµï⌐σêùΦí¿Σ╕¡。
|
|
296
296
|
</div>
|
|
297
|
-
<div id="models-content"
|
|
297
|
+
<div id="models-content">加载中...</div>
|
|
298
298
|
</div>
|
|
299
299
|
</div>
|
|
300
300
|
|
|
301
301
|
<!-- Page: Knowledge (DeepBrain) -->
|
|
302
302
|
<div class="page" id="page-knowledge">
|
|
303
|
-
<div class="page-header"><span
|
|
303
|
+
<div class="page-header"><span>🧠</span> DeepBrain — τƒÑΦ»åσ║ô</div>
|
|
304
304
|
<div class="page-body">
|
|
305
305
|
<div class="upload-zone" onclick="document.getElementById('file-upload').click()">
|
|
306
|
-
<div class="icon"
|
|
307
|
-
<div class="title"
|
|
308
|
-
<div class="desc"
|
|
309
|
-
<div style="font-size:12px;color:var(--text-dim);margin-top:8px;">DeepBrain
|
|
306
|
+
<div class="icon">📂</div>
|
|
307
|
+
<div class="title">µïûσàѵûçµíú,Φç¬σè¿σêåτ▒╗</div>
|
|
308
|
+
<div class="desc">支持 PDF · Word · TXT · Markdown · 网页链接</div>
|
|
309
|
+
<div style="font-size:12px;color:var(--text-dim);margin-top:8px;">DeepBrain Φç¬σè¿σêåµ₧Éσåàσ«╣,σ╜ÆσàÑΦíîΣ╕Ü / 岗位 / σ╖ÑΣ╜ìσ▒é</div>
|
|
310
310
|
<input type="file" id="file-upload" style="display:none" multiple accept=".pdf,.doc,.docx,.txt,.md">
|
|
311
311
|
</div>
|
|
312
312
|
|
|
313
313
|
<div class="stat-row">
|
|
314
|
-
<div class="stat-card" style="background:var(--blue-light);"><div class="num" style="color:var(--blue);" id="kb-total">0</div><div class="label"
|
|
315
|
-
<div class="stat-card" style="background:var(--green-light);"><div class="num" style="color:var(--green);" id="kb-evolve">0</div><div class="label"
|
|
316
|
-
<div class="stat-card" style="background:var(--yellow-light);"><div class="num" style="color:var(--yellow);" id="kb-docs">0</div><div class="label"
|
|
314
|
+
<div class="stat-card" style="background:var(--blue-light);"><div class="num" style="color:var(--blue);" id="kb-total">0</div><div class="label">知识条目</div></div>
|
|
315
|
+
<div class="stat-card" style="background:var(--green-light);"><div class="num" style="color:var(--green);" id="kb-evolve">0</div><div class="label">进化次数</div></div>
|
|
316
|
+
<div class="stat-card" style="background:var(--yellow-light);"><div class="num" style="color:var(--yellow);" id="kb-docs">0</div><div class="label">文档已导入</div></div>
|
|
317
317
|
</div>
|
|
318
318
|
|
|
319
|
-
<input type="text" class="search-input" placeholder="
|
|
319
|
+
<input type="text" class="search-input" placeholder="🔍 搜索知识..." oninput="searchKnowledge(this.value)">
|
|
320
320
|
|
|
321
321
|
<div id="knowledge-layers">
|
|
322
322
|
<div class="knowledge-card industry">
|
|
323
|
-
<div style="font-weight:600;margin-bottom:4px;"
|
|
324
|
-
<div style="font-size:13px;color:var(--text-muted);"
|
|
323
|
+
<div style="font-weight:600;margin-bottom:4px;">🏢 行业知识</div>
|
|
324
|
+
<div style="font-size:13px;color:var(--text-muted);">加载中...</div>
|
|
325
325
|
</div>
|
|
326
326
|
<div class="knowledge-card job">
|
|
327
|
-
<div style="font-weight:600;margin-bottom:4px;"
|
|
328
|
-
<div style="font-size:13px;color:var(--text-muted);"
|
|
327
|
+
<div style="font-weight:600;margin-bottom:4px;">👨 岗位知识</div>
|
|
328
|
+
<div style="font-size:13px;color:var(--text-muted);">加载中...</div>
|
|
329
329
|
</div>
|
|
330
330
|
<div class="knowledge-card station">
|
|
331
|
-
<div style="font-weight:600;margin-bottom:4px;"
|
|
332
|
-
<div style="font-size:13px;color:var(--text-muted);"
|
|
331
|
+
<div style="font-weight:600;margin-bottom:4px;">🖥️ 工位知识</div>
|
|
332
|
+
<div style="font-size:13px;color:var(--text-muted);">加载中...</div>
|
|
333
333
|
</div>
|
|
334
334
|
</div>
|
|
335
335
|
|
|
336
336
|
<div style="font-size:12px;color:var(--text-dim);margin-top:16px;text-align:center;">
|
|
337
|
-
|
|
337
|
+
💡 µëǵ£ë Agent Φç¬σè¿Σ╗ÄτƒÑΦ»åσ║ô recall τ¢╕σà│τƒÑΦ»å,µùáΘ£Çµëïσè¿σêåΘàì
|
|
338
338
|
</div>
|
|
339
339
|
</div>
|
|
340
340
|
</div>
|
|
341
341
|
|
|
342
342
|
<!-- Page: Workstation -->
|
|
343
343
|
<div class="page" id="page-workstation">
|
|
344
|
-
<div class="page-header"><span
|
|
344
|
+
<div class="page-header"><span>🖥️</span> Workstation — 岗位模板库</div>
|
|
345
345
|
<div class="page-body">
|
|
346
|
-
<input type="text" class="search-input" placeholder="
|
|
346
|
+
<input type="text" class="search-input" placeholder="🔍 搜索模板..." oninput="searchTemplates(this.value)">
|
|
347
347
|
<div class="breadcrumb" id="ws-breadcrumb">
|
|
348
|
-
<a onclick="wsNavigate('root')"
|
|
348
|
+
<a onclick="wsNavigate('root')">全部行业</a>
|
|
349
349
|
</div>
|
|
350
|
-
<div id="ws-content"
|
|
350
|
+
<div id="ws-content">加载中...</div>
|
|
351
351
|
</div>
|
|
352
352
|
</div>
|
|
353
353
|
|
|
354
354
|
<!-- Page: Channels -->
|
|
355
355
|
<div class="page" id="page-channels">
|
|
356
|
-
<div class="page-header"><span
|
|
356
|
+
<div class="page-header"><span>📡</span> 渠道配置</div>
|
|
357
357
|
<div class="page-body" id="channels-body">
|
|
358
|
-
<div id="channels-content"
|
|
358
|
+
<div id="channels-content">加载中...</div>
|
|
359
359
|
</div>
|
|
360
360
|
</div>
|
|
361
361
|
|
|
362
362
|
<!-- Page: New Agent -->
|
|
363
363
|
<div class="page" id="page-new-agent">
|
|
364
|
-
<div class="page-header"><span
|
|
364
|
+
<div class="page-header"><span>➕</span> 新建 Agent</div>
|
|
365
365
|
<div class="page-body">
|
|
366
366
|
<div class="card" style="max-width:600px;">
|
|
367
367
|
<div class="form-group">
|
|
368
|
-
<label class="form-label"
|
|
369
|
-
<input class="form-input" id="new-agent-name" placeholder="
|
|
368
|
+
<label class="form-label">名称</label>
|
|
369
|
+
<input class="form-input" id="new-agent-name" placeholder="给你的 Agent 取个名字">
|
|
370
370
|
</div>
|
|
371
371
|
<div class="form-group">
|
|
372
|
-
<label class="form-label"
|
|
373
|
-
<input class="form-input" id="new-agent-desc" placeholder="这个 Agent
|
|
372
|
+
<label class="form-label">描述</label>
|
|
373
|
+
<input class="form-input" id="new-agent-desc" placeholder="这个 Agent 做什么?">
|
|
374
374
|
</div>
|
|
375
375
|
<div class="form-group">
|
|
376
|
-
<label class="form-label"
|
|
377
|
-
<input class="form-input" id="new-agent-icon" placeholder="
|
|
376
|
+
<label class="form-label">图标</label>
|
|
377
|
+
<input class="form-input" id="new-agent-icon" placeholder="🤖" value="🤖" style="width:60px;">
|
|
378
378
|
</div>
|
|
379
379
|
<div class="form-group">
|
|
380
380
|
<label class="form-label">System Prompt</label>
|
|
381
|
-
<textarea class="form-input form-textarea" id="new-agent-prompt" placeholder="
|
|
381
|
+
<textarea class="form-input form-textarea" id="new-agent-prompt" placeholder="描述 Agent 的角色和行为..."></textarea>
|
|
382
382
|
</div>
|
|
383
383
|
<div class="form-group">
|
|
384
|
-
<label class="form-label"
|
|
384
|
+
<label class="form-label">模型</label>
|
|
385
385
|
<select class="form-input" id="new-agent-model">
|
|
386
|
-
<option value=""
|
|
386
|
+
<option value="">使用全局默认</option>
|
|
387
387
|
</select>
|
|
388
388
|
</div>
|
|
389
389
|
<div style="display:flex;gap:8px;margin-top:16px;">
|
|
390
|
-
<button class="btn-primary" onclick="createAgent()"
|
|
391
|
-
<button class="btn-secondary" onclick="navigate('assistant')"
|
|
390
|
+
<button class="btn-primary" onclick="createAgent()">创建 Agent</button>
|
|
391
|
+
<button class="btn-secondary" onclick="navigate('assistant')">取消</button>
|
|
392
392
|
</div>
|
|
393
393
|
</div>
|
|
394
394
|
</div>
|
|
@@ -421,7 +421,7 @@
|
|
|
421
421
|
} else if (page === 'agent' && data) {
|
|
422
422
|
currentAgentId = data.id;
|
|
423
423
|
document.getElementById('page-agent-chat').classList.add('active');
|
|
424
|
-
document.getElementById('agent-chat-icon').textContent = data.icon || '
|
|
424
|
+
document.getElementById('agent-chat-icon').textContent = data.icon || '🤖';
|
|
425
425
|
document.getElementById('agent-chat-name').textContent = data.name || data.id;
|
|
426
426
|
const navItem = document.querySelector(`[data-agent-id="${data.id}"]`);
|
|
427
427
|
if (navItem) navItem.classList.add('active');
|
|
@@ -462,12 +462,12 @@
|
|
|
462
462
|
function renderAgentList() {
|
|
463
463
|
const el = document.getElementById('agent-list');
|
|
464
464
|
if (!agents.length) {
|
|
465
|
-
el.innerHTML = '<div style="padding:4px 12px;font-size:12px;color:var(--text-dim);font-style:italic;"
|
|
465
|
+
el.innerHTML = '<div style="padding:4px 12px;font-size:12px;color:var(--text-dim);font-style:italic;">还没有 Agent</div>';
|
|
466
466
|
return;
|
|
467
467
|
}
|
|
468
468
|
el.innerHTML = agents.map(a => `
|
|
469
469
|
<div class="sidebar-item${currentAgentId === a.id ? ' active' : ''}" data-agent-id="${a.id}" onclick="navigate('agent', ${JSON.stringify(a).replace(/"/g, '"')})">
|
|
470
|
-
<span>${a.icon || '
|
|
470
|
+
<span>${a.icon || '🤖'}</span><span>${a.name || a.id}</span>
|
|
471
471
|
</div>
|
|
472
472
|
`).join('');
|
|
473
473
|
}
|
|
@@ -564,7 +564,7 @@
|
|
|
564
564
|
}
|
|
565
565
|
}
|
|
566
566
|
}
|
|
567
|
-
if (!fullText) bubble.innerHTML = '<em style="color:var(--text-dim);"
|
|
567
|
+
if (!fullText) bubble.innerHTML = '<em style="color:var(--text-dim);">(无响应)</em>';
|
|
568
568
|
} else {
|
|
569
569
|
// JSON response
|
|
570
570
|
const data = await res.json();
|
|
@@ -583,7 +583,7 @@
|
|
|
583
583
|
if (ti) ti.remove();
|
|
584
584
|
const errDiv = document.createElement('div');
|
|
585
585
|
errDiv.className = 'chat-msg agent';
|
|
586
|
-
errDiv.innerHTML = `<div class="bubble" style="color:var(--red);"
|
|
586
|
+
errDiv.innerHTML = `<div class="bubble" style="color:var(--red);">⚠️ 发送失败: ${escapeHtml(e.message)}</div>`;
|
|
587
587
|
messagesEl.appendChild(errDiv);
|
|
588
588
|
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
589
589
|
}
|
|
@@ -600,7 +600,7 @@
|
|
|
600
600
|
`<div class="chat-msg ${m.role === 'user' ? 'user' : 'agent'}"><div class="bubble">${m.role === 'user' ? escapeHtml(m.content) : m.content}</div></div>`
|
|
601
601
|
).join('');
|
|
602
602
|
} else {
|
|
603
|
-
el.innerHTML = `<div class="chat-msg agent"><div class="bubble"
|
|
603
|
+
el.innerHTML = `<div class="chat-msg agent"><div class="bubble">你好!有什么可以帮你的吗?</div></div>`;
|
|
604
604
|
}
|
|
605
605
|
el.scrollTop = el.scrollHeight;
|
|
606
606
|
}
|
|
@@ -635,28 +635,28 @@
|
|
|
635
635
|
|
|
636
636
|
if (tab === 'role') {
|
|
637
637
|
el.innerHTML = `
|
|
638
|
-
<div class="form-group"><label class="form-label"
|
|
639
|
-
<div class="form-group"><label class="form-label"
|
|
640
|
-
<div class="form-group"><label class="form-label"
|
|
638
|
+
<div class="form-group"><label class="form-label">名称</label><input class="form-input" id="s-name" value="${escapeAttr(cfg.name || agent.name || '')}"></div>
|
|
639
|
+
<div class="form-group"><label class="form-label">描述</label><input class="form-input" id="s-desc" value="${escapeAttr(cfg.description || '')}"></div>
|
|
640
|
+
<div class="form-group"><label class="form-label">图标</label><input class="form-input" id="s-icon" value="${escapeAttr(cfg.icon || agent.icon || '🤖')}" style="width:60px"></div>
|
|
641
641
|
<div class="form-group"><label class="form-label">System Prompt</label><textarea class="form-input form-textarea" id="s-prompt">${escapeHtml(cfg.systemPrompt || '')}</textarea></div>
|
|
642
|
-
<button class="btn-primary" onclick="saveAgentSettings()"
|
|
642
|
+
<button class="btn-primary" onclick="saveAgentSettings()">保存</button>
|
|
643
643
|
`;
|
|
644
644
|
} else if (tab === 'model') {
|
|
645
|
-
el.innerHTML = '<div style="color:var(--text-dim);"
|
|
645
|
+
el.innerHTML = '<div style="color:var(--text-dim);">加载可用模型...</div>';
|
|
646
646
|
loadModelTab(agent);
|
|
647
647
|
} else if (tab === 'memory') {
|
|
648
|
-
el.innerHTML = '<div style="color:var(--text-dim);"
|
|
648
|
+
el.innerHTML = '<div style="color:var(--text-dim);">加载记忆...</div>';
|
|
649
649
|
loadMemoryTab(agent.id || currentAgentId);
|
|
650
650
|
} else if (tab === 'skills') {
|
|
651
651
|
const skills = cfg.skills || [];
|
|
652
652
|
el.innerHTML = `
|
|
653
|
-
<div class="form-label"
|
|
654
|
-
${skills.length ? skills.map(s => `<div class="tag tag-blue">${s}</div>`).join(' ') : '<div style="color:var(--text-dim);font-size:13px;"
|
|
653
|
+
<div class="form-label">已安装技能</div>
|
|
654
|
+
${skills.length ? skills.map(s => `<div class="tag tag-blue">${s}</div>`).join(' ') : '<div style="color:var(--text-dim);font-size:13px;">暂无技能</div>'}
|
|
655
655
|
`;
|
|
656
656
|
} else if (tab === 'channel') {
|
|
657
657
|
el.innerHTML = `
|
|
658
|
-
<div class="form-label">Agent
|
|
659
|
-
<div style="color:var(--text-dim);font-size:13px;"
|
|
658
|
+
<div class="form-label">Agent 渠道</div>
|
|
659
|
+
<div style="color:var(--text-dim);font-size:13px;">渠道在全局配置中管理 → <a style="color:var(--blue);cursor:pointer;" onclick="navigate('channels')">前往渠道配置</a></div>
|
|
660
660
|
`;
|
|
661
661
|
}
|
|
662
662
|
}
|
|
@@ -671,7 +671,7 @@
|
|
|
671
671
|
const models = await modelsRes.json();
|
|
672
672
|
const ollama = ollamaRes ? await ollamaRes.json() : { available: false, models: [] };
|
|
673
673
|
|
|
674
|
-
let options = '<option value=""
|
|
674
|
+
let options = '<option value="">使用全局默认</option>';
|
|
675
675
|
if (ollama.available && ollama.models) {
|
|
676
676
|
ollama.models.filter(m => !m.name?.includes('embed')).forEach(m => {
|
|
677
677
|
options += `<option value="ollama:${m.name}" ${agent.model === `ollama:${m.name}` ? 'selected' : ''}>${m.name} (Ollama)</option>`;
|
|
@@ -690,13 +690,13 @@
|
|
|
690
690
|
|
|
691
691
|
el.innerHTML = `
|
|
692
692
|
<div class="form-group">
|
|
693
|
-
<label class="form-label"
|
|
693
|
+
<label class="form-label">选择模型</label>
|
|
694
694
|
<select class="form-input" id="s-model">${options}</select>
|
|
695
695
|
</div>
|
|
696
|
-
<button class="btn-primary" onclick="saveAgentSettings()"
|
|
696
|
+
<button class="btn-primary" onclick="saveAgentSettings()">保存</button>
|
|
697
697
|
`;
|
|
698
698
|
} catch (e) {
|
|
699
|
-
el.innerHTML = `<div style="color:var(--red);"
|
|
699
|
+
el.innerHTML = `<div style="color:var(--red);">加载失败: ${e.message}</div>`;
|
|
700
700
|
}
|
|
701
701
|
}
|
|
702
702
|
|
|
@@ -707,13 +707,13 @@
|
|
|
707
707
|
const data = await res.json();
|
|
708
708
|
const items = data.memories || data.items || [];
|
|
709
709
|
el.innerHTML = `
|
|
710
|
-
<div class="form-label">Agent
|
|
710
|
+
<div class="form-label">Agent 记忆 (${items.length} 条)</div>
|
|
711
711
|
${items.length ? items.slice(0, 20).map(m =>
|
|
712
712
|
`<div style="background:var(--white);border:1px solid var(--border);border-radius:6px;padding:8px;margin:4px 0;font-size:12px;">${escapeHtml(typeof m === 'string' ? m : m.content || JSON.stringify(m))}</div>`
|
|
713
|
-
).join('') : '<div style="color:var(--text-dim);font-size:13px;"
|
|
713
|
+
).join('') : '<div style="color:var(--text-dim);font-size:13px;">暂无记忆</div>'}
|
|
714
714
|
`;
|
|
715
715
|
} catch (e) {
|
|
716
|
-
el.innerHTML = `<div style="color:var(--text-dim);font-size:13px;"
|
|
716
|
+
el.innerHTML = `<div style="color:var(--text-dim);font-size:13px;">暂无记忆数据</div>`;
|
|
717
717
|
}
|
|
718
718
|
}
|
|
719
719
|
|
|
@@ -736,9 +736,9 @@
|
|
|
736
736
|
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
|
737
737
|
body: JSON.stringify(body)
|
|
738
738
|
});
|
|
739
|
-
showToast('
|
|
739
|
+
showToast('✅ 已保存');
|
|
740
740
|
loadAgents();
|
|
741
|
-
} catch (e) { showToast('
|
|
741
|
+
} catch (e) { showToast('❌ 保存失败: ' + e.message); }
|
|
742
742
|
}
|
|
743
743
|
|
|
744
744
|
// ======================== Models Config ========================
|
|
@@ -757,27 +757,27 @@
|
|
|
757
757
|
// Ollama
|
|
758
758
|
html += `<div class="card">
|
|
759
759
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
|
760
|
-
<span style="font-size:20px;"
|
|
761
|
-
${ollama.available ? '<span class="status-dot" style="width:8px;height:8px;border-radius:50%;background:var(--green);display:inline-block;"></span><span style="font-size:12px;color:var(--green);">自动检测
|
|
760
|
+
<span style="font-size:20px;">🦙</span><strong>µ£¼σ£░模型(Ollama)</strong>
|
|
761
|
+
${ollama.available ? '<span class="status-dot" style="width:8px;height:8px;border-radius:50%;background:var(--green);display:inline-block;"></span><span style="font-size:12px;color:var(--green);">Φç¬σ迵úǵ╡ï · µùá需 Key</span>' : '<span style="font-size:12px;color:var(--red);">未检测到</span>'}
|
|
762
762
|
</div>
|
|
763
763
|
${ollama.available && ollama.models?.length ? `<table style="width:100%;font-size:13px;border-collapse:collapse;">
|
|
764
|
-
<tr><th style="text-align:left;padding:6px;border-bottom:1px solid var(--border);"
|
|
765
|
-
${ollama.models.map(m => `<tr><td style="padding:6px;">${m.name}</td><td style="padding:6px;">${formatSize(m.size)}</td><td style="padding:6px;color:var(--green);"
|
|
766
|
-
</table>` : '<div style="font-size:13px;color:var(--text-muted);"
|
|
764
|
+
<tr><th style="text-align:left;padding:6px;border-bottom:1px solid var(--border);">模型</th><th style="text-align:left;padding:6px;border-bottom:1px solid var(--border);">大小</th><th style="text-align:left;padding:6px;border-bottom:1px solid var(--border);">状态</th></tr>
|
|
765
|
+
${ollama.models.map(m => `<tr><td style="padding:6px;">${m.name}</td><td style="padding:6px;">${formatSize(m.size)}</td><td style="padding:6px;color:var(--green);">✅ 可用</td></tr>`).join('')}
|
|
766
|
+
</table>` : '<div style="font-size:13px;color:var(--text-muted);">请安装 Ollama 并拉取模型</div>'}
|
|
767
767
|
</div>`;
|
|
768
768
|
|
|
769
769
|
// Cloud providers
|
|
770
770
|
const cloudProviders = [
|
|
771
|
-
{ id: 'openai', name: 'OpenAI', models: 'GPT-4o
|
|
772
|
-
{ id: 'deepseek', name: 'DeepSeek', models: 'DeepSeek-V3
|
|
773
|
-
{ id: 'anthropic', name: 'Anthropic', models: 'Claude Opus
|
|
774
|
-
{ id: 'google', name: 'Google Gemini', models: 'Gemini Pro
|
|
771
|
+
{ id: 'openai', name: 'OpenAI', models: 'GPT-4o · GPT-4o-mini · o1 · o3' },
|
|
772
|
+
{ id: 'deepseek', name: 'DeepSeek', models: 'DeepSeek-V3 · DeepSeek-R1' },
|
|
773
|
+
{ id: 'anthropic', name: 'Anthropic', models: 'Claude Opus · Sonnet · Haiku' },
|
|
774
|
+
{ id: 'google', name: 'Google Gemini', models: 'Gemini Pro · Flash · Ultra' },
|
|
775
775
|
];
|
|
776
776
|
const providers = modelsConfig.providers || {};
|
|
777
777
|
|
|
778
778
|
html += `<div class="card">
|
|
779
779
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
|
780
|
-
<span style="font-size:20px;">☁️</span><strong
|
|
780
|
+
<span style="font-size:20px;">Γÿü∩╕Å</span><strong>云端 API — 填 Key → 验证 → 解锁模型</strong>
|
|
781
781
|
</div>
|
|
782
782
|
<div class="card-grid">
|
|
783
783
|
${cloudProviders.map(p => {
|
|
@@ -785,11 +785,11 @@
|
|
|
785
785
|
const verified = cfg.verified;
|
|
786
786
|
return `<div class="provider-card${verified ? ' verified' : ''}">
|
|
787
787
|
<div class="provider-name">${p.name} <span style="width:8px;height:8px;border-radius:50%;background:${verified ? 'var(--green)' : 'var(--red)'};display:inline-block;"></span></div>
|
|
788
|
-
<div class="provider-status" style="color:${verified ? 'var(--green)' : 'var(--red)'};">${verified ? '
|
|
789
|
-
<div class="provider-models">${verified ? '
|
|
788
|
+
<div class="provider-status" style="color:${verified ? 'var(--green)' : 'var(--red)'};">${verified ? '✅ 已验证' : '❌ 未配置'}</div>
|
|
789
|
+
<div class="provider-models">${verified ? '解锁:' : ''}${p.models}${verified ? '' : '(需配 Key)'}</div>
|
|
790
790
|
<div class="key-row">
|
|
791
|
-
<input class="key-input" id="key-${p.id}" type="password" placeholder="
|
|
792
|
-
<button class="verify-btn" onclick="verifyProvider('${p.id}')"
|
|
791
|
+
<input class="key-input" id="key-${p.id}" type="password" placeholder="输入 API Key..." value="${cfg.apiKey ? '••••••••' : ''}">
|
|
792
|
+
<button class="verify-btn" onclick="verifyProvider('${p.id}')">验证</button>
|
|
793
793
|
</div>
|
|
794
794
|
</div>`;
|
|
795
795
|
}).join('')}
|
|
@@ -799,27 +799,27 @@
|
|
|
799
799
|
// Agent model assignment
|
|
800
800
|
html += `<div class="card">
|
|
801
801
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
|
802
|
-
<span style="font-size:20px;"
|
|
803
|
-
<span style="font-size:12px;color:var(--text-muted);"
|
|
802
|
+
<span style="font-size:20px;">🤖</span><strong>Agent 模型分配</strong>
|
|
803
|
+
<span style="font-size:12px;color:var(--text-muted);">(σŬµÿ╛τñ║σ╖▓配通τÜä模型)</span>
|
|
804
804
|
</div>
|
|
805
805
|
${agents.length ? `<table style="width:100%;font-size:13px;border-collapse:collapse;">
|
|
806
|
-
<tr><th style="text-align:left;padding:6px;border-bottom:1px solid var(--border);">Agent</th><th style="text-align:left;padding:6px;border-bottom:1px solid var(--border);"
|
|
807
|
-
${agents.map(a => `<tr><td style="padding:6px;">${a.icon || '
|
|
808
|
-
</table>` : '<div style="font-size:13px;color:var(--text-dim);"
|
|
806
|
+
<tr><th style="text-align:left;padding:6px;border-bottom:1px solid var(--border);">Agent</th><th style="text-align:left;padding:6px;border-bottom:1px solid var(--border);">模型</th><th style="text-align:left;padding:6px;border-bottom:1px solid var(--border);">来源</th></tr>
|
|
807
|
+
${agents.map(a => `<tr><td style="padding:6px;">${a.icon || '🤖'} ${a.name || a.id}</td><td style="padding:6px;">${a.model || modelsConfig.chatModel || 'auto'}</td><td style="padding:6px;"><span class="tag ${a.model ? 'tag-green' : 'tag-blue'}">${a.model ? 'Agent 覆盖' : '全局默认'}</span></td></tr>`).join('')}
|
|
808
|
+
</table>` : '<div style="font-size:13px;color:var(--text-dim);">暂无 Agent</div>'}
|
|
809
809
|
</div>`;
|
|
810
810
|
|
|
811
811
|
el.innerHTML = html;
|
|
812
812
|
} catch (e) {
|
|
813
|
-
el.innerHTML = `<div style="color:var(--red);"
|
|
813
|
+
el.innerHTML = `<div style="color:var(--red);">加载失败: ${e.message}</div>`;
|
|
814
814
|
}
|
|
815
815
|
}
|
|
816
816
|
|
|
817
817
|
async function verifyProvider(providerId) {
|
|
818
818
|
const keyInput = document.getElementById(`key-${providerId}`);
|
|
819
819
|
const apiKey = keyInput.value;
|
|
820
|
-
if (!apiKey || apiKey === '
|
|
820
|
+
if (!apiKey || apiKey === '••••••••') { showToast('请输入 API Key'); return; }
|
|
821
821
|
|
|
822
|
-
showToast('
|
|
822
|
+
showToast('验证中...');
|
|
823
823
|
try {
|
|
824
824
|
const res = await fetch(`${API}/api/settings/models/test`, {
|
|
825
825
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -832,12 +832,12 @@
|
|
|
832
832
|
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
|
833
833
|
body: JSON.stringify({ providers: { ...modelsConfig.providers, [providerId]: { apiKey, verified: true, models: data.models || [] } } })
|
|
834
834
|
});
|
|
835
|
-
showToast(
|
|
835
|
+
showToast(`✅ ${providerId} 验证成功`);
|
|
836
836
|
loadModelsConfig();
|
|
837
837
|
} else {
|
|
838
|
-
showToast(
|
|
838
|
+
showToast(`❌ 验证失败: ${data.error || '无效的 API Key'}`);
|
|
839
839
|
}
|
|
840
|
-
} catch (e) { showToast(
|
|
840
|
+
} catch (e) { showToast(`❌ 验证失败: ${e.message}`); }
|
|
841
841
|
}
|
|
842
842
|
|
|
843
843
|
// ======================== Knowledge ========================
|
|
@@ -861,7 +861,7 @@
|
|
|
861
861
|
const data = await res.json();
|
|
862
862
|
renderWorkstation(data);
|
|
863
863
|
} catch (e) {
|
|
864
|
-
document.getElementById('ws-content').innerHTML = `<div style="color:var(--red);"
|
|
864
|
+
document.getElementById('ws-content').innerHTML = `<div style="color:var(--red);">加载失败: ${e.message}</div>`;
|
|
865
865
|
}
|
|
866
866
|
}
|
|
867
867
|
|
|
@@ -879,12 +879,12 @@
|
|
|
879
879
|
const industries = data.industries || [];
|
|
880
880
|
|
|
881
881
|
// Breadcrumb
|
|
882
|
-
let bcHtml = '<a onclick="wsNavigate(\'root\')"
|
|
882
|
+
let bcHtml = '<a onclick="wsNavigate(\'root\')">全部行业</a>';
|
|
883
883
|
if (wsState.industry) {
|
|
884
|
-
bcHtml += `<span class="sep"
|
|
884
|
+
bcHtml += `<span class="sep">›</span><a onclick="wsNavigate('industry','${wsState.industry}')">${wsState.industry}</a>`;
|
|
885
885
|
}
|
|
886
886
|
if (wsState.job) {
|
|
887
|
-
bcHtml += `<span class="sep"
|
|
887
|
+
bcHtml += `<span class="sep">›</span><span style="color:var(--text);font-weight:500;">${wsState.job}</span>`;
|
|
888
888
|
}
|
|
889
889
|
bc.innerHTML = bcHtml;
|
|
890
890
|
|
|
@@ -895,24 +895,24 @@
|
|
|
895
895
|
|
|
896
896
|
el.innerHTML = `
|
|
897
897
|
<div class="card" style="border-left:4px solid var(--blue);">
|
|
898
|
-
<div style="font-weight:600;margin-bottom:12px;"
|
|
898
|
+
<div style="font-weight:600;margin-bottom:12px;">🏢 选择行业</div>
|
|
899
899
|
<div class="filter-row">
|
|
900
900
|
${uniqueIndustries.map(i => `<div class="filter-tag" onclick="wsNavigate('industry','${i}')">${i}</div>`).join('')}
|
|
901
|
-
${!uniqueIndustries.length ? '<div style="color:var(--text-dim);font-size:13px;"
|
|
901
|
+
${!uniqueIndustries.length ? '<div style="color:var(--text-dim);font-size:13px;">暂无模板</div>' : ''}
|
|
902
902
|
</div>
|
|
903
903
|
</div>
|
|
904
904
|
`;
|
|
905
905
|
} else if (wsState.level === 'industry') {
|
|
906
906
|
// Show jobs for selected industry
|
|
907
907
|
const filtered = templates.filter(t => (t.industry || t.industryZh) === wsState.industry);
|
|
908
|
-
const jobs = [...new Set(filtered.map(t => t.tags?.[1] || t.function || '
|
|
908
|
+
const jobs = [...new Set(filtered.map(t => t.tags?.[1] || t.function || '通用').filter(Boolean))];
|
|
909
909
|
|
|
910
910
|
el.innerHTML = `
|
|
911
911
|
<div class="card" style="border-left:4px solid var(--green);">
|
|
912
|
-
<div style="font-weight:600;margin-bottom:12px;"
|
|
912
|
+
<div style="font-weight:600;margin-bottom:12px;">👨 ${wsState.industry} — 选择岗位</div>
|
|
913
913
|
<div class="filter-row">
|
|
914
914
|
${jobs.map(j => `<div class="filter-tag" onclick="wsNavigate('job','${j}')">${j}</div>`).join('')}
|
|
915
|
-
${!jobs.length ? '<div style="color:var(--text-dim);font-size:13px;"
|
|
915
|
+
${!jobs.length ? '<div style="color:var(--text-dim);font-size:13px;">暂无岗位</div>' : ''}
|
|
916
916
|
</div>
|
|
917
917
|
</div>
|
|
918
918
|
<div class="card-grid">
|
|
@@ -923,18 +923,18 @@
|
|
|
923
923
|
// Show specific templates
|
|
924
924
|
const filtered = templates.filter(t =>
|
|
925
925
|
(t.industry || t.industryZh) === wsState.industry &&
|
|
926
|
-
(t.tags?.[1] || t.function || '
|
|
926
|
+
(t.tags?.[1] || t.function || '通用') === wsState.job
|
|
927
927
|
);
|
|
928
928
|
el.innerHTML = `
|
|
929
929
|
<div class="card" style="border-left:4px solid var(--yellow);">
|
|
930
|
-
<div style="font-weight:600;margin-bottom:12px;"
|
|
930
|
+
<div style="font-weight:600;margin-bottom:12px;">🖥️ ${wsState.industry} › ${wsState.job} — 工位模板</div>
|
|
931
931
|
<div class="card-grid" style="margin:0;">
|
|
932
932
|
${filtered.map(t => renderTemplateCard(t)).join('')}
|
|
933
|
-
${!filtered.length ? '<div style="color:var(--text-dim);font-size:13px;"
|
|
933
|
+
${!filtered.length ? '<div style="color:var(--text-dim);font-size:13px;">暂无模板</div>' : ''}
|
|
934
934
|
</div>
|
|
935
935
|
</div>
|
|
936
936
|
<div style="background:var(--blue-light);border:1px solid var(--blue);border-radius:var(--radius-sm);padding:16px;margin-top:16px;font-size:13px;">
|
|
937
|
-
<strong style="color:var(--blue);"
|
|
937
|
+
<strong style="color:var(--blue);">💡 Skill Φç¬σè¿σÅáσèá:</strong>ΘÇëµï⌐工位模板σÉÄ,Agent Φç¬σè¿ΦÄ╖σ╛ùΦíîΣ╕Ü + 岗位 + σ╖ÑΣ╜ìΣ╕ëσ▒é Skill。
|
|
938
938
|
</div>
|
|
939
939
|
`;
|
|
940
940
|
}
|
|
@@ -943,11 +943,11 @@
|
|
|
943
943
|
function renderTemplateCard(t) {
|
|
944
944
|
const skills = t.skills || [];
|
|
945
945
|
return `<div class="card" style="cursor:pointer;" onclick="useTemplate('${t.id}')">
|
|
946
|
-
<div style="font-size:24px;margin-bottom:8px;">${t.icon || '
|
|
946
|
+
<div style="font-size:24px;margin-bottom:8px;">${t.icon || '🤖'}</div>
|
|
947
947
|
<div style="font-weight:600;">${t.nameZh || t.name}</div>
|
|
948
948
|
<div style="font-size:12px;color:var(--text-muted);margin:4px 0;">${t.descriptionZh || t.description || ''}</div>
|
|
949
949
|
${skills.length ? `<div style="margin:4px 0;">${skills.slice(0, 3).map(s => `<span class="tag tag-yellow">${s}</span>`).join('')}</div>` : ''}
|
|
950
|
-
<button class="btn-primary" style="width:100%;margin-top:8px;font-size:12px;" onclick="event.stopPropagation();useTemplate('${t.id}')"
|
|
950
|
+
<button class="btn-primary" style="width:100%;margin-top:8px;font-size:12px;" onclick="event.stopPropagation();useTemplate('${t.id}')">使用模板</button>
|
|
951
951
|
</div>`;
|
|
952
952
|
}
|
|
953
953
|
|
|
@@ -958,11 +958,11 @@
|
|
|
958
958
|
// Pre-fill new agent form
|
|
959
959
|
document.getElementById('new-agent-name').value = tpl.nameZh || tpl.name || '';
|
|
960
960
|
document.getElementById('new-agent-desc').value = tpl.descriptionZh || tpl.description || '';
|
|
961
|
-
document.getElementById('new-agent-icon').value = tpl.icon || '
|
|
961
|
+
document.getElementById('new-agent-icon').value = tpl.icon || '🤖';
|
|
962
962
|
document.getElementById('new-agent-prompt').value = tpl.systemPrompt || '';
|
|
963
963
|
navigate('new-agent');
|
|
964
|
-
showToast(
|
|
965
|
-
} catch (e) { showToast('
|
|
964
|
+
showToast(`已加载模板: ${tpl.nameZh || tpl.name}`);
|
|
965
|
+
} catch (e) { showToast('❌ 加载模板失败: ' + e.message); }
|
|
966
966
|
}
|
|
967
967
|
|
|
968
968
|
function searchTemplates(query) { /* TODO: search filter */ }
|
|
@@ -975,12 +975,12 @@
|
|
|
975
975
|
const channels = await res.json();
|
|
976
976
|
|
|
977
977
|
const channelDefs = [
|
|
978
|
-
{ id: 'telegram', name: 'Telegram', icon: '
|
|
979
|
-
{ id: 'wechat', name: '
|
|
980
|
-
{ id: 'feishu', name: '
|
|
981
|
-
{ id: 'slack', name: 'Slack', icon: '
|
|
982
|
-
{ id: 'discord', name: 'Discord', icon: '
|
|
983
|
-
{ id: 'email', name: 'Email', icon: '
|
|
978
|
+
{ id: 'telegram', name: 'Telegram', icon: '📱', fields: [{ key: 'token', label: 'Bot Token', placeholder: '123456:ABC-DEF...' }] },
|
|
979
|
+
{ id: 'wechat', name: '微信', icon: '💬', fields: [{ key: 'appId', label: 'App ID' }, { key: 'appSecret', label: 'App Secret' }] },
|
|
980
|
+
{ id: 'feishu', name: '飞书', icon: '🐦', fields: [{ key: 'appId', label: 'App ID' }, { key: 'appSecret', label: 'App Secret' }] },
|
|
981
|
+
{ id: 'slack', name: 'Slack', icon: '💼', fields: [{ key: 'token', label: 'Bot Token' }] },
|
|
982
|
+
{ id: 'discord', name: 'Discord', icon: '🎮', fields: [{ key: 'token', label: 'Bot Token' }] },
|
|
983
|
+
{ id: 'email', name: 'Email', icon: '📧', fields: [{ key: 'smtp', label: 'SMTP 地址' }, { key: 'password', label: '密码' }] },
|
|
984
984
|
];
|
|
985
985
|
|
|
986
986
|
el.innerHTML = `<div class="card-grid">${channelDefs.map(ch => {
|
|
@@ -998,11 +998,11 @@
|
|
|
998
998
|
<input class="form-input" style="font-size:13px;" id="ch-${ch.id}-${f.key}" type="password" placeholder="${f.placeholder || ''}" value="${cfg[f.key] || ''}">
|
|
999
999
|
</div>
|
|
1000
1000
|
`).join('')}
|
|
1001
|
-
<button class="btn-primary" style="font-size:12px;" onclick="saveChannel('${ch.id}', [${ch.fields.map(f => `'${f.key}'`).join(',')}])"
|
|
1001
|
+
<button class="btn-primary" style="font-size:12px;" onclick="saveChannel('${ch.id}', [${ch.fields.map(f => `'${f.key}'`).join(',')}])">保存</button>
|
|
1002
1002
|
</div>`;
|
|
1003
1003
|
}).join('')}</div>`;
|
|
1004
1004
|
} catch (e) {
|
|
1005
|
-
el.innerHTML = `<div style="color:var(--red);"
|
|
1005
|
+
el.innerHTML = `<div style="color:var(--red);">加载失败: ${e.message}</div>`;
|
|
1006
1006
|
}
|
|
1007
1007
|
}
|
|
1008
1008
|
|
|
@@ -1014,19 +1014,19 @@
|
|
|
1014
1014
|
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
|
1015
1015
|
body: JSON.stringify(body)
|
|
1016
1016
|
});
|
|
1017
|
-
showToast(
|
|
1018
|
-
} catch (e) { showToast('
|
|
1017
|
+
showToast(`✅ ${channelId} 已保存`);
|
|
1018
|
+
} catch (e) { showToast('❌ 保存失败: ' + e.message); }
|
|
1019
1019
|
}
|
|
1020
1020
|
|
|
1021
1021
|
// ======================== Create Agent ========================
|
|
1022
1022
|
async function createAgent() {
|
|
1023
1023
|
const name = document.getElementById('new-agent-name').value.trim();
|
|
1024
|
-
if (!name) { showToast('
|
|
1024
|
+
if (!name) { showToast('请输入名称'); return; }
|
|
1025
1025
|
|
|
1026
1026
|
const body = {
|
|
1027
1027
|
name,
|
|
1028
1028
|
description: document.getElementById('new-agent-desc').value,
|
|
1029
|
-
icon: document.getElementById('new-agent-icon').value || '
|
|
1029
|
+
icon: document.getElementById('new-agent-icon').value || '🤖',
|
|
1030
1030
|
systemPrompt: document.getElementById('new-agent-prompt').value,
|
|
1031
1031
|
model: document.getElementById('new-agent-model').value,
|
|
1032
1032
|
};
|
|
@@ -1037,15 +1037,15 @@
|
|
|
1037
1037
|
body: JSON.stringify(body)
|
|
1038
1038
|
});
|
|
1039
1039
|
const data = await res.json();
|
|
1040
|
-
showToast(
|
|
1040
|
+
showToast(`✅ Agent "${name}" 已创建`);
|
|
1041
1041
|
await loadAgents();
|
|
1042
1042
|
navigate('agent', { id: data.id || name.toLowerCase().replace(/\s+/g, '-'), name, icon: body.icon });
|
|
1043
|
-
} catch (e) { showToast('
|
|
1043
|
+
} catch (e) { showToast('❌ 创建失败: ' + e.message); }
|
|
1044
1044
|
}
|
|
1045
1045
|
|
|
1046
1046
|
async function loadAvailableModels() {
|
|
1047
1047
|
const sel = document.getElementById('new-agent-model');
|
|
1048
|
-
sel.innerHTML = '<option value=""
|
|
1048
|
+
sel.innerHTML = '<option value="">使用全局默认</option>';
|
|
1049
1049
|
try {
|
|
1050
1050
|
const [modelsRes, ollamaRes] = await Promise.all([
|
|
1051
1051
|
fetch(`${API}/api/settings/models`),
|