@chenkai114/dsh-team-admin 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenkai114/dsh-team-admin",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "DSH 插件:GUI 管理任意会话的智能体团队 —— 查看成员、删除角色并清理其持久会话(进回收站,可恢复)",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/api.mjs CHANGED
@@ -92,6 +92,22 @@ export function createApi({ ctx, getStore, version, logSystem, services }) {
92
92
  return
93
93
  }
94
94
 
95
+ if (path === '/api/team-admin/dismissed') {
96
+ if (method !== 'GET') { sendJson(res, 405, { error: '请求方法不支持' }); return }
97
+ const store = await getStore()
98
+ const state = await store.readAll()
99
+ const buckets = state !== null && typeof state === 'object' && typeof state.dismissed === 'object' && state.dismissed !== null
100
+ ? state.dismissed
101
+ : {}
102
+ const ids = []
103
+ for (const bucket of Object.values(buckets)) {
104
+ if (bucket === null || typeof bucket !== 'object') continue
105
+ for (const memberId of Object.keys(bucket)) ids.push(memberId)
106
+ }
107
+ sendJson(res, 200, { ids })
108
+ return
109
+ }
110
+
95
111
  if (path === '/api/team-admin/team') {
96
112
  if (method !== 'GET') { sendJson(res, 405, { error: '请求方法不支持' }); return }
97
113
  const sessionId = queryValue(url, 'sessionId')
package/src/client.js CHANGED
@@ -170,11 +170,79 @@
170
170
  }
171
171
 
172
172
  const DISMISS_URL = API_BASE + '/dismiss'
173
+ const DISMISSED_URL = API_BASE + '/dismissed'
174
+ const LIST_FILTER_FLAG = '__dshTeamAdminSessionListFilter__'
173
175
 
174
176
  function dismissPayload(sessionId, name) {
175
177
  return { sessionId: sessionId, name: name, deleteSession: true }
176
178
  }
177
179
 
180
+ // —— 宿主会话列表过滤:把已删除成员从 subagentCatalog 投影里摘掉 ——
181
+ // 宿主 catalog 与 roster 都是 append-only,删会话不会撤销 catalog 事件;
182
+ // 这里在本插件内拦截 /api/session/list 的响应,按墓碑把失效条目过滤掉,
183
+ // 不修改宿主源码,也不需要重编译。
184
+ let dismissedIds = new Set()
185
+ let dismissedReady = null
186
+
187
+ function stripDismissedCatalogs(payload, dismissed) {
188
+ if (payload === null || typeof payload !== 'object' || dismissed === undefined || dismissed.size === 0) return payload
189
+ const keep = (entry) => !(entry !== null && typeof entry === 'object' && typeof entry.id === 'string' && dismissed.has(entry.id))
190
+ const visit = (node, depth) => {
191
+ if (node === null || typeof node !== 'object' || depth > 6) return
192
+ // 1) 顶部「子智能体」选择器:宿主 catalog 投影
193
+ if (Array.isArray(node.subagentCatalog)) node.subagentCatalog = node.subagentCatalog.filter(keep)
194
+ // 2) 「智能体团队」面板:宿主 roster 投影(成员数组)
195
+ if (isRecord(node.agentTeam) && Array.isArray(node.agentTeam.members)) node.agentTeam.members = node.agentTeam.members.filter(keep)
196
+ for (const key of Object.keys(node)) {
197
+ if (key === 'subagentCatalog' || key === 'agentTeam') continue
198
+ visit(node[key], depth + 1)
199
+ }
200
+ }
201
+ visit(payload, 0)
202
+ return payload
203
+ }
204
+
205
+ async function refreshDismissedIds() {
206
+ try {
207
+ const response = await fetch(DISMISSED_URL, { headers: { accept: 'application/json' } })
208
+ if (response.ok !== true) return dismissedIds
209
+ const payload = await response.json()
210
+ const ids = isRecord(payload) && Array.isArray(payload.ids) ? payload.ids : []
211
+ dismissedIds = new Set(ids.filter((id) => typeof id === 'string' && id !== ''))
212
+ } catch (error) {
213
+ // 墓碑拿不到就放行原始列表:过滤是增强,不是前置条件。
214
+ }
215
+ return dismissedIds
216
+ }
217
+
218
+ function installSessionListFilter(win) {
219
+ const scope = win !== undefined ? win : (typeof window === 'undefined' ? null : window)
220
+ if (scope === null || scope === undefined) return false
221
+ if (scope[LIST_FILTER_FLAG] === true) {
222
+ dismissedReady = refreshDismissedIds()
223
+ return false
224
+ }
225
+ const original = scope.fetch
226
+ if (typeof original !== 'function') return false
227
+ scope[LIST_FILTER_FLAG] = true
228
+ dismissedReady = refreshDismissedIds()
229
+ scope.fetch = async function teamAdminAwareFetch(input, init) {
230
+ const url = typeof input === 'string' ? input : (isRecord(input) && typeof input.url === 'string' ? input.url : '')
231
+ if (url.indexOf('/api/session/list') < 0) return await original(input, init)
232
+ if (dismissedReady !== null) await dismissedReady
233
+ const response = await original(input, init)
234
+ if (dismissedIds.size === 0 || response.ok !== true) return response
235
+ try {
236
+ const body = await response.clone().json()
237
+ stripDismissedCatalogs(body, dismissedIds)
238
+ return new Response(JSON.stringify(body), { status: response.status, statusText: response.statusText, headers: response.headers })
239
+ } catch (error) {
240
+ return response
241
+ }
242
+ }
243
+ return true
244
+ }
245
+
178
246
  // —— 响应校验:缺字段回退默认值,形态不对返回可展示的错误 ——
