@downcity/plugins 1.0.152 → 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.
- 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 +18 -21
- package/bin/chat/channels/BaseChatChannel.d.ts.map +1 -1
- package/bin/chat/channels/BaseChatChannel.js +48 -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 +2 -0
- package/bin/chat/channels/telegram/Bot.d.ts.map +1 -1
- package/bin/chat/channels/telegram/Bot.js +8 -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 +90 -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 +59 -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 +11 -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
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth 授权判定逻辑。
|
|
3
|
-
*
|
|
4
|
-
* 关键点(中文)
|
|
5
|
-
* - 角色模型替代旧的 allowlist/pairing 模型。
|
|
6
|
-
* - 判定统一走“用户角色 + 权限集合”,群聊只看发言用户自身权限。
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { DowncityConfig } from "@downcity/agent";
|
|
10
|
-
import type {
|
|
11
|
-
ChatAuthorizationConfig,
|
|
12
|
-
ChatAuthorizationChannel,
|
|
13
|
-
ChatAuthorizationEvaluateInput,
|
|
14
|
-
ChatAuthorizationEvaluateResult,
|
|
15
|
-
ChatAuthorizationPermission,
|
|
16
|
-
ChatAuthorizationRole,
|
|
17
|
-
ChatChannelAuthorizationConfig,
|
|
18
|
-
} from "@/auth/types/AuthPlugin.js";
|
|
19
|
-
import { createDefaultChatAuthorizationRoles } from "@/auth/types/AuthPlugin.js";
|
|
20
|
-
import { readChatAuthorizationConfigSync } from "@/auth/runtime/AuthorizationConfig.js";
|
|
21
|
-
|
|
22
|
-
function normalizeText(value: unknown): string | undefined {
|
|
23
|
-
const text = String(value || "").trim();
|
|
24
|
-
return text ? text : undefined;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isDirectMessage(channel: ChatAuthorizationChannel, chatType?: string): boolean {
|
|
28
|
-
const type = String(chatType || "").trim().toLowerCase();
|
|
29
|
-
if (!type) return true;
|
|
30
|
-
if (channel === "telegram") return type === "private";
|
|
31
|
-
if (channel === "feishu") return type === "p2p";
|
|
32
|
-
if (channel === "qq") return type === "private" || type === "c2c";
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function resolveAuthorizationRoles(
|
|
37
|
-
authorizationConfig?: ChatAuthorizationConfig,
|
|
38
|
-
): Record<string, ChatAuthorizationRole> {
|
|
39
|
-
const raw = authorizationConfig?.roles;
|
|
40
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
41
|
-
return createDefaultChatAuthorizationRoles();
|
|
42
|
-
}
|
|
43
|
-
return Object.keys(raw).length > 0 ? raw : createDefaultChatAuthorizationRoles();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function resolveChannelAuthorizationConfig(
|
|
47
|
-
channel: ChatAuthorizationChannel,
|
|
48
|
-
authorizationConfig?: ChatAuthorizationConfig,
|
|
49
|
-
): ChatChannelAuthorizationConfig {
|
|
50
|
-
const raw = authorizationConfig?.channels?.[channel];
|
|
51
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
52
|
-
return {
|
|
53
|
-
defaultUserRoleId: "default",
|
|
54
|
-
userRoles: {},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
return raw;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function resolveRole(
|
|
61
|
-
roleId: string | undefined,
|
|
62
|
-
roles: Record<string, ChatAuthorizationRole> | undefined,
|
|
63
|
-
): ChatAuthorizationRole {
|
|
64
|
-
if (roleId && roles?.[roleId]) return roles[roleId];
|
|
65
|
-
if (roles?.default) return roles.default;
|
|
66
|
-
return { roleId: "default", name: "Default", permissions: [] };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function hasPermission(
|
|
70
|
-
role: ChatAuthorizationRole,
|
|
71
|
-
permission: ChatAuthorizationPermission,
|
|
72
|
-
): boolean {
|
|
73
|
-
return role.permissions.includes(permission);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function resolveUserRole(params: {
|
|
77
|
-
roles: Record<string, ChatAuthorizationRole>;
|
|
78
|
-
channelConfig: ChatChannelAuthorizationConfig;
|
|
79
|
-
userId?: string;
|
|
80
|
-
}): ChatAuthorizationRole {
|
|
81
|
-
const userId = normalizeText(params.userId);
|
|
82
|
-
const defaultRoleId = normalizeText(params.channelConfig.defaultUserRoleId) || "default";
|
|
83
|
-
const boundRoleId = userId ? normalizeText(params.channelConfig.userRoles?.[userId]) : undefined;
|
|
84
|
-
return resolveRole(boundRoleId || defaultRoleId, params.roles);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* 解析用户授权角色。
|
|
89
|
-
*/
|
|
90
|
-
export function resolveAuthorizedUserRole(params: {
|
|
91
|
-
channel: ChatAuthorizationChannel;
|
|
92
|
-
userId?: string;
|
|
93
|
-
authorizationConfig?: ChatAuthorizationConfig;
|
|
94
|
-
rootPath?: string;
|
|
95
|
-
}): ChatAuthorizationRole | undefined {
|
|
96
|
-
const userId = normalizeText(params.userId);
|
|
97
|
-
if (!userId) return undefined;
|
|
98
|
-
const authorizationConfig =
|
|
99
|
-
params.authorizationConfig ||
|
|
100
|
-
(params.rootPath
|
|
101
|
-
? readChatAuthorizationConfigSync(params.rootPath)
|
|
102
|
-
: undefined);
|
|
103
|
-
const roles = resolveAuthorizationRoles(authorizationConfig);
|
|
104
|
-
const channelConfig = resolveChannelAuthorizationConfig(params.channel, authorizationConfig);
|
|
105
|
-
return resolveUserRole({
|
|
106
|
-
roles,
|
|
107
|
-
channelConfig,
|
|
108
|
-
userId,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* 执行入站授权判定。
|
|
114
|
-
*/
|
|
115
|
-
export function evaluateIncomingChatAuthorization(params: {
|
|
116
|
-
config: DowncityConfig;
|
|
117
|
-
channel: ChatAuthorizationChannel;
|
|
118
|
-
input: ChatAuthorizationEvaluateInput;
|
|
119
|
-
authorizationConfig?: ChatAuthorizationConfig;
|
|
120
|
-
}): ChatAuthorizationEvaluateResult {
|
|
121
|
-
const channelConfig = resolveChannelAuthorizationConfig(
|
|
122
|
-
params.channel,
|
|
123
|
-
params.authorizationConfig,
|
|
124
|
-
);
|
|
125
|
-
const roles = resolveAuthorizationRoles(params.authorizationConfig);
|
|
126
|
-
const direct = isDirectMessage(params.channel, params.input.chatType);
|
|
127
|
-
const userRole = resolveUserRole({
|
|
128
|
-
roles,
|
|
129
|
-
channelConfig,
|
|
130
|
-
userId: params.input.userId,
|
|
131
|
-
});
|
|
132
|
-
const isOwner = hasPermission(userRole, "agent.manage");
|
|
133
|
-
|
|
134
|
-
if (direct) {
|
|
135
|
-
return hasPermission(userRole, "chat.dm.use")
|
|
136
|
-
? {
|
|
137
|
-
decision: "allow",
|
|
138
|
-
isOwner,
|
|
139
|
-
userRoleId: userRole.roleId,
|
|
140
|
-
userPermissions: [...userRole.permissions],
|
|
141
|
-
reason: "dm_role_allowed",
|
|
142
|
-
}
|
|
143
|
-
: {
|
|
144
|
-
decision: "block",
|
|
145
|
-
isOwner,
|
|
146
|
-
userRoleId: userRole.roleId,
|
|
147
|
-
userPermissions: [...userRole.permissions],
|
|
148
|
-
reason: "dm_role_blocked",
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return hasPermission(userRole, "chat.group.use")
|
|
153
|
-
? {
|
|
154
|
-
decision: "allow",
|
|
155
|
-
isOwner,
|
|
156
|
-
userRoleId: userRole.roleId,
|
|
157
|
-
userPermissions: [...userRole.permissions],
|
|
158
|
-
reason: "group_user_role_allowed",
|
|
159
|
-
}
|
|
160
|
-
: {
|
|
161
|
-
decision: "block",
|
|
162
|
-
isOwner,
|
|
163
|
-
userRoleId: userRole.roleId,
|
|
164
|
-
userPermissions: [...userRole.permissions],
|
|
165
|
-
reason: "user_group_permission_blocked",
|
|
166
|
-
};
|
|
167
|
-
}
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth 授权运行时状态存储。
|
|
3
|
-
*
|
|
4
|
-
* 关键点(中文)
|
|
5
|
-
* - 所有动态状态(观测用户 / 会话)统一落盘到 `.downcity/chat/authorization/state.json`。
|
|
6
|
-
* - 静态授权配置保存在同目录下的 `config.json`,不和运行时观测数据混写。
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from "fs-extra";
|
|
10
|
-
import path from "node:path";
|
|
11
|
-
import type {
|
|
12
|
-
ChatAuthorizationObservedChat,
|
|
13
|
-
ChatAuthorizationObservedUser,
|
|
14
|
-
ChatAuthorizationSnapshot,
|
|
15
|
-
ChatAuthorizationStateFile,
|
|
16
|
-
} from "@/auth/types/AuthPlugin.js";
|
|
17
|
-
import type { AgentContext } from "@downcity/agent";
|
|
18
|
-
import type { ChatDispatchChannel } from "@/chat/types/ChatDispatcher.js";
|
|
19
|
-
import { readChatAuthorizationConfig } from "@/auth/runtime/AuthorizationConfig.js";
|
|
20
|
-
|
|
21
|
-
function getAuthorizationStatePath(rootPath: string): string {
|
|
22
|
-
return path.join(rootPath, ".downcity", "chat", "authorization", "state.json");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function normalizeText(value: unknown): string | undefined {
|
|
26
|
-
const text = String(value || "").trim();
|
|
27
|
-
return text ? text : undefined;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function buildUserKey(channel: ChatDispatchChannel, userId: string): string {
|
|
31
|
-
return `${channel}:${userId}`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function buildChatKey(channel: ChatDispatchChannel, chatId: string): string {
|
|
35
|
-
return `${channel}:${chatId}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function createEmptyState(): ChatAuthorizationStateFile {
|
|
39
|
-
return {
|
|
40
|
-
v: 1,
|
|
41
|
-
updatedAt: Date.now(),
|
|
42
|
-
usersByKey: {},
|
|
43
|
-
chatsByKey: {},
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function normalizeObservedUser(
|
|
48
|
-
input: Partial<ChatAuthorizationObservedUser> | null | undefined,
|
|
49
|
-
): ChatAuthorizationObservedUser | null {
|
|
50
|
-
if (!input || typeof input !== "object") return null;
|
|
51
|
-
const channel = normalizeText(input.channel) as ChatDispatchChannel | undefined;
|
|
52
|
-
const userId = normalizeText(input.userId);
|
|
53
|
-
if (!channel || !userId) return null;
|
|
54
|
-
return {
|
|
55
|
-
v: 1,
|
|
56
|
-
channel,
|
|
57
|
-
userId,
|
|
58
|
-
...(normalizeText(input.username) ? { username: normalizeText(input.username) } : {}),
|
|
59
|
-
...(normalizeText(input.lastChatId) ? { lastChatId: normalizeText(input.lastChatId) } : {}),
|
|
60
|
-
...(normalizeText(input.lastChatTitle)
|
|
61
|
-
? { lastChatTitle: normalizeText(input.lastChatTitle) }
|
|
62
|
-
: {}),
|
|
63
|
-
...(normalizeText(input.lastChatType)
|
|
64
|
-
? { lastChatType: normalizeText(input.lastChatType) }
|
|
65
|
-
: {}),
|
|
66
|
-
firstSeenAt:
|
|
67
|
-
typeof input.firstSeenAt === "number" && Number.isFinite(input.firstSeenAt)
|
|
68
|
-
? input.firstSeenAt
|
|
69
|
-
: Date.now(),
|
|
70
|
-
lastSeenAt:
|
|
71
|
-
typeof input.lastSeenAt === "number" && Number.isFinite(input.lastSeenAt)
|
|
72
|
-
? input.lastSeenAt
|
|
73
|
-
: Date.now(),
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function normalizeObservedChat(
|
|
78
|
-
input: Partial<ChatAuthorizationObservedChat> | null | undefined,
|
|
79
|
-
): ChatAuthorizationObservedChat | null {
|
|
80
|
-
if (!input || typeof input !== "object") return null;
|
|
81
|
-
const channel = normalizeText(input.channel) as ChatDispatchChannel | undefined;
|
|
82
|
-
const chatId = normalizeText(input.chatId);
|
|
83
|
-
if (!channel || !chatId) return null;
|
|
84
|
-
return {
|
|
85
|
-
v: 1,
|
|
86
|
-
channel,
|
|
87
|
-
chatId,
|
|
88
|
-
...(normalizeText(input.chatTitle) ? { chatTitle: normalizeText(input.chatTitle) } : {}),
|
|
89
|
-
...(normalizeText(input.chatType) ? { chatType: normalizeText(input.chatType) } : {}),
|
|
90
|
-
...(normalizeText(input.lastActorId)
|
|
91
|
-
? { lastActorId: normalizeText(input.lastActorId) }
|
|
92
|
-
: {}),
|
|
93
|
-
...(normalizeText(input.lastActorName)
|
|
94
|
-
? { lastActorName: normalizeText(input.lastActorName) }
|
|
95
|
-
: {}),
|
|
96
|
-
firstSeenAt:
|
|
97
|
-
typeof input.firstSeenAt === "number" && Number.isFinite(input.firstSeenAt)
|
|
98
|
-
? input.firstSeenAt
|
|
99
|
-
: Date.now(),
|
|
100
|
-
lastSeenAt:
|
|
101
|
-
typeof input.lastSeenAt === "number" && Number.isFinite(input.lastSeenAt)
|
|
102
|
-
? input.lastSeenAt
|
|
103
|
-
: Date.now(),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function normalizeStateFile(
|
|
108
|
-
input: Partial<ChatAuthorizationStateFile> | null | undefined,
|
|
109
|
-
): ChatAuthorizationStateFile {
|
|
110
|
-
const out = createEmptyState();
|
|
111
|
-
out.updatedAt =
|
|
112
|
-
typeof input?.updatedAt === "number" && Number.isFinite(input.updatedAt)
|
|
113
|
-
? input.updatedAt
|
|
114
|
-
: Date.now();
|
|
115
|
-
|
|
116
|
-
const users = input?.usersByKey;
|
|
117
|
-
if (users && typeof users === "object") {
|
|
118
|
-
for (const [key, value] of Object.entries(users)) {
|
|
119
|
-
const normalized = normalizeObservedUser(value);
|
|
120
|
-
if (!normalized) continue;
|
|
121
|
-
out.usersByKey[key] = normalized;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const chats = input?.chatsByKey;
|
|
126
|
-
if (chats && typeof chats === "object") {
|
|
127
|
-
for (const [key, value] of Object.entries(chats)) {
|
|
128
|
-
const normalized = normalizeObservedChat(value);
|
|
129
|
-
if (!normalized) continue;
|
|
130
|
-
out.chatsByKey[key] = normalized;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return out;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async function readState(rootPath: string): Promise<ChatAuthorizationStateFile> {
|
|
137
|
-
const file = getAuthorizationStatePath(rootPath);
|
|
138
|
-
const raw = (await fs.readJson(file).catch(() => null)) as Partial<ChatAuthorizationStateFile> | null;
|
|
139
|
-
return normalizeStateFile(raw);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async function writeState(rootPath: string, state: ChatAuthorizationStateFile): Promise<void> {
|
|
143
|
-
const file = getAuthorizationStatePath(rootPath);
|
|
144
|
-
await fs.ensureDir(path.dirname(file));
|
|
145
|
-
await fs.writeJson(file, state, { spaces: 2 });
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function readAuthorizationConfig(
|
|
149
|
-
context: Pick<AgentContext, "rootPath">,
|
|
150
|
-
): ChatAuthorizationSnapshot["config"] {
|
|
151
|
-
return readChatAuthorizationConfig(context.rootPath);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* 记录观测到的用户 / 会话。
|
|
156
|
-
*/
|
|
157
|
-
export async function recordObservedAuthorizationPrincipal(params: {
|
|
158
|
-
context: Pick<AgentContext, "rootPath">;
|
|
159
|
-
channel: ChatDispatchChannel;
|
|
160
|
-
chatId: string;
|
|
161
|
-
chatType?: string;
|
|
162
|
-
chatTitle?: string;
|
|
163
|
-
userId?: string;
|
|
164
|
-
username?: string;
|
|
165
|
-
}): Promise<void> {
|
|
166
|
-
const chatId = normalizeText(params.chatId);
|
|
167
|
-
if (!chatId) return;
|
|
168
|
-
const now = Date.now();
|
|
169
|
-
const state = await readState(params.context.rootPath);
|
|
170
|
-
|
|
171
|
-
const chatKey = buildChatKey(params.channel, chatId);
|
|
172
|
-
const prevChat = state.chatsByKey[chatKey];
|
|
173
|
-
state.chatsByKey[chatKey] = {
|
|
174
|
-
v: 1,
|
|
175
|
-
channel: params.channel,
|
|
176
|
-
chatId,
|
|
177
|
-
...(normalizeText(params.chatTitle) ? { chatTitle: normalizeText(params.chatTitle) } : {}),
|
|
178
|
-
...(normalizeText(params.chatType) ? { chatType: normalizeText(params.chatType) } : {}),
|
|
179
|
-
...(normalizeText(params.userId) ? { lastActorId: normalizeText(params.userId) } : {}),
|
|
180
|
-
...(normalizeText(params.username) ? { lastActorName: normalizeText(params.username) } : {}),
|
|
181
|
-
firstSeenAt: prevChat?.firstSeenAt ?? now,
|
|
182
|
-
lastSeenAt: now,
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const userId = normalizeText(params.userId);
|
|
186
|
-
if (userId) {
|
|
187
|
-
const userKey = buildUserKey(params.channel, userId);
|
|
188
|
-
const prevUser = state.usersByKey[userKey];
|
|
189
|
-
state.usersByKey[userKey] = {
|
|
190
|
-
v: 1,
|
|
191
|
-
channel: params.channel,
|
|
192
|
-
userId,
|
|
193
|
-
...(normalizeText(params.username) ? { username: normalizeText(params.username) } : {}),
|
|
194
|
-
lastChatId: chatId,
|
|
195
|
-
...(normalizeText(params.chatTitle)
|
|
196
|
-
? { lastChatTitle: normalizeText(params.chatTitle) }
|
|
197
|
-
: {}),
|
|
198
|
-
...(normalizeText(params.chatType) ? { lastChatType: normalizeText(params.chatType) } : {}),
|
|
199
|
-
firstSeenAt: prevUser?.firstSeenAt ?? now,
|
|
200
|
-
lastSeenAt: now,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
state.updatedAt = now;
|
|
205
|
-
await writeState(params.context.rootPath, state);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* 读取授权快照(配置 + 动态状态)。
|
|
210
|
-
*/
|
|
211
|
-
export async function readAuthorizationSnapshot(params: {
|
|
212
|
-
context: Pick<AgentContext, "rootPath">;
|
|
213
|
-
}): Promise<ChatAuthorizationSnapshot> {
|
|
214
|
-
const state = await readState(params.context.rootPath);
|
|
215
|
-
return {
|
|
216
|
-
config: readAuthorizationConfig(params.context),
|
|
217
|
-
users: Object.values(state.usersByKey).sort((a, b) => b.lastSeenAt - a.lastSeenAt),
|
|
218
|
-
chats: Object.values(state.chatsByKey).sort((a, b) => b.lastSeenAt - a.lastSeenAt),
|
|
219
|
-
};
|
|
220
|
-
}
|