@downcity/plugins 1.0.153 → 1.0.157
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/bin/chat/ChatPlugin.d.ts +2 -10
- package/bin/chat/ChatPlugin.d.ts.map +1 -1
- package/bin/chat/ChatPlugin.js +3 -13
- package/bin/chat/ChatPlugin.js.map +1 -1
- package/bin/chat/PROMPT.direct.d.ts +1 -1
- package/bin/chat/PROMPT.direct.d.ts.map +1 -1
- package/bin/chat/PROMPT.direct.js +1 -1
- package/bin/chat/PROMPT.direct.js.map +1 -1
- package/bin/chat/access/ChatAccessActions.d.ts +11 -0
- package/bin/chat/access/ChatAccessActions.d.ts.map +1 -0
- package/bin/chat/access/ChatAccessActions.js +137 -0
- package/bin/chat/access/ChatAccessActions.js.map +1 -0
- package/bin/chat/access/ChatAccessRuntime.d.ts +17 -0
- package/bin/chat/access/ChatAccessRuntime.d.ts.map +1 -0
- package/bin/chat/access/ChatAccessRuntime.js +31 -0
- package/bin/chat/access/ChatAccessRuntime.js.map +1 -0
- package/bin/chat/access/ChatAccessSchema.d.ts +16 -0
- package/bin/chat/access/ChatAccessSchema.d.ts.map +1 -0
- package/bin/chat/access/ChatAccessSchema.js +99 -0
- package/bin/chat/access/ChatAccessSchema.js.map +1 -0
- package/bin/chat/access/ChatAccessService.d.ts +44 -0
- package/bin/chat/access/ChatAccessService.d.ts.map +1 -0
- package/bin/chat/access/ChatAccessService.js +281 -0
- package/bin/chat/access/ChatAccessService.js.map +1 -0
- package/bin/chat/access/ChatAccessStore.d.ts +67 -0
- package/bin/chat/access/ChatAccessStore.d.ts.map +1 -0
- package/bin/chat/access/ChatAccessStore.js +367 -0
- package/bin/chat/access/ChatAccessStore.js.map +1 -0
- package/bin/chat/accounts/Crypto.d.ts +1 -1
- package/bin/chat/accounts/Crypto.js +1 -1
- package/bin/chat/accounts/Store.d.ts +1 -1
- package/bin/chat/accounts/Store.js +1 -1
- package/bin/chat/channels/BaseChatChannel.d.ts +34 -21
- package/bin/chat/channels/BaseChatChannel.d.ts.map +1 -1
- package/bin/chat/channels/BaseChatChannel.js +71 -58
- package/bin/chat/channels/BaseChatChannel.js.map +1 -1
- package/bin/chat/channels/BaseChatChannelQueue.d.ts +1 -1
- package/bin/chat/channels/BaseChatChannelQueue.d.ts.map +1 -1
- package/bin/chat/channels/BaseChatChannelQueue.js +2 -14
- package/bin/chat/channels/BaseChatChannelQueue.js.map +1 -1
- package/bin/chat/channels/Configuration.d.ts +2 -2
- package/bin/chat/channels/Configuration.d.ts.map +1 -1
- package/bin/chat/channels/Configuration.js +3 -3
- package/bin/chat/channels/Configuration.js.map +1 -1
- package/bin/chat/channels/feishu/Configuration.js +4 -4
- package/bin/chat/channels/feishu/Configuration.js.map +1 -1
- package/bin/chat/channels/feishu/Feishu.d.ts +2 -0
- package/bin/chat/channels/feishu/Feishu.d.ts.map +1 -1
- package/bin/chat/channels/feishu/Feishu.js +8 -7
- package/bin/chat/channels/feishu/Feishu.js.map +1 -1
- package/bin/chat/channels/feishu/FeishuMessageHandler.d.ts +9 -17
- package/bin/chat/channels/feishu/FeishuMessageHandler.d.ts.map +1 -1
- package/bin/chat/channels/feishu/FeishuMessageHandler.js +6 -17
- package/bin/chat/channels/feishu/FeishuMessageHandler.js.map +1 -1
- package/bin/chat/channels/qq/Configuration.js +4 -4
- package/bin/chat/channels/qq/Configuration.js.map +1 -1
- package/bin/chat/channels/qq/QQ.d.ts +2 -0
- package/bin/chat/channels/qq/QQ.d.ts.map +1 -1
- package/bin/chat/channels/qq/QQ.js +8 -7
- package/bin/chat/channels/qq/QQ.js.map +1 -1
- package/bin/chat/channels/qq/QQMessageHandler.d.ts +9 -17
- package/bin/chat/channels/qq/QQMessageHandler.d.ts.map +1 -1
- package/bin/chat/channels/qq/QQMessageHandler.js +7 -25
- package/bin/chat/channels/qq/QQMessageHandler.js.map +1 -1
- package/bin/chat/channels/telegram/Bot.d.ts +12 -0
- package/bin/chat/channels/telegram/Bot.d.ts.map +1 -1
- package/bin/chat/channels/telegram/Bot.js +24 -7
- package/bin/chat/channels/telegram/Bot.js.map +1 -1
- package/bin/chat/channels/telegram/Configuration.js +4 -4
- package/bin/chat/channels/telegram/Configuration.js.map +1 -1
- package/bin/chat/channels/telegram/TelegramMessageHandler.d.ts +9 -17
- package/bin/chat/channels/telegram/TelegramMessageHandler.d.ts.map +1 -1
- package/bin/chat/channels/telegram/TelegramMessageHandler.js +5 -16
- package/bin/chat/channels/telegram/TelegramMessageHandler.js.map +1 -1
- package/bin/chat/runtime/ChatChannelActions.d.ts +3 -2
- package/bin/chat/runtime/ChatChannelActions.d.ts.map +1 -1
- package/bin/chat/runtime/ChatChannelActions.js +57 -0
- package/bin/chat/runtime/ChatChannelActions.js.map +1 -1
- package/bin/chat/runtime/ChatChannelConfig.d.ts +2 -2
- package/bin/chat/runtime/ChatChannelConfig.js +3 -3
- package/bin/chat/runtime/ChatChannelConfig.js.map +1 -1
- package/bin/chat/runtime/ChatChannelCore.d.ts +2 -2
- package/bin/chat/runtime/ChatChannelCore.js +1 -1
- package/bin/chat/runtime/ChatChannelLifecycle.d.ts +1 -1
- package/bin/chat/runtime/ChatChannelLifecycle.js +1 -1
- package/bin/chat/runtime/PluginPoints.d.ts +0 -24
- package/bin/chat/runtime/PluginPoints.d.ts.map +1 -1
- package/bin/chat/runtime/PluginPoints.js +0 -24
- package/bin/chat/runtime/PluginPoints.js.map +1 -1
- package/bin/chat/runtime/QueuedUserMessage.d.ts.map +1 -1
- package/bin/chat/runtime/QueuedUserMessage.js +0 -2
- package/bin/chat/runtime/QueuedUserMessage.js.map +1 -1
- package/bin/chat/types/ChannelConfiguration.d.ts +4 -4
- package/bin/chat/types/ChannelConfiguration.d.ts.map +1 -1
- package/bin/chat/types/ChatAccess.d.ts +300 -0
- package/bin/chat/types/ChatAccess.d.ts.map +1 -0
- package/bin/chat/types/ChatAccess.js +22 -0
- package/bin/chat/types/ChatAccess.js.map +1 -0
- package/bin/chat/types/ChatPromptContext.d.ts +0 -15
- package/bin/chat/types/ChatPromptContext.d.ts.map +1 -1
- package/bin/index.d.ts +4 -4
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +3 -3
- package/bin/index.js.map +1 -1
- package/bin/skill/Config.d.ts +1 -1
- package/bin/skill/Config.js +1 -1
- package/bin/skill/runtime/Paths.d.ts +1 -1
- package/bin/skill/runtime/Paths.js +1 -1
- package/package.json +6 -4
- package/scripts/chat-access.test.mjs +126 -0
- package/scripts/chat-channel-config.test.mjs +82 -0
- package/src/chat/ChatPlugin.ts +4 -24
- package/src/chat/PROMPT.direct.ts +1 -1
- package/src/chat/PROMPT.direct.ts.txt +1 -1
- package/src/chat/access/ChatAccessActions.ts +159 -0
- package/src/chat/access/ChatAccessRuntime.ts +41 -0
- package/src/chat/access/ChatAccessSchema.ts +104 -0
- package/src/chat/access/ChatAccessService.ts +311 -0
- package/src/chat/access/ChatAccessStore.ts +462 -0
- package/src/chat/accounts/Crypto.ts +1 -1
- package/src/chat/accounts/Store.ts +1 -1
- package/src/chat/channels/BaseChatChannel.ts +86 -80
- package/src/chat/channels/BaseChatChannelQueue.ts +2 -14
- package/src/chat/channels/Configuration.ts +3 -3
- package/src/chat/channels/feishu/Configuration.ts +4 -4
- package/src/chat/channels/feishu/Feishu.ts +11 -9
- package/src/chat/channels/feishu/FeishuMessageHandler.ts +16 -40
- package/src/chat/channels/qq/Configuration.ts +4 -4
- package/src/chat/channels/qq/QQ.ts +11 -9
- package/src/chat/channels/qq/QQMessageHandler.ts +17 -49
- package/src/chat/channels/telegram/Bot.ts +29 -9
- package/src/chat/channels/telegram/Configuration.ts +4 -4
- package/src/chat/channels/telegram/TelegramMessageHandler.ts +15 -39
- package/src/chat/runtime/ChatChannelActions.ts +72 -1
- package/src/chat/runtime/ChatChannelConfig.ts +3 -3
- package/src/chat/runtime/ChatChannelCore.ts +1 -1
- package/src/chat/runtime/ChatChannelLifecycle.ts +1 -1
- package/src/chat/runtime/PluginPoints.ts +0 -24
- package/src/chat/runtime/QueuedUserMessage.ts +0 -2
- package/src/chat/types/ChannelConfiguration.ts +4 -4
- package/src/chat/types/ChatAccess.ts +335 -0
- package/src/chat/types/ChatPromptContext.ts +0 -17
- package/src/index.ts +27 -31
- package/src/skill/Config.ts +1 -1
- package/src/skill/runtime/Paths.ts +1 -1
- package/bin/auth/runtime/AuthorizationConfig.d.ts +0 -41
- package/bin/auth/runtime/AuthorizationConfig.d.ts.map +0 -1
- package/bin/auth/runtime/AuthorizationConfig.js +0 -185
- package/bin/auth/runtime/AuthorizationConfig.js.map +0 -1
- package/bin/auth/runtime/AuthorizationPolicy.d.ts +0 -28
- package/bin/auth/runtime/AuthorizationPolicy.d.ts.map +0 -1
- package/bin/auth/runtime/AuthorizationPolicy.js +0 -124
- package/bin/auth/runtime/AuthorizationPolicy.js.map +0 -1
- package/bin/auth/runtime/AuthorizationStore.d.ts +0 -29
- package/bin/auth/runtime/AuthorizationStore.d.ts.map +0 -1
- package/bin/auth/runtime/AuthorizationStore.js +0 -179
- package/bin/auth/runtime/AuthorizationStore.js.map +0 -1
- package/bin/auth/types/AuthPlugin.d.ts +0 -403
- package/bin/auth/types/AuthPlugin.d.ts.map +0 -1
- package/bin/auth/types/AuthPlugin.js +0 -111
- package/bin/auth/types/AuthPlugin.js.map +0 -1
- package/bin/chat/runtime/ChatAuthorizationRuntime.d.ts +0 -22
- package/bin/chat/runtime/ChatAuthorizationRuntime.d.ts.map +0 -1
- package/bin/chat/runtime/ChatAuthorizationRuntime.js +0 -230
- package/bin/chat/runtime/ChatAuthorizationRuntime.js.map +0 -1
- package/bin/chat/runtime/PluginDispatch.d.ts +0 -43
- package/bin/chat/runtime/PluginDispatch.d.ts.map +0 -1
- package/bin/chat/runtime/PluginDispatch.js +0 -57
- package/bin/chat/runtime/PluginDispatch.js.map +0 -1
- package/src/auth/runtime/AuthorizationConfig.ts +0 -247
- package/src/auth/runtime/AuthorizationPolicy.ts +0 -167
- package/src/auth/runtime/AuthorizationStore.ts +0 -220
- package/src/auth/types/AuthPlugin.ts +0 -540
- package/src/chat/runtime/ChatAuthorizationRuntime.ts +0 -270
- package/src/chat/runtime/PluginDispatch.ts +0 -89
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 关键点(中文)
|
|
5
5
|
* - 通过 context 显式注入 runtime 依赖。
|
|
6
6
|
* - 统一注册 dispatcher,暴露 sendText/sendAction 能力。
|
|
7
|
-
* -
|
|
7
|
+
* - 基类只保留 Chat Access、工具发送与入站编排;存储/队列细节已下沉到辅助模块。
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { registerChatSender } from "@/chat/runtime/ChatSendRegistry.js";
|
|
@@ -19,9 +19,10 @@ import type { AgentContext } from "@downcity/agent";
|
|
|
19
19
|
import { resolveChatQueueStore } from "@/chat/runtime/ChatQueue.js";
|
|
20
20
|
import { deleteChatSessionById } from "@/chat/runtime/ChatSessionDelete.js";
|
|
21
21
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
} from "@/chat/
|
|
22
|
+
create_chat_access_service,
|
|
23
|
+
resolve_chat_access_issuer,
|
|
24
|
+
} from "@/chat/access/ChatAccessRuntime.js";
|
|
25
|
+
import type { ChatAccessDecision } from "@/chat/types/ChatAccess.js";
|
|
25
26
|
import {
|
|
26
27
|
appendToolOutboundChannelHistory,
|
|
27
28
|
resolveChannelSessionId,
|
|
@@ -108,9 +109,9 @@ export type IncomingChatMessage = {
|
|
|
108
109
|
};
|
|
109
110
|
|
|
110
111
|
/**
|
|
111
|
-
*
|
|
112
|
+
* 入站 Chat Access 判定输入。
|
|
112
113
|
*/
|
|
113
|
-
export type
|
|
114
|
+
export type IncomingChatAccessParams = {
|
|
114
115
|
chatId: string;
|
|
115
116
|
chatType?: string;
|
|
116
117
|
userId?: string;
|
|
@@ -119,12 +120,9 @@ export type IncomingAuthorizationParams = {
|
|
|
119
120
|
};
|
|
120
121
|
|
|
121
122
|
/**
|
|
122
|
-
*
|
|
123
|
+
* 入站 Chat Access 判定结果。
|
|
123
124
|
*/
|
|
124
|
-
export type
|
|
125
|
-
decision: "allow" | "block";
|
|
126
|
-
reason: string;
|
|
127
|
-
};
|
|
125
|
+
export type IncomingChatAccessResult = ChatAccessDecision;
|
|
128
126
|
|
|
129
127
|
/**
|
|
130
128
|
* Chat channel 基类。
|
|
@@ -134,6 +132,7 @@ export abstract class BaseChatChannel {
|
|
|
134
132
|
protected readonly context: AgentContext;
|
|
135
133
|
protected readonly rootPath: string;
|
|
136
134
|
protected readonly logger: Logger;
|
|
135
|
+
private readonly access_notice_sent_at = new Map<string, number>();
|
|
137
136
|
|
|
138
137
|
protected constructor(params: {
|
|
139
138
|
channel: ChatDispatchChannel;
|
|
@@ -159,22 +158,57 @@ export abstract class BaseChatChannel {
|
|
|
159
158
|
params: ChannelSendTextParams,
|
|
160
159
|
): Promise<void>;
|
|
161
160
|
|
|
161
|
+
/**
|
|
162
|
+
* SDK 未绑定 City Chat Account 时的平台稳定 issuer 回退值。
|
|
163
|
+
*/
|
|
164
|
+
protected getAccessIssuerFallback(): string {
|
|
165
|
+
return "";
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 格式化 Chat Access 中需要原样展示的值。
|
|
170
|
+
*
|
|
171
|
+
* 说明(中文)
|
|
172
|
+
* - 默认返回纯文本,避免通用层绑定具体平台的富文本语法。
|
|
173
|
+
* - 支持富文本的平台可覆写此方法,确保标识符和命令可被准确展示、复制。
|
|
174
|
+
*/
|
|
175
|
+
protected format_access_code(value: string): string {
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 格式化 Chat Access 管理命令。
|
|
181
|
+
*
|
|
182
|
+
* 说明(中文)
|
|
183
|
+
* - 默认复用平台的原样值格式,避免通用层引入具体富文本语法。
|
|
184
|
+
* - 平台可单独覆写为块级代码,改善长命令的阅读和复制体验。
|
|
185
|
+
*/
|
|
186
|
+
protected format_access_command(command: string): string {
|
|
187
|
+
return this.format_access_code(command);
|
|
188
|
+
}
|
|
189
|
+
|
|
162
190
|
protected sendActionToPlatform?(
|
|
163
191
|
params: ChannelSendActionParams,
|
|
164
192
|
): Promise<void>;
|
|
165
193
|
|
|
166
194
|
/**
|
|
167
|
-
*
|
|
195
|
+
* 发送 Chat Access 提示文本。
|
|
168
196
|
*
|
|
169
197
|
* 关键点(中文)
|
|
170
198
|
* - 一律按普通消息发送,不挂 reply,避免把“权限提示”误挂到某条消息下面。
|
|
171
199
|
*/
|
|
172
|
-
protected async
|
|
200
|
+
protected async sendAccessText(params: {
|
|
173
201
|
chatId: string;
|
|
174
202
|
text: string;
|
|
175
203
|
chatType?: string;
|
|
176
204
|
messageThreadId?: number;
|
|
177
205
|
}): Promise<void> {
|
|
206
|
+
const notice_key = `${params.chatId}:${params.text}`;
|
|
207
|
+
const current_time = Date.now();
|
|
208
|
+
const last_sent_at = this.access_notice_sent_at.get(notice_key) || 0;
|
|
209
|
+
if (current_time - last_sent_at < 60_000) return;
|
|
210
|
+
if (this.access_notice_sent_at.size >= 1_000) this.access_notice_sent_at.clear();
|
|
211
|
+
this.access_notice_sent_at.set(notice_key, current_time);
|
|
178
212
|
await this.sendTextToPlatform({
|
|
179
213
|
chatId: params.chatId,
|
|
180
214
|
text: params.text,
|
|
@@ -186,83 +220,55 @@ export abstract class BaseChatChannel {
|
|
|
186
220
|
}
|
|
187
221
|
|
|
188
222
|
/**
|
|
189
|
-
*
|
|
223
|
+
* 执行入站 Chat Access 判定。
|
|
190
224
|
*/
|
|
191
|
-
protected async
|
|
192
|
-
params:
|
|
193
|
-
): Promise<
|
|
194
|
-
|
|
195
|
-
context
|
|
225
|
+
protected async evaluateIncomingAccess(
|
|
226
|
+
params: IncomingChatAccessParams,
|
|
227
|
+
): Promise<IncomingChatAccessResult> {
|
|
228
|
+
const issuer =
|
|
229
|
+
resolve_chat_access_issuer(this.context, this.channel) ||
|
|
230
|
+
String(this.getAccessIssuerFallback() || "").trim();
|
|
231
|
+
return create_chat_access_service(this.context).evaluate({
|
|
196
232
|
channel: this.channel,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
233
|
+
issuer,
|
|
234
|
+
subject_id: String(params.userId || "").trim(),
|
|
235
|
+
display_name: String(params.username || "").trim() || undefined,
|
|
236
|
+
chat_id: String(params.chatId || "").trim(),
|
|
237
|
+
chat_type: String(params.chatType || "").trim() || undefined,
|
|
238
|
+
chat_title: String(params.chatTitle || "").trim() || undefined,
|
|
202
239
|
});
|
|
203
240
|
}
|
|
204
241
|
|
|
205
242
|
/**
|
|
206
|
-
*
|
|
243
|
+
* 生成 Chat Access 拒绝提示文案。
|
|
207
244
|
*/
|
|
208
|
-
protected
|
|
209
|
-
|
|
210
|
-
): Promise<IncomingAuthorizationResult> {
|
|
211
|
-
try {
|
|
212
|
-
await guardIncomingChat({
|
|
213
|
-
context: this.context,
|
|
214
|
-
channel: this.channel,
|
|
215
|
-
input: {
|
|
216
|
-
channel: this.channel,
|
|
217
|
-
chatId: params.chatId,
|
|
218
|
-
chatType: params.chatType,
|
|
219
|
-
userId: params.userId,
|
|
220
|
-
username: params.username,
|
|
221
|
-
chatTitle: params.chatTitle,
|
|
222
|
-
},
|
|
223
|
-
});
|
|
224
|
-
return {
|
|
225
|
-
decision: "allow",
|
|
226
|
-
reason: "allow",
|
|
227
|
-
};
|
|
228
|
-
} catch (error) {
|
|
229
|
-
return {
|
|
230
|
-
decision: "block",
|
|
231
|
-
reason: String(error || "blocked"),
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* 生成未授权提示文案。
|
|
238
|
-
*/
|
|
239
|
-
protected buildUnauthorizedBlockedText(params?: {
|
|
240
|
-
userId?: string;
|
|
241
|
-
chatId?: string;
|
|
242
|
-
chatType?: string;
|
|
245
|
+
protected buildAccessBlockedText(params: {
|
|
246
|
+
result: IncomingChatAccessResult;
|
|
243
247
|
}): string {
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
"当前会话权限不足,已拒绝处理。请联系管理员调整你的角色。",
|
|
249
|
-
];
|
|
250
|
-
|
|
251
|
-
if (userId) {
|
|
252
|
-
lines.push(
|
|
253
|
-
"",
|
|
254
|
-
"请把下面命令发给管理员:",
|
|
255
|
-
`downcity chat auth set ${this.channel}:${userId}`,
|
|
256
|
-
);
|
|
248
|
+
const agent_id = String(this.context.config.id || "agent").trim() || "agent";
|
|
249
|
+
const displayed_agent_id = this.format_access_code(agent_id);
|
|
250
|
+
if (params.result.reason === "identity_missing") {
|
|
251
|
+
return "当前平台身份无法识别,请联系管理员检查 Chat 账号配置。";
|
|
257
252
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
lines.push("", "来源信息:");
|
|
261
|
-
if (chatId) lines.push(`chatId: ${chatId}`);
|
|
262
|
-
if (chatType) lines.push(`chatType: ${chatType}`);
|
|
253
|
+
if (params.result.reason === "grant_denied") {
|
|
254
|
+
return `当前账号未获准访问 Agent "${displayed_agent_id}"。`;
|
|
263
255
|
}
|
|
264
|
-
|
|
265
|
-
|
|
256
|
+
const request_id = String(params.result.request_id || "").trim();
|
|
257
|
+
if (!request_id) {
|
|
258
|
+
return `当前账号尚未获准访问 Agent "${displayed_agent_id}"。`;
|
|
259
|
+
}
|
|
260
|
+
const displayed_request_id = this.format_access_code(request_id);
|
|
261
|
+
const approval_command = this.format_access_command(
|
|
262
|
+
`downcity chat access approve ${request_id} --agent ${agent_id}`,
|
|
263
|
+
);
|
|
264
|
+
return [
|
|
265
|
+
`当前账号尚未获准访问 Agent "${displayed_agent_id}"。`,
|
|
266
|
+
"",
|
|
267
|
+
`访问请求:${displayed_request_id}`,
|
|
268
|
+
"",
|
|
269
|
+
"请将下面命令发送给管理员:",
|
|
270
|
+
approval_command,
|
|
271
|
+
].join("\n");
|
|
266
272
|
}
|
|
267
273
|
|
|
268
274
|
/**
|
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
5
|
* - audit / exec 入队都需要走同一套 prepare + emit 流程。
|
|
6
|
-
* - 这些逻辑抽离后,`BaseChatChannel`
|
|
6
|
+
* - 这些逻辑抽离后,`BaseChatChannel` 只负责渠道层输入整理与 Chat Access 判断。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { resolveChatQueueStore } from "@/chat/runtime/ChatQueue.js";
|
|
10
10
|
import { buildQueuedUserMessageWithInfo } from "@/chat/runtime/QueuedUserMessage.js";
|
|
11
11
|
import { appendExecIngress } from "@/chat/runtime/ChatIngressStore.js";
|
|
12
|
-
import { resolveIncomingChatUserRole } from "@/chat/runtime/PluginDispatch.js";
|
|
13
12
|
import {
|
|
14
13
|
emitChatEnqueueEffect,
|
|
15
14
|
prepareChatEnqueue,
|
|
@@ -190,18 +189,9 @@ export async function enqueueExecChannelMessage(
|
|
|
190
189
|
params: EnqueueExecChannelMessageParams,
|
|
191
190
|
): Promise<{ chatKey: string; position: number }> {
|
|
192
191
|
const msg = params.message;
|
|
193
|
-
const userRole = await resolveIncomingChatUserRole({
|
|
194
|
-
context: params.context,
|
|
195
|
-
channel: params.channel,
|
|
196
|
-
userId: msg.userId,
|
|
197
|
-
});
|
|
198
192
|
const inboundExtra =
|
|
199
193
|
msg.extra && typeof msg.extra === "object" ? stripUndefinedMeta(msg.extra) : {};
|
|
200
|
-
const mergedExtra: JsonObject = {
|
|
201
|
-
...inboundExtra,
|
|
202
|
-
roleId: userRole?.roleId || "unknown",
|
|
203
|
-
permissions: userRole?.permissions || [],
|
|
204
|
-
};
|
|
194
|
+
const mergedExtra: JsonObject = { ...inboundExtra };
|
|
205
195
|
|
|
206
196
|
const chatKey = await resolveOrCreateChannelSessionId({
|
|
207
197
|
context: params.context,
|
|
@@ -218,8 +208,6 @@ export async function enqueueExecChannelMessage(
|
|
|
218
208
|
messageId: msg.messageId,
|
|
219
209
|
userId: msg.userId,
|
|
220
210
|
username: msg.username,
|
|
221
|
-
roleId: userRole?.roleId,
|
|
222
|
-
permissions: userRole?.permissions,
|
|
223
211
|
receivedAt: msg.receivedAt,
|
|
224
212
|
userTimezone: msg.userTimezone,
|
|
225
213
|
text: msg.text,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
5
|
* - 每个 channel 继承该基类,集中声明自己的配置字段。
|
|
6
|
-
* - 统一提供“可写
|
|
6
|
+
* - 统一提供“可写 Agent 配置字段”提取逻辑,避免在 service 中重复硬编码。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type {
|
|
@@ -29,7 +29,7 @@ export abstract class ChatChannelConfiguration {
|
|
|
29
29
|
/**
|
|
30
30
|
* 返回可通过 `chat.configure` 更新的运行态字段定义。
|
|
31
31
|
*/
|
|
32
|
-
|
|
33
|
-
return this.describe().fields.
|
|
32
|
+
get_writable_agent_config_fields(): ChatChannelConfigurationField[] {
|
|
33
|
+
return this.describe().fields.agent_config.filter((field) => field.writable);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -28,14 +28,14 @@ export class FeishuChannelConfiguration extends ChatChannelConfiguration {
|
|
|
28
28
|
canConfigure: true,
|
|
29
29
|
},
|
|
30
30
|
fields: {
|
|
31
|
-
|
|
31
|
+
agent_config: [
|
|
32
32
|
{
|
|
33
33
|
key: "enabled",
|
|
34
34
|
label: "Enabled",
|
|
35
35
|
description:
|
|
36
36
|
"Whether feishu channel should be started by runtime.",
|
|
37
37
|
type: "boolean",
|
|
38
|
-
source: "
|
|
38
|
+
source: "agent_config",
|
|
39
39
|
required: false,
|
|
40
40
|
nullable: false,
|
|
41
41
|
writable: true,
|
|
@@ -47,9 +47,9 @@ export class FeishuChannelConfiguration extends ChatChannelConfiguration {
|
|
|
47
47
|
key: "channelAccountId",
|
|
48
48
|
label: "Chat Account ID",
|
|
49
49
|
description:
|
|
50
|
-
"Bind this chat platform to a
|
|
50
|
+
"Bind this chat platform to a channel_accounts row in the global downcity.db.",
|
|
51
51
|
type: "string",
|
|
52
|
-
source: "
|
|
52
|
+
source: "agent_config",
|
|
53
53
|
required: false,
|
|
54
54
|
nullable: true,
|
|
55
55
|
writable: true,
|
|
@@ -81,6 +81,11 @@ export class FeishuBot extends BaseChatChannel {
|
|
|
81
81
|
return this.buildChatKey(params.chatId);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
/** SDK 直连时使用飞书 App ID 作为 issuer。 */
|
|
85
|
+
protected getAccessIssuerFallback(): string {
|
|
86
|
+
return String(this.appId || "").trim();
|
|
87
|
+
}
|
|
88
|
+
|
|
84
89
|
protected async sendTextToPlatform(
|
|
85
90
|
params: ChannelSendTextParams,
|
|
86
91
|
): Promise<void> {
|
|
@@ -209,16 +214,13 @@ export class FeishuBot extends BaseChatChannel {
|
|
|
209
214
|
rememberChat: (threadId, value) => {
|
|
210
215
|
this.knownChats.set(threadId, value);
|
|
211
216
|
},
|
|
212
|
-
|
|
213
|
-
await this.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
await this.evaluateIncomingAuthorization(params),
|
|
217
|
-
sendAuthorizationText: async (params) => {
|
|
218
|
-
await this.sendAuthorizationText(params);
|
|
217
|
+
evaluateIncomingAccess: async (params) =>
|
|
218
|
+
await this.evaluateIncomingAccess(params),
|
|
219
|
+
sendAccessText: async (params) => {
|
|
220
|
+
await this.sendAccessText(params);
|
|
219
221
|
},
|
|
220
|
-
|
|
221
|
-
this.
|
|
222
|
+
buildAccessBlockedText: (params) =>
|
|
223
|
+
this.buildAccessBlockedText(params),
|
|
222
224
|
runInChat: async (chatKey, fn) => {
|
|
223
225
|
await this.runInChat(chatKey, fn);
|
|
224
226
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Feishu 入站消息处理器。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
|
-
* -
|
|
5
|
+
* - 负责单条飞书消息的去重、解析、Chat Access、附件保存与执行入队。
|
|
6
6
|
* - 不持有 `FeishuBot` 实例;所有副作用通过显式依赖注入。
|
|
7
7
|
* - 解析失败/执行失败通过渠道门面提供的发送函数回写错误消息。
|
|
8
8
|
*/
|
|
@@ -11,8 +11,8 @@ import path from "path";
|
|
|
11
11
|
import type { Logger } from "@downcity/agent";
|
|
12
12
|
import type { AgentContext } from "@downcity/agent";
|
|
13
13
|
import type {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
IncomingChatAccessParams,
|
|
15
|
+
IncomingChatAccessResult,
|
|
16
16
|
} from "@/chat/channels/BaseChatChannel.js";
|
|
17
17
|
import type { JsonObject } from "@downcity/agent";
|
|
18
18
|
import type { InboundReplyContext } from "@/chat/types/ReplyContext.js";
|
|
@@ -109,34 +109,22 @@ export interface FeishuMessageHandlerOptions {
|
|
|
109
109
|
threadId: string,
|
|
110
110
|
value: { chatId: string; chatType: string; chatTitle?: string },
|
|
111
111
|
): void;
|
|
112
|
+
/** 入站 Chat Access 判定。 */
|
|
113
|
+
evaluateIncomingAccess(
|
|
114
|
+
params: IncomingChatAccessParams,
|
|
115
|
+
): Promise<IncomingChatAccessResult>;
|
|
112
116
|
/**
|
|
113
|
-
*
|
|
117
|
+
* 发送 Chat Access 失败提示。
|
|
114
118
|
*/
|
|
115
|
-
|
|
116
|
-
params: IncomingAuthorizationParams,
|
|
117
|
-
): Promise<void>;
|
|
118
|
-
/**
|
|
119
|
-
* 入站授权判定。
|
|
120
|
-
*/
|
|
121
|
-
evaluateIncomingAuthorization(
|
|
122
|
-
params: IncomingAuthorizationParams,
|
|
123
|
-
): Promise<IncomingAuthorizationResult>;
|
|
124
|
-
/**
|
|
125
|
-
* 发送授权失败提示。
|
|
126
|
-
*/
|
|
127
|
-
sendAuthorizationText(params: {
|
|
119
|
+
sendAccessText(params: {
|
|
128
120
|
chatId: string;
|
|
129
121
|
text: string;
|
|
130
122
|
chatType?: string;
|
|
131
123
|
}): Promise<void>;
|
|
132
124
|
/**
|
|
133
|
-
*
|
|
125
|
+
* 构建 Chat Access 失败提示文案。
|
|
134
126
|
*/
|
|
135
|
-
|
|
136
|
-
userId?: string;
|
|
137
|
-
chatId?: string;
|
|
138
|
-
chatType?: string;
|
|
139
|
-
}): string;
|
|
127
|
+
buildAccessBlockedText(params: { result: IncomingChatAccessResult }): string;
|
|
140
128
|
/**
|
|
141
129
|
* 按 chatKey 串行执行。
|
|
142
130
|
*/
|
|
@@ -337,7 +325,7 @@ async function parseIncomingMessage(params: {
|
|
|
337
325
|
}
|
|
338
326
|
|
|
339
327
|
/**
|
|
340
|
-
*
|
|
328
|
+
* Chat Access 通过后处理消息内容。
|
|
341
329
|
*/
|
|
342
330
|
async function handleAuthorizedMessage(params: {
|
|
343
331
|
options: FeishuMessageHandlerOptions;
|
|
@@ -374,31 +362,19 @@ async function handleAuthorizedMessage(params: {
|
|
|
374
362
|
const resolvedChatTitle = await options.resolveChatTitle(chatId);
|
|
375
363
|
const chatTitle = resolvedChatTitle || (chatType === "p2p" ? actorName : undefined);
|
|
376
364
|
|
|
377
|
-
await options.
|
|
378
|
-
chatId,
|
|
379
|
-
chatType,
|
|
380
|
-
chatTitle,
|
|
381
|
-
userId: actorId,
|
|
382
|
-
username: actorName,
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
const authResult = await options.evaluateIncomingAuthorization({
|
|
365
|
+
const access_result = await options.evaluateIncomingAccess({
|
|
386
366
|
chatId,
|
|
387
367
|
chatType,
|
|
388
368
|
chatTitle,
|
|
389
369
|
userId: actorId,
|
|
390
370
|
username: actorName,
|
|
391
371
|
});
|
|
392
|
-
if (
|
|
372
|
+
if (!access_result.allowed) {
|
|
393
373
|
if (chatType === "p2p") {
|
|
394
|
-
await options.
|
|
374
|
+
await options.sendAccessText({
|
|
395
375
|
chatId,
|
|
396
376
|
chatType,
|
|
397
|
-
text: options.
|
|
398
|
-
chatId,
|
|
399
|
-
chatType,
|
|
400
|
-
userId: actorId,
|
|
401
|
-
}),
|
|
377
|
+
text: options.buildAccessBlockedText({ result: access_result }),
|
|
402
378
|
});
|
|
403
379
|
}
|
|
404
380
|
return true;
|
|
@@ -28,13 +28,13 @@ export class QqChannelConfiguration extends ChatChannelConfiguration {
|
|
|
28
28
|
canConfigure: true,
|
|
29
29
|
},
|
|
30
30
|
fields: {
|
|
31
|
-
|
|
31
|
+
agent_config: [
|
|
32
32
|
{
|
|
33
33
|
key: "enabled",
|
|
34
34
|
label: "Enabled",
|
|
35
35
|
description: "Whether qq channel should be started by runtime.",
|
|
36
36
|
type: "boolean",
|
|
37
|
-
source: "
|
|
37
|
+
source: "agent_config",
|
|
38
38
|
required: false,
|
|
39
39
|
nullable: false,
|
|
40
40
|
writable: true,
|
|
@@ -46,9 +46,9 @@ export class QqChannelConfiguration extends ChatChannelConfiguration {
|
|
|
46
46
|
key: "channelAccountId",
|
|
47
47
|
label: "Chat Account ID",
|
|
48
48
|
description:
|
|
49
|
-
"Bind this chat platform to a
|
|
49
|
+
"Bind this chat platform to a channel_accounts row in the global downcity.db.",
|
|
50
50
|
type: "string",
|
|
51
|
-
source: "
|
|
51
|
+
source: "agent_config",
|
|
52
52
|
required: false,
|
|
53
53
|
nullable: true,
|
|
54
54
|
writable: true,
|
|
@@ -82,6 +82,11 @@ export class QQBot extends BaseChatChannel {
|
|
|
82
82
|
return `qq-${chatType}-${params.chatId}`;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/** SDK 直连时使用 QQ App ID 作为 issuer。 */
|
|
86
|
+
protected getAccessIssuerFallback(): string {
|
|
87
|
+
return String(this.appId || "").trim();
|
|
88
|
+
}
|
|
89
|
+
|
|
85
90
|
protected async sendTextToPlatform(
|
|
86
91
|
params: ChannelSendTextParams,
|
|
87
92
|
): Promise<void> {
|
|
@@ -266,16 +271,13 @@ export class QQBot extends BaseChatChannel {
|
|
|
266
271
|
logger: this.logger,
|
|
267
272
|
getBotUserId: () => this.botUserId,
|
|
268
273
|
getChatKey: (params) => this.getChatKey(params),
|
|
269
|
-
|
|
270
|
-
await this.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
await this.evaluateIncomingAuthorization(params),
|
|
274
|
-
sendAuthorizationText: async (params) => {
|
|
275
|
-
await this.sendAuthorizationText(params);
|
|
274
|
+
evaluateIncomingAccess: async (params) =>
|
|
275
|
+
await this.evaluateIncomingAccess(params),
|
|
276
|
+
sendAccessText: async (params) => {
|
|
277
|
+
await this.sendAccessText(params);
|
|
276
278
|
},
|
|
277
|
-
|
|
278
|
-
this.
|
|
279
|
+
buildAccessBlockedText: (params) =>
|
|
280
|
+
this.buildAccessBlockedText(params),
|
|
279
281
|
shouldSkipDuplicatedInboundMessage: async (eventType, messageId) =>
|
|
280
282
|
await this.shouldSkipDuplicatedInboundMessage(eventType, messageId),
|
|
281
283
|
enqueueAuditMessage: async (params) => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* QQ 入站消息处理器。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
|
-
* - 负责 group / c2c / channel
|
|
5
|
+
* - 负责 group / c2c / channel 三类入站消息的 Chat Access、去重、审计与执行入队。
|
|
6
6
|
* - 不直接持有 `QQBot` 实例;所有副作用通过显式函数注入。
|
|
7
7
|
* - `QQBot` 因此只保留渠道生命周期、dispatch 分流、发送与命令处理。
|
|
8
8
|
*/
|
|
@@ -11,8 +11,8 @@ import type { Logger } from "@downcity/agent";
|
|
|
11
11
|
import type { AgentContext } from "@downcity/agent";
|
|
12
12
|
import type {
|
|
13
13
|
ChannelChatKeyParams,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
IncomingChatAccessParams,
|
|
15
|
+
IncomingChatAccessResult,
|
|
16
16
|
} from "@/chat/channels/BaseChatChannel.js";
|
|
17
17
|
import type { ChannelUserMessageMeta } from "@/chat/channels/BaseChatChannelSupport.js";
|
|
18
18
|
import {
|
|
@@ -69,34 +69,22 @@ export interface QQMessageHandlerOptions {
|
|
|
69
69
|
* 计算 channel chatKey。
|
|
70
70
|
*/
|
|
71
71
|
getChatKey(params: ChannelChatKeyParams): string;
|
|
72
|
+
/** 入站 Chat Access 判定。 */
|
|
73
|
+
evaluateIncomingAccess(
|
|
74
|
+
params: IncomingChatAccessParams,
|
|
75
|
+
): Promise<IncomingChatAccessResult>;
|
|
72
76
|
/**
|
|
73
|
-
*
|
|
77
|
+
* 发送 Chat Access 失败提示。
|
|
74
78
|
*/
|
|
75
|
-
|
|
76
|
-
params: IncomingAuthorizationParams,
|
|
77
|
-
): Promise<void>;
|
|
78
|
-
/**
|
|
79
|
-
* 入站授权判定。
|
|
80
|
-
*/
|
|
81
|
-
evaluateIncomingAuthorization(
|
|
82
|
-
params: IncomingAuthorizationParams,
|
|
83
|
-
): Promise<IncomingAuthorizationResult>;
|
|
84
|
-
/**
|
|
85
|
-
* 发送授权失败提示。
|
|
86
|
-
*/
|
|
87
|
-
sendAuthorizationText(params: {
|
|
79
|
+
sendAccessText(params: {
|
|
88
80
|
chatId: string;
|
|
89
81
|
text: string;
|
|
90
82
|
chatType?: string;
|
|
91
83
|
}): Promise<void>;
|
|
92
84
|
/**
|
|
93
|
-
*
|
|
85
|
+
* 构建 Chat Access 失败提示文案。
|
|
94
86
|
*/
|
|
95
|
-
|
|
96
|
-
userId?: string;
|
|
97
|
-
chatId?: string;
|
|
98
|
-
chatType?: string;
|
|
99
|
-
}): string;
|
|
87
|
+
buildAccessBlockedText(params: { result: IncomingChatAccessResult }): string;
|
|
100
88
|
/**
|
|
101
89
|
* 入站去重判断。
|
|
102
90
|
*/
|
|
@@ -234,22 +222,14 @@ export async function handleQqChannelMessage(
|
|
|
234
222
|
actorName: actor.username,
|
|
235
223
|
});
|
|
236
224
|
|
|
237
|
-
await options.
|
|
238
|
-
chatId: channelId,
|
|
239
|
-
chatType,
|
|
240
|
-
chatTitle,
|
|
241
|
-
userId: actor.userId,
|
|
242
|
-
username: actor.username,
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
const authResult = await options.evaluateIncomingAuthorization({
|
|
225
|
+
const access_result = await options.evaluateIncomingAccess({
|
|
246
226
|
chatId: channelId,
|
|
247
227
|
chatType,
|
|
248
228
|
chatTitle,
|
|
249
229
|
userId: actor.userId,
|
|
250
230
|
username: actor.username,
|
|
251
231
|
});
|
|
252
|
-
if (
|
|
232
|
+
if (!access_result.allowed) return;
|
|
253
233
|
|
|
254
234
|
const botUserId = options.getBotUserId();
|
|
255
235
|
if (actor.userId && botUserId && actor.userId === botUserId) {
|
|
@@ -347,31 +327,19 @@ async function handleQqInboundMessage(
|
|
|
347
327
|
? stripQqBotMention(rawContent, botUserId)
|
|
348
328
|
: extractQqTextContent(rawContent);
|
|
349
329
|
|
|
350
|
-
await options.
|
|
351
|
-
chatId,
|
|
352
|
-
chatType,
|
|
353
|
-
chatTitle,
|
|
354
|
-
userId: actor.userId,
|
|
355
|
-
username: actor.username,
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
const authResult = await options.evaluateIncomingAuthorization({
|
|
330
|
+
const access_result = await options.evaluateIncomingAccess({
|
|
359
331
|
chatId,
|
|
360
332
|
chatType,
|
|
361
333
|
chatTitle,
|
|
362
334
|
userId: actor.userId,
|
|
363
335
|
username: actor.username,
|
|
364
336
|
});
|
|
365
|
-
if (
|
|
337
|
+
if (!access_result.allowed) {
|
|
366
338
|
if (!isGroup) {
|
|
367
|
-
await options.
|
|
339
|
+
await options.sendAccessText({
|
|
368
340
|
chatId,
|
|
369
341
|
chatType,
|
|
370
|
-
text: options.
|
|
371
|
-
chatId,
|
|
372
|
-
chatType,
|
|
373
|
-
userId: actor.userId,
|
|
374
|
-
}),
|
|
342
|
+
text: options.buildAccessBlockedText({ result: access_result }),
|
|
375
343
|
});
|
|
376
344
|
}
|
|
377
345
|
return;
|