179
247
  function normalizeSessionRow(raw) {
180
248
  const row = isRecord(raw) ? raw : {}
@@ -788,6 +856,7 @@
788
856
  return
789
857
  }
790
858
  state.confirm = null
859
+ dismissedReady = refreshDismissedIds()
791
860
  state.notice = { text: dismissResultText(readDismissResult(outcome.data, target)) }
792
861
  render()
793
862
  await loadTeam(state.selectedId)
@@ -847,6 +916,7 @@
847
916
  throw new Error('mount(container) 需要一个可挂载的 DOM 元素')
848
917
  }
849
918
  ensureStyle(typeof document === 'undefined' ? null : document)
919
+ installSessionListFilter()
850
920
  const controller = createPanelController()
851
921
  container.appendChild(controller.root)
852
922
  controller.start()
@@ -922,6 +992,7 @@
922
992
  return {
923
993
  inject: ['slots'],
924
994
  apply(ctx) {
995
+ installSessionListFilter()
925
996
  const slots = ctx !== null && ctx !== undefined && ctx.slots !== undefined ? ctx.slots : null
926
997
  if (slots === null || typeof slots.inject !== 'function' || typeof slots.register !== 'function') {
927
998
  console.warn('[team-admin] 宿主未提供 slots 服务,配置卡片未注册')
@@ -979,6 +1050,10 @@
979
1050
  PANEL_LABEL: PANEL_LABEL,
980
1051
  PANEL_ORDER: PANEL_ORDER,
981
1052
  API_BASE: API_BASE,
1053
+ DISMISSED_URL: DISMISSED_URL,
1054
+ stripDismissedCatalogs: stripDismissedCatalogs,
1055
+ installSessionListFilter: installSessionListFilter,
1056
+ refreshDismissedIds: refreshDismissedIds,
982
1057
  DEBOUNCE_MS: DEBOUNCE_MS,
983
1058
  CARD_TITLE: CARD_TITLE,
984
1059
  CARD_SUMMARY: CARD_SUMMARY,
@@ -1011,4 +1086,4 @@
1011
1086
  && typeof window.__ModuleLoader__.load === 'function') {
1012
1087
  window.__ModuleLoader__.load({ id: PKG_ID, factory: factory })
1013
1088
  }
1014
- })()
1089
+ })()
package/src/index.js CHANGED
@@ -17,7 +17,7 @@ const DATA_DIR_ENV = 'DSH_TEAM_ADMIN_DATA_DIR'
17
17
  /** 路由前缀:webServer 只支持 exact/prefix 注册。 */
18
18
  const ROUTE_PREFIX = '/api/team-admin'
19
19
  /** 与 package.json 版本保持一致(发布前由测试校验)。 */
20
- const PLUGIN_VERSION = '0.1.4'
20
+ const PLUGIN_VERSION = '0.1.6'
21
21
 
22
22
  /**
23
23
  * 解析插件数据目录。