@auto-ai/agent 2.1.221 → 2.1.223

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.
Files changed (41) hide show
  1. package/dist/safe-a/404/index.html +1 -1
  2. package/dist/safe-a/404.html +1 -1
  3. package/dist/safe-a/index.html +2 -2
  4. package/dist/safe-a/index.txt +1 -1
  5. package/dist/safe-a/manage/about/index.html +2 -2
  6. package/dist/safe-a/manage/about/index.txt +1 -1
  7. package/dist/safe-a/manage/env/index.html +2 -2
  8. package/dist/safe-a/manage/env/index.txt +1 -1
  9. package/dist/safe-a/manage/geelib/index.html +2 -2
  10. package/dist/safe-a/manage/geelib/index.txt +1 -1
  11. package/dist/safe-a/manage/general/index.html +2 -2
  12. package/dist/safe-a/manage/general/index.txt +1 -1
  13. package/dist/safe-a/manage/git/index.html +2 -2
  14. package/dist/safe-a/manage/git/index.txt +1 -1
  15. package/dist/safe-a/manage/im/index.html +2 -2
  16. package/dist/safe-a/manage/im/index.txt +1 -1
  17. package/dist/safe-a/manage/index.html +2 -2
  18. package/dist/safe-a/manage/index.txt +1 -1
  19. package/dist/safe-a/manage/library/index.html +2 -2
  20. package/dist/safe-a/manage/library/index.txt +1 -1
  21. package/dist/safe-a/manage/mcp/index.html +2 -2
  22. package/dist/safe-a/manage/mcp/index.txt +1 -1
  23. package/dist/safe-a/manage/permissions/index.html +2 -2
  24. package/dist/safe-a/manage/permissions/index.txt +1 -1
  25. package/dist/safe-a/manage/skills/index.html +2 -2
  26. package/dist/safe-a/manage/skills/index.txt +1 -1
  27. package/dist/safe-a/manage/task/index.html +2 -2
  28. package/dist/safe-a/manage/task/index.txt +1 -1
  29. package/dist/safe-a/manage/teams/index.html +2 -2
  30. package/dist/safe-a/manage/teams/index.txt +1 -1
  31. package/dist/safe-a/manage/tools/index.html +2 -2
  32. package/dist/safe-a/manage/tools/index.txt +1 -1
  33. package/dist/ws-test/agent-teams.html +1400 -0
  34. package/dist/ws-test/team-chat.js +760 -0
  35. package/dist/ws-test/ws-test.css +613 -6
  36. package/dist/ws-test/ws-test.html +23 -7
  37. package/dist/ws-test/ws-test.js +275 -89
  38. package/package.json +6 -6
  39. /package/dist/safe-a/_next/static/{1lVS2YCkhJeMjvIi_ropq → UA2zKbiNyTrZsAV1wjY-_}/_buildManifest.js +0 -0
  40. /package/dist/safe-a/_next/static/{1lVS2YCkhJeMjvIi_ropq → UA2zKbiNyTrZsAV1wjY-_}/_clientMiddlewareManifest.json +0 -0
  41. /package/dist/safe-a/_next/static/{1lVS2YCkhJeMjvIi_ropq → UA2zKbiNyTrZsAV1wjY-_}/_ssgManifest.js +0 -0
