@downcity/plugins 1.0.153 → 1.0.156

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 (175) hide show
  1. package/bin/chat/ChatPlugin.d.ts +2 -10
  2. package/bin/chat/ChatPlugin.d.ts.map +1 -1
  3. package/bin/chat/ChatPlugin.js +3 -13
  4. package/bin/chat/ChatPlugin.js.map +1 -1
  5. package/bin/chat/PROMPT.direct.d.ts +1 -1
  6. package/bin/chat/PROMPT.direct.d.ts.map +1 -1
  7. package/bin/chat/PROMPT.direct.js +1 -1
  8. package/bin/chat/PROMPT.direct.js.map +1 -1
  9. package/bin/chat/access/ChatAccessActions.d.ts +11 -0
  10. package/bin/chat/access/ChatAccessActions.d.ts.map +1 -0
  11. package/bin/chat/access/ChatAccessActions.js +137 -0
  12. package/bin/chat/access/ChatAccessActions.js.map +1 -0
  13. package/bin/chat/access/ChatAccessRuntime.d.ts +17 -0
  14. package/bin/chat/access/ChatAccessRuntime.d.ts.map +1 -0
  15. package/bin/chat/access/ChatAccessRuntime.js +31 -0
  16. package/bin/chat/access/ChatAccessRuntime.js.map +1 -0
  17. package/bin/chat/access/ChatAccessSchema.d.ts +16 -0
  18. package/bin/chat/access/ChatAccessSchema.d.ts.map +1 -0
  19. package/bin/chat/access/ChatAccessSchema.js +99 -0
  20. package/bin/chat/access/ChatAccessSchema.js.map +1 -0
  21. package/bin/chat/access/ChatAccessService.d.ts +44 -0
  22. package/bin/chat/access/ChatAccessService.d.ts.map +1 -0
  23. package/bin/chat/access/ChatAccessService.js +281 -0
  24. package/bin/chat/access/ChatAccessService.js.map +1 -0
  25. package/bin/chat/access/ChatAccessStore.d.ts +67 -0
  26. package/bin/chat/access/ChatAccessStore.d.ts.map +1 -0
  27. package/bin/chat/access/ChatAccessStore.js +367 -0
  28. package/bin/chat/access/ChatAccessStore.js.map +1 -0
  29. package/bin/chat/accounts/Crypto.d.ts +1 -1
  30. package/bin/chat/accounts/Crypto.js +1 -1
  31. package/bin/chat/accounts/Store.d.ts +1 -1
  32. package/bin/chat/accounts/Store.js +1 -1
  33. package/bin/chat/channels/BaseChatChannel.d.ts +18 -21
  34. package/bin/chat/channels/BaseChatChannel.d.ts.map +1 -1
  35. package/bin/chat/channels/BaseChatChannel.js +48 -58
  36. package/bin/chat/channels/BaseChatChannel.js.map +1 -1
  37. package/bin/chat/channels/BaseChatChannelQueue.d.ts +1 -1
  38. package/bin/chat/channels/BaseChatChannelQueue.d.ts.map +1 -1
  39. package/bin/chat/channels/BaseChatChannelQueue.js +2 -14
  40. package/bin/chat/channels/BaseChatChannelQueue.js.map +1 -1
  41. package/bin/chat/channels/Configuration.d.ts +2 -2
  42. package/bin/chat/channels/Configuration.d.ts.map +1 -1
  43. package/bin/chat/channels/Configuration.js +3 -3
  44. package/bin/chat/channels/Configuration.js.map +1 -1
  45. package/bin/chat/channels/feishu/Configuration.js +4 -4
  46. package/bin/chat/channels/feishu/Configuration.js.map +1 -1
  47. package/bin/chat/channels/feishu/Feishu.d.ts +2 -0
  48. package/bin/chat/channels/feishu/Feishu.d.ts.map +1 -1
  49. package/bin/chat/channels/feishu/Feishu.js +8 -7
  50. package/bin/chat/channels/feishu/Feishu.js.map +1 -1
  51. package/bin/chat/channels/feishu/FeishuMessageHandler.d.ts +9 -17
  52. package/bin/chat/channels/feishu/FeishuMessageHandler.d.ts.map +1 -1
  53. package/bin/chat/channels/feishu/FeishuMessageHandler.js +6 -17
  54. package/bin/chat/channels/feishu/FeishuMessageHandler.js.map +1 -1
  55. package/bin/chat/channels/qq/Configuration.js +4 -4
  56. package/bin/chat/channels/qq/Configuration.js.map +1 -1
  57. package/bin/chat/channels/qq/QQ.d.ts +2 -0
  58. package/bin/chat/channels/qq/QQ.d.ts.map +1 -1
  59. package/bin/chat/channels/qq/QQ.js +8 -7
  60. package/bin/chat/channels/qq/QQ.js.map +1 -1
  61. package/bin/chat/channels/qq/QQMessageHandler.d.ts +9 -17
  62. package/bin/chat/channels/qq/QQMessageHandler.d.ts.map +1 -1
  63. package/bin/chat/channels/qq/QQMessageHandler.js +7 -25
  64. package/bin/chat/channels/qq/QQMessageHandler.js.map +1 -1
  65. package/bin/chat/channels/telegram/Bot.d.ts +2 -0
  66. package/bin/chat/channels/telegram/Bot.d.ts.map +1 -1
  67. package/bin/chat/channels/telegram/Bot.js +8 -7
  68. package/bin/chat/channels/telegram/Bot.js.map +1 -1
  69. package/bin/chat/channels/telegram/Configuration.js +4 -4
  70. package/bin/chat/channels/telegram/Configuration.js.map +1 -1
  71. package/bin/chat/channels/telegram/TelegramMessageHandler.d.ts +9 -17
  72. package/bin/chat/channels/telegram/TelegramMessageHandler.d.ts.map +1 -1
  73. package/bin/chat/channels/telegram/TelegramMessageHandler.js +5 -16
  74. package/bin/chat/channels/telegram/TelegramMessageHandler.js.map +1 -1
  75. package/bin/chat/runtime/ChatChannelActions.d.ts +3 -2
  76. package/bin/chat/runtime/ChatChannelActions.d.ts.map +1 -1
  77. package/bin/chat/runtime/ChatChannelActions.js +57 -0
  78. package/bin/chat/runtime/ChatChannelActions.js.map +1 -1
  79. package/bin/chat/runtime/ChatChannelConfig.d.ts +2 -2
  80. package/bin/chat/runtime/ChatChannelConfig.js +3 -3
  81. package/bin/chat/runtime/ChatChannelConfig.js.map +1 -1
  82. package/bin/chat/runtime/ChatChannelCore.d.ts +2 -2
  83. package/bin/chat/runtime/ChatChannelCore.js +1 -1
  84. package/bin/chat/runtime/ChatChannelLifecycle.d.ts +1 -1
  85. package/bin/chat/runtime/ChatChannelLifecycle.js +1 -1
  86. package/bin/chat/runtime/PluginPoints.d.ts +0 -24
  87. package/bin/chat/runtime/PluginPoints.d.ts.map +1 -1
  88. package/bin/chat/runtime/PluginPoints.js +0 -24
  89. package/bin/chat/runtime/PluginPoints.js.map +1 -1
  90. package/bin/chat/runtime/QueuedUserMessage.d.ts.map +1 -1
  91. package/bin/chat/runtime/QueuedUserMessage.js +0 -2
  92. package/bin/chat/runtime/QueuedUserMessage.js.map +1 -1
  93. package/bin/chat/types/ChannelConfiguration.d.ts +4 -4
  94. package/bin/chat/types/ChannelConfiguration.d.ts.map +1 -1
  95. package/bin/chat/types/ChatAccess.d.ts +300 -0
  96. package/bin/chat/types/ChatAccess.d.ts.map +1 -0
  97. package/bin/chat/types/ChatAccess.js +22 -0
  98. package/bin/chat/types/ChatAccess.js.map +1 -0
  99. package/bin/chat/types/ChatPromptContext.d.ts +0 -15
  100. package/bin/chat/types/ChatPromptContext.d.ts.map +1 -1
  101. package/bin/index.d.ts +4 -4
  102. package/bin/index.d.ts.map +1 -1
  103. package/bin/index.js +3 -3
  104. package/bin/index.js.map +1 -1
  105. package/bin/skill/Config.d.ts +1 -1
  106. package/bin/skill/Config.js +1 -1
  107. package/bin/skill/runtime/Paths.d.ts +1 -1
  108. package/bin/skill/runtime/Paths.js +1 -1
  109. package/package.json +6 -4
  110. package/scripts/chat-access.test.mjs +90 -0
  111. package/scripts/chat-channel-config.test.mjs +82 -0
  112. package/src/chat/ChatPlugin.ts +4 -24
  113. package/src/chat/PROMPT.direct.ts +1 -1
  114. package/src/chat/PROMPT.direct.ts.txt +1 -1
  115. package/src/chat/access/ChatAccessActions.ts +159 -0
  116. package/src/chat/access/ChatAccessRuntime.ts +41 -0
  117. package/src/chat/access/ChatAccessSchema.ts +104 -0
  118. package/src/chat/access/ChatAccessService.ts +311 -0
  119. package/src/chat/access/ChatAccessStore.ts +462 -0
  120. package/src/chat/accounts/Crypto.ts +1 -1
  121. package/src/chat/accounts/Store.ts +1 -1
  122. package/src/chat/channels/BaseChatChannel.ts +59 -80
  123. package/src/chat/channels/BaseChatChannelQueue.ts +2 -14
  124. package/src/chat/channels/Configuration.ts +3 -3
  125. package/src/chat/channels/feishu/Configuration.ts +4 -4
  126. package/src/chat/channels/feishu/Feishu.ts +11 -9
  127. package/src/chat/channels/feishu/FeishuMessageHandler.ts +16 -40
  128. package/src/chat/channels/qq/Configuration.ts +4 -4
  129. package/src/chat/channels/qq/QQ.ts +11 -9
  130. package/src/chat/channels/qq/QQMessageHandler.ts +17 -49
  131. package/src/chat/channels/telegram/Bot.ts +11 -9
  132. package/src/chat/channels/telegram/Configuration.ts +4 -4
  133. package/src/chat/channels/telegram/TelegramMessageHandler.ts +15 -39
  134. package/src/chat/runtime/ChatChannelActions.ts +72 -1
  135. package/src/chat/runtime/ChatChannelConfig.ts +3 -3
  136. package/src/chat/runtime/ChatChannelCore.ts +1 -1
  137. package/src/chat/runtime/ChatChannelLifecycle.ts +1 -1
  138. package/src/chat/runtime/PluginPoints.ts +0 -24
  139. package/src/chat/runtime/QueuedUserMessage.ts +0 -2
  140. package/src/chat/types/ChannelConfiguration.ts +4 -4
  141. package/src/chat/types/ChatAccess.ts +335 -0
  142. package/src/chat/types/ChatPromptContext.ts +0 -17
  143. package/src/index.ts +27 -31
  144. package/src/skill/Config.ts +1 -1
  145. package/src/skill/runtime/Paths.ts +1 -1
  146. package/bin/auth/runtime/AuthorizationConfig.d.ts +0 -41
  147. package/bin/auth/runtime/AuthorizationConfig.d.ts.map +0 -1
  148. package/bin/auth/runtime/AuthorizationConfig.js +0 -185
  149. package/bin/auth/runtime/AuthorizationConfig.js.map +0 -1
  150. package/bin/auth/runtime/AuthorizationPolicy.d.ts +0 -28
  151. package/bin/auth/runtime/AuthorizationPolicy.d.ts.map +0 -1
  152. package/bin/auth/runtime/AuthorizationPolicy.js +0 -124
  153. package/bin/auth/runtime/AuthorizationPolicy.js.map +0 -1
  154. package/bin/auth/runtime/AuthorizationStore.d.ts +0 -29
  155. package/bin/auth/runtime/AuthorizationStore.d.ts.map +0 -1
  156. package/bin/auth/runtime/AuthorizationStore.js +0 -179
  157. package/bin/auth/runtime/AuthorizationStore.js.map +0 -1
  158. package/bin/auth/types/AuthPlugin.d.ts +0 -403
  159. package/bin/auth/types/AuthPlugin.d.ts.map +0 -1
  160. package/bin/auth/types/AuthPlugin.js +0 -111
  161. package/bin/auth/types/AuthPlugin.js.map +0 -1
  162. package/bin/chat/runtime/ChatAuthorizationRuntime.d.ts +0 -22
  163. package/bin/chat/runtime/ChatAuthorizationRuntime.d.ts.map +0 -1
  164. package/bin/chat/runtime/ChatAuthorizationRuntime.js +0 -230
  165. package/bin/chat/runtime/ChatAuthorizationRuntime.js.map +0 -1
  166. package/bin/chat/runtime/PluginDispatch.d.ts +0 -43
  167. package/bin/chat/runtime/PluginDispatch.d.ts.map +0 -1
  168. package/bin/chat/runtime/PluginDispatch.js +0 -57
  169. package/bin/chat/runtime/PluginDispatch.js.map +0 -1
  170. package/src/auth/runtime/AuthorizationConfig.ts +0 -247
  171. package/src/auth/runtime/AuthorizationPolicy.ts +0 -167
  172. package/src/auth/runtime/AuthorizationStore.ts +0 -220
  173. package/src/auth/types/AuthPlugin.ts +0 -540
  174. package/src/chat/runtime/ChatAuthorizationRuntime.ts +0 -270
  175. package/src/chat/runtime/PluginDispatch.ts +0 -89
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Chat Access 应用服务。
3
+ *
4
+ * 关键点(中文)
5
+ * - 所有平台共用相同的 Principal、Grant、Request 和 Audit 业务规则。
6
+ * - Chat Access 只做准入判定,不向 Agent Session 或其他 Plugin 传播权限。
7
+ * - 每个方法自行打开并关闭项目级 Store,避免 Channel 生命周期遗漏连接释放。
8
+ */
9
+
10
+ import path from "node:path";
11
+ import { ChatAccessStore } from "@/chat/access/ChatAccessStore.js";
12
+ import type {
13
+ ApproveChatAccessRequestInput,
14
+ ChatAccessDecision,
15
+ ChatAccessEffect,
16
+ ChatAccessGrant,
17
+ ChatAccessIdentityInput,
18
+ ChatAccessPrincipal,
19
+ ChatAccessPrincipalView,
20
+ ChatAccessRequestStatus,
21
+ ChatAccessRequestView,
22
+ ChatAccessScope,
23
+ ChatAccessScopeInput,
24
+ ChatAccessServiceOptions,
25
+ ChatAccessSnapshot,
26
+ DenyChatAccessRequestInput,
27
+ RevokeChatAccessGrantInput,
28
+ SetChatAccessPrincipalEffectInput,
29
+ } from "@/chat/types/ChatAccess.js";
30
+ import type { ChatDispatchChannel } from "@/chat/types/ChatDispatcher.js";
31
+
32
+ const CHANNELS: ChatDispatchChannel[] = ["telegram", "feishu", "qq"];
33
+
34
+ function normalize_text(value: unknown): string {
35
+ return String(value || "").trim();
36
+ }
37
+
38
+ /** 判断是否为 Chat Access 支持渠道。 */
39
+ export function is_chat_access_channel(value: unknown): value is ChatDispatchChannel {
40
+ return CHANNELS.includes(normalize_text(value).toLowerCase() as ChatDispatchChannel);
41
+ }
42
+
43
+ /** 解析管理输入范围。 */
44
+ export function resolve_chat_access_scopes(scope: ChatAccessScopeInput): ChatAccessScope[] {
45
+ if (scope === "all") return ["direct", "group"];
46
+ if (scope === "direct" || scope === "group") return [scope];
47
+ throw new Error(`Invalid Chat Access scope: ${scope}`);
48
+ }
49
+
50
+ /** 根据平台会话类型解析准入范围。 */
51
+ export function resolve_chat_access_scope(
52
+ channel: ChatDispatchChannel,
53
+ chat_type?: string,
54
+ ): ChatAccessScope {
55
+ const type = normalize_text(chat_type).toLowerCase();
56
+ if (!type) return "direct";
57
+ if (channel === "telegram") return type === "private" ? "direct" : "group";
58
+ if (channel === "feishu") return type === "p2p" ? "direct" : "group";
59
+ return type === "private" || type === "c2c" ? "direct" : "group";
60
+ }
61
+
62
+ /**
63
+ * 当前 Agent 的 Chat Access 应用服务。
64
+ */
65
+ export class ChatAccessService {
66
+ private readonly project_root: string;
67
+ constructor(options: ChatAccessServiceOptions) {
68
+ this.project_root = path.resolve(normalize_text(options.project_root) || ".");
69
+ }
70
+
71
+ private with_store<T>(operation: (store: ChatAccessStore) => T): T {
72
+ const store = new ChatAccessStore(this.project_root);
73
+ try {
74
+ return operation(store);
75
+ } finally {
76
+ store.close();
77
+ }
78
+ }
79
+
80
+ /** 观测并规范化平台主体。 */
81
+ observe_identity(input: ChatAccessIdentityInput): ChatAccessPrincipal {
82
+ const channel = input.channel;
83
+ const issuer = normalize_text(input.issuer);
84
+ const subject_id = normalize_text(input.subject_id);
85
+ const chat_id = normalize_text(input.chat_id);
86
+ if (!is_chat_access_channel(channel)) throw new Error(`Invalid Chat Access channel: ${channel}`);
87
+ if (!issuer) throw new Error("Chat Access issuer is required");
88
+ if (!subject_id) throw new Error("Chat Access subject_id is required");
89
+ if (!chat_id) throw new Error("Chat Access chat_id is required");
90
+ return this.with_store((store) => store.upsert_principal({
91
+ channel,
92
+ issuer,
93
+ subject_id,
94
+ display_name: normalize_text(input.display_name) || undefined,
95
+ chat_id,
96
+ chat_type: normalize_text(input.chat_type) || undefined,
97
+ }));
98
+ }
99
+
100
+ /** 判定当前消息是否允许进入 Agent。 */
101
+ evaluate(input: ChatAccessIdentityInput): ChatAccessDecision {
102
+ const scope = resolve_chat_access_scope(input.channel, input.chat_type);
103
+ const issuer = normalize_text(input.issuer);
104
+ const subject_id = normalize_text(input.subject_id);
105
+ const chat_id = normalize_text(input.chat_id);
106
+ if (!issuer || !subject_id || !chat_id) {
107
+ return {
108
+ allowed: false,
109
+ principal_id: "",
110
+ scope,
111
+ reason: "identity_missing",
112
+ };
113
+ }
114
+
115
+ return this.with_store((store) => store.transaction(() => {
116
+ const principal = store.upsert_principal({
117
+ channel: input.channel,
118
+ issuer,
119
+ subject_id,
120
+ display_name: normalize_text(input.display_name) || undefined,
121
+ chat_id,
122
+ chat_type: normalize_text(input.chat_type) || undefined,
123
+ });
124
+ const grant = store.get_grant(principal.principal_id, scope);
125
+ if (grant?.effect === "deny") {
126
+ store.insert_audit_event({
127
+ principal_id: principal.principal_id,
128
+ action: "message_blocked",
129
+ scope,
130
+ decision: "deny",
131
+ detail: { reason: "grant_denied" },
132
+ });
133
+ return {
134
+ allowed: false,
135
+ principal_id: principal.principal_id,
136
+ scope,
137
+ reason: "grant_denied",
138
+ };
139
+ }
140
+ if (grant?.effect === "allow") {
141
+ store.insert_audit_event({
142
+ principal_id: principal.principal_id,
143
+ action: "message_allowed",
144
+ scope,
145
+ decision: "allow",
146
+ });
147
+ return {
148
+ allowed: true,
149
+ principal_id: principal.principal_id,
150
+ scope,
151
+ reason: "grant_allowed",
152
+ };
153
+ }
154
+
155
+ const request_result = store.create_or_touch_request({
156
+ principal_id: principal.principal_id,
157
+ scope,
158
+ chat_id,
159
+ chat_type: normalize_text(input.chat_type),
160
+ });
161
+ store.insert_audit_event({
162
+ principal_id: principal.principal_id,
163
+ request_id: request_result.request.request_id,
164
+ action: request_result.created ? "request_created" : "request_reused",
165
+ scope,
166
+ decision: "block",
167
+ });
168
+ return {
169
+ allowed: false,
170
+ principal_id: principal.principal_id,
171
+ scope,
172
+ reason: "request_pending",
173
+ request_id: request_result.request.request_id,
174
+ };
175
+ }));
176
+ }
177
+
178
+ /** 批准待处理请求。 */
179
+ approve_request(input: ApproveChatAccessRequestInput): ChatAccessGrant[] {
180
+ return this.resolve_request_with_effect({
181
+ request_id: input.request_id,
182
+ scope: input.scope,
183
+ operator: input.operator,
184
+ effect: "allow",
185
+ });
186
+ }
187
+
188
+ /** 拒绝待处理请求。 */
189
+ deny_request(input: DenyChatAccessRequestInput): ChatAccessGrant[] {
190
+ return this.resolve_request_with_effect({
191
+ request_id: input.request_id,
192
+ scope: input.scope,
193
+ operator: input.operator,
194
+ effect: "deny",
195
+ });
196
+ }
197
+
198
+ private resolve_request_with_effect(input: {
199
+ request_id: string;
200
+ scope?: ChatAccessScopeInput;
201
+ operator: string;
202
+ effect: ChatAccessEffect;
203
+ }): ChatAccessGrant[] {
204
+ const request_id = normalize_text(input.request_id);
205
+ const operator = normalize_text(input.operator) || "unknown";
206
+ if (!request_id) throw new Error("Chat Access request_id is required");
207
+ return this.with_store((store) => store.transaction(() => {
208
+ const request = store.get_request(request_id);
209
+ if (!request || request.status !== "pending") {
210
+ throw new Error(`Pending Chat Access request not found: ${request_id}`);
211
+ }
212
+ const scopes = resolve_chat_access_scopes(input.scope || request.scope);
213
+ const grants = scopes.map((scope) => store.upsert_grant({
214
+ principal_id: request.principal_id,
215
+ scope,
216
+ effect: input.effect,
217
+ operator,
218
+ }));
219
+ const status = input.effect === "allow" ? "approved" : "denied";
220
+ store.resolve_pending_requests({
221
+ principal_id: request.principal_id,
222
+ scopes,
223
+ status,
224
+ operator,
225
+ });
226
+ store.insert_audit_event({
227
+ principal_id: request.principal_id,
228
+ request_id,
229
+ action: input.effect === "allow" ? "request_approved" : "request_denied",
230
+ scope: request.scope,
231
+ decision: input.effect,
232
+ operator,
233
+ detail: { scopes },
234
+ });
235
+ return grants;
236
+ }));
237
+ }
238
+
239
+ /** 直接设置已知主体的 Allow/Deny。 */
240
+ set_principal_effect(input: SetChatAccessPrincipalEffectInput): ChatAccessGrant[] {
241
+ const principal_id = normalize_text(input.principal_id);
242
+ const operator = normalize_text(input.operator) || "unknown";
243
+ if (!principal_id) throw new Error("Chat Access principal_id is required");
244
+ return this.with_store((store) => store.transaction(() => {
245
+ const principal = store.get_principal(principal_id);
246
+ if (!principal) throw new Error(`Chat Access principal not found: ${principal_id}`);
247
+ const scopes = resolve_chat_access_scopes(input.scope);
248
+ const grants = scopes.map((scope) => store.upsert_grant({
249
+ principal_id,
250
+ scope,
251
+ effect: input.effect,
252
+ operator,
253
+ }));
254
+ store.resolve_pending_requests({
255
+ principal_id,
256
+ scopes,
257
+ status: input.effect === "allow" ? "approved" : "denied",
258
+ operator,
259
+ });
260
+ store.insert_audit_event({
261
+ principal_id,
262
+ action: "grant_updated",
263
+ decision: input.effect,
264
+ operator,
265
+ detail: { scopes },
266
+ });
267
+ return grants;
268
+ }));
269
+ }
270
+
271
+ /** 撤销主体指定范围的 Grant。 */
272
+ revoke_grant(input: RevokeChatAccessGrantInput): number {
273
+ const principal_id = normalize_text(input.principal_id);
274
+ const operator = normalize_text(input.operator) || "unknown";
275
+ if (!principal_id) throw new Error("Chat Access principal_id is required");
276
+ return this.with_store((store) => store.transaction(() => {
277
+ const principal = store.get_principal(principal_id);
278
+ if (!principal) throw new Error(`Chat Access principal not found: ${principal_id}`);
279
+ const scopes = resolve_chat_access_scopes(input.scope);
280
+ let removed_count = 0;
281
+ for (const scope of scopes) {
282
+ if (store.delete_grant(principal_id, scope)) removed_count += 1;
283
+ }
284
+ store.insert_audit_event({
285
+ principal_id,
286
+ action: "grant_revoked",
287
+ operator,
288
+ detail: { scopes, removed_count },
289
+ });
290
+ return removed_count;
291
+ }));
292
+ }
293
+
294
+ /** 列出主体和 Grant。 */
295
+ list_principals(): ChatAccessPrincipalView[] {
296
+ return this.with_store((store) => store.list_principals());
297
+ }
298
+
299
+ /** 列出 Access Request。 */
300
+ list_requests(status?: ChatAccessRequestStatus): ChatAccessRequestView[] {
301
+ return this.with_store((store) => store.list_requests(status));
302
+ }
303
+
304
+ /** 读取完整 Chat Access 快照。 */
305
+ snapshot(): ChatAccessSnapshot {
306
+ return this.with_store((store) => ({
307
+ principals: store.list_principals(),
308
+ requests: store.list_requests(),
309
+ }));
310
+ }
311
+ }