@auto-ai/agent 2.1.174 → 2.1.176

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 (31) 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/general/index.html +2 -2
  10. package/dist/safe-a/manage/general/index.txt +1 -1
  11. package/dist/safe-a/manage/index.html +2 -2
  12. package/dist/safe-a/manage/index.txt +1 -1
  13. package/dist/safe-a/manage/mcp/index.html +2 -2
  14. package/dist/safe-a/manage/mcp/index.txt +1 -1
  15. package/dist/safe-a/manage/permissions/index.html +2 -2
  16. package/dist/safe-a/manage/permissions/index.txt +1 -1
  17. package/dist/safe-a/manage/skills/index.html +2 -2
  18. package/dist/safe-a/manage/skills/index.txt +1 -1
  19. package/dist/safe-a/manage/task/index.html +2 -2
  20. package/dist/safe-a/manage/task/index.txt +1 -1
  21. package/dist/safe-a/manage/teams/index.html +2 -2
  22. package/dist/safe-a/manage/teams/index.txt +1 -1
  23. package/dist/safe-a/manage/tools/index.html +2 -2
  24. package/dist/safe-a/manage/tools/index.txt +1 -1
  25. package/dist/ws-test/ws-test.css +337 -84
  26. package/dist/ws-test/ws-test.html +124 -180
  27. package/dist/ws-test/ws-test.js +770 -1119
  28. package/package.json +6 -6
  29. /package/dist/safe-a/_next/static/{dqDFW64gE_H1NTMvUHYb- → PYiNVxkl5EMV1G0oiBi-Z}/_buildManifest.js +0 -0
  30. /package/dist/safe-a/_next/static/{dqDFW64gE_H1NTMvUHYb- → PYiNVxkl5EMV1G0oiBi-Z}/_clientMiddlewareManifest.json +0 -0
  31. /package/dist/safe-a/_next/static/{dqDFW64gE_H1NTMvUHYb- → PYiNVxkl5EMV1G0oiBi-Z}/_ssgManifest.js +0 -0
@@ -105,11 +105,14 @@
105
105
  const runGroupModalTitle = $('runGroupModalTitle')
106
106
  const runGroupModalBody = $('runGroupModalBody')
107
107
  const btnRunGroupModalClose = $('btnRunGroupModalClose')
108
- const scheduleManageModal = $('scheduleManageModal')
109
- const scheduleManageModalBackdrop = $('scheduleManageModalBackdrop')
108
+ const scheduleManageView = $('scheduleManageView')
110
109
  const scheduleManageMeta = $('scheduleManageMeta')
110
+ const scheduleTasksWrap = $('scheduleTasksWrap')
111
+ const scheduleRunsWrap = $('scheduleRunsWrap')
111
112
  const scheduleTaskTableBody = $('scheduleTaskTableBody')
112
113
  const scheduleManageEmpty = $('scheduleManageEmpty')
114
+ const btnScheduleRunsBack = $('btnScheduleRunsBack')
115
+ const scheduleRunsTitle = $('scheduleRunsTitle')
113
116
  const btnScheduleManageModalClose = $('btnScheduleManageModalClose')
114
117
  const btnScheduleRefresh = $('btnScheduleRefresh')
115
118
  const btnScheduleCreateToggle = $('btnScheduleCreateToggle')
@@ -121,9 +124,6 @@
121
124
  const scheduleRecurringInput = $('scheduleRecurringInput')
122
125
  const scheduleDelayRow = $('scheduleDelayRow')
123
126
  const scheduleCronRow = $('scheduleCronRow')
124
- const scheduleRunsModal = $('scheduleRunsModal')
125
- const scheduleRunsModalBackdrop = $('scheduleRunsModalBackdrop')
126
- const scheduleRunsModalTitle = $('scheduleRunsModalTitle')
127
127
  const scheduleRunsMeta = $('scheduleRunsMeta')
128
128
  const scheduleRunsTableBody = $('scheduleRunsTableBody')
129
129
  const scheduleRunsEmpty = $('scheduleRunsEmpty')
@@ -132,7 +132,6 @@
132
132
  let scheduleRunsViewTaskName = ''
133
133
  const btnScheduleCreateCancel = $('btnScheduleCreateCancel')
134
134
  const btnScheduleCreateSubmit = $('btnScheduleCreateSubmit')
135
- const btnManageAgents = $('btnManageAgents')
136
135
  const btnManageRepo = $('btnManageRepo')
137
136
  const repoManageView = $('repoManageView')
138
137
  const chatWorkspace = $('chatWorkspace')
@@ -166,9 +165,6 @@
166
165
  const brandAgentSwitchBtnEl = $('brandAgentSwitchBtn')
167
166
  /** agent id → 展示名(来自 /api/agents) */
168
167
  const agentDisplayNameById = Object.create(null)
169
- const agentSwitchWrapEl = $('agentSwitchWrap')
170
- const agentSwitchDropdownEl = $('agentSwitchDropdown')
171
- let agentSwitchOpen = false
172
168
  const sidebarHoverTipEl = $('sidebarHoverTip')
173
169
  const sidebarAsideEl = document.querySelector('aside.sidebar')
174
170
  let sidebarHoverTipHideTimer = null
@@ -262,20 +258,20 @@
262
258
  const composerModeSelect = $('composerModeSelect')
263
259
  const composerToolAskSelect = $('composerToolAskSelect')
264
260
  const btnSaveAgentConfig = $('btnSaveAgentConfig')
265
- const agentPickerModal = $('agentPickerModal')
266
- const agentPickerModalBackdrop = $('agentPickerModalBackdrop')
267
- const agentPickerModalCard = $('agentPickerModalCard')
268
- const agentPickerModalTitle = $('agentPickerModalTitle')
269
- const agentPickerSearch = $('agentPickerSearch')
270
- const agentPickerSessionTabStrip = $('agentPickerSessionTabStrip')
271
- const agentPickerSessionTabList = $('agentPickerSessionTabList')
272
- const agentPickerList = $('agentPickerList')
273
- const btnAgentPickerClose = $('btnAgentPickerClose')
274
- const btnAgentPickerCancel = $('btnAgentPickerCancel')
275
- const btnAgentPickerOk = $('btnAgentPickerOk')
276
- const btnAgentPickerMcpJson = $('btnAgentPickerMcpJson')
277
- const btnAgentPickerToolImport = $('btnAgentPickerToolImport')
278
- const agentPickerToolImportInput = $('agentPickerToolImportInput')
261
+ const pickerManageView = $('pickerManageView')
262
+ const pickerNavPanel = $('pickerNavPanel')
263
+ const pickerNavList = $('pickerNavList')
264
+ const pickerSearch = $('pickerSearch')
265
+ const btnPickerMcpJson = $('btnPickerMcpJson')
266
+ const btnPickerImport = $('btnPickerImport')
267
+ const btnPickerCancel = $('btnPickerCancel')
268
+ const btnPickerSave = $('btnPickerSave')
269
+ const pickerEntriesTable = $('pickerEntriesTable')
270
+ const pickerTableHeadRow = $('pickerTableHeadRow')
271
+ const pickerEntriesBody = $('pickerEntriesBody')
272
+ const pickerCloudPanel = $('pickerCloudPanel')
273
+ const pickerContentState = $('pickerContentState')
274
+ const pickerImportInput = $('pickerImportInput')
279
275
  const mcpJsonInputModal = $('mcpJsonInputModal')
280
276
  const mcpJsonInputModalBackdrop = $('mcpJsonInputModalBackdrop')
281
277
  const mcpJsonInputEditor = $('mcpJsonInputEditor')
@@ -334,11 +330,18 @@
334
330
  let agentEnvFieldValues = {}
335
331
  let agentEnvActiveTab = 'settings'
336
332
  let agentEnvAgentMdLoadOk = false
333
+ const pickerManageState = {
334
+ viewActive: false,
335
+ kind: 'tools',
336
+ activeTab: '',
337
+ draftSelected: new Set(),
338
+ }
337
339
  let pickerKind = 'tools'
338
- let pickerSelected = new Set()
340
+ let pickerSelected = pickerManageState.draftSelected
339
341
  let refreshToolPickerRows = null
340
342
  let refreshMcpPickerRows = null
341
343
  let refreshSkillPickerRows = null
344
+ let pickerRenderContent = null
342
345
  let remoteMcpServers = []
343
346
  const WS_PERMISSION_MODE_KEY = 'WS_PERMISSION_MODE'
344
347
  const LLM_MODEL_KEY = 'LLM_MODEL'
@@ -2187,11 +2190,24 @@
2187
2190
  const label = agentBrandLabel(id)
2188
2191
  if (brandAgentNameEl) brandAgentNameEl.textContent = label
2189
2192
  if (brandAgentSwitchBtnEl) {
2190
- brandAgentSwitchBtnEl.setAttribute('title', '切换 Agent:' + label)
2191
- brandAgentSwitchBtnEl.setAttribute(
2192
- 'aria-label',
2193
- '当前 Agent ' + label + ',点击切换',
2194
- )
2193
+ brandAgentSwitchBtnEl.setAttribute('title', '返回首页:' + label)
2194
+ brandAgentSwitchBtnEl.setAttribute('aria-label', '返回首页:' + label)
2195
+ }
2196
+ }
2197
+
2198
+ /** 跳转 agent 办公室首页 */
2199
+ function goToAgentHomePage() {
2200
+ const cur = currentAgentParam()
2201
+ window.location.href = cur ? '/?agent=' + encodeURIComponent(cur) : '/'
2202
+ }
2203
+
2204
+ /** 清除侧栏管理导航选中态 */
2205
+ function clearSidebarNavActive() {
2206
+ const navEl = document.querySelector('.sidebar-nav')
2207
+ if (!navEl) return
2208
+ const items = navEl.querySelectorAll('.sidebar-nav-row')
2209
+ for (let i = 0; i < items.length; i++) {
2210
+ items[i].classList.remove('is-active')
2195
2211
  }
2196
2212
  }
2197
2213
 
@@ -2223,175 +2239,6 @@
2223
2239
  ).toString()
2224
2240
  }
2225
2241
 
2226
- /** 关闭 agent 切换下拉,恢复头像按钮状态 */
2227
- function closeAgentSwitchMenu() {
2228
- agentSwitchOpen = false
2229
- if (agentSwitchWrapEl) agentSwitchWrapEl.classList.remove('open')
2230
- if (agentSwitchDropdownEl) {
2231
- agentSwitchDropdownEl.classList.remove('is-open')
2232
- agentSwitchDropdownEl.hidden = true
2233
- agentSwitchDropdownEl.style.left = ''
2234
- agentSwitchDropdownEl.style.top = ''
2235
- }
2236
- if (brandAgentSwitchBtnEl) brandAgentSwitchBtnEl.setAttribute('aria-expanded', 'false')
2237
- }
2238
-
2239
- /** 根据品牌按钮位置计算浮层坐标,显示在侧栏右侧避免被 overflow 裁剪 */
2240
- function positionAgentSwitchDropdown() {
2241
- if (!brandAgentSwitchBtnEl || !agentSwitchDropdownEl || !agentSwitchOpen) return
2242
- requestAnimationFrame(function () {
2243
- if (!agentSwitchOpen || !brandAgentSwitchBtnEl || !agentSwitchDropdownEl) return
2244
- const rect = brandAgentSwitchBtnEl.getBoundingClientRect()
2245
- const gap = 10
2246
- const menuW = agentSwitchDropdownEl.offsetWidth
2247
- const menuH = agentSwitchDropdownEl.offsetHeight
2248
- let left = rect.right + gap
2249
- let top = rect.top
2250
- if (left + menuW > window.innerWidth - 12) {
2251
- left = rect.left
2252
- top = rect.bottom + gap
2253
- }
2254
- left = Math.max(12, Math.min(left, window.innerWidth - menuW - 12))
2255
- top = Math.max(12, Math.min(top, window.innerHeight - menuH - 12))
2256
- agentSwitchDropdownEl.style.left = left + 'px'
2257
- agentSwitchDropdownEl.style.top = top + 'px'
2258
- })
2259
- }
2260
-
2261
- /** 在 agent 切换下拉底部追加「管理 Agent」入口 */
2262
- function appendAgentSwitchManageFooter() {
2263
- if (!agentSwitchDropdownEl) return
2264
- const foot = document.createElement('div')
2265
- foot.className = 'agent-switch-dropdown-foot'
2266
- const manageBtn = document.createElement('button')
2267
- manageBtn.type = 'button'
2268
- manageBtn.className = 'agent-switch-manage-btn'
2269
- manageBtn.textContent = '管理 Agent…'
2270
- manageBtn.addEventListener('click', function (ev) {
2271
- ev.preventDefault()
2272
- ev.stopPropagation()
2273
- closeAgentSwitchMenu()
2274
- const cur = currentAgentParam()
2275
- window.location.href = cur ? '/?agent=' + encodeURIComponent(cur) : '/'
2276
- })
2277
- foot.appendChild(manageBtn)
2278
- agentSwitchDropdownEl.appendChild(foot)
2279
- }
2280
-
2281
- /** 渲染 agent 列表项;当前 agent 高亮,点击后立即切换 workspace */
2282
- function renderAgentSwitchList(agents) {
2283
- if (!agentSwitchDropdownEl) return
2284
- agentSwitchDropdownEl.innerHTML = ''
2285
- const current = currentAgentParam()
2286
- const head = document.createElement('div')
2287
- head.className = 'agent-switch-dropdown-head'
2288
- head.textContent = '切换 Agent'
2289
- agentSwitchDropdownEl.appendChild(head)
2290
- if (!Array.isArray(agents) || !agents.length) {
2291
- const empty = document.createElement('div')
2292
- empty.className = 'agent-switch-empty'
2293
- empty.textContent = '暂无 agent'
2294
- agentSwitchDropdownEl.appendChild(empty)
2295
- appendAgentSwitchManageFooter()
2296
- positionAgentSwitchDropdown()
2297
- return
2298
- }
2299
- const list = document.createElement('div')
2300
- list.className = 'agent-switch-dropdown-list'
2301
- const frag = document.createDocumentFragment()
2302
- for (let i = 0; i < agents.length; i++) {
2303
- const row = agents[i]
2304
- const id = String(row && row.id != null ? row.id : '').trim()
2305
- if (!id) continue
2306
- const displayName =
2307
- row && typeof row.displayName === 'string' && row.displayName.trim()
2308
- ? row.displayName.trim()
2309
- : id
2310
- const btn = document.createElement('button')
2311
- btn.type = 'button'
2312
- btn.className = 'agent-switch-item' + (id === current ? ' is-current' : '')
2313
- btn.setAttribute('role', 'option')
2314
- btn.setAttribute('aria-selected', id === current ? 'true' : 'false')
2315
- const mainEl = document.createElement('span')
2316
- mainEl.className = 'agent-switch-item-main'
2317
- const nameEl = document.createElement('span')
2318
- nameEl.className = 'agent-switch-item-name'
2319
- nameEl.textContent = displayName
2320
- mainEl.appendChild(nameEl)
2321
- if (displayName !== id) {
2322
- const idEl = document.createElement('span')
2323
- idEl.className = 'agent-switch-item-id'
2324
- idEl.textContent = id
2325
- mainEl.appendChild(idEl)
2326
- }
2327
- btn.appendChild(mainEl)
2328
- const checkEl = document.createElement('span')
2329
- checkEl.className = 'agent-switch-item-check'
2330
- checkEl.setAttribute('aria-hidden', 'true')
2331
- checkEl.innerHTML =
2332
- '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3.5 8.2 6.6 11.3 12.5 4.8"/></svg>'
2333
- btn.appendChild(checkEl)
2334
- btn.addEventListener('click', function (ev) {
2335
- ev.preventDefault()
2336
- ev.stopPropagation()
2337
- selectWorkspaceAgent(id)
2338
- })
2339
- frag.appendChild(btn)
2340
- }
2341
- list.appendChild(frag)
2342
- agentSwitchDropdownEl.appendChild(list)
2343
- appendAgentSwitchManageFooter()
2344
- cacheAgentDisplayNames(agents)
2345
- updateBrandAgent()
2346
- positionAgentSwitchDropdown()
2347
- }
2348
-
2349
- /** 选中目标 agent:复用 onAgentChanged 完成 WS 切换、会话刷新与配置重载 */
2350
- function selectWorkspaceAgent(nextId) {
2351
- const id = String(nextId || '').trim()
2352
- if (!id || !agentIdInput) return
2353
- closeAgentSwitchMenu()
2354
- if (id === currentAgentParam()) return
2355
- agentIdInput.value = id
2356
- onAgentChanged()
2357
- }
2358
-
2359
- /** 打开 agent 切换下拉,按需拉取 /api/agents 列表 */
2360
- async function openAgentSwitchMenu() {
2361
- if (!agentSwitchWrapEl || !agentSwitchDropdownEl || !brandAgentSwitchBtnEl) return
2362
- hideSidebarHoverTip()
2363
- agentSwitchOpen = true
2364
- agentSwitchWrapEl.classList.add('open')
2365
- agentSwitchDropdownEl.hidden = false
2366
- agentSwitchDropdownEl.classList.add('is-open')
2367
- brandAgentSwitchBtnEl.setAttribute('aria-expanded', 'true')
2368
- agentSwitchDropdownEl.innerHTML = '<div class="agent-switch-loading">加载中…</div>'
2369
- positionAgentSwitchDropdown()
2370
- try {
2371
- const r = await fetch(buildAgentsListUrl())
2372
- const body = await r.json().catch(function () {
2373
- return {}
2374
- })
2375
- if (!r.ok) {
2376
- throw new Error(body.message || body.error || r.statusText)
2377
- }
2378
- renderAgentSwitchList(body.agents)
2379
- } catch (e) {
2380
- agentSwitchDropdownEl.innerHTML =
2381
- '<div class="agent-switch-empty">加载失败: ' + escapeHtml(String(e)) + '</div>'
2382
- positionAgentSwitchDropdown()
2383
- }
2384
- }
2385
-
2386
- /** 点击头像时切换下拉开关 */
2387
- async function toggleAgentSwitchMenu() {
2388
- if (agentSwitchOpen) {
2389
- closeAgentSwitchMenu()
2390
- return
2391
- }
2392
- await openAgentSwitchMenu()
2393
- }
2394
-
2395
2242
  function rewriteSameOriginNavLinks() {
2396
2243
  const agent = currentAgentParam()
2397
2244
  const origin = window.location.origin
@@ -2683,9 +2530,66 @@
2683
2530
  }