@@ -0,0 +1,1400 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>团队</title>
7
+ <link rel="stylesheet" href="/ws-test.css" />
8
+ </head>
9
+ <body>
10
+ <div id="teamsApp" class="app">
11
+ <aside id="teamsSidebarPanel" class="sidebar teams-sidebar-panel" aria-label="团队列表" hidden>
12
+ <div class="sidebar-top">
13
+ <div class="sidebar-brand">
14
+ <div class="sidebar-brand-title-row">
15
+ <span class="sidebar-agent-name">团队</span>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ <div class="sidebar-sessions-wrap">
20
+ <ul id="teamSidebar" class="sidebar-session-list teams-sidebar-list" role="list"></ul>
21
+ </div>
22
+ <div class="sidebar-footer teams-sidebar-footer">
23
+ <button type="button" class="sidebar-nav-row" id="btnNewTeam" title="新建团队" aria-label="新建团队">
24
+ <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
25
+ <path d="M12 5v14M5 12h14"/>
26
+ </svg>
27
+ <span>新建团队</span>
28
+ </button>
29
+ <button type="button" class="sidebar-nav-row" id="btnTeamSettings" title="团队设置" aria-label="团队设置" hidden>
30
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
31
+ <circle cx="12" cy="12" r="3.1"/>
32
+ <path d="M19.4 15a1 1 0 0 0 .2 1.1l.1.1a1.8 1.8 0 1 1-2.6 2.6l-.1-.1a1 1 0 0 0-1.1-.2 1 1 0 0 0-.6.9V20a1.8 1.8 0 1 1-3.6 0v-.2a1 1 0 0 0-.6-.9 1 1 0 0 0-1.1.2l-.1.1a1.8 1.8 0 1 1-2.6-2.6l.1-.1a1 1 0 0 0 .2-1.1 1 1 0 0 0-.9-.6H4a1.8 1.8 0 1 1 0-3.6h.2a1 1 0 0 0 .9-.6 1 1 0 0 0-.2-1.1l-.1-.1a1.8 1.8 0 1 1 2.6-2.6l.1.1a1 1 0 0 0 1.1.2h.1a1 1 0 0 0 .6-.9V4a1.8 1.8 0 1 1 3.6 0v.2a1 1 0 0 0 .6.9h.1a1 1 0 0 0 1.1-.2l.1-.1a1.8 1.8 0 1 1 2.6 2.6l-.1.1a1 1 0 0 0-.2 1.1v.1a1 1 0 0 0 .9.6H20a1.8 1.8 0 1 1 0 3.6h-.2a1 1 0 0 0-.9.6z"/>
33
+ </svg>
34
+ <span>设置</span>
35
+ </button>
36
+ <span id="teamsSubtitle" class="teams-footer-meta">加载中…</span>
37
+ </div>
38
+ </aside>
39
+
40
+ <button
41
+ type="button"
42
+ class="teams-sidebar-edge-toggle"
43
+ id="btnToggleTeamsSidebar"
44
+ title="显示团队列表"
45
+ aria-label="显示团队列表"
46
+ aria-controls="teamsSidebarPanel"
47
+ aria-expanded="false"
48
+ >
49
+ <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
50
+ <polyline points="9 18 15 12 9 6"/>
51
+ </svg>
52
+ </button>
53
+
54
+ <main class="main teams-main">
55
+ <header class="main-header teams-main-header">
56
+ <div class="main-header-title-wrap">
57
+ <h1 class="main-header-title" id="teamContentTitle">选择团队</h1>
58
+ <p id="teamContentDesc" class="manage-view-meta teams-content-desc" hidden></p>
59
+ </div>
60
+ </header>
61
+ <div class="teams-workspace">
62
+ <aside class="teams-members-panel" aria-label="团队成员">
63
+ <div class="teams-members-head">
64
+ <div class="teams-members-head-left">
65
+ <h2>成员</h2>
66
+ <span id="teamsMemberCount" class="teams-members-count">—</span>
67
+ </div>
68
+ <div class="teams-members-head-actions">
69
+ <button type="button" class="btn-primary" id="btnOpenAddMemberModal" hidden>添加队员</button>
70
+ </div>
71
+ </div>
72
+ <div id="agentListRoot" class="teams-members-scroll" aria-label="Agent 列表"></div>
73
+ </aside>
74
+
75
+ <div class="teams-chat-column main-body">
76
+ <div id="teamChatWorkspace" class="chat-workspace">
77
+ <div id="teamChatThread" class="chat-thread teams-chat-thread" aria-live="polite"></div>
78
+ </div>
79
+ <div class="composer" id="teamChatComposer">
80
+ <div class="composer-inner">
81
+ <div class="composer-meta">
82
+ <div id="teamChatStatus" class="conn-status">未连接</div>
83
+ </div>
84
+ <div class="composer-card">
85
+ <div class="composer-prompt-wrap">
86
+ <div
87
+ id="teamChatPrompt"
88
+ class="prompt-editor"
89
+ contenteditable="true"
90
+ role="textbox"
91
+ aria-multiline="true"
92
+ data-placeholder="@agentId 指定单个队员,否则全员处理"
93
+ spellcheck="false"
94
+ ></div>
95
+ </div>
96
+ <div class="composer-card-footer">
97
+ <div class="composer-tools-left" aria-hidden="true"></div>
98
+ <div class="composer-tools-right" aria-label="发送操作">
99
+ <button type="button" class="sidebar-icon-btn composer-action-btn" id="btnTeamChatClear" disabled title="清理聊天记录" aria-label="清理聊天记录">
100
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
101
+ <polyline points="3 6 5 6 21 6" />
102
+ <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" />
103
+ </svg>
104
+ </button>
105
+ <button type="button" class="sidebar-icon-btn composer-action-btn" id="btnTeamChatAbort" disabled title="中断" aria-label="中断">
106
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
107
+ <rect x="5" y="5" width="14" height="14" rx="2" ry="2" />
108
+ </svg>
109
+ </button>
110
+ <button type="button" class="sidebar-icon-btn composer-action-btn" id="btnTeamChatSend" disabled title="发送" aria-label="发送">
111
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
112
+ <line x1="22" y1="2" x2="11" y2="13" />
113
+ <polygon points="22 2 15 22 11 13 2 9 22 2" />
114
+ </svg>
115
+ </button>
116
+ </div>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ </div>
121
+ </div>
122
+
123
+ <aside id="teamsPanel" class="teams-edit-panel" aria-label="团队编辑">
124
+ <div class="teams-edit-head">
125
+ <h2 id="teamPanelTitle">编辑团队</h2>
126
+ <button type="button" class="sidebar-icon-btn" id="btnTeamPanelClose" aria-label="关闭">
127
+ <span class="ic" aria-hidden="true">×</span>
128
+ </button>
129
+ </div>
130
+ <div class="teams-edit-scroll">
131
+ <form id="teamForm" class="settings-unified-panel" onsubmit="return false">
132
+ <section class="settings-section">
133
+ <h3 class="settings-section-title">团队信息</h3>
134
+ <div class="settings-fields">
135
+ <div class="settings-field">
136
+ <label class="settings-field-label" for="teamIdInput">团队 ID</label>
137
+ <input id="teamIdInput" class="agent-env-kv-input" type="text" spellcheck="false" placeholder="例如 dev-team" />
138
+ </div>
139
+ <div class="settings-field">
140
+ <label class="settings-field-label" for="teamDisplayName">显示名称</label>
141
+ <input id="teamDisplayName" class="agent-env-kv-input" type="text" spellcheck="false" placeholder="团队名称" />
142
+ </div>
143
+ <div class="settings-field">
144
+ <label class="settings-field-label" for="teamDescription">描述</label>
145
+ <textarea id="teamDescription" class="agent-env-kv-input settings-textarea settings-textarea--compact" spellcheck="false" placeholder="团队用途说明"></textarea>
146
+ </div>
147
+ </div>
148
+ <div class="teams-edit-actions">
149
+ <button type="button" class="btn-primary" id="btnSaveTeam">保存</button>
150
+ <button type="button" class="btn-repo-file-delete" id="btnDeleteTeam">删除团队</button>
151
+ </div>
152
+ </section>
153
+ </form>
154
+ </div>
155
+ </aside>
156
+ <aside id="agentPanel" class="teams-edit-panel" aria-label="Agent 详情">
157
+ <div class="teams-edit-head">
158
+ <h2 id="agentPanelTitle">Agent</h2>
159
+ <button type="button" class="sidebar-icon-btn" id="btnAgentPanelClose" aria-label="关闭">
160
+ <span class="ic" aria-hidden="true">×</span>
161
+ </button>
162
+ </div>
163
+ <div class="teams-edit-scroll">
164
+ <form id="agentForm" class="settings-unified-panel" onsubmit="return false">
165
+ <section class="settings-section">
166
+ <h3 class="settings-section-title">Agent 配置</h3>
167
+ <div class="settings-fields">
168
+ <div class="settings-field">
169
+ <label class="settings-field-label" for="agentIdInput">Agent ID</label>
170
+ <input id="agentIdInput" class="agent-env-kv-input" type="text" spellcheck="false" placeholder="例如 my-agent" />
171
+ </div>
172
+ <div class="settings-field">
173
+ <label class="settings-field-label" for="agentWhenInput">使用时机(whentouse)</label>
174
+ <textarea id="agentWhenInput" class="agent-env-kv-input settings-textarea settings-textarea--compact" spellcheck="false"></textarea>
175
+ </div>
176
+ <div class="settings-field">
177
+ <label class="settings-field-label" for="agentPromptInput">agent.md</label>
178
+ <textarea id="agentPromptInput" class="agent-env-kv-input settings-textarea agent-settings-md-editor" spellcheck="false"></textarea>
179
+ </div>
180
+ </div>
181
+ <div class="teams-edit-actions">
182
+ <button type="button" class="btn-primary" id="btnSaveAgent">保存</button>
183
+ <button type="button" class="btn-manage-head-action" id="btnAgentWorkbench" hidden>进入工作台</button>
184
+ </div>
185
+ </section>
186
+ </form>
187
+ </div>
188
+ </aside>
189
+ </div>
190
+ </main>
191
+
192
+ <div id="teamsToast" role="status" aria-live="polite"></div>
193
+ </div>
194
+
195
+ <div id="addMemberModal" class="agent-md-modal" aria-hidden="true">
196
+ <div class="agent-md-modal-backdrop" id="addMemberModalBackdrop"></div>
197
+ <div
198
+ class="agent-md-modal-card modal-unified teams-add-member-modal"
199
+ role="dialog"
200
+ aria-modal="true"
201
+ aria-labelledby="addMemberModalTitle"
202
+ >
203
+ <div class="agent-md-modal-head">
204
+ <h2 id="addMemberModalTitle">添加队员</h2>
205
+ <button type="button" class="agent-md-modal-close" id="btnAddMemberModalClose" aria-label="关闭">
206
+ <span aria-hidden="true">×</span>
207
+ </button>
208
+ </div>
209
+ <div class="teams-add-member-body">
210
+ <input
211
+ type="search"
212
+ id="addMemberSearch"
213
+ class="agent-picker-search"
214
+ placeholder="搜索 Agent ID 或说明…"
215
+ autocomplete="off"
216
+ />
217
+ <div id="addMemberList" class="agent-picker-scroll" role="listbox" aria-multiselectable="true"></div>
218
+ <p id="addMemberEmpty" class="agent-picker-empty" hidden>暂无可添加的 Agent</p>
219
+ </div>
220
+ <div class="agent-md-modal-foot" style="display: flex; gap: 8px; justify-content: flex-end">
221
+ <button type="button" class="btn-agent-settings-cancel" id="btnAddMemberCancel">取消</button>
222
+ <button type="button" class="btn-primary" id="btnAddMemberConfirm" disabled>添加选中</button>
223
+ </div>
224
+ </div>
225
+ </div>
226
+ <script src="https://cdn.jsdelivr.net/npm/marked@12.0.2/marked.min.js"></script>
227
+ <script>
228
+ if (typeof marked !== 'undefined' && marked.setOptions) {
229
+ marked.setOptions({ breaks: true, gfm: true })
230
+ }
231
+ </script>
232
+ <script src="/team-chat.js"></script>
233
+ <script>
234
+ ;(function () {
235
+ 'use strict'
236
+
237
+ /** 虚拟「未分组」侧栏项 id */
238
+ var UNGROUPED_ID = '__ungrouped__'
239
+
240
+ function httpOriginForApi() {
241
+ try {
242
+ var q = new URLSearchParams(window.location.search).get('apiBase')
243
+ if (q && String(q).trim()) {
244
+ return new URL(String(q).trim().replace(/\/$/, '') + '/').origin
245
+ }
246
+ } catch (e) { /* ignore */ }
247
+ var loc = window.location
248
+ if (loc.protocol === 'http:' || loc.protocol === 'https:') {
249
+ return loc.origin
250
+ }
251
+ return 'http://127.0.0.1:8765'
252
+ }
253
+
254
+ /** 与页面同源时用相对路径,避免 apiBase 指向错误端口导致 CONNECTION_REFUSED */
255
+ function apiUrl(path) {
256
+ var p = path.indexOf('/') === 0 ? path : '/' + path
257
+ if (httpOriginForApi() === window.location.origin) {
258
+ return p
259
+ }
260
+ return new URL(p, httpOriginForApi() + '/').toString()
261
+ }
262
+
263
+ function friendlyFetchError(e) {
264
+ var msg = e && e.message ? String(e.message) : String(e)
265
+ if (msg === 'Failed to fetch') {
266
+ return '无法连接 API(' + httpOriginForApi() + '),请确认 WS 服务已启动'
267
+ }
268
+ return msg
269
+ }
270
+
271
+ /** 拉取团队列表 */
272
+ async function fetchTeamsList() {
273
+ var r = await fetch(apiUrl('/api/teams'))
274
+ var body = await r.json().catch(function () { return {} })
275
+ if (!r.ok) throw new Error(body.message || body.error || r.statusText)
276
+ return Array.isArray(body.teams) ? body.teams : []
277
+ }
278
+
279
+ async function fetchAgentsList(teamId) {
280
+ var u = apiUrl('/api/agents')
281
+ if (teamId && teamId !== UNGROUPED_ID) {
282
+ u += '?team=' + encodeURIComponent(teamId)
283
+ }
284
+ var r = await fetch(u)
285
+ var body = await r.json().catch(function () { return {} })
286
+ if (!r.ok) throw new Error(body.message || body.error || r.statusText)
287
+ return Array.isArray(body.agents) ? body.agents : []
288
+ }
289
+
290
+ async function fetchAgentDetail(id) {
291
+ var r = await fetch(apiUrl('/api/agents/' + encodeURIComponent(id)))
292
+ var body = await r.json().catch(function () { return {} })
293
+ if (!r.ok) throw new Error(body.message || body.error || r.statusText)
294
+ return body.agent || { id: id }
295
+ }
296
+
297
+ async function createTeam(payload) {
298
+ var r = await fetch(apiUrl('/api/teams'), {
299
+ method: 'POST',
300
+ headers: { 'content-type': 'application/json; charset=utf-8' },
301
+ body: JSON.stringify(payload),
302
+ })
303
+ var body = await r.json().catch(function () { return {} })
304
+ if (!r.ok) throw new Error(body.message || body.error || r.statusText)
305
+ return body.team
306
+ }
307
+
308
+ async function patchTeam(id, payload) {
309
+ var r = await fetch(apiUrl('/api/teams/' + encodeURIComponent(id)), {
310
+ method: 'PATCH',
311
+ headers: { 'content-type': 'application/json; charset=utf-8' },
312
+ body: JSON.stringify(payload),
313
+ })
314
+ var body = await r.json().catch(function () { return {} })
315
+ if (!r.ok) throw new Error(body.message || body.error || r.statusText)
316
+ return body.team
317
+ }
318
+
319
+ async function deleteTeam(id) {
320
+ var r = await fetch(apiUrl('/api/teams/' + encodeURIComponent(id)), { method: 'DELETE' })
321
+ var body = await r.json().catch(function () { return {} })
322
+ if (!r.ok) throw new Error(body.message || body.error || r.statusText)
323
+ }
324
+
325
+ async function addTeamMember(teamId, agentId) {
326
+ var r = await fetch(apiUrl('/api/teams/' + encodeURIComponent(teamId) + '/members'), {
327
+ method: 'POST',
328
+ headers: { 'content-type': 'application/json; charset=utf-8' },
329
+ body: JSON.stringify({ agentId: agentId }),
330
+ })
331
+ var body = await r.json().catch(function () { return {} })
332
+ if (!r.ok) throw new Error(body.message || body.error || r.statusText)
333
+ return body.team
334
+ }
335
+
336
+ async function removeTeamMember(teamId, agentId) {
337
+ var r = await fetch(
338
+ apiUrl('/api/teams/' + encodeURIComponent(teamId) + '/members/' + encodeURIComponent(agentId)),
339
+ { method: 'DELETE' },
340
+ )
341
+ var body = await r.json().catch(function () { return {} })
342
+ if (!r.ok) throw new Error(body.message || body.error || r.statusText)
343
+ return body.team
344
+ }
345
+
346
+ function isValidId(id) {
347
+ return /^[a-zA-Z0-9_-]{1,64}$/.test(String(id || '').trim())
348
+ }
349
+
350
+ function buildAgentConfigPatch(baseConfig, whentouse) {
351
+ var c = baseConfig && typeof baseConfig === 'object' ? baseConfig : {}
352
+ var env = c.env && typeof c.env === 'object' && !Array.isArray(c.env) ? c.env : {}
353
+ var patch = {
354
+ tools: Array.isArray(c.tools) ? c.tools.map(String) : [],
355
+ disallowedTools: Array.isArray(c.disallowedTools) ? c.disallowedTools.map(String) : [],
356
+ mcpServers: Array.isArray(c.mcpServers) ? c.mcpServers.map(String) : [],
357
+ skills: Array.isArray(c.skills) ? c.skills.map(String) : [],
358
+ agentTeams: Array.isArray(c.agentTeams) ? c.agentTeams.map(String) : [],
359
+ model: typeof c.model === 'string' ? c.model : '',
360
+ background: c.background === true,
361
+ isolation: typeof c.isolation === 'string' ? c.isolation : '',
362
+ whentouse: typeof whentouse === 'string' ? whentouse : '',
363
+ env: env,
364
+ }
365
+ if (typeof c.effort === 'string' || typeof c.effort === 'number') patch.effort = c.effort
366
+ return patch
367
+ }
368
+
369
+ async function saveAgent(params) {
370
+ var id = params.id
371
+ var whentouse = params.whentouse
372
+ var prompt = params.prompt
373
+ var isEdit = params.isEdit
374
+ var baseConfig = params.baseConfig
375
+ var teamIds = params.teamIds
376
+
377
+ if (isEdit) {
378
+ var patchRes = await fetch(apiUrl('/api/agents/' + encodeURIComponent(id)), {
379
+ method: 'PATCH',
380
+ headers: { 'content-type': 'application/json; charset=utf-8' },
381
+ body: JSON.stringify({ prompt: prompt, config: buildAgentConfigPatch(baseConfig, whentouse) }),
382
+ })
383
+ var patchBody = await patchRes.json().catch(function () { return {} })
384
+ if (!patchRes.ok) throw new Error(patchBody.message || patchBody.error || patchRes.statusText)
385
+ return id
386
+ }
387
+
388
+ var createBody = { name: id, prompt: prompt }
389
+ if (Array.isArray(teamIds) && teamIds.length) createBody.teamIds = teamIds
390
+ var createRes = await fetch(apiUrl('/api/agents'), {
391
+ method: 'POST',
392
+ headers: { 'content-type': 'application/json; charset=utf-8' },
393
+ body: JSON.stringify(createBody),
394
+ })
395
+ var createResult = await createRes.json().catch(function () { return {} })
396
+ if (!createRes.ok) throw new Error(createResult.message || createResult.error || createRes.statusText)
397
+
398
+ var cfgBody = await fetchAgentDetail(id)
399
+ var baseCfg = cfgBody.config && typeof cfgBody.config === 'object' ? cfgBody.config : null
400
+ var patchRes2 = await fetch(apiUrl('/api/agents/' + encodeURIComponent(id)), {
401
+ method: 'PATCH',
402
+ headers: { 'content-type': 'application/json; charset=utf-8' },
403
+ body: JSON.stringify({ config: buildAgentConfigPatch(baseCfg, whentouse) }),
404
+ })
405
+ var patchBody2 = await patchRes2.json().catch(function () { return {} })
406
+ if (!patchRes2.ok) throw new Error(patchBody2.message || patchBody2.error || patchRes2.statusText)
407
+ return id
408
+ }
409
+
410
+ async function deleteAgent(id) {
411
+ var r = await fetch(apiUrl('/api/agents/' + encodeURIComponent(id)), { method: 'DELETE' })
412
+ var body = await r.json().catch(function () { return {} })
413
+ if (!r.ok) throw new Error(body.message || body.error || r.statusText)
414
+ }
415
+
416
+ function agentListTitle(row) {
417
+ var id = String(row.id || '').trim()
418
+ var dn = row && typeof row.displayName === 'string' && row.displayName.trim() ? row.displayName.trim() : ''
419
+ return dn || id
420
+ }
421
+
422
+ function agentListSubId(row) {
423
+ var id = String(row.id || '').trim()
424
+ var title = agentListTitle(row)
425
+ return title !== id ? id : ''
426
+ }
427
+
428
+ function truncateDesc(text, max) {
429
+ var s = String(text || '').replace(/\s+/g, ' ').trim()
430
+ if (!s) return '暂无使用说明'
431
+ return s.length <= max ? s : s.slice(0, max - 1) + '…'
432
+ }
433
+
434
+ function teamDisplayName(team) {
435
+ if (!team) return ''
436
+ if (team.id === UNGROUPED_ID) return '未分组'
437
+ var dn = typeof team.displayName === 'string' && team.displayName.trim() ? team.displayName.trim() : ''
438
+ return dn || team.id
439
+ }
440
+
441
+ /** 维护当前选中团队 */
442
+ function TeamsController() {
443
+ this.selectedTeamId = null
444
+ this.onTeamSelect = null
445
+ }
446
+ TeamsController.prototype.selectTeam = function (id) {
447
+ this.selectedTeamId = id || null
448
+ if (this.onTeamSelect) this.onTeamSelect(this.selectedTeamId)
449
+ }
450
+
451
+ function TeamSidebar(container, onPick) {
452
+ this.container = container
453
+ this.onPick = onPick
454
+ }
455
+ TeamSidebar.prototype.render = function (teams, ungroupedCount, selectedId) {
456
+ if (!this.container) return
457
+ this.container.innerHTML = ''
458
+
459
+ for (var i = 0; i < teams.length; i++) {
460
+ var team = teams[i]
461
+ this.container.appendChild(
462
+ this._item(team.id, teamDisplayName(team), String(team.memberCount || 0) + ' 名成员', selectedId),
463
+ )
464
+ }
465
+ this.container.appendChild(
466
+ this._item(UNGROUPED_ID, '未分组', String(ungroupedCount) + ' 个 Agent', selectedId),
467
+ )
468
+ }
469
+ TeamSidebar.prototype._item = function (id, name, meta, selectedId) {
470
+ var li = document.createElement('li')
471
+ li.className = 'sidebar-session-item' + (selectedId === id ? ' active' : '')
472
+ var btn = document.createElement('button')
473
+ btn.type = 'button'
474
+ btn.className = 'sidebar-session-main'
475
+ var n = document.createElement('span')
476
+ n.className = 'sidebar-session-title'
477
+ n.textContent = name
478
+ var m = document.createElement('span')
479
+ m.className = 'sidebar-session-meta'
480
+ m.textContent = meta
481
+ btn.appendChild(n)
482
+ btn.appendChild(m)
483
+ var self = this
484
+ btn.addEventListener('click', function () { if (self.onPick) self.onPick(id) })
485
+ li.appendChild(btn)
486
+ return li
487
+ }
488
+
489
+ function TeamAgentList(container, handlers) {
490
+ this.container = container
491
+ this.handlers = handlers || {}
492
+ }
493
+ TeamAgentList.prototype.render = function (agents, options) {
494
+ if (!this.container) return
495
+ var opts = options || {}
496
+ var canRemove = opts.canRemoveFromTeam === true
497
+ this.container.innerHTML = ''
498
+ if (!agents.length) {
499
+ var empty = document.createElement('div')
500
+ empty.className = 'agent-list-empty'
501
+ empty.textContent = canRemove ? '暂无队员,点击右上角添加' : '暂无 Agent'
502
+ this.container.appendChild(empty)
503
+ return
504
+ }
505
+ var ul = document.createElement('ul')
506
+ ul.className = 'sidebar-session-list'
507
+ ul.setAttribute('role', 'list')
508
+ for (var i = 0; i < agents.length; i++) {
509
+ var row = agents[i]
510
+ var id = String(row.id || '').trim()
511
+ if (!id) continue
512
+ var li = document.createElement('li')
513
+ li.className = 'teams-member-row'
514
+
515
+ var mainBtn = document.createElement('button')
516
+ mainBtn.type = 'button'
517
+ mainBtn.className = 'teams-member-item'
518
+ mainBtn.setAttribute('aria-label', '查看 ' + id)
519
+
520
+ var avatar = document.createElement('span')
521
+ avatar.className = 'teams-member-avatar'
522
+ avatar.textContent = (agentListTitle(row).charAt(0) || id.charAt(0) || '?').toUpperCase()
523
+ avatar.setAttribute('aria-hidden', 'true')
524
+
525
+ var main = document.createElement('span')
526
+ main.className = 'teams-member-main'
527
+ var title = document.createElement('span')
528
+ title.className = 'teams-member-name'
529
+ title.textContent = agentListTitle(row)
530
+ main.appendChild(title)
531
+ var sub = agentListSubId(row)
532
+ if (sub) {
533
+ var subEl = document.createElement('span')
534
+ subEl.className = 'teams-member-sub'
535
+ subEl.textContent = sub
536
+ main.appendChild(subEl)
537
+ }
538
+
539
+ mainBtn.appendChild(avatar)
540
+ mainBtn.appendChild(main)
541
+ mainBtn.addEventListener('click', function (agentId) {
542
+ return function (e) {
543
+ e.stopPropagation()
544
+ if (this.handlers.onView) this.handlers.onView(agentId)
545
+ }.bind(this)
546
+ }.bind(this)(id))
547
+
548
+ var actions = document.createElement('div')
549
+ actions.className = 'teams-member-actions'
550
+
551
+ var btnView = document.createElement('button')
552
+ btnView.type = 'button'
553
+ btnView.className = 'btn-manage-row-action'
554
+ btnView.textContent = '编辑'
555
+ btnView.setAttribute('aria-label', '编辑 ' + id)
556
+ btnView.addEventListener('click', function (agentId) {
557
+ return function (e) {
558
+ e.stopPropagation()
559
+ if (this.handlers.onView) this.handlers.onView(agentId)
560
+ }.bind(this)
561
+ }.bind(this)(id))
562
+ actions.appendChild(btnView)
563
+
564
+ if (canRemove) {
565
+ var btnRemove = document.createElement('button')
566
+ btnRemove.type = 'button'
567
+ btnRemove.className = 'btn-manage-row-action is-delete'
568
+ btnRemove.textContent = '移除'
569
+ btnRemove.setAttribute('aria-label', '从团队移除 ' + id)
570
+ btnRemove.addEventListener('click', function (agentId) {
571
+ return function (e) {
572
+ e.stopPropagation()
573
+ if (this.handlers.onRemove) this.handlers.onRemove(agentId)
574
+ }.bind(this)
575
+ }.bind(this)(id))
576
+ actions.appendChild(btnRemove)
577
+ }
578
+
579
+ li.appendChild(mainBtn)
580
+ li.appendChild(actions)
581
+ ul.appendChild(li)
582
+ }
583
+ this.container.appendChild(ul)
584
+ }
585
+
586
+ function TeamsUI(root, controller, sidebar, agentList) {
587
+ this.root = root
588
+ this.controller = controller
589
+ this.sidebar = sidebar
590
+ this.agentList = agentList
591
+ this.teams = []
592
+ this.allAgents = []
593
+ this.currentAgents = []
594
+ this.panelMode = 'idle'
595
+ this.editingTeamId = null
596
+ this.editingAgentId = null
597
+ this.agentBaseConfig = null
598
+
599
+ this.subtitle = root.querySelector('#teamsSubtitle')
600
+ this.contentTitle = root.querySelector('#teamContentTitle')
601
+ this.contentDesc = root.querySelector('#teamContentDesc')
602
+ this.memberCountEl = root.querySelector('#teamsMemberCount')
603
+ this.teamChat = null
604
+ this.btnTeamSettings = root.querySelector('#btnTeamSettings')
605
+ this.btnOpenAddMemberModal = root.querySelector('#btnOpenAddMemberModal')
606
+ this.addMemberModal = document.getElementById('addMemberModal')
607
+ this.addMemberModalBackdrop = document.getElementById('addMemberModalBackdrop')
608
+ this.addMemberSearch = document.getElementById('addMemberSearch')
609
+ this.addMemberList = document.getElementById('addMemberList')
610
+ this.addMemberEmpty = document.getElementById('addMemberEmpty')
611
+ this.btnAddMemberConfirm = document.getElementById('btnAddMemberConfirm')
612
+ this.addMemberSelected = new Set()
613
+ this.teamPanel = root.querySelector('#teamsPanel')
614
+ this.agentPanel = root.querySelector('#agentPanel')
615
+ this.teamPanelTitle = root.querySelector('#teamPanelTitle')
616
+ this.agentPanelTitle = root.querySelector('#agentPanelTitle')
617
+ this.btnTeamPanelClose = root.querySelector('#btnTeamPanelClose')
618
+ this.btnAgentPanelClose = root.querySelector('#btnAgentPanelClose')
619
+ this.teamForm = root.querySelector('#teamForm')
620
+ this.agentForm = root.querySelector('#agentForm')
621
+ this.teamIdInput = root.querySelector('#teamIdInput')
622
+ this.teamDisplayName = root.querySelector('#teamDisplayName')
623
+ this.teamDescription = root.querySelector('#teamDescription')
624
+ this.agentIdInput = root.querySelector('#agentIdInput')
625
+ this.agentWhenInput = root.querySelector('#agentWhenInput')
626
+ this.agentPromptInput = root.querySelector('#agentPromptInput')
627
+ this.btnSaveAgent = root.querySelector('#btnSaveAgent')
628
+ this.btnAgentWorkbench = root.querySelector('#btnAgentWorkbench')
629
+ this.toastEl = root.querySelector('#teamsToast')
630
+ this.teamsSidebarPanel = root.querySelector('#teamsSidebarPanel')
631
+ this.btnToggleTeamsSidebar = root.querySelector('#btnToggleTeamsSidebar')
632
+
633
+ var self = this
634
+ if (this.btnToggleTeamsSidebar) {
635
+ this.btnToggleTeamsSidebar.addEventListener('click', function () {
636
+ self.toggleTeamsSidebar()
637
+ })
638
+ }
639
+ var btnNewTeam = root.querySelector('#btnNewTeam')
640
+ if (btnNewTeam) {
641
+ btnNewTeam.addEventListener('click', function () { self.showCreateTeamForm() })
642
+ }
643
+ if (this.btnTeamSettings) {
644
+ this.btnTeamSettings.addEventListener('click', function () { self.showEditTeamForm() })
645
+ }
646
+ if (this.btnOpenAddMemberModal) {
647
+ this.btnOpenAddMemberModal.addEventListener('click', function () {
648
+ self.openAddMemberModal()
649
+ })
650
+ }
651
+ if (this.addMemberModalBackdrop) {
652
+ this.addMemberModalBackdrop.addEventListener('click', function () { self.closeAddMemberModal() })
653
+ }
654
+ document.getElementById('btnAddMemberModalClose').addEventListener('click', function () {
655
+ self.closeAddMemberModal()
656
+ })
657
+ document.getElementById('btnAddMemberCancel').addEventListener('click', function () {
658
+ self.closeAddMemberModal()
659
+ })
660
+ if (this.addMemberSearch) {
661
+ this.addMemberSearch.addEventListener('input', function () { self.renderAddMemberList() })
662
+ }
663
+ if (this.btnAddMemberConfirm) {
664
+ this.btnAddMemberConfirm.addEventListener('click', function () {
665
+ void self.handleAddSelectedMembers()
666
+ })
667
+ }
668
+ if (this.btnTeamPanelClose) {
669
+ this.btnTeamPanelClose.addEventListener('click', function () { self.closeTeamPanel() })
670
+ }
671
+ if (this.btnAgentPanelClose) {
672
+ this.btnAgentPanelClose.addEventListener('click', function () { self.closeAgentPanel() })
673
+ }
674
+ root.querySelector('#btnSaveTeam').addEventListener('click', function () { void self.submitTeamForm() })
675
+ root.querySelector('#btnDeleteTeam').addEventListener('click', function () { void self.handleDeleteTeam() })
676
+ root.querySelector('#btnSaveAgent').addEventListener('click', function () { void self.submitAgentForm() })
677
+ if (this.btnAgentWorkbench) {
678
+ this.btnAgentWorkbench.addEventListener('click', function () {
679
+ var id = self.editingAgentId || (self.agentIdInput ? self.agentIdInput.value.trim() : '')
680
+ if (id) window.location.href = '/ws-test.html?agent=' + encodeURIComponent(id)
681
+ })
682
+ }
683
+ }
684
+
685
+ /** 切换左侧团队列表侧栏 */
686
+ TeamsUI.prototype.toggleTeamsSidebar = function (forceOpen) {
687
+ var panel = this.teamsSidebarPanel
688
+ var btn = this.btnToggleTeamsSidebar
689
+ if (!panel) return
690
+ var open = forceOpen === true ? true : forceOpen === false ? false : panel.hasAttribute('hidden')
691
+ if (open) {
692
+ panel.removeAttribute('hidden')
693
+ this.root.classList.add('is-team-sidebar-open')
694
+ } else {
695
+ panel.setAttribute('hidden', '')
696
+ this.root.classList.remove('is-team-sidebar-open')
697
+ }
698
+ if (btn) {
699
+ btn.setAttribute('aria-expanded', open ? 'true' : 'false')
700
+ btn.title = open ? '隐藏团队列表' : '显示团队列表'
701
+ btn.setAttribute('aria-label', btn.title)
702
+ }
703
+ }
704
+
705
+ /** 按抽屉模式更新 Agent 表单底部按钮文案与可见性 */
706
+ TeamsUI.prototype.updateAgentFormFooter = function (mode) {
707
+ if (this.btnSaveAgent) {
708
+ this.btnSaveAgent.textContent = mode === 'create' ? '创建' : '保存'
709
+ }
710
+ if (this.btnAgentWorkbench) {
711
+ this.btnAgentWorkbench.hidden = mode !== 'edit'
712
+ }
713
+ }
714
+
715
+ TeamsUI.prototype.showToast = function (msg, isError) {
716
+ if (!this.toastEl) return
717
+ this.toastEl.textContent = msg
718
+ this.toastEl.classList.toggle('is-error', !!isError)
719
+ this.toastEl.classList.add('is-visible')
720
+ var self = this
721
+ clearTimeout(this._toastTimer)
722
+ this._toastTimer = setTimeout(function () { self.toastEl.classList.remove('is-visible') }, 3200)
723
+ }
724
+
725
+ /** 关闭团队编辑抽屉 */
726
+ TeamsUI.prototype.closeTeamPanel = function () {
727
+ if (this.teamPanel) this.teamPanel.classList.remove('is-open')
728
+ if (this.panelMode === 'team-create' || this.panelMode === 'team-edit') {
729
+ this.panelMode = 'idle'
730
+ }
731
+ this.editingTeamId = null
732
+ }
733
+
734
+ /** 关闭 Agent 详情抽屉 */
735
+ TeamsUI.prototype.closeAgentPanel = function () {
736
+ if (this.agentPanel) this.agentPanel.classList.remove('is-open')
737
+ if (this.panelMode === 'agent-create' || this.panelMode === 'agent-edit') {
738
+ this.panelMode = 'idle'
739
+ }
740
+ this.editingAgentId = null
741
+ this.agentBaseConfig = null
742
+ this.syncUrlParams()
743
+ }
744
+
745
+ /** 关闭全部抽屉 */
746
+ TeamsUI.prototype.closePanel = function () {
747
+ this.closeTeamPanel()
748
+ this.closeAgentPanel()
749
+ this.panelMode = 'idle'
750
+ this.syncUrlParams()
751
+ }
752
+
753
+ /** 打开团队编辑抽屉(互斥关闭 Agent 抽屉) */
754
+ TeamsUI.prototype.openTeamPanel = function () {
755
+ this.closeAgentPanel()
756
+ if (this.teamPanel) this.teamPanel.classList.add('is-open')
757
+ }
758
+
759
+ /** 打开 Agent 详情抽屉(互斥关闭团队抽屉) */
760
+ TeamsUI.prototype.openAgentPanel = function () {
761
+ this.closeTeamPanel()
762
+ if (this.agentPanel) this.agentPanel.classList.add('is-open')
763
+ }
764
+
765
+ /** 打开抽屉查看并编辑 Agent 详情 */
766
+ TeamsUI.prototype.viewAgent = function (agentId) {
767
+ void this.openAgentViewDrawer(agentId)
768
+ }
769
+
770
+ TeamsUI.prototype.openAgentViewDrawer = async function (agentId) {
771
+ var id = String(agentId || '').trim()
772
+ if (!id) return
773
+ this.panelMode = 'agent-edit'
774
+ this.editingAgentId = id
775
+ this.openAgentPanel()
776
+ if (this.agentPanelTitle) this.agentPanelTitle.textContent = id
777
+ if (this.agentIdInput) {
778
+ this.agentIdInput.value = id
779
+ this.agentIdInput.readOnly = true
780
+ }
781
+ if (this.agentWhenInput) this.agentWhenInput.value = ''
782
+ if (this.agentPromptInput) this.agentPromptInput.value = ''
783
+ this.updateAgentFormFooter('edit')
784
+ this.syncUrlParams()
785
+
786
+ try {
787
+ var agent = await fetchAgentDetail(id)
788
+ this.agentBaseConfig =
789
+ agent.config && typeof agent.config === 'object' ? agent.config : null
790
+ var whentouse =
791
+ agent.config && typeof agent.config.whentouse === 'string'
792
+ ? agent.config.whentouse
793
+ : typeof agent.whentouse === 'string'
794
+ ? agent.whentouse
795
+ : ''
796
+ if (this.agentWhenInput) this.agentWhenInput.value = whentouse
797
+ if (this.agentPromptInput) {
798
+ this.agentPromptInput.value =
799
+ typeof agent.prompt === 'string' ? agent.prompt : ''
800
+ }
801
+ if (this.agentPanelTitle) this.agentPanelTitle.textContent = agentListTitle(agent)
802
+ } catch (e) {
803
+ this.showToast('加载 Agent 失败: ' + String(e), true)
804
+ }
805
+ }
806
+
807
+ TeamsUI.prototype.renderAgentList = function () {
808
+ var teamId = this.controller.selectedTeamId
809
+ var canRemove = !!teamId && teamId !== UNGROUPED_ID
810
+ this.agentList.render(this.currentAgents, { canRemoveFromTeam: canRemove })
811
+ this.syncTeamChat()
812
+ }
813
+
814
+ /** 按当前团队连接群聊 WS(/ws/team?teamId=) */
815
+ TeamsUI.prototype.syncTeamChat = function () {
816
+ if (!this.teamChat) return
817
+ var teamId = this.controller.selectedTeamId
818
+ var isUngrouped = teamId === UNGROUPED_ID
819
+ if (this.memberCountEl) {
820
+ this.memberCountEl.textContent =
821
+ teamId && !isUngrouped ? String(this.currentAgents.length) : '—'
822
+ }
823
+ if (!teamId || isUngrouped) {
824
+ this.teamChat.disconnect()
825
+ this.teamChat.setIdleHint('')
826
+ return
827
+ }
828
+ if (!this.currentAgents.length) {
829
+ this.teamChat.disconnect()
830
+ this.teamChat.setIdleHint('添加队员后可开始群聊')
831
+ return
832
+ }
833
+ this.teamChat.setIdleHint('')
834
+ this.teamChat.setMembers(this.currentAgents)
835
+ this.teamChat.connect(teamId)
836
+ }
837
+
838
+ TeamsUI.prototype.computeUngroupedAgents = function () {
839
+ var inTeam = new Set()
840
+ for (var i = 0; i < this.teams.length; i++) {
841
+ var members = this.teams[i].members || []
842
+ for (var j = 0; j < members.length; j++) {
843
+ var mid = String(members[j].agentId || members[j].agentType || '').trim()
844
+ if (mid) inTeam.add(mid)
845
+ }
846
+ var ids = this.teams[i].memberIds
847
+ if (Array.isArray(ids)) {
848
+ for (var k = 0; k < ids.length; k++) inTeam.add(String(ids[k]))
849
+ }
850
+ }
851
+ return this.allAgents.filter(function (a) { return !inTeam.has(String(a.id)) })
852
+ }
853
+
854
+ TeamsUI.prototype.getSelectedTeam = function () {
855
+ var id = this.controller.selectedTeamId
856
+ if (!id || id === UNGROUPED_ID) return null
857
+ for (var i = 0; i < this.teams.length; i++) {
858
+ if (this.teams[i].id === id) return this.teams[i]
859
+ }
860
+ return null
861
+ }
862
+
863
+ TeamsUI.prototype.refreshAll = async function () {
864
+ try {
865
+ this.teams = await fetchTeamsList()
866
+ } catch (e) {
867
+ this.teams = []
868
+ this.showToast('加载团队失败: ' + friendlyFetchError(e), true)
869
+ }
870
+ try {
871
+ this.allAgents = await fetchAgentsList()
872
+ } catch (e) {
873
+ this.allAgents = []
874
+ this.showToast('加载 Agent 失败: ' + friendlyFetchError(e), true)
875
+ }
876
+ await this.refreshCurrentAgents()
877
+ var ungrouped = this.computeUngroupedAgents()
878
+ if (this.sidebar) {
879
+ this.sidebar.render(this.teams, ungrouped.length, this.controller.selectedTeamId)
880
+ }
881
+ if (this.subtitle) {
882
+ this.subtitle.textContent = this.teams.length + ' 个团队 · ' + this.allAgents.length + ' 个 Agent'
883
+ }
884
+ this.updateContentHead()
885
+ this.refreshAddAgentPick()
886
+ }
887
+
888
+ /** 从已加载的 allAgents + 团队 membership 推导成员,切换团队不再额外请求 API */
889
+ TeamsUI.prototype.agentsForTeam = function (team) {
890
+ if (!team) return []
891
+ var memberIds = []
892
+ var seen = new Set()
893
+ if (Array.isArray(team.memberIds)) {
894
+ for (var i = 0; i < team.memberIds.length; i++) {
895
+ var id = String(team.memberIds[i] || '').trim()
896
+ if (!id || seen.has(id)) continue
897
+ seen.add(id)
898
+ memberIds.push(id)
899
+ }
900
+ }
901
+ if (Array.isArray(team.members)) {
902
+ for (var j = 0; j < team.members.length; j++) {
903
+ var mid = String(team.members[j].agentId || team.members[j].agentType || '').trim()
904
+ if (!mid || seen.has(mid)) continue
905
+ seen.add(mid)
906
+ memberIds.push(mid)
907
+ }
908
+ }
909
+ var byId = new Map()
910
+ for (var k = 0; k < this.allAgents.length; k++) {
911
+ byId.set(String(this.allAgents[k].id), this.allAgents[k])
912
+ }
913
+ var out = []
914
+ for (var m = 0; m < memberIds.length; m++) {
915
+ var row = byId.get(memberIds[m])
916
+ if (row) out.push(row)
917
+ }
918
+ return out
919
+ }
920
+
921
+ TeamsUI.prototype.refreshCurrentAgents = async function () {
922
+ var teamId = this.controller.selectedTeamId
923
+ if (!teamId) {
924
+ this.currentAgents = []
925
+ this.renderAgentList()
926
+ return
927
+ }
928
+ if (teamId === UNGROUPED_ID) {
929
+ this.currentAgents = this.computeUngroupedAgents()
930
+ } else {
931
+ this.currentAgents = this.agentsForTeam(this.getSelectedTeam())
932
+ }
933
+ this.renderAgentList()
934
+ }
935
+
936
+ TeamsUI.prototype.updateContentHead = function () {
937
+ var teamId = this.controller.selectedTeamId
938
+ var isUngrouped = teamId === UNGROUPED_ID
939
+ var team = this.getSelectedTeam()
940
+
941
+ if (!teamId) {
942
+ if (this.contentTitle) this.contentTitle.textContent = '选择团队'
943
+ if (this.contentDesc) this.contentDesc.hidden = true
944
+ if (this.btnTeamSettings) this.btnTeamSettings.hidden = true
945
+ if (this.btnOpenAddMemberModal) this.btnOpenAddMemberModal.hidden = true
946
+ return
947
+ }
948
+
949
+ if (isUngrouped) {
950
+ if (this.contentTitle) this.contentTitle.textContent = '未分组'
951
+ if (this.contentDesc) {
952
+ this.contentDesc.textContent = '尚未加入任何团队的 Agent'
953
+ this.contentDesc.hidden = false
954
+ }
955
+ if (this.btnTeamSettings) this.btnTeamSettings.hidden = true
956
+ if (this.btnOpenAddMemberModal) this.btnOpenAddMemberModal.hidden = true
957
+ return
958
+ }
959
+
960
+ if (this.contentTitle) this.contentTitle.textContent = teamDisplayName(team)
961
+ if (this.contentDesc) {
962
+ var desc = team && typeof team.description === 'string' ? team.description.trim() : ''
963
+ if (desc) {
964
+ this.contentDesc.textContent = desc
965
+ this.contentDesc.hidden = false
966
+ } else {
967
+ this.contentDesc.hidden = true
968
+ }
969
+ }
970
+ if (this.btnTeamSettings) this.btnTeamSettings.hidden = false
971
+ if (this.btnOpenAddMemberModal) this.btnOpenAddMemberModal.hidden = false
972
+ }
973
+
974
+ /** 当前团队尚未加入、可作为队员的 Agent 列表 */
975
+ TeamsUI.prototype.getAddMemberCandidates = function () {
976
+ var teamId = this.controller.selectedTeamId
977
+ if (!teamId || teamId === UNGROUPED_ID) return []
978
+ var team = this.getSelectedTeam()
979
+ var memberSet = new Set()
980
+ if (team && Array.isArray(team.members)) {
981
+ for (var i = 0; i < team.members.length; i++) {
982
+ memberSet.add(String(team.members[i].agentId || ''))
983
+ }
984
+ }
985
+ var out = []
986
+ for (var j = 0; j < this.allAgents.length; j++) {
987
+ var a = this.allAgents[j]
988
+ var id = String(a.id || '').trim()
989
+ if (!id || memberSet.has(id)) continue
990
+ out.push(a)
991
+ }
992
+ return out
993
+ }
994
+
995
+ TeamsUI.prototype.updateAddMemberConfirmBtn = function () {
996
+ if (!this.btnAddMemberConfirm) return
997
+ var n = this.addMemberSelected.size
998
+ this.btnAddMemberConfirm.disabled = n === 0
999
+ this.btnAddMemberConfirm.textContent = n > 0 ? '添加选中(' + n + ')' : '添加选中'
1000
+ }
1001
+
1002
+ /** 渲染弹窗内可选队员列表,支持搜索与多选 */
1003
+ TeamsUI.prototype.renderAddMemberList = function () {
1004
+ if (!this.addMemberList) return
1005
+ var q = this.addMemberSearch ? this.addMemberSearch.value.trim().toLowerCase() : ''
1006
+ var candidates = this.getAddMemberCandidates()
1007
+ var filtered = candidates.filter(function (a) {
1008
+ if (!q) return true
1009
+ var id = String(a.id || '').trim().toLowerCase()
1010
+ var title = agentListTitle(a).toLowerCase()
1011
+ var when = String(a.whentouse || '').toLowerCase()
1012
+ return id.indexOf(q) >= 0 || title.indexOf(q) >= 0 || when.indexOf(q) >= 0
1013
+ })
1014
+
1015
+ this.addMemberList.innerHTML = ''
1016
+ if (this.addMemberEmpty) {
1017
+ this.addMemberEmpty.hidden = filtered.length > 0
1018
+ this.addMemberEmpty.textContent = candidates.length
1019
+ ? '没有匹配的 Agent'
1020
+ : '暂无可添加的 Agent'
1021
+ }
1022
+
1023
+ var self = this
1024
+ for (var i = 0; i < filtered.length; i++) {
1025
+ var row = filtered[i]
1026
+ var id = String(row.id || '').trim()
1027
+ if (!id) continue
1028
+ var btn = document.createElement('button')
1029
+ btn.type = 'button'
1030
+ btn.className =
1031
+ 'teams-add-member-item' + (self.addMemberSelected.has(id) ? ' is-selected' : '')
1032
+ btn.setAttribute('role', 'option')
1033
+ btn.setAttribute('aria-selected', self.addMemberSelected.has(id) ? 'true' : 'false')
1034
+
1035
+ var cb = document.createElement('input')
1036
+ cb.type = 'checkbox'
1037
+ cb.checked = self.addMemberSelected.has(id)
1038
+ cb.tabIndex = -1
1039
+ cb.setAttribute('aria-label', '选择 ' + id)
1040
+
1041
+ var main = document.createElement('span')
1042
+ main.className = 'teams-add-member-item-main'
1043
+ var name = document.createElement('span')
1044
+ name.className = 'teams-add-member-item-name'
1045
+ name.textContent = agentListTitle(row)
1046
+ main.appendChild(name)
1047
+ var sub = agentListSubId(row)
1048
+ var descText = truncateDesc(row.whentouse, 80)
1049
+ if (sub || descText) {
1050
+ var desc = document.createElement('span')
1051
+ desc.className = 'teams-add-member-item-desc'
1052
+ desc.textContent = sub ? sub + ' · ' + descText : descText
1053
+ main.appendChild(desc)
1054
+ }
1055
+
1056
+ btn.appendChild(cb)
1057
+ btn.appendChild(main)
1058
+ btn.addEventListener('click', function (agentId, checkbox, itemBtn) {
1059
+ return function (e) {
1060
+ if (e.target === checkbox) return
1061
+ checkbox.checked = !checkbox.checked
1062
+ if (checkbox.checked) self.addMemberSelected.add(agentId)
1063
+ else self.addMemberSelected.delete(agentId)
1064
+ itemBtn.classList.toggle('is-selected', checkbox.checked)
1065
+ itemBtn.setAttribute('aria-selected', checkbox.checked ? 'true' : 'false')
1066
+ self.updateAddMemberConfirmBtn()
1067
+ }
1068
+ }(id, cb, btn))
1069
+ cb.addEventListener('click', function (agentId, checkbox, itemBtn) {
1070
+ return function (e) {
1071
+ e.stopPropagation()
1072
+ if (checkbox.checked) self.addMemberSelected.add(agentId)
1073
+ else self.addMemberSelected.delete(agentId)
1074
+ itemBtn.classList.toggle('is-selected', checkbox.checked)
1075
+ itemBtn.setAttribute('aria-selected', checkbox.checked ? 'true' : 'false')
1076
+ self.updateAddMemberConfirmBtn()
1077
+ }
1078
+ }(id, cb, btn))
1079
+
1080
+ this.addMemberList.appendChild(btn)
1081
+ }
1082
+ this.updateAddMemberConfirmBtn()
1083
+ }
1084
+
1085
+ TeamsUI.prototype.openAddMemberModal = function () {
1086
+ var teamId = this.controller.selectedTeamId
1087
+ if (!teamId || teamId === UNGROUPED_ID) return
1088
+ this.addMemberSelected.clear()
1089
+ if (this.addMemberSearch) this.addMemberSearch.value = ''
1090
+ var titleEl = document.getElementById('addMemberModalTitle')
1091
+ if (titleEl) {
1092
+ var team = this.getSelectedTeam()
1093
+ titleEl.textContent = '添加队员 · ' + (team ? teamDisplayName(team) : teamId)
1094
+ }
1095
+ this.renderAddMemberList()
1096
+ if (this.addMemberModal) {
1097
+ this.addMemberModal.classList.add('is-open')
1098
+ this.addMemberModal.setAttribute('aria-hidden', 'false')
1099
+ }
1100
+ if (this.addMemberSearch) this.addMemberSearch.focus()
1101
+ }
1102
+
1103
+ TeamsUI.prototype.closeAddMemberModal = function () {
1104
+ if (this.addMemberModal) {
1105
+ this.addMemberModal.classList.remove('is-open')
1106
+ this.addMemberModal.setAttribute('aria-hidden', 'true')
1107
+ }
1108
+ this.addMemberSelected.clear()
1109
+ this.updateAddMemberConfirmBtn()
1110
+ }
1111
+
1112
+ TeamsUI.prototype.handleAddSelectedMembers = async function () {
1113
+ var teamId = this.controller.selectedTeamId
1114
+ if (!teamId || teamId === UNGROUPED_ID) return
1115
+ var ids = Array.from(this.addMemberSelected)
1116
+ if (!ids.length) {
1117
+ this.showToast('请先选择 Agent', true)
1118
+ return
1119
+ }
1120
+ try {
1121
+ for (var i = 0; i < ids.length; i++) {
1122
+ await addTeamMember(teamId, ids[i])
1123
+ }
1124
+ this.closeAddMemberModal()
1125
+ await this.refreshAll()
1126
+ this.showToast(ids.length === 1 ? '已添加队员' : '已添加 ' + ids.length + ' 名队员')
1127
+ } catch (e) {
1128
+ this.showToast('添加失败: ' + String(e), true)
1129
+ }
1130
+ }
1131
+
1132
+ TeamsUI.prototype.refreshAddAgentPick = function () {
1133
+ /* 队员选择已改为弹窗,保留空实现避免旧调用报错 */
1134
+ }
1135
+
1136
+ TeamsUI.prototype.onTeamSelected = async function (teamId) {
1137
+ try {
1138
+ this.closePanel()
1139
+ this.controller.selectTeam(teamId)
1140
+ this.syncUrlParams()
1141
+ await this.refreshCurrentAgents()
1142
+ if (this.sidebar) {
1143
+ this.sidebar.render(this.teams, this.computeUngroupedAgents().length, teamId)
1144
+ }
1145
+ this.syncTeamChat()
1146
+ this.updateContentHead()
1147
+ this.refreshAddAgentPick()
1148
+ } catch (e) {
1149
+ this.showToast('切换团队失败: ' + friendlyFetchError(e), true)
1150
+ }
1151
+ }
1152
+
1153
+ TeamsUI.prototype.showCreateTeamForm = function () {
1154
+ this.panelMode = 'team-create'
1155
+ this.editingTeamId = null
1156
+ this.openTeamPanel()
1157
+ if (this.teamPanelTitle) this.teamPanelTitle.textContent = '新建团队'
1158
+ if (this.teamIdInput) { this.teamIdInput.value = ''; this.teamIdInput.readOnly = false }
1159
+ if (this.teamDisplayName) this.teamDisplayName.value = ''
1160
+ if (this.teamDescription) this.teamDescription.value = ''
1161
+ var btnDel = this.root.querySelector('#btnDeleteTeam')
1162
+ if (btnDel) btnDel.hidden = true
1163
+ }
1164
+
1165
+ TeamsUI.prototype.showEditTeamForm = function () {
1166
+ var team = this.getSelectedTeam()
1167
+ if (!team) return
1168
+ this.panelMode = 'team-edit'
1169
+ this.editingTeamId = team.id
1170
+ this.openTeamPanel()
1171
+ if (this.teamPanelTitle) this.teamPanelTitle.textContent = teamDisplayName(team)
1172
+ if (this.teamIdInput) { this.teamIdInput.value = team.id; this.teamIdInput.readOnly = true }
1173
+ if (this.teamDisplayName) this.teamDisplayName.value = team.displayName || ''
1174
+ if (this.teamDescription) this.teamDescription.value = team.description || ''
1175
+ var btnDel = this.root.querySelector('#btnDeleteTeam')
1176
+ if (btnDel) btnDel.hidden = false
1177
+ }
1178
+
1179
+ TeamsUI.prototype.showCreateAgentForm = function () {
1180
+ var teamId = this.controller.selectedTeamId
1181
+ if (!teamId || teamId === UNGROUPED_ID) return
1182
+ this.panelMode = 'agent-create'
1183
+ this.editingAgentId = null
1184
+ this.agentBaseConfig = null
1185
+ this.openAgentPanel()
1186
+ if (this.agentPanelTitle) this.agentPanelTitle.textContent = '新建 Agent'
1187
+ if (this.agentIdInput) {
1188
+ this.agentIdInput.value = ''
1189
+ this.agentIdInput.readOnly = false
1190
+ }
1191
+ if (this.agentWhenInput) this.agentWhenInput.value = ''
1192
+ if (this.agentPromptInput) this.agentPromptInput.value = ''
1193
+ this.updateAgentFormFooter('create')
1194
+ this.syncUrlParams()
1195
+ }
1196
+
1197
+ TeamsUI.prototype.submitTeamForm = async function () {
1198
+ var isEdit = this.panelMode === 'team-edit'
1199
+ var id = isEdit ? this.editingTeamId : (this.teamIdInput ? this.teamIdInput.value.trim() : '')
1200
+ if (!isEdit && !isValidId(id)) {
1201
+ this.showToast('团队 ID 仅支持字母、数字、下划线和中划线,长度 1~64', true)
1202
+ return
1203
+ }
1204
+ var displayName = this.teamDisplayName ? this.teamDisplayName.value : ''
1205
+ var description = this.teamDescription ? this.teamDescription.value : ''
1206
+ try {
1207
+ if (isEdit) {
1208
+ await patchTeam(id, { displayName: displayName, description: description })
1209
+ } else {
1210
+ await createTeam({ id: id, displayName: displayName, description: description })
1211
+ this.controller.selectTeam(id)
1212
+ }
1213
+ await this.refreshAll()
1214
+ this.showEditTeamForm()
1215
+ this.syncUrlParams()
1216
+ this.showToast(isEdit ? '已保存团队' : '已创建团队')
1217
+ } catch (e) {
1218
+ this.showToast((isEdit ? '保存失败: ' : '创建失败: ') + String(e), true)
1219
+ }
1220
+ }
1221
+
1222
+ TeamsUI.prototype.handleDeleteTeam = async function () {
1223
+ var id = this.editingTeamId
1224
+ if (!id) return
1225
+ if (!window.confirm('确定删除团队「' + id + '」?团队内 Agent 将保留。')) return
1226
+ try {
1227
+ await deleteTeam(id)
1228
+ this.closeTeamPanel()
1229
+ this.panelMode = 'idle'
1230
+ this.controller.selectTeam(UNGROUPED_ID)
1231
+ await this.refreshAll()
1232
+ this.syncUrlParams()
1233
+ this.showToast('已删除团队')
1234
+ } catch (e) {
1235
+ this.showToast('删除失败: ' + String(e), true)
1236
+ }
1237
+ }
1238
+
1239
+ TeamsUI.prototype.submitAgentForm = async function () {
1240
+ var isCreate = this.panelMode === 'agent-create'
1241
+ var isEdit = this.panelMode === 'agent-edit' && !!this.editingAgentId
1242
+ if (!isCreate && !isEdit) return
1243
+
1244
+ var id = isEdit
1245
+ ? this.editingAgentId
1246
+ : this.agentIdInput
1247
+ ? this.agentIdInput.value.trim()
1248
+ : ''
1249
+ if (!isEdit && !isValidId(id)) {
1250
+ this.showToast('Agent ID 仅支持字母、数字、下划线和中划线,长度 1~64', true)
1251
+ return
1252
+ }
1253
+ var whentouse = this.agentWhenInput ? this.agentWhenInput.value : ''
1254
+ var prompt = this.agentPromptInput ? this.agentPromptInput.value : ''
1255
+ var teamId = this.controller.selectedTeamId
1256
+ var teamIds = isCreate && teamId && teamId !== UNGROUPED_ID ? [teamId] : undefined
1257
+ try {
1258
+ await saveAgent({
1259
+ id: id,
1260
+ whentouse: whentouse,
1261
+ prompt: prompt,
1262
+ isEdit: isEdit,
1263
+ baseConfig: this.agentBaseConfig,
1264
+ teamIds: teamIds,
1265
+ })
1266
+ await this.refreshAll()
1267
+ if (isEdit) {
1268
+ await this.openAgentViewDrawer(id)
1269
+ this.showToast('已保存')
1270
+ } else {
1271
+ this.closeAgentPanel()
1272
+ this.panelMode = 'idle'
1273
+ this.showToast('已创建')
1274
+ }
1275
+ } catch (e) {
1276
+ this.showToast((isEdit ? '保存失败: ' : '创建失败: ') + String(e), true)
1277
+ }
1278
+ }
1279
+
1280
+ TeamsUI.prototype.handleRemoveFromTeam = async function (agentId) {
1281
+ var teamId = this.controller.selectedTeamId
1282
+ if (!agentId || !teamId || teamId === UNGROUPED_ID) return
1283
+ if (!window.confirm('确定将「' + agentId + '」移出团队?')) return
1284
+ try {
1285
+ await removeTeamMember(teamId, agentId)
1286
+ await this.refreshAll()
1287
+ this.syncUrlParams()
1288
+ this.showToast('已移出团队')
1289
+ } catch (e) {
1290
+ this.showToast('移出失败: ' + String(e), true)
1291
+ }
1292
+ }
1293
+
1294
+
1295
+ TeamsUI.prototype.syncUrlParams = function () {
1296
+ try {
1297
+ var u = new URL(window.location.href)
1298
+ var teamId = this.controller.selectedTeamId
1299
+ if (teamId) u.searchParams.set('team', teamId)
1300
+ else u.searchParams.delete('team')
1301
+ u.searchParams.delete('session')
1302
+ if (this.panelMode === 'agent-edit' && this.editingAgentId) {
1303
+ u.searchParams.set('agent', this.editingAgentId)
1304
+ } else {
1305
+ u.searchParams.delete('agent')
1306
+ }
1307
+ var qs = u.searchParams.toString()
1308
+ window.history.replaceState(null, '', u.pathname + (qs ? '?' + qs : ''))
1309
+ } catch (e) { /* ignore */ }
1310
+ }
1311
+
1312
+ TeamsUI.prototype.handleUrlParams = async function () {
1313
+ var teamParam = ''
1314
+ var agentParam = ''
1315
+ try {
1316
+ teamParam = new URLSearchParams(window.location.search).get('team') || ''
1317
+ agentParam = new URLSearchParams(window.location.search).get('agent') || ''
1318
+ } catch (e) { return }
1319
+
1320
+ var teamId = String(teamParam).trim()
1321
+ if (teamId) {
1322
+ if (teamId === UNGROUPED_ID) {
1323
+ this.controller.selectTeam(UNGROUPED_ID)
1324
+ } else if (this.teams.some(function (t) { return t.id === teamId })) {
1325
+ this.controller.selectTeam(teamId)
1326
+ } else {
1327
+ this.showToast('团队不存在', true)
1328
+ }
1329
+ } else if (this.teams.length) {
1330
+ this.controller.selectTeam(this.teams[0].id)
1331
+ } else {
1332
+ this.controller.selectTeam(UNGROUPED_ID)
1333
+ }
1334
+
1335
+ await this.refreshCurrentAgents()
1336
+ if (this.sidebar) {
1337
+ this.sidebar.render(this.teams, this.computeUngroupedAgents().length, this.controller.selectedTeamId)
1338
+ }
1339
+ this.syncTeamChat()
1340
+ this.updateContentHead()
1341
+ this.refreshAddAgentPick()
1342
+
1343
+ var agentId = String(agentParam).trim()
1344
+ if (agentId) {
1345
+ var inCurrent = this.currentAgents.some(function (a) {
1346
+ return String(a.id) === agentId
1347
+ })
1348
+ var inAll = this.allAgents.some(function (a) {
1349
+ return String(a.id) === agentId
1350
+ })
1351
+ if (inCurrent || inAll) {
1352
+ await this.openAgentViewDrawer(agentId)
1353
+ } else {
1354
+ this.showToast('Agent 不存在', true)
1355
+ }
1356
+ }
1357
+ }
1358
+
1359
+ async function main() {
1360
+ var app = document.getElementById('teamsApp')
1361
+ var sidebarRoot = document.getElementById('teamSidebar')
1362
+ var listRoot = document.getElementById('agentListRoot')
1363
+ if (!app || !sidebarRoot || !listRoot) return
1364
+
1365
+ var controller = new TeamsController()
1366
+ var ui = new TeamsUI(app, controller, null, null)
1367
+ var sidebar = new TeamSidebar(sidebarRoot, function (id) { void ui.onTeamSelected(id) })
1368
+ var agentList = new TeamAgentList(listRoot, {
1369
+ onView: function (id) { ui.viewAgent(id) },
1370
+ onRemove: function (id) { void ui.handleRemoveFromTeam(id) },
1371
+ })
1372
+ ui.sidebar = sidebar
1373
+ ui.agentList = agentList
1374
+
1375
+ if (typeof TeamChat !== 'undefined') {
1376
+ ui.teamChat = new TeamChat({
1377
+ threadEl: document.getElementById('teamChatThread'),
1378
+ promptEl: document.getElementById('teamChatPrompt'),
1379
+ statusEl: document.getElementById('teamChatStatus'),
1380
+ sendBtn: document.getElementById('btnTeamChatSend'),
1381
+ abortBtn: document.getElementById('btnTeamChatAbort'),
1382
+ clearBtn: document.getElementById('btnTeamChatClear'),
1383
+ httpOrigin: httpOriginForApi(),
1384
+ })
1385
+ }
1386
+
1387
+ try {
1388
+ await ui.refreshAll()
1389
+ await ui.handleUrlParams()
1390
+ } catch (e) {
1391
+ ui.showToast('加载失败: ' + String(e), true)
1392
+ if (ui.subtitle) ui.subtitle.textContent = '加载失败'
1393
+ }
1394
+ }
1395
+
1396
+ main().catch(function (e) { console.error(e) })
1397
+ })()
1398
+ </script>
1399
+ </body>
1400
+ </html>