@chenkai114/dsh-team-admin 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.5",
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,77 @@
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 visit = (node, depth) => {
190
+ if (node === null || typeof node !== 'object' || depth > 6) return
191
+ if (Array.isArray(node.subagentCatalog)) {
192
+ node.subagentCatalog = node.subagentCatalog.filter((entry) => !(entry !== null && typeof entry === 'object' && typeof entry.id === 'string' && dismissed.has(entry.id)))
193
+ }
194
+ for (const key of Object.keys(node)) {
195
+ if (key === 'subagentCatalog') continue
196
+ visit(node[key], depth + 1)
197
+ }
198
+ }
199
+ visit(payload, 0)
200
+ return payload
201
+ }
202
+
203
+ async function refreshDismissedIds() {
204
+ try {
205
+ const response = await fetch(DISMISSED_URL, { headers: { accept: 'application/json' } })
206
+ if (response.ok !== true) return dismissedIds
207
+ const payload = await response.json()
208
+ const ids = isRecord(payload) && Array.isArray(payload.ids) ? payload.ids : []
209
+ dismissedIds = new Set(ids.filter((id) => typeof id === 'string' && id !== ''))
210
+ } catch (error) {
211
+ // 墓碑拿不到就放行原始列表:过滤是增强,不是前置条件。
212
+ }
213
+ return dismissedIds
214
+ }
215
+
216
+ function installSessionListFilter(win) {
217
+ const scope = win !== undefined ? win : (typeof window === 'undefined' ? null : window)
218
+ if (scope === null || scope === undefined) return false
219
+ if (scope[LIST_FILTER_FLAG] === true) {
220
+ dismissedReady = refreshDismissedIds()
221
+ return false
222
+ }
223
+ const original = scope.fetch
224
+ if (typeof original !== 'function') return false
225
+ scope[LIST_FILTER_FLAG] = true
226
+ dismissedReady = refreshDismissedIds()
227
+ scope.fetch = async function teamAdminAwareFetch(input, init) {
228
+ const url = typeof input === 'string' ? input : (isRecord(input) && typeof input.url === 'string' ? input.url : '')
229
+ if (url.indexOf('/api/session/list') < 0) return await original(input, init)
230
+ if (dismissedReady !== null) await dismissedReady
231
+ const response = await original(input, init)
232
+ if (dismissedIds.size === 0 || response.ok !== true) return response
233
+ try {
234
+ const body = await response.clone().json()
235
+ stripDismissedCatalogs(body, dismissedIds)
236
+ return new Response(JSON.stringify(body), { status: response.status, statusText: response.statusText, headers: response.headers })
237
+ } catch (error) {
238
+ return response
239
+ }
240
+ }
241
+ return true
242
+ }
243
+
178
244
  // —— 响应校验:缺字段回退默认值,形态不对返回可展示的错误 ——
179
245
  function normalizeSessionRow(raw) {
180
246
  const row = isRecord(raw) ? raw : {}
@@ -788,6 +854,7 @@
788
854
  return
789
855
  }
790
856
  state.confirm = null
857
+ dismissedReady = refreshDismissedIds()
791
858
  state.notice = { text: dismissResultText(readDismissResult(outcome.data, target)) }
792
859
  render()
793
860
  await loadTeam(state.selectedId)
@@ -847,6 +914,7 @@
847
914
  throw new Error('mount(container) 需要一个可挂载的 DOM 元素')
848
915
  }
849
916
  ensureStyle(typeof document === 'undefined' ? null : document)
917
+ installSessionListFilter()
850
918
  const controller = createPanelController()
851
919
  container.appendChild(controller.root)
852
920
  controller.start()
@@ -922,6 +990,7 @@
922
990
  return {
923
991
  inject: ['slots'],
924
992
  apply(ctx) {
993
+ installSessionListFilter()
925
994
  const slots = ctx !== null && ctx !== undefined && ctx.slots !== undefined ? ctx.slots : null
926
995
  if (slots === null || typeof slots.inject !== 'function' || typeof slots.register !== 'function') {
927
996
  console.warn('[team-admin] 宿主未提供 slots 服务,配置卡片未注册')
@@ -979,6 +1048,10 @@
979
1048
  PANEL_LABEL: PANEL_LABEL,
980
1049
  PANEL_ORDER: PANEL_ORDER,
981
1050
  API_BASE: API_BASE,
1051
+ DISMISSED_URL: DISMISSED_URL,
1052
+ stripDismissedCatalogs: stripDismissedCatalogs,
1053
+ installSessionListFilter: installSessionListFilter,
1054
+ refreshDismissedIds: refreshDismissedIds,
982
1055
  DEBOUNCE_MS: DEBOUNCE_MS,
983
1056
  CARD_TITLE: CARD_TITLE,
984
1057
  CARD_SUMMARY: CARD_SUMMARY,
@@ -1011,4 +1084,4 @@
1011
1084
  && typeof window.__ModuleLoader__.load === 'function') {
1012
1085
  window.__ModuleLoader__.load({ id: PKG_ID, factory: factory })
1013
1086
  }
1014
- })()
1087
+ })()
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.5'
21
21
 
22
22
  /**
23
23
  * 解析插件数据目录。