2684
2531
 
2685
2532
  /** 显示/隐藏文件浏览主区域、聊天区;顶栏在文件视图下展示「文件」而非会话标题 */
2533
+ const scheduleManageState = {
2534
+ viewActive: false,
2535
+ contentView: 'list',
2536
+ }
2537
+
2538
+ /** 切换定时任务列表与执行记录面板 */
2539
+ function setScheduleContentView(view) {
2540
+ scheduleManageState.contentView = view === 'runs' ? 'runs' : 'list'
2541
+ if (scheduleTasksWrap) scheduleTasksWrap.hidden = scheduleManageState.contentView !== 'list'
2542
+ if (scheduleRunsWrap) scheduleRunsWrap.hidden = scheduleManageState.contentView !== 'runs'
2543
+ }
2544
+
2545
+ /** 显示/隐藏定时任务主视图,与聊天区等互斥 */
2546
+ function setScheduleManageVisible(visible) {
2547
+ if (visible) {
2548
+ closeAgentSettingsView()
2549
+ closePickerManageView(false)
2550
+ repoManageState.viewActive = false
2551
+ if (repoManageView) repoManageView.hidden = true
2552
+ if (btnManageRepo) btnManageRepo.classList.remove('is-active')
2553
+ }
2554
+ scheduleManageState.viewActive = visible
2555
+ if (scheduleManageView) scheduleManageView.hidden = !visible
2556
+ if (chatWorkspace) chatWorkspace.hidden = visible
2557
+ if (chatComposer) chatComposer.hidden = visible
2558
+ if (mainHeaderEl) mainHeaderEl.hidden = false
2559
+ if (visible) {
2560
+ if (mainTitleEl) mainTitleEl.textContent = '定时任务'
2561
+ if (mainSubEl) {
2562
+ mainSubEl.textContent = ''
2563
+ mainSubEl.hidden = true
2564
+ }
2565
+ } else {
2566
+ updateMainHeader()
2567
+ }
2568
+ }
2569
+
2570
+ /** 关闭定时任务主视图 */
2571
+ function closeScheduleManageView(restoreOffice) {
2572
+ if (!scheduleManageState.viewActive) return
2573
+ setScheduleContentView('list')
2574
+ scheduleRunsViewTaskId = null
2575
+ scheduleRunsViewTaskName = ''
2576
+ if (scheduleCreateForm) scheduleCreateForm.hidden = true
2577
+ setScheduleManageVisible(false)
2578
+ const navEl = document.querySelector('.sidebar-nav')
2579
+ if (navEl) {
2580
+ const items = navEl.querySelectorAll('.sidebar-nav-row[data-add]')
2581
+ for (let i = 0; i < items.length; i++) {
2582
+ items[i].classList.remove('is-active')
2583
+ }
2584
+ }
2585
+ if (restoreOffice !== false) clearSidebarNavActive()
2586
+ }
2587
+
2686
2588
  function setRepoManageVisible(visible) {
2687
2589
  if (visible) {
2688
2590
  closeAgentSettingsView()
2591
+ closePickerManageView(false)
2592
+ closeScheduleManageView(false)
2689
2593
  }
2690
2594
  repoManageState.viewActive = visible
2691
2595
  if (repoManageView) repoManageView.hidden = !visible
@@ -2715,6 +2619,8 @@
2715
2619
  */
2716
2620
  function setAgentSettingsVisible(visible) {
2717
2621
  if (visible) {
2622
+ closePickerManageView(false)
2623
+ closeScheduleManageView(false)
2718
2624
  repoManageState.viewActive = false
2719
2625
  if (repoManageView) repoManageView.hidden = true
2720
2626
  if (btnManageRepo) btnManageRepo.classList.remove('is-active')
@@ -2744,6 +2650,154 @@
2744
2650
  if (agentEnvSessionTabList) agentEnvSessionTabList.innerHTML = ''
2745
2651
  }
2746
2652
 
2653
+ const PICKER_KIND_TITLES = {
2654
+ tools: '工具',
2655
+ mcp: 'MCP',
2656
+ skills: 'Skill',
2657
+ agentTeams: 'Teams',
2658
+ }
2659
+
2660
+ /** 设置 Picker 主视图与聊天区等互斥可见性 */
2661
+ function setPickerManageVisible(visible) {
2662
+ if (visible) {
2663
+ closeAgentSettingsView()
2664
+ closeScheduleManageView(false)
2665
+ repoManageState.viewActive = false
2666
+ if (repoManageView) repoManageView.hidden = true
2667
+ if (btnManageRepo) btnManageRepo.classList.remove('is-active')
2668
+ }
2669
+ pickerManageState.viewActive = visible
2670
+ if (pickerManageView) pickerManageView.hidden = !visible
2671
+ if (chatWorkspace) chatWorkspace.hidden = visible
2672
+ if (chatComposer) chatComposer.hidden = visible
2673
+ if (mainHeaderEl) mainHeaderEl.hidden = false
2674
+ if (visible) {
2675
+ if (mainTitleEl) {
2676
+ mainTitleEl.textContent = PICKER_KIND_TITLES[pickerManageState.kind] || '管理'
2677
+ }
2678
+ if (mainSubEl) {
2679
+ mainSubEl.textContent = ''
2680
+ mainSubEl.hidden = true
2681
+ }
2682
+ } else {
2683
+ updateMainHeader()
2684
+ }
2685
+ }
2686
+
2687
+ /** 关闭 Picker 主视图;回到聊天时清除侧栏导航选中态 */
2688
+ function closePickerManageView(restoreOffice) {
2689
+ if (!pickerManageState.viewActive) return
2690
+ pickerManageState.draftSelected.clear()
2691
+ pickerKind = pickerManageState.kind
2692
+ pickerSelected = pickerManageState.draftSelected
2693
+ refreshToolPickerRows = null
2694
+ refreshMcpPickerRows = null
2695
+ refreshSkillPickerRows = null
2696
+ pickerRenderContent = null
2697
+ setPickerManageVisible(false)
2698
+ const navEl = document.querySelector('.sidebar-nav')
2699
+ if (navEl) {
2700
+ const items = navEl.querySelectorAll('.sidebar-nav-row[data-add]')
2701
+ for (let i = 0; i < items.length; i++) {
2702
+ items[i].classList.remove('is-active')
2703
+ }
2704
+ }
2705
+ if (restoreOffice !== false) clearSidebarNavActive()
2706
+ }
2707
+
2708
+ /** Picker 内容区加载/错误态;有文案时隐藏表格 */
2709
+ function setPickerContentState(message, isError) {
2710
+ setRepoPanelState(pickerContentState, message, isError)
2711
+ if (pickerEntriesTable) {
2712
+ pickerEntriesTable.hidden = !!message
2713
+ }
2714
+ }
2715
+
2716
+ /** 获取 Picker 顶栏筛选关键词 */
2717
+ function getPickerSearchQuery() {
2718
+ return (pickerSearch && pickerSearch.value.trim().toLowerCase()) || ''
2719
+ }
2720
+
2721
+ /** 切换表格与云端面板显示 */
2722
+ function setPickerContentMode(mode) {
2723
+ const isCloud = mode === 'cloud'
2724
+ if (pickerEntriesTable) pickerEntriesTable.hidden = isCloud
2725
+ if (pickerCloudPanel) pickerCloudPanel.hidden = !isCloud
2726
+ if (!isCloud && pickerContentState) {
2727
+ pickerContentState.hidden = true
2728
+ pickerContentState.textContent = ''
2729
+ }
2730
+ }
2731
+
2732
+ /** 设置 Picker 表格表头;最后一列标记为操作列并右对齐 */
2733
+ function setPickerTableHead(cols) {
2734
+ if (!pickerTableHeadRow) return
2735
+ pickerTableHeadRow.innerHTML = ''
2736
+ for (let i = 0; i < cols.length; i++) {
2737
+ const th = document.createElement('th')
2738
+ th.textContent = cols[i]
2739
+ if (i === cols.length - 1) th.className = 'col-actions'
2740
+ pickerTableHeadRow.appendChild(th)
2741
+ }
2742
+ }
2743
+
2744
+ /** 清空 Picker 表格内容 */
2745
+ function clearPickerTableBody() {
2746
+ if (pickerEntriesBody) pickerEntriesBody.innerHTML = ''
2747
+ }
2748
+
2749
+ /** 创建主视图表格行操作按钮 */
2750
+ function createManageRowBtn(text, className, onClick, opts) {
2751
+ const options = opts || {}
2752
+ const btn = document.createElement('button')
2753
+ btn.type = 'button'
2754
+ btn.className = 'btn-manage-row-action' + (className ? ' ' + className : '')
2755
+ btn.textContent = text
2756
+ if (options.disabled) btn.disabled = true
2757
+ if (options.title) btn.title = options.title
2758
+ btn.addEventListener('click', onClick)
2759
+ return btn
2760
+ }
2761
+
2762
+ /** 创建主视图表格操作列(按钮靠右) */
2763
+ function createManageActionsTd(buttons) {
2764
+ const td = document.createElement('td')
2765
+ td.className = 'col-actions'
2766
+ const wrap = document.createElement('div')
2767
+ wrap.className = 'manage-row-actions'
2768
+ for (let i = 0; i < buttons.length; i++) wrap.appendChild(buttons[i])
2769
+ td.appendChild(wrap)
2770
+ return td
2771
+ }
2772
+
2773
+ /** 控制 Picker 左侧分类栏显隐(仅 Skill 页保留分类,其余合并为单表) */
2774
+ function setPickerNavPanelVisible(visible) {
2775
+ if (!pickerNavPanel) return
2776
+ pickerNavPanel.hidden = !visible
2777
+ if (!visible && pickerNavList) pickerNavList.innerHTML = ''
2778
+ }
2779
+
2780
+ /** 渲染 Picker 左侧分类导航 */
2781
+ function renderPickerNavTabs(tabDefs, activeId, onChange) {
2782
+ if (!pickerNavList) return
2783
+ pickerNavList.innerHTML = ''
2784
+ pickerManageState.activeTab = activeId
2785
+ for (let i = 0; i < tabDefs.length; i++) {
2786
+ const tab = tabDefs[i]
2787
+ const btn = document.createElement('button')
2788
+ btn.type = 'button'
2789
+ btn.className = 'picker-nav-item' + (tab.id === activeId ? ' is-selected' : '')
2790
+ btn.setAttribute('role', 'tab')
2791
+ btn.setAttribute('aria-selected', tab.id === activeId ? 'true' : 'false')
2792
+ btn.textContent = tab.label
2793
+ btn.addEventListener('click', function () {
2794
+ if (tab.id === activeId) return
2795
+ onChange(tab.id)
2796
+ })
2797
+ pickerNavList.appendChild(btn)
2798
+ }
2799
+ }
2800
+
2747
2801
  /** 设置 entries/history 面板加载或错误态 */
2748
2802
  function setRepoPanelState(el, message, isError) {
2749
2803
  if (!el) return
@@ -3276,6 +3330,7 @@
3276
3330
  scheduleTaskTableBody.innerHTML = ''
3277
3331
  const list = Array.isArray(tasks) ? tasks : []
3278
3332
  scheduleManageEmpty.hidden = list.length > 0
3333
+ if (list.length > 0) scheduleManageEmpty.classList.remove('is-error')
3279
3334
  for (let i = 0; i < list.length; i++) {
3280
3335
  const task = list[i]
3281
3336
  if (!task || !task.id) continue
@@ -3289,7 +3344,7 @@
3289
3344
  sessionBtn.textContent = task.taskName || task.id || task.sessionId.slice(0, 8) + '…'
3290
3345
  sessionBtn.title = task.sessionId
3291
3346
  sessionBtn.addEventListener('click', function () {
3292
- closeScheduleManageModal()
3347
+ closeScheduleManageView(false)
3293
3348
  switchToSession(task.sessionId)
3294
3349
  })
3295
3350
  tdSession.appendChild(sessionBtn)
@@ -3305,39 +3360,20 @@
3305
3360
  const tdEnabled = document.createElement('td')
3306
3361
  const isEnabled = task.enabled !== false
3307
3362
  tdEnabled.textContent = isEnabled ? '启用中' : '已禁用'
3308
- const tdAction = document.createElement('td')
3309
- const runsBtn = document.createElement('button')
3310
- runsBtn.type = 'button'
3311
- runsBtn.className = 'btn-system-settings'
3312
- runsBtn.textContent = '执行记录'
3313
- runsBtn.addEventListener('click', function () {
3314
- void openScheduleRunsModal(task.id, task.taskName || task.id)
3315
- })
3316
- const runBtn = document.createElement('button')
3317
- runBtn.type = 'button'
3318
- runBtn.className = 'btn-system-settings'
3319
- runBtn.textContent = '立即执行'
3320
- runBtn.addEventListener('click', function () {
3321
- void runScheduleTaskNow(task.id)
3322
- })
3323
- const toggleBtn = document.createElement('button')
3324
- toggleBtn.type = 'button'
3325
- toggleBtn.className = 'btn-system-settings'
3326
- toggleBtn.textContent = isEnabled ? '禁用' : '启用'
3327
- toggleBtn.addEventListener('click', function () {
3328
- void patchScheduleTaskEnabled(task.id, !isEnabled)
3329
- })
3330
- const delBtn = document.createElement('button')
3331
- delBtn.type = 'button'
3332
- delBtn.className = 'btn-system-settings'
3333
- delBtn.textContent = '删除'
3334
- delBtn.addEventListener('click', function () {
3335
- void deleteScheduleTask(task.id)
3336
- })
3337
- tdAction.appendChild(runsBtn)
3338
- tdAction.appendChild(runBtn)
3339
- tdAction.appendChild(toggleBtn)
3340
- tdAction.appendChild(delBtn)
3363
+ const tdAction = createManageActionsTd([
3364
+ createManageRowBtn('执行记录', '', function () {
3365
+ void openScheduleRunsView(task.id, task.taskName || task.id)
3366
+ }),
3367
+ createManageRowBtn('立即执行', '', function () {
3368
+ void runScheduleTaskNow(task.id)
3369
+ }),
3370
+ createManageRowBtn(isEnabled ? '禁用' : '启用', isEnabled ? 'is-on' : '', function () {
3371
+ void patchScheduleTaskEnabled(task.id, !isEnabled)
3372
+ }),
3373
+ createManageRowBtn('删除', 'is-delete', function () {
3374
+ void deleteScheduleTask(task.id)
3375
+ }),
3376
+ ])
3341
3377
  tr.appendChild(tdId)
3342
3378
  tr.appendChild(tdSession)
3343
3379
  tr.appendChild(tdCron)
@@ -3493,6 +3529,7 @@
3493
3529
  scheduleRunsTableBody.innerHTML = ''
3494
3530
  const list = Array.isArray(runs) ? runs : []
3495
3531
  scheduleRunsEmpty.hidden = list.length > 0
3532
+ if (list.length > 0) scheduleRunsEmpty.classList.remove('is-error')
3496
3533
  if (scheduleRunResultPre) {
3497
3534
  scheduleRunResultPre.hidden = true
3498
3535
  scheduleRunResultPre.textContent = ''
@@ -3521,15 +3558,14 @@
3521
3558
  tdSummary.textContent = scheduleRunResultSummary(run)
3522
3559
  tdSummary.title = run.resultText || run.errorMessage || ''
3523
3560
  const tdAction = document.createElement('td')
3561
+ tdAction.className = 'col-actions'
3524
3562
  if (run.resultText && String(run.resultText).trim()) {
3525
- const viewBtn = document.createElement('button')
3526
- viewBtn.type = 'button'
3527
- viewBtn.className = 'btn-system-settings'
3528
- viewBtn.textContent = '查看'
3529
- viewBtn.addEventListener('click', function () {
3563
+ const wrap = document.createElement('div')
3564
+ wrap.className = 'manage-row-actions'
3565
+ wrap.appendChild(createManageRowBtn('查看', '', function () {
3530
3566
  showScheduleRunResultText(run.resultText)
3531
- })
3532
- tdAction.appendChild(viewBtn)
3567
+ }))
3568
+ tdAction.appendChild(wrap)
3533
3569
  }
3534
3570
  tr.appendChild(tdId)
3535
3571
  tr.appendChild(tdCreated)
@@ -3561,11 +3597,9 @@
3561
3597
  return body
3562
3598
  }
3563
3599
 
3564
- function closeScheduleRunsModal() {
3565
- if (!scheduleRunsModal) return
3566
- scheduleRunsModal.classList.remove('is-open')
3567
- scheduleRunsModal.setAttribute('aria-hidden', 'true')
3568
- document.body.style.overflow = ''
3600
+ /** 返回定时任务列表 */
3601
+ function closeScheduleRunsView() {
3602
+ setScheduleContentView('list')
3569
3603
  scheduleRunsViewTaskId = null
3570
3604
  scheduleRunsViewTaskName = ''
3571
3605
  if (scheduleRunResultPre) {
@@ -3574,29 +3608,27 @@
3574
3608
  }
3575
3609
  }
3576
3610
 
3577
- /** 打开执行记录弹窗 */
3578
- async function openScheduleRunsModal(taskId, taskName) {
3579
- if (!scheduleRunsModal || !taskId) return
3611
+ /** 在主视图内打开执行记录面板 */
3612
+ async function openScheduleRunsView(taskId, taskName) {
3613
+ if (!scheduleManageView || !taskId) return
3580
3614
  scheduleRunsViewTaskId = taskId
3581
3615
  scheduleRunsViewTaskName = taskName || taskId
3582
- if (scheduleRunsModalTitle) {
3583
- scheduleRunsModalTitle.textContent = '执行记录 · ' + scheduleRunsViewTaskName
3616
+ if (scheduleRunsTitle) {
3617
+ scheduleRunsTitle.textContent = '执行记录 · ' + scheduleRunsViewTaskName
3584
3618
  }
3585
- scheduleRunsModal.classList.add('is-open')
3586
- scheduleRunsModal.setAttribute('aria-hidden', 'false')
3587
- document.body.style.overflow = 'hidden'
3588
- if (scheduleRunsTableBody) {
3589
- scheduleRunsTableBody.innerHTML =
3590
- '<tr><td colspan="7" style="padding:1rem;text-align:center;color:#8892a8">加载中…</td></tr>'
3619
+ setScheduleContentView('runs')
3620
+ if (scheduleRunsTableBody) scheduleRunsTableBody.innerHTML = ''
3621
+ if (scheduleRunsEmpty) {
3622
+ scheduleRunsEmpty.hidden = false
3623
+ scheduleRunsEmpty.textContent = '加载中…'
3591
3624
  }
3592
3625
  try {
3593
3626
  await loadScheduleRuns(taskId)
3594
3627
  } catch (e) {
3595
- if (scheduleRunsTableBody) {
3596
- scheduleRunsTableBody.innerHTML =
3597
- '<tr><td colspan="7" style="padding:1rem;color:#c0392b">加载失败: ' +
3598
- String(e) +
3599
- '</td></tr>'
3628
+ if (scheduleRunsEmpty) {
3629
+ scheduleRunsEmpty.hidden = false
3630
+ scheduleRunsEmpty.textContent = '加载失败: ' + String(e)
3631
+ scheduleRunsEmpty.classList.add('is-error')
3600
3632
  }
3601
3633
  }
3602
3634
  }
@@ -3619,7 +3651,7 @@
3619
3651
  ? '\n执行记录 ID: ' + run.id + '(状态: ' + scheduleRunStatusLabel(run.status) + ')'
3620
3652
  : ''
3621
3653
  window.alert('已加入执行队列' + runHint)
3622
- if (scheduleRunsViewTaskId === id && scheduleRunsModal && scheduleRunsModal.classList.contains('is-open')) {
3654
+ if (scheduleRunsViewTaskId === id && scheduleManageState.viewActive && scheduleManageState.contentView === 'runs') {
3623
3655
  try {
3624
3656
  await loadScheduleRuns(id)
3625
3657
  } catch (e) {
@@ -3655,32 +3687,24 @@
3655
3687
  await loadScheduleTasks()
3656
3688
  }
3657
3689
 
3658
- function closeScheduleManageModal() {
3659
- if (!scheduleManageModal) return
3660
- scheduleManageModal.classList.remove('is-open')
3661
- scheduleManageModal.setAttribute('aria-hidden', 'true')
3662
- document.body.style.overflow = ''
3663
- if (scheduleCreateForm) scheduleCreateForm.hidden = true
3664
- }
3665
-
3666
- /** 打开定时任务管理弹窗 */
3667
- async function openScheduleManageModal() {
3668
- if (!scheduleManageModal) return
3669
- scheduleManageModal.classList.add('is-open')
3670
- scheduleManageModal.setAttribute('aria-hidden', 'false')
3671
- document.body.style.overflow = 'hidden'
3672
- if (scheduleTaskTableBody) {
3673
- scheduleTaskTableBody.innerHTML =
3674
- '<tr><td colspan="8" style="padding:1rem;text-align:center;color:#8892a8">加载中…</td></tr>'
3690
+ /** 打开定时任务主视图 */
3691
+ async function openScheduleManageView() {
3692
+ if (!scheduleManageView) return
3693
+ setScheduleContentView('list')
3694
+ setScheduleManageVisible(true)
3695
+ if (scheduleTaskTableBody) scheduleTaskTableBody.innerHTML = ''
3696
+ if (scheduleManageEmpty) {
3697
+ scheduleManageEmpty.hidden = false
3698
+ scheduleManageEmpty.textContent = '加载中…'
3699
+ scheduleManageEmpty.classList.remove('is-error')
3675
3700
  }
3676
3701
  try {
3677
3702
  await loadScheduleTasks()
3678
3703
  } catch (e) {
3679
- if (scheduleTaskTableBody) {
3680
- scheduleTaskTableBody.innerHTML =
3681
- '<tr><td colspan="8" style="padding:1rem;color:#c0392b">加载失败: ' +
3682
- String(e) +
3683
- '</td></tr>'
3704
+ if (scheduleManageEmpty) {
3705
+ scheduleManageEmpty.hidden = false
3706
+ scheduleManageEmpty.textContent = '加载失败: ' + String(e)
3707
+ scheduleManageEmpty.classList.add('is-error')
3684
3708
  }
3685
3709
  }
3686
3710
  }
@@ -4733,6 +4757,8 @@
4733
4757
  function switchToSession(id) {
4734
4758
  closeRepoManageView()
4735
4759
  closeAgentSettingsView()
4760
+ closePickerManageView()
4761
+ closeScheduleManageView(false)
4736
4762
  const nextSid = typeof id === 'string' ? id : ''
4737
4763
  sessionIdInput.value = nextSid
4738
4764
  currentSessionContextPercent = null
@@ -6368,22 +6394,6 @@
6368
6394
  }
6369
6395
  }
6370
6396
 
6371
- function closeAgentPickerModal() {
6372
- if (!agentPickerModal) return
6373
- agentPickerModal.classList.remove('is-open')
6374
- agentPickerModal.setAttribute('aria-hidden', 'true')
6375
- document.body.style.overflow = ''
6376
- if (agentPickerModalCard) {
6377
- agentPickerModalCard.classList.remove('agent-picker--tabbed')
6378
- agentPickerModalCard.removeAttribute('aria-label')
6379
- agentPickerModalCard.setAttribute('aria-labelledby', 'agentPickerModalTitle')
6380
- }
6381
- if (agentPickerSessionTabStrip) agentPickerSessionTabStrip.setAttribute('hidden', '')
6382
- if (agentPickerSessionTabList) agentPickerSessionTabList.innerHTML = ''
6383
- refreshToolPickerRows = null
6384
- refreshMcpPickerRows = null
6385
- refreshSkillPickerRows = null
6386
- }
6387
6397
 
6388
6398
  function closeMcpJsonInputModal() {
6389
6399
  if (!mcpJsonInputModal) return
@@ -6413,62 +6423,12 @@
6413
6423
  })
6414
6424
  }
6415
6425
 
6416
- function createPickerTabs(activeTab, onChange, options) {
6417
- const wrap = document.createElement('div')
6418
- wrap.className = 'agent-picker-tabs'
6419
- const showCloud = !(options && options.hideCloud)
6420
- const L = (options && options.tabLabels) || {}
6421
- const tabs = [
6422
- { id: 'base', label: L.base || '基础工具列表' },
6423
- { id: 'installed', label: L.installed || '已安装工具列表' },
6424
- ]
6425
- if (showCloud) tabs.push({ id: 'cloud', label: L.cloud || '云端工具列表' })
6426
- for (let i = 0; i < tabs.length; i++) {
6427
- const tab = tabs[i]
6428
- const btn = document.createElement('button')
6429
- btn.type = 'button'
6430
- btn.className = 'btn-picker-tab' + (tab.id === activeTab ? ' is-on' : '')
6431
- btn.textContent = tab.label
6432
- btn.addEventListener('click', function () {
6433
- if (tab.id === activeTab) return
6434
- onChange(tab.id)
6435
- })
6436
- wrap.appendChild(btn)
6437
- }
6438
- return wrap
6439
- }
6440
-
6441
- function renderHeaderTabsIn(listEl, tabDefs, activeId, onChange) {
6442
- if (!listEl) return
6443
- listEl.innerHTML = ''
6444
- for (let i = 0; i < tabDefs.length; i++) {
6445
- const tab = tabDefs[i]
6446
- const btn = document.createElement('button')
6447
- btn.type = 'button'
6448
- btn.className = 'session-tab' + (tab.id === activeId ? ' active' : '')
6449
- btn.setAttribute('role', 'tab')
6450
- btn.setAttribute('aria-selected', tab.id === activeId ? 'true' : 'false')
6451
- const span = document.createElement('span')
6452
- span.className = 'session-tab-label'
6453
- span.textContent = tab.label
6454
- btn.appendChild(span)
6455
- btn.addEventListener('click', function () {
6456
- if (tab.id === activeId) return
6457
- onChange(tab.id)
6458
- })
6459
- listEl.appendChild(btn)
6460
- }
6461
- }
6462
- function renderPickerHeaderTabs(tabDefs, activeId, onChange) {
6463
- renderHeaderTabsIn(agentPickerSessionTabList, tabDefs, activeId, onChange)
6464
- }
6465
-
6466
6426
  function createCloudPager(state, onPageChange) {
6467
6427
  const pager = document.createElement('div')
6468
6428
  pager.className = 'agent-picker-cloud-pager'
6469
6429
  const prev = document.createElement('button')
6470
6430
  prev.type = 'button'
6471
- prev.className = 'btn-picker-action'
6431
+ prev.className = 'btn-manage-row-action'
6472
6432
  prev.textContent = '上一页'
6473
6433
  prev.disabled = state.page <= 1 || state.loading
6474
6434
  prev.addEventListener('click', function () {
@@ -6477,7 +6437,7 @@
6477
6437
  })
6478
6438
  const next = document.createElement('button')
6479
6439
  next.type = 'button'
6480
- next.className = 'btn-picker-action'
6440
+ next.className = 'btn-manage-row-action'
6481
6441
  next.textContent = '下一页'
6482
6442
  const totalPages = state.totalPages || 0
6483
6443
  next.disabled = state.loading || totalPages <= 0 || state.page >= totalPages
@@ -6494,176 +6454,49 @@
6494
6454
  return pager
6495
6455
  }
6496
6456
 
6497
- function openAgentPickerModal(kind) {
6457
+ /** 打开 Picker 主视图:工具 / MCP / Skill / Teams */
6458
+ function openPickerManageView(kind) {
6459
+ if (!pickerManageView) return
6460
+ pickerManageState.kind = kind
6498
6461
  pickerKind = kind
6499
- pickerSelected = new Set()
6462
+ pickerManageState.draftSelected.clear()
6500
6463
  if (kind === 'tools') {
6501
6464
  for (let i = 0; i < agentConfigState.tools.length; i++) {
6502
- pickerSelected.add(agentConfigState.tools[i])
6465
+ pickerManageState.draftSelected.add(agentConfigState.tools[i])
6503
6466
  }
6504
6467
  } else if (kind === 'mcp') {
6505
6468
  for (let i = 0; i < agentConfigState.mcpServers.length; i++) {
6506
- pickerSelected.add(agentConfigState.mcpServers[i])
6469
+ pickerManageState.draftSelected.add(agentConfigState.mcpServers[i])
6507
6470
  }
6508
6471
  } else if (kind === 'agentTeams') {
6509
6472
  for (let i = 0; i < agentConfigState.agentTeams.length; i++) {
6510
- pickerSelected.add(agentConfigState.agentTeams[i])
6473
+ pickerManageState.draftSelected.add(agentConfigState.agentTeams[i])
6511
6474
  }
6512
6475
  } else {
6513
6476
  for (let i = 0; i < agentConfigState.skills.length; i++) {
6514
- pickerSelected.add(agentConfigState.skills[i])
6515
- }
6516
- }
6517
- if (!agentPickerModal || !agentPickerModalTitle || !agentPickerList) return
6518
- if (agentPickerModalCard) {
6519
- if (kind === 'tools' || kind === 'skills' || kind === 'mcp' || kind === 'agentTeams') {
6520
- agentPickerModalCard.classList.add('agent-picker--tabbed')
6521
- if (kind === 'tools') {
6522
- agentPickerModalCard.setAttribute('aria-label', '工具')
6523
- } else if (kind === 'skills') {
6524
- agentPickerModalCard.setAttribute('aria-label', 'Skill')
6525
- } else if (kind === 'mcp') {
6526
- agentPickerModalCard.setAttribute('aria-label', 'MCP')
6527
- } else {
6528
- agentPickerModalCard.setAttribute('aria-label', 'Teams')
6529
- }
6530
- agentPickerModalCard.removeAttribute('aria-labelledby')
6531
- if (agentPickerSessionTabStrip) {
6532
- if (kind === 'tools') {
6533
- agentPickerSessionTabStrip.setAttribute('aria-label', '工具分类')
6534
- } else if (kind === 'skills') {
6535
- agentPickerSessionTabStrip.setAttribute('aria-label', 'Skill 分类')
6536
- } else if (kind === 'mcp') {
6537
- agentPickerSessionTabStrip.setAttribute('aria-label', 'MCP 分类')
6538
- } else {
6539
- agentPickerSessionTabStrip.setAttribute('aria-label', 'Teams 分类')
6540
- }
6541
- agentPickerSessionTabStrip.removeAttribute('hidden')
6542
- }
6543
- } else {
6544
- agentPickerModalCard.classList.remove('agent-picker--tabbed')
6545
- agentPickerModalCard.removeAttribute('aria-label')
6546
- agentPickerModalCard.setAttribute('aria-labelledby', 'agentPickerModalTitle')
6547
- if (agentPickerSessionTabStrip) agentPickerSessionTabStrip.setAttribute('hidden', '')
6548
- if (agentPickerSessionTabList) agentPickerSessionTabList.innerHTML = ''
6549
- }
6550
- }
6551
- agentPickerModal.classList.add('is-open')
6552
- agentPickerModal.setAttribute('aria-hidden', 'false')
6553
- document.body.style.overflow = 'hidden'
6554
- if (agentPickerSearch) agentPickerSearch.value = ''
6555
- const titles = {
6556
- tools: '工具',
6557
- mcp: 'MCP',
6558
- skills: 'Skill',
6559
- agentTeams: 'Teams',
6560
- }
6561
- agentPickerModalTitle.textContent = titles[kind] || '选择'
6562
- if (btnAgentPickerToolImport) {
6563
- btnAgentPickerToolImport.style.display =
6564
- kind === 'tools' || kind === 'mcp' || kind === 'skills' ? '' : 'none'
6565
- const importLabel =
6566
- kind === 'mcp' ? '导入MCP' : kind === 'skills' ? '导入Skill' : '导入zip'
6567
- btnAgentPickerToolImport.setAttribute('aria-label', importLabel)
6568
- btnAgentPickerToolImport.setAttribute('title', importLabel)
6569
- btnAgentPickerToolImport.disabled = false
6570
- }
6571
- if (btnAgentPickerMcpJson) {
6572
- btnAgentPickerMcpJson.style.display = kind === 'mcp' ? '' : 'none'
6573
- btnAgentPickerMcpJson.disabled = false
6574
- }
6575
- agentPickerList.innerHTML = '加载中…'
6576
-
6577
- function renderList(rows, labelFn) {
6578
- if (!agentPickerList) return
6579
- agentPickerList.innerHTML = ''
6580
- const q = (agentPickerSearch && agentPickerSearch.value.trim().toLowerCase()) || ''
6581
- const frag = document.createDocumentFragment()
6582
- for (let i = 0; i < rows.length; i++) {
6583
- const row = rows[i]
6584
- const id = row.id
6585
- const lab = labelFn(row)
6586
- if (q && String(lab).toLowerCase().indexOf(q) < 0 && String(id).toLowerCase().indexOf(q) < 0) {
6587
- continue
6588
- }
6589
- const div = document.createElement('div')
6590
- div.className = 'agent-picker-item'
6591
- const labelEl = document.createElement('div')
6592
- labelEl.className = 'agent-picker-item-label'
6593
- labelEl.textContent = String(lab)
6594
- div.appendChild(labelEl)
6595
-
6596
- const actions = document.createElement('div')
6597
- actions.className = 'agent-picker-actions'
6598
-
6599
- const toggleBtn = document.createElement('button')
6600
- toggleBtn.type = 'button'
6601
- const selected = pickerSelected.has(id)
6602
- toggleBtn.className = 'btn-picker-action' + (selected ? ' is-on' : '')
6603
- toggleBtn.textContent = selected ? '禁用' : '启用'
6604
- toggleBtn.addEventListener('click', function () {
6605
- if (pickerSelected.has(id)) pickerSelected.delete(id)
6606
- else pickerSelected.add(id)
6607
- renderList(rows, labelFn)
6608
- })
6609
- actions.appendChild(toggleBtn)
6610
-
6611
- if (pickerKind === 'tools') {
6612
- const delBtn = document.createElement('button')
6613
- delBtn.type = 'button'
6614
- delBtn.className = 'btn-picker-action is-delete'
6615
- delBtn.textContent = '删除'
6616
- const isSystemTool = !(row.t && row.t.isRuntime === true)
6617
- delBtn.disabled = isSystemTool
6618
- if (isSystemTool) {
6619
- delBtn.title = '系统工具不可删除'
6620
- }
6621
- delBtn.addEventListener('click', function () {
6622
- if (delBtn.disabled) return
6623
- const targetTool = id
6624
- const ok = window.confirm('确认删除工具包?\n工具名:' + targetTool)
6625
- if (!ok) return
6626
- delBtn.disabled = true
6627
- fetch(buildRuntimeToolDeleteUrl(targetTool), { method: 'DELETE' })
6628
- .then(function (r) {
6629
- return r.json().catch(function () {
6630
- return {}
6631
- }).then(function (b) {
6632
- return { r: r, b: b }
6633
- })
6634
- })
6635
- .then(function (x) {
6636
- if (!x.r.ok) throw new Error(x.b.message || x.r.statusText)
6637
- pickerSelected.delete(targetTool)
6638
- const nextRows = rows.filter(function (item) {
6639
- return item.id !== targetTool
6640
- })
6641
- rows.splice(0, rows.length)
6642
- for (let j = 0; j < nextRows.length; j++) rows.push(nextRows[j])
6643
- renderList(rows, labelFn)
6644
- agentConfigState.tools = agentConfigState.tools.filter(function (name) {
6645
- return name !== targetTool
6646
- })
6647
- renderBindChips()
6648
- void persistAgentConfigAndRestartSession({ silent: true })
6649
- })
6650
- .catch(function (e) {
6651
- window.alert('删除失败: ' + e)
6652
- delBtn.disabled = false
6653
- })
6654
- })
6655
- actions.appendChild(delBtn)
6656
- }
6657
-
6658
- div.appendChild(actions)
6659
- frag.appendChild(div)
6660
- }
6661
- if (!frag.childNodes.length) {
6662
- agentPickerList.textContent = '(无匹配项)'
6663
- return
6664
- }
6665
- agentPickerList.appendChild(frag)
6666
- }
6477
+ pickerManageState.draftSelected.add(agentConfigState.skills[i])
6478
+ }
6479
+ }
6480
+ pickerSelected = pickerManageState.draftSelected
6481
+ setPickerManageVisible(true)
6482
+ if (mainTitleEl) mainTitleEl.textContent = PICKER_KIND_TITLES[kind] || '管理'
6483
+ if (pickerSearch) pickerSearch.value = ''
6484
+ if (btnPickerImport) {
6485
+ btnPickerImport.hidden = !(kind === 'tools' || kind === 'mcp' || kind === 'skills')
6486
+ btnPickerImport.textContent =
6487
+ kind === 'mcp' ? '导入 MCP' : kind === 'skills' ? '导入 Skill' : '导入 zip'
6488
+ btnPickerImport.disabled = false
6489
+ }
6490
+ if (btnPickerMcpJson) {
6491
+ btnPickerMcpJson.hidden = kind !== 'mcp'
6492
+ btnPickerMcpJson.disabled = false
6493
+ }
6494
+ if (btnPickerCancel) btnPickerCancel.hidden = kind === 'skills' || kind === 'mcp'
6495
+ if (btnPickerSave) btnPickerSave.hidden = kind === 'skills' || kind === 'mcp'
6496
+ setPickerNavPanelVisible(kind === 'skills')
6497
+ setPickerContentState('加载中…', false)
6498
+ setPickerContentMode('table')
6499
+ clearPickerTableBody()
6667
6500
 
6668
6501
  if (kind === 'tools') {
6669
6502
  const toolPickerState = {
@@ -6717,49 +6550,33 @@
6717
6550
  })
6718
6551
  })
6719
6552
  }
6720
- const makeManageRow = function (row) {
6553
+ const makeToolTableRow = function (row) {
6721
6554
  const id = row.id
6722
6555
  const canDelete = row.deletable === true
6723
6556
  const packageId = row.packageId || ''
6724
- const div = document.createElement('div')
6725
- div.className = 'agent-picker-item'
6726
- const labelEl = document.createElement('div')
6727
- labelEl.className = 'agent-picker-item-label'
6728
- labelEl.textContent = toolLabel(row) + (row.layerTag || '')
6729
- div.appendChild(labelEl)
6730
- const actions = document.createElement('div')
6731
- actions.className = 'agent-picker-actions'
6732
- const toggleBtn = document.createElement('button')
6733
- toggleBtn.type = 'button'
6557
+ const tr = document.createElement('tr')
6558
+ const nameTd = document.createElement('td')
6559
+ nameTd.textContent = toolLabel(row)
6560
+ tr.appendChild(nameTd)
6561
+ const typeTd = document.createElement('td')
6562
+ const layer = row.t && row.t.layer === 'system' ? '系统' : row.t && row.t.layer === 'agent' ? 'Agent' : '—'
6563
+ typeTd.textContent = layer
6564
+ tr.appendChild(typeTd)
6734
6565
  const selected = pickerSelected.has(id)
6735
- toggleBtn.className = 'btn-picker-action' + (selected ? ' is-on' : '')
6736
- toggleBtn.textContent = selected ? '禁用' : '启用'
6737
- toggleBtn.addEventListener('click', function () {
6738
- if (pickerSelected.has(id)) pickerSelected.delete(id)
6739
- else pickerSelected.add(id)
6740
- renderToolsRows()
6741
- })
6742
- actions.appendChild(toggleBtn)
6566
+ const buttons = [
6567
+ createManageRowBtn(selected ? '禁用' : '启用', selected ? 'is-on' : '', function () {
6568
+ if (pickerSelected.has(id)) pickerSelected.delete(id)
6569
+ else pickerSelected.add(id)
6570
+ renderToolsRows()
6571
+ }),
6572
+ ]
6743
6573
  if (canDelete && packageId) {
6744
- const exportBtn = document.createElement('button')
6745
- exportBtn.type = 'button'
6746
- exportBtn.className = 'btn-picker-action'
6747
- exportBtn.textContent = '导出'
6748
- exportBtn.addEventListener('click', function () {
6574
+ buttons.push(createManageRowBtn('导出', '', function () {
6749
6575
  window.open(buildRuntimeToolExportUrl(packageId), '_blank', 'noopener')
6750
- })
6751
- actions.appendChild(exportBtn)
6576
+ }))
6752
6577
  }
6753
- const delBtn = document.createElement('button')
6754
- delBtn.type = 'button'
6755
- delBtn.className = 'btn-picker-action is-delete'
6756
- delBtn.textContent = '删除'
6757
- delBtn.disabled = !canDelete || !packageId
6758
- if (!canDelete) {
6759
- delBtn.title = '系统内置工具不可删除'
6760
- }
6761
- delBtn.addEventListener('click', function () {
6762
- if (delBtn.disabled) return
6578
+ buttons.push(createManageRowBtn('删除', 'is-delete', function () {
6579
+ if (!canDelete || !packageId) return
6763
6580
  fetch(buildRuntimeToolDeleteUrl(packageId), { method: 'DELETE' })
6764
6581
  .then(function (r) {
6765
6582
  return r.json().catch(function () { return {} }).then(function (b) { return { r: r, b: b } })
@@ -6773,23 +6590,28 @@
6773
6590
  return refreshToolPickerRows()
6774
6591
  })
6775
6592
  .catch(function (e) { window.alert('删除失败: ' + e) })
6776
- })
6777
- actions.appendChild(delBtn)
6778
- div.appendChild(actions)
6779
- return div
6593
+ }, { disabled: !canDelete || !packageId, title: !canDelete ? '系统内置工具不可删除' : '' }))
6594
+ tr.appendChild(createManageActionsTd(buttons))
6595
+ return tr
6780
6596
  }
6781
6597
  const renderToolsRows = function () {
6782
- if (!agentPickerList) return
6783
- agentPickerList.innerHTML = ''
6784
- const rows = toolPickerState.installedRows
6598
+ setPickerContentMode('table')
6599
+ setPickerTableHead(['名称', '类型', '操作'])
6600
+ clearPickerTableBody()
6601
+ setPickerContentState('', false)
6602
+ const q = getPickerSearchQuery()
6603
+ const rows = toolPickerState.installedRows.filter(function (row) {
6604
+ return toolMatches(row, q)
6605
+ })
6785
6606
  if (!rows.length) {
6786
- agentPickerList.appendChild(document.createTextNode('(暂无已安装工具)'))
6607
+ setPickerContentState(toolPickerState.installedRows.length ? '(无匹配项)' : '(暂无已安装工具)', false)
6787
6608
  return
6788
6609
  }
6789
- for (let i = 0; i < rows.length; i++) {
6790
- agentPickerList.appendChild(makeManageRow(rows[i]))
6791
- }
6610
+ const frag = document.createDocumentFragment()
6611
+ for (let i = 0; i < rows.length; i++) frag.appendChild(makeToolTableRow(rows[i]))
6612
+ if (pickerEntriesBody) pickerEntriesBody.appendChild(frag)
6792
6613
  }
6614
+ pickerRenderContent = renderToolsRows
6793
6615
  refreshToolPickerRows = function () {
6794
6616
  return fetchToolRows(buildToolInstalledListUrl).then(function (rows) {
6795
6617
  toolPickerState.installedRows = rows
@@ -6797,12 +6619,9 @@
6797
6619
  })
6798
6620
  }
6799
6621
  void refreshToolPickerRows().catch(function (e) {
6800
- if (agentPickerList) agentPickerList.textContent = '加载失败: ' + e
6622
+ setPickerContentState('加载失败: ' + e, true)
6801
6623
  })
6802
6624
  } else if (kind === 'mcp') {
6803
- const mcpPickerState = {
6804
- activeTab: 'base',
6805
- }
6806
6625
  fetch(buildMcpServersListUrl())
6807
6626
  .then(function (r) {
6808
6627
  return r.json().then(function (b) {
@@ -6818,231 +6637,153 @@
6818
6637
  const mcpLabel = function (row) {
6819
6638
  return row.s.name + (row.s.disabled ? '(已禁用)' : '')
6820
6639
  }
6821
- const createMcpSection = function (title, sectionRows, opts) {
6640
+ /** MCP 启用状态写入 agent 配置并立即持久化 */
6641
+ const persistMcpSelectionNow = function () {
6642
+ agentConfigState.mcpServers = [...pickerSelected].sort()
6643
+ renderBindChips()
6644
+ return persistAgentConfigAndRestartSession({ silent: true })
6645
+ }
6646
+ const mcpSourceLabel = function (source) {
6647
+ if (source === 'runtime') return '本地'
6648
+ if (source === 'remote') return '远程'
6649
+ return source || '—'
6650
+ }
6651
+ const makeMcpTableRow = function (row, opts) {
6822
6652
  const options = opts || {}
6823
6653
  const showExport = !!options.showExport
6824
6654
  const showDelete = !!options.showDelete
6825
- const sec = document.createElement('div')
6826
- sec.className = 'agent-picker-section'
6827
- const titleEl = document.createElement('div')
6828
- titleEl.className = 'agent-picker-section-title'
6829
- titleEl.textContent = title
6830
- sec.appendChild(titleEl)
6831
- if (!sectionRows.length) {
6832
- const empty = document.createElement('div')
6833
- empty.className = 'agent-picker-empty'
6834
- empty.textContent = '(暂无)'
6835
- sec.appendChild(empty)
6836
- return sec
6655
+ const id = row.id
6656
+ const source = row.s && row.s.source ? String(row.s.source) : ''
6657
+ const tr = document.createElement('tr')
6658
+ const nameTd = document.createElement('td')
6659
+ nameTd.textContent = mcpLabel(row)
6660
+ tr.appendChild(nameTd)
6661
+ const sourceTd = document.createElement('td')
6662
+ sourceTd.textContent = mcpSourceLabel(source)
6663
+ tr.appendChild(sourceTd)
6664
+ const buttons = []
6665
+ if (showExport) {
6666
+ const canExport = source === 'runtime'
6667
+ buttons.push(createManageRowBtn('导出', '', function () {
6668
+ if (!canExport) return
6669
+ const pkgId = row.s && row.s.packageId ? String(row.s.packageId) : id
6670
+ window.open(buildRuntimeMcpExportUrl(pkgId), '_blank', 'noopener')
6671
+ }, { disabled: !canExport, title: !canExport ? '仅 runtime MCP 支持导出' : '' }))
6837
6672
  }
6838
- for (let i = 0; i < sectionRows.length; i++) {
6839
- const row = sectionRows[i]
6840
- const id = row.id
6841
- const source = row.s && row.s.source ? String(row.s.source) : ''
6842
- const div = document.createElement('div')
6843
- div.className = 'agent-picker-item'
6844
- const labelEl = document.createElement('div')
6845
- labelEl.className = 'agent-picker-item-label'
6846
- labelEl.textContent = mcpLabel(row)
6847
- div.appendChild(labelEl)
6848
- const actions = document.createElement('div')
6849
- actions.className = 'agent-picker-actions'
6850
- if (showExport) {
6851
- const exportBtn = document.createElement('button')
6852
- exportBtn.type = 'button'
6853
- exportBtn.className = 'btn-picker-action'
6854
- exportBtn.textContent = '导出'
6855
- const canExport = source === 'runtime'
6856
- exportBtn.disabled = !canExport
6857
- if (!canExport) {
6858
- exportBtn.title = '仅 runtime MCP 支持导出'
6673
+ buttons.push(createManageRowBtn('编辑', '', function () {
6674
+ const src = source === 'remote' ? 'remote' : 'runtime'
6675
+ openMcpItemConfigEditor(id, src, function () {
6676
+ if (typeof refreshMcpPickerRows === 'function') {
6677
+ void refreshMcpPickerRows().then(function () { renderMcpRows() })
6859
6678
  }
6860
- exportBtn.addEventListener('click', function () {
6861
- if (exportBtn.disabled) return
6862
- const pkgId = row.s && row.s.packageId ? String(row.s.packageId) : id
6863
- window.open(buildRuntimeMcpExportUrl(pkgId), '_blank', 'noopener')
6679
+ })
6680
+ }))
6681
+ const selected = pickerSelected.has(id)
6682
+ const toggleBtn = createManageRowBtn(selected ? '禁用' : '启用', selected ? 'is-on' : '', function () {
6683
+ toggleBtn.disabled = true
6684
+ if (pickerSelected.has(id)) pickerSelected.delete(id)
6685
+ else pickerSelected.add(id)
6686
+ persistMcpSelectionNow()
6687
+ .then(function () {
6688
+ renderMcpRows()
6864
6689
  })
6865
- actions.appendChild(exportBtn)
6866
- }
6867
- const editBtn = document.createElement('button')
6868
- editBtn.type = 'button'
6869
- editBtn.className = 'btn-picker-action'
6870
- editBtn.textContent = '编辑'
6871
- editBtn.addEventListener('click', function () {
6872
- const src = source === 'remote' ? 'remote' : 'runtime'
6873
- openMcpItemConfigEditor(id, src, function () {
6874
- if (typeof refreshMcpPickerRows === 'function') {
6875
- void refreshMcpPickerRows().then(function () {
6876
- renderMcpRows()
6877
- })
6878
- }
6690
+ .catch(function (e) {
6691
+ window.alert('切换启用状态失败: ' + e)
6879
6692
  })
6880
- })
6881
- actions.appendChild(editBtn)
6882
- const toggleBtn = document.createElement('button')
6883
- toggleBtn.type = 'button'
6884
- toggleBtn.className = 'btn-picker-action'
6885
- const enabledNow = pickerSelected.has(id)
6886
- toggleBtn.textContent = enabledNow ? '禁用' : '启用'
6887
- toggleBtn.addEventListener('click', function () {
6888
- toggleBtn.disabled = true
6889
- if (enabledNow) {
6890
- pickerSelected.delete(id)
6891
- agentConfigState.mcpServers = agentConfigState.mcpServers.filter(function (name) {
6892
- return name !== id
6893
- })
6894
- } else {
6895
- pickerSelected.add(id)
6896
- if (agentConfigState.mcpServers.indexOf(id) < 0) {
6897
- agentConfigState.mcpServers.push(id)
6898
- }
6899
- }
6900
- persistAgentConfigAndRestartSession({ silent: true })
6901
- .then(function () {
6902
- renderBindChips()
6903
- renderMcpRows()
6904
- })
6905
- .catch(function (e) {
6906
- window.alert('切换启用状态失败: ' + e)
6693
+ .finally(function () {
6694
+ toggleBtn.disabled = false
6907
6695
  })
6908
- .finally(function () {
6909
- toggleBtn.disabled = false
6910
- })
6911
- })
6912
- actions.appendChild(toggleBtn)
6913
- if (showDelete) {
6914
- const delBtn = document.createElement('button')
6915
- delBtn.type = 'button'
6916
- delBtn.className = 'btn-picker-action is-delete'
6917
- delBtn.textContent = '删除'
6918
- const canDelete = source === 'runtime' || source === 'remote'
6919
- delBtn.disabled = !canDelete
6920
- if (!canDelete) {
6921
- delBtn.title = '基础MCP不可删除'
6922
- }
6923
- delBtn.addEventListener('click', function () {
6924
- if (delBtn.disabled) return
6925
- const ok = window.confirm('确认删除 MCP?\n服务器名:' + id)
6926
- if (!ok) return
6927
- delBtn.disabled = true
6928
- if (source === 'remote') {
6929
- fetch(buildRemoteMcpJsonUrl())
6930
- .then(function (r) {
6931
- return r.json().then(function (body) {
6932
- return { r: r, body: body }
6933
- })
6934
- })
6935
- .then(function (x) {
6936
- if (!x.r.ok) throw new Error(x.body.message || x.body.error || x.r.statusText)
6937
- let parsed
6938
- try {
6939
- parsed = JSON.parse(String(x.body.content || '{}'))
6940
- } catch {
6941
- parsed = { mcpServers: {} }
6942
- }
6943
- if (!parsed.mcpServers || typeof parsed.mcpServers !== 'object' || Array.isArray(parsed.mcpServers)) {
6944
- parsed.mcpServers = {}
6945
- }
6946
- delete parsed.mcpServers[id]
6947
- return fetch(buildRemoteMcpJsonUrl(), {
6948
- method: 'POST',
6949
- headers: { 'Content-Type': 'application/json; charset=utf-8' },
6950
- body: JSON.stringify({ content: JSON.stringify(parsed, null, 2) }),
6951
- })
6952
- })
6953
- .then(function (r) {
6954
- return r.json().then(function (body) {
6955
- return { r: r, body: body }
6956
- })
6957
- })
6958
- .then(function (x) {
6959
- if (!x.r.ok) throw new Error(x.body.message || x.body.error || x.r.statusText)
6960
- remoteMcpServers = Array.isArray(x.body.servers)
6961
- ? x.body.servers.map(String)
6962
- : []
6963
- pickerSelected.delete(id)
6964
- agentConfigState.mcpServers = agentConfigState.mcpServers.filter(function (name) {
6965
- return name !== id
6966
- })
6967
- renderBindChips()
6968
- renderMcpRows()
6969
- })
6970
- .catch(function (e) {
6971
- window.alert('删除失败: ' + e)
6972
- })
6973
- .finally(function () {
6974
- delBtn.disabled = !canDelete
6975
- })
6976
- return
6977
- }
6978
- const pkgId = row.s && row.s.packageId ? String(row.s.packageId) : id
6979
- fetch(buildRuntimeMcpDeleteUrl(pkgId), { method: 'DELETE' })
6696
+ })
6697
+ buttons.push(toggleBtn)
6698
+ if (showDelete) {
6699
+ const canDelete = source === 'runtime' || source === 'remote'
6700
+ buttons.push(createManageRowBtn('删除', 'is-delete', function () {
6701
+ if (!canDelete) return
6702
+ const ok = window.confirm('确认删除 MCP?\n服务器名:' + id)
6703
+ if (!ok) return
6704
+ if (source === 'remote') {
6705
+ fetch(buildRemoteMcpJsonUrl())
6980
6706
  .then(function (r) {
6981
- return r.json().catch(function () {
6982
- return {}
6983
- }).then(function (b) {
6984
- return { r: r, b: b }
6707
+ return r.json().then(function (body) { return { r: r, body: body } })
6708
+ })
6709
+ .then(function (x) {
6710
+ if (!x.r.ok) throw new Error(x.body.message || x.body.error || x.r.statusText)
6711
+ let parsed
6712
+ try { parsed = JSON.parse(String(x.body.content || '{}')) } catch { parsed = { mcpServers: {} } }
6713
+ if (!parsed.mcpServers || typeof parsed.mcpServers !== 'object' || Array.isArray(parsed.mcpServers)) {
6714
+ parsed.mcpServers = {}
6715
+ }
6716
+ delete parsed.mcpServers[id]
6717
+ return fetch(buildRemoteMcpJsonUrl(), {
6718
+ method: 'POST',
6719
+ headers: { 'Content-Type': 'application/json; charset=utf-8' },
6720
+ body: JSON.stringify({ content: JSON.stringify(parsed, null, 2) }),
6985
6721
  })
6986
6722
  })
6723
+ .then(function (r) {
6724
+ return r.json().then(function (body) { return { r: r, body: body } })
6725
+ })
6987
6726
  .then(function (x) {
6988
- if (!x.r.ok) throw new Error(x.b.message || x.r.statusText)
6727
+ if (!x.r.ok) throw new Error(x.body.message || x.body.error || x.r.statusText)
6728
+ remoteMcpServers = Array.isArray(x.body.servers) ? x.body.servers.map(String) : []
6989
6729
  pickerSelected.delete(id)
6990
- agentConfigState.mcpServers = agentConfigState.mcpServers.filter(function (name) {
6991
- return name !== id
6992
- })
6730
+ agentConfigState.mcpServers = agentConfigState.mcpServers.filter(function (name) { return name !== id })
6993
6731
  renderBindChips()
6994
6732
  return Promise.all([
6995
6733
  persistAgentConfigAndRestartSession({ silent: true }),
6996
- fetch(buildMcpServersListUrl())
6997
- .then(function (r) {
6998
- return r.json().then(function (b) {
6999
- return { r: r, b: b }
7000
- })
7001
- })
7002
- .then(function (res) {
7003
- if (!res.r.ok) throw new Error(res.b.message || res.r.statusText)
7004
- const serv = res.b.servers || []
7005
- rows.splice(0, rows.length)
7006
- for (let i = 0; i < serv.length; i++) {
7007
- rows.push({ id: serv[i].name, s: serv[i] })
7008
- }
7009
- }),
7010
- ]).then(function () {
7011
- renderMcpRows()
7012
- })
7013
- })
7014
- .catch(function (e) {
7015
- window.alert('删除失败: ' + e)
6734
+ typeof refreshMcpPickerRows === 'function' ? refreshMcpPickerRows() : Promise.resolve(),
6735
+ ])
7016
6736
  })
7017
- .finally(function () {
7018
- delBtn.disabled = !canDelete
6737
+ .then(function () {
7019
6738
  renderMcpRows()
7020
6739
  })
7021
- })
7022
- actions.appendChild(delBtn)
7023
- }
7024
- div.appendChild(actions)
7025
- sec.appendChild(div)
6740
+ .catch(function (e) { window.alert('删除失败: ' + e) })
6741
+ return
6742
+ }
6743
+ const pkgId = row.s && row.s.packageId ? String(row.s.packageId) : id
6744
+ fetch(buildRuntimeMcpDeleteUrl(pkgId), { method: 'DELETE' })
6745
+ .then(function (r) {
6746
+ return r.json().catch(function () { return {} }).then(function (b) { return { r: r, b: b } })
6747
+ })
6748
+ .then(function (x) {
6749
+ if (!x.r.ok) throw new Error(x.b.message || x.r.statusText)
6750
+ pickerSelected.delete(id)
6751
+ agentConfigState.mcpServers = agentConfigState.mcpServers.filter(function (name) { return name !== id })
6752
+ renderBindChips()
6753
+ return Promise.all([
6754
+ persistAgentConfigAndRestartSession({ silent: true }),
6755
+ fetch(buildMcpServersListUrl())
6756
+ .then(function (r) { return r.json().then(function (b) { return { r: r, b: b } }) })
6757
+ .then(function (res) {
6758
+ if (!res.r.ok) throw new Error(res.b.message || res.r.statusText)
6759
+ const serv = res.b.servers || []
6760
+ rows.splice(0, rows.length)
6761
+ for (let i = 0; i < serv.length; i++) rows.push({ id: serv[i].name, s: serv[i] })
6762
+ }),
6763
+ ]).then(function () { renderMcpRows() })
6764
+ })
6765
+ .catch(function (e) { window.alert('删除失败: ' + e) })
6766
+ }, { disabled: !canDelete, title: !canDelete ? '基础MCP不可删除' : '' }))
7026
6767
  }
7027
- return sec
6768
+ tr.appendChild(createManageActionsTd(buttons))
6769
+ return tr
7028
6770
  }
7029
6771
  const renderMcpRows = function () {
7030
- if (!agentPickerList) return
7031
- agentPickerList.innerHTML = ''
7032
- renderPickerHeaderTabs(
7033
- [
7034
- { id: 'base', label: '本地MCP' },
7035
- { id: 'installed', label: '远程MCP' },
7036
- ],
7037
- mcpPickerState.activeTab,
7038
- function (id) {
7039
- mcpPickerState.activeTab = id
7040
- renderMcpRows()
7041
- },
7042
- )
7043
- const mergedRows = rows
6772
+ setPickerContentMode('table')
6773
+ setPickerTableHead(['名称', '来源', '操作'])
6774
+ clearPickerTableBody()
6775
+ setPickerContentState('', false)
6776
+ const q = getPickerSearchQuery()
6777
+ const sectionRows = rows
7044
6778
  .filter(function (row) {
7045
- return !(row && row.s && row.s.source === 'remote')
6779
+ if (!q) return true
6780
+ const src = row.s && row.s.source ? String(row.s.source) : ''
6781
+ const srcText = mcpSourceLabel(src)
6782
+ return (
6783
+ mcpLabel(row).toLowerCase().indexOf(q) >= 0 ||
6784
+ String(row.id).toLowerCase().indexOf(q) >= 0 ||
6785
+ srcText.toLowerCase().indexOf(q) >= 0
6786
+ )
7046
6787
  })
7047
6788
  .sort(function (a, b) {
7048
6789
  const as = pickerSelected.has(a.id) ? 0 : 1
@@ -7050,28 +6791,21 @@
7050
6791
  if (as !== bs) return as - bs
7051
6792
  return a.id.localeCompare(b.id)
7052
6793
  })
7053
- if (mcpPickerState.activeTab === 'base') {
7054
- agentPickerList.appendChild(createMcpSection('本地MCP', mergedRows, {
7055
- showExport: true,
7056
- showDelete: true,
7057
- }))
6794
+ if (!sectionRows.length) {
6795
+ setPickerContentState(rows.length ? '(无匹配项)' : '(暂无)', false)
7058
6796
  return
7059
6797
  }
7060
- const remoteRows = rows
7061
- .filter(function (row) {
7062
- return !!(row && row.s && row.s.source === 'remote')
7063
- })
7064
- .sort(function (a, b) {
7065
- const as = pickerSelected.has(a.id) ? 0 : 1
7066
- const bs = pickerSelected.has(b.id) ? 0 : 1
7067
- if (as !== bs) return as - bs
7068
- return a.id.localeCompare(b.id)
7069
- })
7070
- agentPickerList.appendChild(createMcpSection('远程MCP', remoteRows, {
7071
- showExport: false,
7072
- showDelete: false,
7073
- }))
6798
+ const frag = document.createDocumentFragment()
6799
+ for (let i = 0; i < sectionRows.length; i++) {
6800
+ const rowSource = sectionRows[i].s && sectionRows[i].s.source ? String(sectionRows[i].s.source) : ''
6801
+ frag.appendChild(makeMcpTableRow(sectionRows[i], {
6802
+ showExport: rowSource === 'runtime',
6803
+ showDelete: rowSource === 'runtime' || rowSource === 'remote',
6804
+ }))
6805
+ }
6806
+ if (pickerEntriesBody) pickerEntriesBody.appendChild(frag)
7074
6807
  }
6808
+ pickerRenderContent = renderMcpRows
7075
6809
  const loadRemoteMcpRows = function () {
7076
6810
  return fetch(buildRemoteMcpJsonUrl())
7077
6811
  .then(function (r) {
@@ -7111,93 +6845,85 @@
7111
6845
  })
7112
6846
  })
7113
6847
  .catch(function (e) {
7114
- if (agentPickerList) agentPickerList.textContent = '加载失败: ' + e
6848
+ setPickerContentState('加载失败: ' + e, true)
7115
6849
  })
7116
6850
  } else if (kind === 'agentTeams') {
7117
- const teamTabId = 'base'
7118
- const renderAgentTeamsRows = function (rows) {
7119
- if (!agentPickerList) return
7120
- agentPickerList.innerHTML = ''
7121
- renderPickerHeaderTabs(
7122
- [{ id: teamTabId, label: '成员' }],
7123
- teamTabId,
7124
- function () {},
7125
- )
7126
- const labelFn = function (row) {
7127
- const s = row.s || {}
7128
- const parts = [String(s.agentType || row.id || '')]
7129
- if (s.model) parts.push('model:' + s.model)
7130
- if (s.effort !== undefined && s.effort !== null && String(s.effort) !== '') {
7131
- parts.push('effort:' + String(s.effort))
7132
- }
7133
- if (s.background === true) parts.push('bg:true')
7134
- if (s.isolation) parts.push('iso:' + s.isolation)
7135
- const head = parts.join(' · ')
7136
- const tags = []
7137
- if (Array.isArray(s.tools) && s.tools.length > 0) {
7138
- tags.push('tools:' + s.tools.length)
7139
- }
7140
- if (Array.isArray(s.disallowedTools) && s.disallowedTools.length > 0) {
7141
- tags.push('deny:' + s.disallowedTools.length)
7142
- }
7143
- const summary = typeof s.whenToUse === 'string' && s.whenToUse.trim()
7144
- ? String(s.whenToUse).slice(0, 100)
7145
- : ''
7146
- return head + (tags.length ? ' · ' + tags.join(' ') : '') + (summary ? ' — ' + summary : '')
6851
+ let teamRows = []
6852
+ const teamLabelFn = function (row) {
6853
+ const s = row.s || {}
6854
+ const parts = [String(s.agentType || row.id || '')]
6855
+ if (s.model) parts.push('model:' + s.model)
6856
+ if (s.effort !== undefined && s.effort !== null && String(s.effort) !== '') {
6857
+ parts.push('effort:' + String(s.effort))
6858
+ }
6859
+ if (s.background === true) parts.push('bg:true')
6860
+ if (s.isolation) parts.push('iso:' + s.isolation)
6861
+ return parts.join(' · ')
6862
+ }
6863
+ const teamConfigFn = function (row) {
6864
+ const s = row.s || {}
6865
+ const tags = []
6866
+ if (Array.isArray(s.tools) && s.tools.length > 0) tags.push('tools:' + s.tools.length)
6867
+ if (Array.isArray(s.disallowedTools) && s.disallowedTools.length > 0) tags.push('deny:' + s.disallowedTools.length)
6868
+ const summary = typeof s.whenToUse === 'string' && s.whenToUse.trim()
6869
+ ? String(s.whenToUse).slice(0, 100) : ''
6870
+ return (tags.length ? tags.join(' ') : '—') + (summary ? ' — ' + summary : '')
6871
+ }
6872
+ const renderAgentTeamsRows = function () {
6873
+ setPickerContentMode('table')
6874
+ setPickerTableHead(['成员', '配置', '操作'])
6875
+ clearPickerTableBody()
6876
+ setPickerContentState('', false)
6877
+ const q = getPickerSearchQuery()
6878
+ const filtered = teamRows.filter(function (row) {
6879
+ if (!q) return true
6880
+ const lab = teamLabelFn(row) + ' ' + teamConfigFn(row)
6881
+ return lab.toLowerCase().indexOf(q) >= 0 || String(row.id).toLowerCase().indexOf(q) >= 0
6882
+ })
6883
+ if (!filtered.length) {
6884
+ setPickerContentState(teamRows.length ? '(无匹配项)' : '(暂无成员)', false)
6885
+ return
7147
6886
  }
7148
6887
  const frag = document.createDocumentFragment()
7149
- for (let i = 0; i < rows.length; i++) {
7150
- const row = rows[i]
6888
+ for (let i = 0; i < filtered.length; i++) {
6889
+ const row = filtered[i]
7151
6890
  const id = row.id
7152
- const lab = labelFn(row)
7153
- const div = document.createElement('div')
7154
- div.className = 'agent-picker-item'
7155
- const labelEl = document.createElement('div')
7156
- labelEl.className = 'agent-picker-item-label'
7157
- labelEl.textContent = String(lab)
7158
- div.appendChild(labelEl)
7159
- const actions = document.createElement('div')
7160
- actions.className = 'agent-picker-actions'
7161
- const toggleBtn = document.createElement('button')
7162
- toggleBtn.type = 'button'
6891
+ const tr = document.createElement('tr')
6892
+ const memberTd = document.createElement('td')
6893
+ memberTd.textContent = teamLabelFn(row)
6894
+ tr.appendChild(memberTd)
6895
+ const configTd = document.createElement('td')
6896
+ configTd.textContent = teamConfigFn(row)
6897
+ tr.appendChild(configTd)
7163
6898
  const selected = pickerSelected.has(id)
7164
- toggleBtn.className = 'btn-picker-action' + (selected ? ' is-on' : '')
7165
- toggleBtn.textContent = selected ? '禁用' : '启用'
7166
- toggleBtn.addEventListener('click', function () {
7167
- if (pickerSelected.has(id)) pickerSelected.delete(id)
7168
- else pickerSelected.add(id)
7169
- renderAgentTeamsRows(rows)
7170
- })
7171
- actions.appendChild(toggleBtn)
7172
- div.appendChild(actions)
7173
- frag.appendChild(div)
7174
- }
7175
- if (!frag.childNodes.length) {
7176
- agentPickerList.appendChild(document.createTextNode('(无匹配项)'))
7177
- return
6899
+ tr.appendChild(createManageActionsTd([
6900
+ createManageRowBtn(selected ? '禁用' : '启用', selected ? 'is-on' : '', function () {
6901
+ if (pickerSelected.has(id)) pickerSelected.delete(id)
6902
+ else pickerSelected.add(id)
6903
+ renderAgentTeamsRows()
6904
+ }),
6905
+ ]))
6906
+ frag.appendChild(tr)
7178
6907
  }
7179
- agentPickerList.appendChild(frag)
6908
+ if (pickerEntriesBody) pickerEntriesBody.appendChild(frag)
7180
6909
  }
6910
+ pickerRenderContent = renderAgentTeamsRows
7181
6911
  fetch(buildAgentTeamsListUrl())
7182
6912
  .then(function (r) {
7183
- return r.json().then(function (b) {
7184
- return { r: r, b: b }
7185
- })
6913
+ return r.json().then(function (b) { return { r: r, b: b } })
7186
6914
  })
7187
6915
  .then(function (x) {
7188
6916
  if (!x.r.ok) throw new Error(x.b.message || x.r.statusText)
7189
6917
  const list = x.b.teamAgentTypes || []
7190
- const rows = list.map(function (s) {
7191
- return { id: s.agentType, s: s }
7192
- })
7193
- renderAgentTeamsRows(rows)
6918
+ teamRows = list.map(function (s) { return { id: s.agentType, s: s } })
6919
+ renderAgentTeamsRows()
7194
6920
  })
7195
6921
  .catch(function (e) {
7196
- if (agentPickerList) agentPickerList.textContent = '加载失败: ' + e
6922
+ setPickerContentState('加载失败: ' + e, true)
7197
6923
  })
7198
6924
  } else {
7199
6925
  const skillPickerState = {
7200
- activeTab: 'base',
6926
+ activeTab: 'list',
7201
6927
  baseRows: [],
7202
6928
  installedRows: [],
7203
6929
  cloudRows: [],
@@ -7233,112 +6959,87 @@
7233
6959
  (s.description ? ' — ' + String(s.description).slice(0, 100) : '')
7234
6960
  )
7235
6961
  }
7236
- const makeSkillRow = function (row, canDelete) {
6962
+ /** Skill 启用状态写入 agent 配置并立即持久化 */
6963
+ const persistSkillSelectionNow = function () {
6964
+ agentConfigState.skills = [...pickerSelected].sort()
6965
+ renderBindChips()
6966
+ return persistAgentConfigAndRestartSession({ silent: true })
6967
+ }
6968
+ /** 系统 skill 不可删除;agent 层已安装 skill 可删除 */
6969
+ const isSkillDeletable = function (row) {
6970
+ const s = row.s || {}
6971
+ return s.canManage === true || s.runtimeLayer === 'agent'
6972
+ }
6973
+ const mergeSkillListRows = function () {
6974
+ const merged = skillPickerState.baseRows.concat(skillPickerState.installedRows)
6975
+ merged.sort(function (a, b) {
6976
+ const as = a.s && a.s.runtimeLayer === 'system' ? 0 : 1
6977
+ const bs = b.s && b.s.runtimeLayer === 'system' ? 0 : 1
6978
+ if (as !== bs) return as - bs
6979
+ return String(a.id).localeCompare(String(b.id))
6980
+ })
6981
+ return merged
6982
+ }
6983
+ const makeSkillTableRow = function (row) {
7237
6984
  const id = row.id
7238
- const div = document.createElement('div')
7239
- div.className = 'agent-picker-item'
7240
- const labelEl = document.createElement('div')
7241
- labelEl.className = 'agent-picker-item-label'
7242
- labelEl.textContent = skillLabel(row)
7243
- div.appendChild(labelEl)
7244
- const actions = document.createElement('div')
7245
- actions.className = 'agent-picker-actions'
7246
- const toggleBtn = document.createElement('button')
7247
- toggleBtn.type = 'button'
6985
+ const canDelete = isSkillDeletable(row)
6986
+ const tr = document.createElement('tr')
6987
+ const nameTd = document.createElement('td')
6988
+ nameTd.textContent = '/' + (row.s && row.s.name ? row.s.name : id)
6989
+ tr.appendChild(nameTd)
6990
+ const descTd = document.createElement('td')
6991
+ const desc = row.s && row.s.description ? String(row.s.description).slice(0, 100) : ''
6992
+ descTd.textContent = desc
6993
+ tr.appendChild(descTd)
7248
6994
  const selected = pickerSelected.has(id)
7249
- toggleBtn.className = 'btn-picker-action' + (selected ? ' is-on' : '')
7250
- toggleBtn.textContent = selected ? '禁用' : '启用'
7251
- toggleBtn.addEventListener('click', function () {
6995
+ const toggleBtn = createManageRowBtn(selected ? '禁用' : '启用', selected ? 'is-on' : '', function () {
6996
+ toggleBtn.disabled = true
7252
6997
  if (pickerSelected.has(id)) pickerSelected.delete(id)
7253
6998
  else pickerSelected.add(id)
7254
- renderSkillRows()
6999
+ persistSkillSelectionNow()
7000
+ .then(function () {
7001
+ renderSkillRows()
7002
+ })
7003
+ .catch(function (e) {
7004
+ window.alert('切换启用状态失败: ' + e)
7005
+ })
7006
+ .finally(function () {
7007
+ toggleBtn.disabled = false
7008
+ })
7255
7009
  })
7256
- actions.appendChild(toggleBtn)
7010
+ const buttons = [toggleBtn]
7257
7011
  if (!canDelete) {
7258
- const editBtn = document.createElement('button')
7259
- editBtn.type = 'button'
7260
- editBtn.className = 'btn-picker-action'
7261
- editBtn.textContent = '编辑'
7262
- editBtn.addEventListener('click', function () {
7263
- openSkillMdModal(id)
7264
- })
7265
- actions.appendChild(editBtn)
7012
+ buttons.push(createManageRowBtn('编辑', '', function () { openSkillMdModal(id) }))
7266
7013
  }
7267
7014
  if (canDelete) {
7268
- const exportBtn = document.createElement('button')
7269
- exportBtn.type = 'button'
7270
- exportBtn.className = 'btn-picker-action'
7271
- exportBtn.textContent = '导出'
7272
- exportBtn.addEventListener('click', function () {
7015
+ buttons.push(createManageRowBtn('导出', '', function () {
7273
7016
  window.open(buildRuntimeSkillExportUrl(id), '_blank', 'noopener')
7274
- })
7275
- actions.appendChild(exportBtn)
7017
+ }))
7276
7018
  }
7277
- const delBtn = document.createElement('button')
7278
- delBtn.type = 'button'
7279
- delBtn.className = 'btn-picker-action is-delete'
7280
- delBtn.textContent = '删除'
7281
- delBtn.disabled = !canDelete
7282
- if (!canDelete) delBtn.title = '系统技能不可删除'
7283
- delBtn.addEventListener('click', function () {
7284
- if (delBtn.disabled) return
7285
- const targetSkill = id
7286
- const ok = window.confirm('确认删除 Skill 包?\nSkill:' + targetSkill)
7019
+ buttons.push(createManageRowBtn('删除', 'is-delete', function () {
7020
+ if (!canDelete) return
7021
+ const ok = window.confirm('确认删除 Skill 包?\nSkill:' + id)
7287
7022
  if (!ok) return
7288
- delBtn.disabled = true
7289
- fetch(buildRuntimeSkillDeleteUrl(targetSkill), { method: 'DELETE' })
7023
+ fetch(buildRuntimeSkillDeleteUrl(id), { method: 'DELETE' })
7290
7024
  .then(function (r) {
7291
- return r.json().catch(function () {
7292
- return {}
7293
- }).then(function (b) {
7294
- return { r: r, b: b }
7295
- })
7025
+ return r.json().catch(function () { return {} }).then(function (b) { return { r: r, b: b } })
7296
7026
  })
7297
7027
  .then(function (x) {
7298
7028
  if (!x.r.ok) throw new Error(x.b.message || x.r.statusText)
7299
- pickerSelected.delete(targetSkill)
7300
- agentConfigState.skills = agentConfigState.skills.filter(function (name) {
7301
- return name !== targetSkill
7302
- })
7029
+ pickerSelected.delete(id)
7030
+ agentConfigState.skills = agentConfigState.skills.filter(function (name) { return name !== id })
7303
7031
  renderBindChips()
7304
7032
  void persistAgentConfigAndRestartSession({ silent: true })
7305
7033
  return refreshSkillPickerRows()
7306
7034
  })
7307
- .catch(function (e) {
7308
- window.alert('删除失败: ' + e)
7309
- delBtn.disabled = false
7310
- })
7311
- })
7312
- actions.appendChild(delBtn)
7313
- div.appendChild(actions)
7314
- return div
7035
+ .catch(function (e) { window.alert('删除失败: ' + e) })
7036
+ }, { disabled: !canDelete, title: !canDelete ? '系统技能不可删除' : '' }))
7037
+ tr.appendChild(createManageActionsTd(buttons))
7038
+ return tr
7315
7039
  }
7316
- const createSkillSection = function (title, rows, canDelete) {
7317
- const sec = document.createElement('div')
7318
- sec.className = 'agent-picker-section'
7319
- const titleEl = document.createElement('div')
7320
- titleEl.className = 'agent-picker-section-title'
7321
- titleEl.textContent = title
7322
- sec.appendChild(titleEl)
7323
- if (!rows.length) {
7324
- const empty = document.createElement('div')
7325
- empty.className = 'agent-picker-empty'
7326
- empty.textContent = '(暂无)'
7327
- sec.appendChild(empty)
7328
- return sec
7329
- }
7330
- for (let i = 0; i < rows.length; i++) {
7331
- sec.appendChild(makeSkillRow(rows[i], canDelete))
7332
- }
7333
- return sec
7334
- }
7335
- const renderCloudSkillSection = function () {
7336
- const sec = document.createElement('div')
7337
- sec.className = 'agent-picker-section'
7338
- const titleEl = document.createElement('div')
7339
- titleEl.className = 'agent-picker-section-title'
7340
- titleEl.textContent = 'skill商店'
7341
- sec.appendChild(titleEl)
7040
+ const renderCloudSkillPanel = function () {
7041
+ if (!pickerCloudPanel) return
7042
+ pickerCloudPanel.innerHTML = ''
7342
7043
  const cloudSearch = document.createElement('input')
7343
7044
  cloudSearch.type = 'search'
7344
7045
  cloudSearch.className = 'agent-picker-cloud-search'
@@ -7349,120 +7050,112 @@
7349
7050
  skillPickerState.cloudPage = 1
7350
7051
  void loadCloudSkillRows()
7351
7052
  }
7352
- sec.appendChild(cloudSearch)
7053
+ pickerCloudPanel.appendChild(cloudSearch)
7353
7054
  const meta = document.createElement('div')
7354
7055
  meta.className = 'agent-picker-cloud-meta'
7355
- if (skillPickerState.cloudLoading) {
7356
- meta.textContent = '加载中…'
7357
- } else if (skillPickerState.cloudError) {
7358
- meta.textContent = '加载失败:' + skillPickerState.cloudError
7359
- } else {
7360
- meta.textContent = '展示前 ' + skillPickerState.cloudRows.length + ' 项'
7361
- }
7362
- sec.appendChild(meta)
7363
- if (skillPickerState.cloudError) return sec
7364
- if (!skillPickerState.cloudRows.length) {
7056
+ if (skillPickerState.cloudLoading) meta.textContent = '加载中…'
7057
+ else if (skillPickerState.cloudError) meta.textContent = '加载失败:' + skillPickerState.cloudError
7058
+ else meta.textContent = '展示前 ' + skillPickerState.cloudRows.length + ' 项'
7059
+ pickerCloudPanel.appendChild(meta)
7060
+ if (!skillPickerState.cloudError && skillPickerState.cloudRows.length) {
7061
+ for (let i = 0; i < skillPickerState.cloudRows.length; i++) {
7062
+ const item = skillPickerState.cloudRows[i]
7063
+ const row = document.createElement('div')
7064
+ row.className = 'agent-picker-item'
7065
+ const label = document.createElement('div')
7066
+ label.className = 'agent-picker-item-label'
7067
+ label.textContent = item.title + ' · /' + item.skillName + (item.description ? ' — ' + item.description : '')
7068
+ row.appendChild(label)
7069
+ const actions = document.createElement('div')
7070
+ actions.className = 'agent-picker-actions'
7071
+ const installing = !!skillPickerState.installingMap[item.id]
7072
+ const installed = !!item.installed
7073
+ const installBtn = createManageRowBtn(
7074
+ installing ? '处理中…' : installed ? '卸载' : '安装', '', function () {
7075
+ if (installBtn.disabled) return
7076
+ skillPickerState.installingMap[item.id] = true
7077
+ renderSkillRows()
7078
+ const request = installed
7079
+ ? fetch(buildRuntimeSkillDeleteUrl(item.packageId), { method: 'DELETE' })
7080
+ : fetch(buildCloudSkillInstallUrl(), {
7081
+ method: 'POST',
7082
+ headers: { 'Content-Type': 'application/json; charset=utf-8' },
7083
+ body: JSON.stringify({ id: item.id }),
7084
+ })
7085
+ request
7086
+ .then(function (r) {
7087
+ return r.json().catch(function () { return {} }).then(function (b) { return { r: r, b: b } })
7088
+ })
7089
+ .then(function (x) {
7090
+ if (!x.r.ok) throw new Error(x.b.message || x.r.statusText)
7091
+ if (installed) pickerSelected.delete(item.skillName || item.id)
7092
+ else pickerSelected.add(item.skillName || item.id)
7093
+ setStatus('云端技能已' + (installed ? '卸载' : '安装') + ':' + item.title, serverReady ? 'ready' : '')
7094
+ return persistSkillSelectionNow().then(function () {
7095
+ return refreshSkillPickerRows()
7096
+ })
7097
+ })
7098
+ .catch(function (e) { window.alert((installed ? '卸载' : '安装') + '失败: ' + e) })
7099
+ .finally(function () {
7100
+ delete skillPickerState.installingMap[item.id]
7101
+ renderSkillRows()
7102
+ })
7103
+ }, { disabled: installing },
7104
+ )
7105
+ actions.appendChild(installBtn)
7106
+ row.appendChild(actions)
7107
+ pickerCloudPanel.appendChild(row)
7108
+ }
7109
+ pickerCloudPanel.appendChild(createCloudPager({
7110
+ page: skillPickerState.cloudPage,
7111
+ totalPages: skillPickerState.cloudTotalPages,
7112
+ loading: skillPickerState.cloudLoading,
7113
+ }, function (nextPage) {
7114
+ skillPickerState.cloudPage = nextPage
7115
+ void loadCloudSkillRows()
7116
+ }))
7117
+ } else if (!skillPickerState.cloudError) {
7365
7118
  const empty = document.createElement('div')
7366
7119
  empty.className = 'agent-picker-empty'
7367
7120
  empty.textContent = skillPickerState.cloudLoading ? '(技能商店加载中)' : '(无匹配项)'
7368
- sec.appendChild(empty)
7369
- return sec
7370
- }
7371
- for (let i = 0; i < skillPickerState.cloudRows.length; i++) {
7372
- const item = skillPickerState.cloudRows[i]
7373
- const row = document.createElement('div')
7374
- row.className = 'agent-picker-item'
7375
- const label = document.createElement('div')
7376
- label.className = 'agent-picker-item-label'
7377
- label.textContent = item.title + ' · /' + item.skillName + (item.description ? ' — ' + item.description : '')
7378
- row.appendChild(label)
7379
- const actions = document.createElement('div')
7380
- actions.className = 'agent-picker-actions'
7381
- const installBtn = document.createElement('button')
7382
- installBtn.type = 'button'
7383
- installBtn.className = 'btn-picker-action'
7384
- const installing = !!skillPickerState.installingMap[item.id]
7385
- const installed = !!item.installed
7386
- installBtn.textContent = installing ? '处理中…' : installed ? '卸载' : '安装'
7387
- installBtn.disabled = installing
7388
- installBtn.addEventListener('click', function () {
7389
- if (installBtn.disabled) return
7390
- skillPickerState.installingMap[item.id] = true
7391
- renderSkillRows()
7392
- const request = installed
7393
- ? fetch(buildRuntimeSkillDeleteUrl(item.packageId), { method: 'DELETE' })
7394
- : fetch(buildCloudSkillInstallUrl(), {
7395
- method: 'POST',
7396
- headers: { 'Content-Type': 'application/json; charset=utf-8' },
7397
- body: JSON.stringify({ id: item.id }),
7398
- })
7399
- request
7400
- .then(function (r) {
7401
- return r.json().catch(function () {
7402
- return {}
7403
- }).then(function (b) {
7404
- return { r: r, b: b }
7405
- })
7406
- })
7407
- .then(function (x) {
7408
- if (!x.r.ok) throw new Error(x.b.message || x.r.statusText)
7409
- if (installed) {
7410
- pickerSelected.delete(item.skillName || item.id)
7411
- } else {
7412
- pickerSelected.add(item.skillName || item.id)
7413
- }
7414
- setStatus('云端技能已' + (installed ? '卸载' : '安装') + ':' + item.title, serverReady ? 'ready' : '')
7415
- restartCurrentSessionSubprocessWithReason(installed ? 'cloud-skill-uninstall' : 'cloud-skill-install')
7416
- return refreshSkillPickerRows()
7417
- })
7418
- .catch(function (e) {
7419
- window.alert((installed ? '卸载' : '安装') + '失败: ' + e)
7420
- })
7421
- .finally(function () {
7422
- delete skillPickerState.installingMap[item.id]
7423
- renderSkillRows()
7424
- })
7425
- })
7426
- actions.appendChild(installBtn)
7427
- row.appendChild(actions)
7428
- sec.appendChild(row)
7121
+ pickerCloudPanel.appendChild(empty)
7429
7122
  }
7430
- return sec
7431
7123
  }
7432
7124
  const renderSkillRows = function () {
7433
- if (!agentPickerList) return
7434
- agentPickerList.innerHTML = ''
7435
- renderPickerHeaderTabs(
7125
+ renderPickerNavTabs(
7436
7126
  [
7437
- { id: 'base', label: '系统skill' },
7438
- { id: 'installed', label: '已安装skill' },
7127
+ { id: 'list', label: 'Skill' },
7439
7128
  { id: 'cloud', label: 'skill商店' },
7440
7129
  ],
7441
7130
  skillPickerState.activeTab,
7442
- function (id) {
7443
- skillPickerState.activeTab = id
7444
- renderSkillRows()
7445
- },
7131
+ function (id) { skillPickerState.activeTab = id; renderSkillRows() },
7446
7132
  )
7447
- if (skillPickerState.activeTab === 'installed') {
7448
- agentPickerList.appendChild(createSkillSection('已安装skill', skillPickerState.installedRows, true))
7133
+ if (skillPickerState.activeTab === 'cloud') {
7134
+ setPickerContentMode('cloud')
7135
+ renderCloudSkillPanel()
7449
7136
  return
7450
7137
  }
7451
- if (skillPickerState.activeTab === 'base') {
7452
- agentPickerList.appendChild(createSkillSection('系统skill', skillPickerState.baseRows, false))
7138
+ setPickerContentMode('table')
7139
+ setPickerTableHead(['名称', '描述', '操作'])
7140
+ clearPickerTableBody()
7141
+ setPickerContentState('', false)
7142
+ const q = getPickerSearchQuery()
7143
+ const sourceRows = mergeSkillListRows()
7144
+ const rows = sourceRows.filter(function (row) {
7145
+ if (!q) return true
7146
+ return skillLabel(row).toLowerCase().indexOf(q) >= 0 || String(row.id).toLowerCase().indexOf(q) >= 0
7147
+ })
7148
+ if (!rows.length) {
7149
+ setPickerContentState(sourceRows.length ? '(无匹配项)' : '(暂无 Skill)', false)
7453
7150
  return
7454
7151
  }
7455
- const cloud = renderCloudSkillSection()
7456
- cloud.appendChild(createCloudPager({
7457
- page: skillPickerState.cloudPage,
7458
- totalPages: skillPickerState.cloudTotalPages,
7459
- loading: skillPickerState.cloudLoading,
7460
- }, function (nextPage) {
7461
- skillPickerState.cloudPage = nextPage
7462
- void loadCloudSkillRows()
7463
- }))
7464
- agentPickerList.appendChild(cloud)
7152
+ const frag = document.createDocumentFragment()
7153
+ for (let i = 0; i < rows.length; i++) {
7154
+ frag.appendChild(makeSkillTableRow(rows[i]))
7155
+ }
7156
+ if (pickerEntriesBody) pickerEntriesBody.appendChild(frag)
7465
7157
  }
7158
+ pickerRenderContent = renderSkillRows
7466
7159
  const loadCloudSkillRows = function () {
7467
7160
  const seq = skillPickerState.cloudSeq + 1
7468
7161
  skillPickerState.cloudSeq = seq
@@ -7566,7 +7259,7 @@
7566
7259
  }
7567
7260
  }
7568
7261
 
7569
- function applyPickerSelection() {
7262
+ function applyPickerSave() {
7570
7263
  const ids = [...pickerSelected].sort()
7571
7264
  if (pickerKind === 'tools') {
7572
7265
  agentConfigState.tools = ids
@@ -7578,7 +7271,7 @@
7578
7271
  agentConfigState.skills = ids
7579
7272
  }
7580
7273
  renderBindChips()
7581
- closeAgentPickerModal()
7274
+ closePickerManageView(false)
7582
7275
  void persistAgentConfigAndRestartSession({ silent: true })
7583
7276
  }
7584
7277
 
@@ -7593,16 +7286,19 @@
7593
7286
  ev.stopPropagation()
7594
7287
  closeRepoManageView()
7595
7288
  closeAgentSettingsView()
7289
+ closePickerManageView(false)
7596
7290
  const items = sidebarNavEl.querySelectorAll('.sidebar-nav-row')
7597
7291
  for (let i = 0; i < items.length; i++) {
7598
7292
  items[i].classList.toggle('is-active', items[i] === btn)
7599
7293
  }
7600
- void openScheduleManageModal()
7294
+ void openScheduleManageView()
7601
7295
  return
7602
7296
  }
7603
7297
  if (k === 'repo') {
7604
7298
  ev.preventDefault()
7605
7299
  ev.stopPropagation()
7300
+ closeScheduleManageView(false)
7301
+ closePickerManageView(false)
7606
7302
  const items = sidebarNavEl.querySelectorAll('.sidebar-nav-row')
7607
7303
  for (let i = 0; i < items.length; i++) {
7608
7304
  items[i].classList.toggle('is-active', items[i] === btn)
@@ -7615,30 +7311,36 @@
7615
7311
  ev.stopPropagation()
7616
7312
  closeRepoManageView()
7617
7313
  closeAgentSettingsView()
7314
+ closeScheduleManageView(false)
7618
7315
  const items = sidebarNavEl.querySelectorAll('.sidebar-nav-row')
7619
7316
  for (let i = 0; i < items.length; i++) {
7620
7317
  items[i].classList.toggle('is-active', items[i] === btn)
7621
7318
  }
7622
- openAgentPickerModal(k)
7319
+ openPickerManageView(k)
7623
7320
  }
7624
7321
  })
7625
7322
  }
7626
- if (btnAgentPickerClose) btnAgentPickerClose.addEventListener('click', closeAgentPickerModal)
7627
- if (btnAgentPickerCancel) btnAgentPickerCancel.addEventListener('click', closeAgentPickerModal)
7628
- if (agentPickerModalBackdrop) {
7629
- agentPickerModalBackdrop.addEventListener('click', closeAgentPickerModal)
7323
+ if (btnPickerCancel) {
7324
+ btnPickerCancel.addEventListener('click', function () {
7325
+ closePickerManageView(true)
7326
+ })
7630
7327
  }
7631
- if (btnAgentPickerOk) {
7632
- btnAgentPickerOk.addEventListener('click', function () {
7633
- applyPickerSelection()
7328
+ if (btnPickerSave) {
7329
+ btnPickerSave.addEventListener('click', function () {
7330
+ applyPickerSave()
7634
7331
  })
7635
7332
  }
7636
- if (btnAgentPickerMcpJson) {
7637
- btnAgentPickerMcpJson.addEventListener('click', function () {
7333
+ if (btnPickerMcpJson) {
7334
+ btnPickerMcpJson.addEventListener('click', function () {
7638
7335
  if (pickerKind !== 'mcp') return
7639
7336
  openMcpJsonInputModal()
7640
7337
  })
7641
7338
  }
7339
+ if (pickerSearch) {
7340
+ pickerSearch.addEventListener('input', function () {
7341
+ if (typeof pickerRenderContent === 'function') pickerRenderContent()
7342
+ })
7343
+ }
7642
7344
  if (btnMcpJsonInputModalClose) {
7643
7345
  btnMcpJsonInputModalClose.addEventListener('click', closeMcpJsonInputModal)
7644
7346
  }
@@ -7768,7 +7470,7 @@
7768
7470
  setStatus('remote mcp.json 已保存', serverReady ? 'ready' : '')
7769
7471
  if (pickerKind === 'mcp' && typeof refreshMcpPickerRows === 'function') {
7770
7472
  void refreshMcpPickerRows().catch(function (e) {
7771
- if (agentPickerList) agentPickerList.textContent = '加载失败: ' + e
7473
+ setPickerContentState('加载失败: ' + e, true)
7772
7474
  })
7773
7475
  }
7774
7476
  })
@@ -7783,19 +7485,9 @@
7783
7485
  })
7784
7486
  }
7785
7487
 
7786
- if (btnScheduleManageModalClose) {
7787
- btnScheduleManageModalClose.addEventListener('click', closeScheduleManageModal)
7788
- }
7789
- if (scheduleManageModalBackdrop) {
7790
- scheduleManageModalBackdrop.addEventListener('click', closeScheduleManageModal)
7791
- }
7792
- const btnScheduleRunsModalClose = $('btnScheduleRunsModalClose')
7793
7488
  const btnScheduleRunsRefresh = $('btnScheduleRunsRefresh')
7794
- if (btnScheduleRunsModalClose) {
7795
- btnScheduleRunsModalClose.addEventListener('click', closeScheduleRunsModal)
7796
- }
7797
- if (scheduleRunsModalBackdrop) {
7798
- scheduleRunsModalBackdrop.addEventListener('click', closeScheduleRunsModal)
7489
+ if (btnScheduleRunsBack) {
7490
+ btnScheduleRunsBack.addEventListener('click', closeScheduleRunsView)
7799
7491
  }
7800
7492
  if (btnScheduleRunsRefresh) {
7801
7493
  btnScheduleRunsRefresh.addEventListener('click', function () {
@@ -7838,21 +7530,6 @@
7838
7530
  })
7839
7531
  })
7840
7532
 
7841
- if (btnManageAgents) {
7842
- btnManageAgents.addEventListener('click', function (ev) {
7843
- ev.preventDefault()
7844
- ev.stopPropagation()
7845
- closeRepoManageView()
7846
- closeAgentSettingsView()
7847
- if (sidebarNavEl) {
7848
- const items = sidebarNavEl.querySelectorAll('.sidebar-nav-row')
7849
- for (let i = 0; i < items.length; i++) {
7850
- items[i].classList.toggle('is-active', items[i] === btnManageAgents)
7851
- }
7852
- }
7853
- })
7854
- }
7855
-
7856
7533
  function closeAgentMdModal() {
7857
7534
  if (!agentMdModal) return
7858
7535
  agentMdModal.classList.remove('is-open')
@@ -8019,6 +7696,8 @@
8019
7696
  function openAgentSettingsView(initialTab) {
8020
7697
  if (!agentSettingsView) return
8021
7698
  closeRepoManageView()
7699
+ closePickerManageView(false)
7700
+ closeScheduleManageView(false)
8022
7701
  if (initialTab === 'agentMd' || initialTab === 'settings') {
8023
7702
  agentEnvActiveTab = initialTab
8024
7703
  } else {
@@ -8581,18 +8260,18 @@
8581
8260
  })
8582
8261
  })
8583
8262
  }
8584
- if (btnAgentPickerToolImport && agentPickerToolImportInput) {
8585
- btnAgentPickerToolImport.addEventListener('click', function () {
8263
+ if (btnPickerImport && pickerImportInput) {
8264
+ btnPickerImport.addEventListener('click', function () {
8586
8265
  if (pickerKind !== 'tools' && pickerKind !== 'mcp' && pickerKind !== 'skills') return
8587
- agentPickerToolImportInput.value = ''
8588
- agentPickerToolImportInput.click()
8266
+ pickerImportInput.value = ''
8267
+ pickerImportInput.click()
8589
8268
  })
8590
- agentPickerToolImportInput.addEventListener('change', function () {
8591
- const f = agentPickerToolImportInput.files && agentPickerToolImportInput.files[0]
8269
+ pickerImportInput.addEventListener('change', function () {
8270
+ const f = pickerImportInput.files && pickerImportInput.files[0]
8592
8271
  if (!f) return
8593
8272
  const fd = new FormData()
8594
8273
  fd.append('file', f)
8595
- btnAgentPickerToolImport.disabled = true
8274
+ btnPickerImport.disabled = true
8596
8275
  const importUrl = pickerKind === 'mcp'
8597
8276
  ? buildRuntimeMcpImportUrl()
8598
8277
  : pickerKind === 'skills'
@@ -8608,7 +8287,7 @@
8608
8287
  })
8609
8288
  })
8610
8289
  .then(function (x) {
8611
- btnAgentPickerToolImport.disabled = false
8290
+ btnPickerImport.disabled = false
8612
8291
  if (!x.r.ok) {
8613
8292
  window.alert('导入失败:' + (x.body.message || x.body.error || x.r.statusText))
8614
8293
  return
@@ -8634,20 +8313,20 @@
8634
8313
  )
8635
8314
  if (pickerKind === 'tools' && typeof refreshToolPickerRows === 'function') {
8636
8315
  void refreshToolPickerRows().catch(function (e) {
8637
- if (agentPickerList) agentPickerList.textContent = '加载失败: ' + e
8316
+ setPickerContentState('加载失败: ' + e, true)
8638
8317
  })
8639
8318
  } else if (pickerKind === 'mcp' && typeof refreshMcpPickerRows === 'function') {
8640
8319
  void refreshMcpPickerRows().catch(function (e) {
8641
- if (agentPickerList) agentPickerList.textContent = '加载失败: ' + e
8320
+ setPickerContentState('加载失败: ' + e, true)
8642
8321
  })
8643
8322
  } else if (pickerKind === 'skills' && typeof refreshSkillPickerRows === 'function') {
8644
8323
  void refreshSkillPickerRows().catch(function (e) {
8645
- if (agentPickerList) agentPickerList.textContent = '加载失败: ' + e
8324
+ setPickerContentState('加载失败: ' + e, true)
8646
8325
  })
8647
8326
  }
8648
8327
  })
8649
8328
  .catch(function (e) {
8650
- btnAgentPickerToolImport.disabled = false
8329
+ btnPickerImport.disabled = false
8651
8330
  window.alert('导入失败: ' + e)
8652
8331
  })
8653
8332
  })
@@ -8722,12 +8401,8 @@
8722
8401
  }
8723
8402
  document.addEventListener('keydown', function (ev) {
8724
8403
  if (ev.key !== 'Escape') return
8725
- if (agentSwitchOpen) {
8726
- closeAgentSwitchMenu()
8727
- return
8728
- }
8729
- if (agentPickerModal && agentPickerModal.classList.contains('is-open')) {
8730
- closeAgentPickerModal()
8404
+ if (pickerManageState.viewActive) {
8405
+ closePickerManageView(true)
8731
8406
  return
8732
8407
  }
8733
8408
  if (mcpJsonInputModal && mcpJsonInputModal.classList.contains('is-open')) {
@@ -8742,12 +8417,12 @@
8742
8417
  closeSkillMdModal()
8743
8418
  return
8744
8419
  }
8745
- if (scheduleRunsModal && scheduleRunsModal.classList.contains('is-open')) {
8746
- closeScheduleRunsModal()
8420
+ if (scheduleManageState.viewActive && scheduleManageState.contentView === 'runs') {
8421
+ closeScheduleRunsView()
8747
8422
  return
8748
8423
  }
8749
- if (scheduleManageModal && scheduleManageModal.classList.contains('is-open')) {
8750
- closeScheduleManageModal()
8424
+ if (scheduleManageState.viewActive) {
8425
+ closeScheduleManageView(true)
8751
8426
  return
8752
8427
  }
8753
8428
  if (toolFilesModal && toolFilesModal.classList.contains('is-open')) {
@@ -8807,33 +8482,9 @@
8807
8482
  brandAgentSwitchBtnEl.addEventListener('click', function (ev) {
8808
8483
  ev.preventDefault()
8809
8484
  ev.stopPropagation()
8810
- void toggleAgentSwitchMenu()
8485
+ goToAgentHomePage()
8811
8486
  })
8812
8487
  }
8813
- if (agentSwitchDropdownEl) {
8814
- agentSwitchDropdownEl.addEventListener('click', function (ev) {
8815
- ev.stopPropagation()
8816
- })
8817
- }
8818
- document.addEventListener('click', function (ev) {
8819
- if (!agentSwitchOpen) return
8820
- const t = ev.target
8821
- if (t instanceof Node) {
8822
- if (agentSwitchWrapEl && agentSwitchWrapEl.contains(t)) return
8823
- if (agentSwitchDropdownEl && agentSwitchDropdownEl.contains(t)) return
8824
- }
8825
- closeAgentSwitchMenu()
8826
- })
8827
- window.addEventListener('resize', function () {
8828
- if (agentSwitchOpen) positionAgentSwitchDropdown()
8829
- })
8830
- window.addEventListener(
8831
- 'scroll',
8832
- function () {
8833
- if (agentSwitchOpen) positionAgentSwitchDropdown()
8834
- },
8835
- true
8836
- )
8837
8488
 
8838
8489
  updateBrandAgent()
8839
8490
  void syncAgentDisplayNameCatalog()