@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 +1 -1
- package/src/api.mjs +16 -0
- package/src/client.js +76 -1
- package/src/index.js +1 -1
package/package.json
CHANGED
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.
|
|
20
|
+
const PLUGIN_VERSION = '0.1.6'
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* 解析插件数据目录。
|