chatccc 0.2.47 → 0.2.48
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/__tests__/feishu-platform.test.ts +56 -54
- package/src/__tests__/session.test.ts +2 -0
- package/src/cards.ts +3 -1
- package/src/feishu-api.ts +12 -0
- package/src/feishu-platform.ts +138 -133
- package/src/index.ts +35 -0
- package/src/session-chat-binding.ts +102 -86
- package/src/session.ts +103 -90
- package/src/sim-platform.ts +152 -147
- package/src/sim-store.ts +316 -312
package/package.json
CHANGED
|
@@ -1,55 +1,57 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
-
import { getPlatform, setPlatform, getTenantAccessToken, getChatInfo, createGroupChat, updateChatInfo, sendTextReply, sendCardReply, extractSessionInfo, formatDelayNotice, reportPermissionResults, verifyAllPermissions, addReaction, recallMessage, updateCardMessage, setChatAvatar, sendRestartCard } from "../feishu-platform.ts";
|
|
3
|
-
import type { FeishuPlatform } from "../feishu-platform.ts";
|
|
4
|
-
|
|
5
|
-
const realPlatform = getPlatform();
|
|
6
|
-
|
|
7
|
-
describe("feishu-platform", () => {
|
|
8
|
-
it("默认使用真实实现", () => {
|
|
9
|
-
expect(realPlatform).toBeDefined();
|
|
10
|
-
expect(typeof realPlatform.getTenantAccessToken).toBe("function");
|
|
11
|
-
expect(typeof realPlatform.sendTextReply).toBe("function");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("setPlatform 可替换实现,包装函数委托到新实现", async () => {
|
|
15
|
-
const mock: FeishuPlatform = {
|
|
16
|
-
getTenantAccessToken: async () => "mock_token",
|
|
17
|
-
sendTextReply: async () => true,
|
|
18
|
-
sendCardReply: async () => true,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
expect(await
|
|
42
|
-
expect(await
|
|
43
|
-
|
|
44
|
-
expect(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
+
import { getPlatform, setPlatform, getTenantAccessToken, getChatInfo, createGroupChat, updateChatInfo, sendTextReply, sendCardReply, sendRawCard, extractSessionInfo, formatDelayNotice, reportPermissionResults, verifyAllPermissions, addReaction, recallMessage, updateCardMessage, setChatAvatar, disbandChat, sendRestartCard } from "../feishu-platform.ts";
|
|
3
|
+
import type { FeishuPlatform } from "../feishu-platform.ts";
|
|
4
|
+
|
|
5
|
+
const realPlatform = getPlatform();
|
|
6
|
+
|
|
7
|
+
describe("feishu-platform", () => {
|
|
8
|
+
it("默认使用真实实现", () => {
|
|
9
|
+
expect(realPlatform).toBeDefined();
|
|
10
|
+
expect(typeof realPlatform.getTenantAccessToken).toBe("function");
|
|
11
|
+
expect(typeof realPlatform.sendTextReply).toBe("function");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("setPlatform 可替换实现,包装函数委托到新实现", async () => {
|
|
15
|
+
const mock: FeishuPlatform = {
|
|
16
|
+
getTenantAccessToken: async () => "mock_token",
|
|
17
|
+
sendTextReply: async () => true,
|
|
18
|
+
sendCardReply: async () => true,
|
|
19
|
+
sendRawCard: async () => true,
|
|
20
|
+
sendImageReply: async () => true,
|
|
21
|
+
sendFileReply: async () => true,
|
|
22
|
+
addReaction: async () => {},
|
|
23
|
+
recallMessage: async () => false,
|
|
24
|
+
updateCardMessage: async () => true,
|
|
25
|
+
createGroupChat: async () => "mock_chat",
|
|
26
|
+
updateChatInfo: async () => {},
|
|
27
|
+
getChatInfo: async () => ({ name: "x", description: "y" }),
|
|
28
|
+
disbandChat: async () => {},
|
|
29
|
+
setChatAvatar: async () => {},
|
|
30
|
+
getOrDownloadImage: async () => "/tmp/img.png",
|
|
31
|
+
verifyAllPermissions: async () => [],
|
|
32
|
+
reportPermissionResults: realPlatform.reportPermissionResults,
|
|
33
|
+
extractSessionInfo: realPlatform.extractSessionInfo,
|
|
34
|
+
extractSessionId: realPlatform.extractSessionId,
|
|
35
|
+
formatDelayNotice: realPlatform.formatDelayNotice,
|
|
36
|
+
sendRestartCard: async () => {},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
setPlatform(mock);
|
|
40
|
+
try {
|
|
41
|
+
expect(await getTenantAccessToken()).toBe("mock_token");
|
|
42
|
+
expect(await sendTextReply("t", "c", "hi")).toBe(true);
|
|
43
|
+
expect(await createGroupChat("t", "g", [])).toBe("mock_chat");
|
|
44
|
+
expect(await getChatInfo("t", "mock_chat")).toEqual({ name: "x", description: "y" });
|
|
45
|
+
const perms = await verifyAllPermissions("t");
|
|
46
|
+
expect(perms).toEqual([]);
|
|
47
|
+
} finally {
|
|
48
|
+
// 恢复到真实实现,避免影响后续测试
|
|
49
|
+
setPlatform(realPlatform);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("恢复真实实现后函数正常工作", async () => {
|
|
54
|
+
setPlatform(realPlatform);
|
|
55
|
+
expect(getPlatform()).toBe(realPlatform);
|
|
56
|
+
});
|
|
55
57
|
});
|
|
@@ -237,6 +237,7 @@ describe("getAllSessionsStatus", () => {
|
|
|
237
237
|
beforeEach(async () => {
|
|
238
238
|
chatSessionMap.clear();
|
|
239
239
|
sessionInfoMap.clear();
|
|
240
|
+
activePrompts.clear();
|
|
240
241
|
const dir = await mkdtemp(join(tmpdir(), "chatccc-session-registry-"));
|
|
241
242
|
registryFile = join(dir, "session-registry.json");
|
|
242
243
|
_setSessionRegistryFileForTest(registryFile);
|
|
@@ -337,6 +338,7 @@ describe("getAllSessionsStatus", () => {
|
|
|
337
338
|
running: false, // registry says false, but activePrompts wins
|
|
338
339
|
updatedAt: 1000,
|
|
339
340
|
});
|
|
341
|
+
mockSessionInfo("chat1", { sessionId: "s1" });
|
|
340
342
|
mockActiveSession("chat1");
|
|
341
343
|
|
|
342
344
|
const result = await getAllSessionsStatus();
|
package/src/cards.ts
CHANGED
|
@@ -255,6 +255,7 @@ export function buildCdCard(
|
|
|
255
255
|
export function buildSessionsCard(sessions: Array<{
|
|
256
256
|
sessionId: string;
|
|
257
257
|
chatName: string;
|
|
258
|
+
chatId: string;
|
|
258
259
|
active: boolean;
|
|
259
260
|
turnCount: number;
|
|
260
261
|
elapsedSeconds: number | null;
|
|
@@ -293,7 +294,8 @@ export function buildSessionsCard(sessions: Array<{
|
|
|
293
294
|
}
|
|
294
295
|
const toolLabel = s.tool === "cursor" ? "Cursor" : s.tool === "codex" ? "Codex" : "Claude Code";
|
|
295
296
|
const namePart = s.chatName ? `**${s.chatName}** ` : "";
|
|
296
|
-
|
|
297
|
+
const groupTag = s.chatId && s.chatId.startsWith("oc_") ? " (群聊)" : "";
|
|
298
|
+
return `**${i + 1}.** ${namePart}${groupTag} \`${shortId}\` ${status} | 工具: ${toolLabel} | 轮数: ${s.turnCount} | ${s.model}${extra}`;
|
|
297
299
|
};
|
|
298
300
|
|
|
299
301
|
const lines: string[] = [`共 **${sessions.length}** 个会话:`, ""];
|
package/src/feishu-api.ts
CHANGED
|
@@ -236,6 +236,18 @@ export async function getChatInfo(
|
|
|
236
236
|
};
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
export async function disbandChat(
|
|
240
|
+
token: string,
|
|
241
|
+
chatId: string
|
|
242
|
+
): Promise<void> {
|
|
243
|
+
const resp = await fetch(`${BASE_URL}/im/v1/chats/${chatId}`, {
|
|
244
|
+
method: "DELETE",
|
|
245
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
246
|
+
});
|
|
247
|
+
const data = (await resp.json()) as { code: number; msg?: string };
|
|
248
|
+
if (data.code !== 0) throw new Error(`[${data.code}] ${data.msg}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
239
251
|
export function extractSessionInfo(description: string): { sessionId: string; tool: string } | null {
|
|
240
252
|
const PREFIXES: Array<{ prefix: string; tool: string }> = [
|
|
241
253
|
{ prefix: CLAUDE_SESSION_PREFIX, tool: "claude" },
|
package/src/feishu-platform.ts
CHANGED
|
@@ -1,134 +1,139 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* feishu-platform.ts — 可替换的飞书 API 实现层
|
|
3
|
-
*
|
|
4
|
-
* 默认情况下所有函数直接委托给 feishu-api.ts(真实飞书 API)。
|
|
5
|
-
* 在 --simulate 模式下通过 setPlatform() 整体替换为 SimulatedPlatform。
|
|
6
|
-
*
|
|
7
|
-
* 设计:每个导出函数都是一个"通过 _impl 代理"的包装器,
|
|
8
|
-
* 消费者不需要感知底层是真实飞书还是模拟实现。
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import * as realApi from "./feishu-api.ts";
|
|
12
|
-
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
// 平台接口:覆盖 feishu-api.ts 的所有公开导出函数签名
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
export interface FeishuPlatform {
|
|
18
|
-
getTenantAccessToken: typeof realApi.getTenantAccessToken;
|
|
19
|
-
sendTextReply: typeof realApi.sendTextReply;
|
|
20
|
-
sendCardReply: typeof realApi.sendCardReply;
|
|
21
|
-
sendRawCard: typeof realApi.sendRawCard;
|
|
22
|
-
sendImageReply: typeof realApi.sendImageReply;
|
|
23
|
-
sendFileReply: typeof realApi.sendFileReply;
|
|
24
|
-
addReaction: typeof realApi.addReaction;
|
|
25
|
-
recallMessage: typeof realApi.recallMessage;
|
|
26
|
-
updateCardMessage: typeof realApi.updateCardMessage;
|
|
27
|
-
createGroupChat: typeof realApi.createGroupChat;
|
|
28
|
-
updateChatInfo: typeof realApi.updateChatInfo;
|
|
29
|
-
getChatInfo: typeof realApi.getChatInfo;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
1
|
+
/**
|
|
2
|
+
* feishu-platform.ts — 可替换的飞书 API 实现层
|
|
3
|
+
*
|
|
4
|
+
* 默认情况下所有函数直接委托给 feishu-api.ts(真实飞书 API)。
|
|
5
|
+
* 在 --simulate 模式下通过 setPlatform() 整体替换为 SimulatedPlatform。
|
|
6
|
+
*
|
|
7
|
+
* 设计:每个导出函数都是一个"通过 _impl 代理"的包装器,
|
|
8
|
+
* 消费者不需要感知底层是真实飞书还是模拟实现。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as realApi from "./feishu-api.ts";
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// 平台接口:覆盖 feishu-api.ts 的所有公开导出函数签名
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
export interface FeishuPlatform {
|
|
18
|
+
getTenantAccessToken: typeof realApi.getTenantAccessToken;
|
|
19
|
+
sendTextReply: typeof realApi.sendTextReply;
|
|
20
|
+
sendCardReply: typeof realApi.sendCardReply;
|
|
21
|
+
sendRawCard: typeof realApi.sendRawCard;
|
|
22
|
+
sendImageReply: typeof realApi.sendImageReply;
|
|
23
|
+
sendFileReply: typeof realApi.sendFileReply;
|
|
24
|
+
addReaction: typeof realApi.addReaction;
|
|
25
|
+
recallMessage: typeof realApi.recallMessage;
|
|
26
|
+
updateCardMessage: typeof realApi.updateCardMessage;
|
|
27
|
+
createGroupChat: typeof realApi.createGroupChat;
|
|
28
|
+
updateChatInfo: typeof realApi.updateChatInfo;
|
|
29
|
+
getChatInfo: typeof realApi.getChatInfo;
|
|
30
|
+
disbandChat: typeof realApi.disbandChat;
|
|
31
|
+
setChatAvatar: typeof realApi.setChatAvatar;
|
|
32
|
+
getOrDownloadImage: typeof realApi.getOrDownloadImage;
|
|
33
|
+
verifyAllPermissions: typeof realApi.verifyAllPermissions;
|
|
34
|
+
reportPermissionResults: typeof realApi.reportPermissionResults;
|
|
35
|
+
extractSessionInfo: typeof realApi.extractSessionInfo;
|
|
36
|
+
extractSessionId: typeof realApi.extractSessionId;
|
|
37
|
+
formatDelayNotice: typeof realApi.formatDelayNotice;
|
|
38
|
+
sendRestartCard: typeof realApi.sendRestartCard;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let _impl: FeishuPlatform = realApi;
|
|
42
|
+
|
|
43
|
+
/** 替换当前平台实现(模拟模式入口) */
|
|
44
|
+
export function setPlatform(impl: FeishuPlatform): void {
|
|
45
|
+
_impl = impl;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** 获取当前平台实现(仅供诊断/测试) */
|
|
49
|
+
export function getPlatform(): FeishuPlatform {
|
|
50
|
+
return _impl;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// 包装器:每个函数直接委托到 _impl,签名与原函数完全一致
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
export function getTenantAccessToken(): ReturnType<typeof realApi.getTenantAccessToken> {
|
|
58
|
+
return _impl.getTenantAccessToken();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function sendTextReply(...args: Parameters<typeof realApi.sendTextReply>): ReturnType<typeof realApi.sendTextReply> {
|
|
62
|
+
return _impl.sendTextReply(...args);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function sendCardReply(...args: Parameters<typeof realApi.sendCardReply>): ReturnType<typeof realApi.sendCardReply> {
|
|
66
|
+
return _impl.sendCardReply(...args);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function sendRawCard(...args: Parameters<typeof realApi.sendRawCard>): ReturnType<typeof realApi.sendRawCard> {
|
|
70
|
+
return _impl.sendRawCard(...args);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function sendImageReply(...args: Parameters<typeof realApi.sendImageReply>): ReturnType<typeof realApi.sendImageReply> {
|
|
74
|
+
return _impl.sendImageReply(...args);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function sendFileReply(...args: Parameters<typeof realApi.sendFileReply>): ReturnType<typeof realApi.sendFileReply> {
|
|
78
|
+
return _impl.sendFileReply(...args);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function addReaction(...args: Parameters<typeof realApi.addReaction>): ReturnType<typeof realApi.addReaction> {
|
|
82
|
+
return _impl.addReaction(...args);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function recallMessage(...args: Parameters<typeof realApi.recallMessage>): ReturnType<typeof realApi.recallMessage> {
|
|
86
|
+
return _impl.recallMessage(...args);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function updateCardMessage(...args: Parameters<typeof realApi.updateCardMessage>): ReturnType<typeof realApi.updateCardMessage> {
|
|
90
|
+
return _impl.updateCardMessage(...args);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function createGroupChat(...args: Parameters<typeof realApi.createGroupChat>): ReturnType<typeof realApi.createGroupChat> {
|
|
94
|
+
return _impl.createGroupChat(...args);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function updateChatInfo(...args: Parameters<typeof realApi.updateChatInfo>): ReturnType<typeof realApi.updateChatInfo> {
|
|
98
|
+
return _impl.updateChatInfo(...args);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function getChatInfo(...args: Parameters<typeof realApi.getChatInfo>): ReturnType<typeof realApi.getChatInfo> {
|
|
102
|
+
return _impl.getChatInfo(...args);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function disbandChat(...args: Parameters<typeof realApi.disbandChat>): ReturnType<typeof realApi.disbandChat> {
|
|
106
|
+
return _impl.disbandChat(...args);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function setChatAvatar(...args: Parameters<typeof realApi.setChatAvatar>): ReturnType<typeof realApi.setChatAvatar> {
|
|
110
|
+
return _impl.setChatAvatar(...args);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function getOrDownloadImage(...args: Parameters<typeof realApi.getOrDownloadImage>): ReturnType<typeof realApi.getOrDownloadImage> {
|
|
114
|
+
return _impl.getOrDownloadImage(...args);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function verifyAllPermissions(...args: Parameters<typeof realApi.verifyAllPermissions>): ReturnType<typeof realApi.verifyAllPermissions> {
|
|
118
|
+
return _impl.verifyAllPermissions(...args);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function reportPermissionResults(...args: Parameters<typeof realApi.reportPermissionResults>): ReturnType<typeof realApi.reportPermissionResults> {
|
|
122
|
+
return _impl.reportPermissionResults(...args);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function extractSessionInfo(...args: Parameters<typeof realApi.extractSessionInfo>): ReturnType<typeof realApi.extractSessionInfo> {
|
|
126
|
+
return _impl.extractSessionInfo(...args);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function extractSessionId(...args: Parameters<typeof realApi.extractSessionId>): ReturnType<typeof realApi.extractSessionId> {
|
|
130
|
+
return _impl.extractSessionId(...args);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function formatDelayNotice(...args: Parameters<typeof realApi.formatDelayNotice>): ReturnType<typeof realApi.formatDelayNotice> {
|
|
134
|
+
return _impl.formatDelayNotice(...args);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function sendRestartCard(...args: Parameters<typeof realApi.sendRestartCard>): ReturnType<typeof realApi.sendRestartCard> {
|
|
138
|
+
return _impl.sendRestartCard(...args);
|
|
134
139
|
}
|
package/src/index.ts
CHANGED
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
setChatAvatar,
|
|
78
78
|
updateCardMessage,
|
|
79
79
|
updateChatInfo,
|
|
80
|
+
disbandChat,
|
|
80
81
|
getOrDownloadImage,
|
|
81
82
|
sendRestartCard,
|
|
82
83
|
verifyAllPermissions,
|
|
@@ -103,6 +104,7 @@ import {
|
|
|
103
104
|
getAdapterForTool,
|
|
104
105
|
stopSession,
|
|
105
106
|
loadSessionRegistryForBinding,
|
|
107
|
+
removeSessionRegistryRecord,
|
|
106
108
|
} from "./session.ts";
|
|
107
109
|
import {
|
|
108
110
|
bindChatToSession,
|
|
@@ -112,6 +114,7 @@ import {
|
|
|
112
114
|
activePrompts,
|
|
113
115
|
rebuildSessionChatsFromRegistry,
|
|
114
116
|
displayCards,
|
|
117
|
+
recordLastActiveChat,
|
|
115
118
|
} from "./session-chat-binding.ts";
|
|
116
119
|
import { fixStaleStreamStates } from "./stream-state.ts";
|
|
117
120
|
|
|
@@ -685,6 +688,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
685
688
|
const cardData = others.map(s => ({
|
|
686
689
|
sessionId: s.sessionId,
|
|
687
690
|
chatName: s.chatName,
|
|
691
|
+
chatId: s.chatId,
|
|
688
692
|
active: s.active,
|
|
689
693
|
turnCount: s.turnCount,
|
|
690
694
|
elapsedSeconds: s.active ? Math.floor((now - s.startTime) / 1000) : null,
|
|
@@ -726,6 +730,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
726
730
|
|
|
727
731
|
// 绑定新 session
|
|
728
732
|
bindChatToSession(newSessionId, chatId);
|
|
733
|
+
recordLastActiveChat(newSessionId, chatId);
|
|
729
734
|
|
|
730
735
|
const descPrefix = sessionPrefixForTool(descriptionTool);
|
|
731
736
|
const newName = sessionChatName("新会话", cwd);
|
|
@@ -772,6 +777,35 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
772
777
|
return;
|
|
773
778
|
}
|
|
774
779
|
|
|
780
|
+
if (textLower === "/deleteg") {
|
|
781
|
+
logTrace(tid, "BRANCH", { cmd: "/deleteg" });
|
|
782
|
+
if (chatType === "p2p") {
|
|
783
|
+
await sendTextReply(freshToken, chatId, "私聊无法使用 /deleteg,该指令仅用于群聊。").catch(() => {});
|
|
784
|
+
logTrace(tid, "DONE", { outcome: "deleteg_p2p" });
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
console.log(`[${ts()}] [DELETEG] Disbanding group chat ${chatId}, session=${sessionId}`);
|
|
788
|
+
|
|
789
|
+
// 先解绑 session(不删除 Agent 会话)
|
|
790
|
+
unbindChatFromSession(sessionId, chatId);
|
|
791
|
+
displayCards.delete(chatId);
|
|
792
|
+
sessionInfoMap.delete(chatId);
|
|
793
|
+
await removeSessionRegistryRecord(chatId);
|
|
794
|
+
|
|
795
|
+
await sendTextReply(freshToken, chatId, "群聊已解散,Agent 会话保留。").catch(() => {});
|
|
796
|
+
|
|
797
|
+
// 解散群聊(飞书 API)
|
|
798
|
+
try {
|
|
799
|
+
await disbandChat(freshToken, chatId);
|
|
800
|
+
console.log(`[${ts()}] [DELETEG] Group disbanded: ${chatId}`);
|
|
801
|
+
} catch (err) {
|
|
802
|
+
console.error(`[${ts()}] [DELETEG] Disband API failed: ${(err as Error).message}`);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
logTrace(tid, "DONE", { outcome: "deleteg", chatId, sessionId });
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
|
|
775
809
|
// /session <number>:切换到 /sessions 列表中的指定会话
|
|
776
810
|
const sessionMatch = textLower.match(/^\/session\s+(\d+)$/);
|
|
777
811
|
if (sessionMatch) {
|
|
@@ -812,6 +846,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
812
846
|
|
|
813
847
|
// 绑定到新 session
|
|
814
848
|
bindChatToSession(target.sessionId, chatId);
|
|
849
|
+
recordLastActiveChat(target.sessionId, chatId);
|
|
815
850
|
|
|
816
851
|
const descPrefix2 = sessionPrefixForTool(target.tool);
|
|
817
852
|
const newName2 = target.chatName || sessionChatName("新会话", cwd2);
|