chatccc 0.2.179 → 0.2.181
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/README.md +27 -9
- package/package.json +1 -1
- package/src/__tests__/card-plain-text.test.ts +1 -0
- package/src/__tests__/cards.test.ts +55 -0
- package/src/__tests__/codex-reset-actions.test.ts +113 -0
- package/src/__tests__/feishu-avatar.test.ts +90 -9
- package/src/__tests__/feishu-platform.test.ts +22 -16
- package/src/__tests__/orchestrator.test.ts +91 -37
- package/src/__tests__/shared-prefix.test.ts +36 -0
- package/src/cards.ts +70 -1
- package/src/codex-reset-actions.ts +171 -0
- package/src/feishu-api.ts +131 -6
- package/src/feishu-platform.ts +20 -15
- package/src/index.ts +11 -0
- package/src/orchestrator.ts +73 -32
- package/src/shared-prefix.ts +29 -0
- package/src/sim-platform.ts +20 -14
package/src/feishu-platform.ts
CHANGED
|
@@ -29,9 +29,10 @@ export interface FeishuPlatform {
|
|
|
29
29
|
updateChatInfo: typeof realApi.updateChatInfo;
|
|
30
30
|
getChatInfo: typeof realApi.getChatInfo;
|
|
31
31
|
disbandChat: typeof realApi.disbandChat;
|
|
32
|
-
setChatAvatar: typeof realApi.setChatAvatar;
|
|
33
|
-
getCodexUsageSummary: typeof realApi.getCodexUsageSummary;
|
|
34
|
-
|
|
32
|
+
setChatAvatar: typeof realApi.setChatAvatar;
|
|
33
|
+
getCodexUsageSummary: typeof realApi.getCodexUsageSummary;
|
|
34
|
+
consumeCodexRateLimitResetCredit: typeof realApi.consumeCodexRateLimitResetCredit;
|
|
35
|
+
getOrDownloadImage: typeof realApi.getOrDownloadImage;
|
|
35
36
|
verifyAllPermissions: typeof realApi.verifyAllPermissions;
|
|
36
37
|
reportPermissionResults: typeof realApi.reportPermissionResults;
|
|
37
38
|
extractSessionInfo: typeof realApi.extractSessionInfo;
|
|
@@ -109,17 +110,21 @@ export function disbandChat(...args: Parameters<typeof realApi.disbandChat>): Re
|
|
|
109
110
|
return _impl.disbandChat(...args);
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
export function setChatAvatar(...args: Parameters<typeof realApi.setChatAvatar>): ReturnType<typeof realApi.setChatAvatar> {
|
|
113
|
-
return _impl.setChatAvatar(...args);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function getCodexUsageSummary(...args: Parameters<typeof realApi.getCodexUsageSummary>): ReturnType<typeof realApi.getCodexUsageSummary> {
|
|
117
|
-
return _impl.getCodexUsageSummary(...args);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function
|
|
121
|
-
return _impl.
|
|
122
|
-
}
|
|
113
|
+
export function setChatAvatar(...args: Parameters<typeof realApi.setChatAvatar>): ReturnType<typeof realApi.setChatAvatar> {
|
|
114
|
+
return _impl.setChatAvatar(...args);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function getCodexUsageSummary(...args: Parameters<typeof realApi.getCodexUsageSummary>): ReturnType<typeof realApi.getCodexUsageSummary> {
|
|
118
|
+
return _impl.getCodexUsageSummary(...args);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function consumeCodexRateLimitResetCredit(...args: Parameters<typeof realApi.consumeCodexRateLimitResetCredit>): ReturnType<typeof realApi.consumeCodexRateLimitResetCredit> {
|
|
122
|
+
return _impl.consumeCodexRateLimitResetCredit(...args);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function getOrDownloadImage(...args: Parameters<typeof realApi.getOrDownloadImage>): ReturnType<typeof realApi.getOrDownloadImage> {
|
|
126
|
+
return _impl.getOrDownloadImage(...args);
|
|
127
|
+
}
|
|
123
128
|
|
|
124
129
|
export function verifyAllPermissions(...args: Parameters<typeof realApi.verifyAllPermissions>): ReturnType<typeof realApi.verifyAllPermissions> {
|
|
125
130
|
return _impl.verifyAllPermissions(...args);
|
|
@@ -151,4 +156,4 @@ export function sendRestartCard(...args: Parameters<typeof realApi.sendRestartCa
|
|
|
151
156
|
|
|
152
157
|
export function getMergeForwardMessages(...args: Parameters<typeof realApi.getMergeForwardMessages>): ReturnType<typeof realApi.getMergeForwardMessages> {
|
|
153
158
|
return _impl.getMergeForwardMessages(...args);
|
|
154
|
-
}
|
|
159
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -74,6 +74,7 @@ import {
|
|
|
74
74
|
verifyAllPermissions,
|
|
75
75
|
reportPermissionResults,
|
|
76
76
|
setPlatform,
|
|
77
|
+
consumeCodexRateLimitResetCredit,
|
|
77
78
|
} from "./feishu-platform.ts";
|
|
78
79
|
import { SimulatedPlatform, SIM_DEFAULT_CHAT_ID } from "./sim-platform.ts";
|
|
79
80
|
import { setMessageHandler } from "./sim-store.ts";
|
|
@@ -103,6 +104,7 @@ import {
|
|
|
103
104
|
import { fixStaleStreamStates } from "./stream-state.ts";
|
|
104
105
|
import { handleCommand, type PlatformAdapter } from "./orchestrator.ts";
|
|
105
106
|
import { createWechatAdapter, startWechatPlatform } from "./wechat-platform.ts";
|
|
107
|
+
import { handleCodexResetCardAction } from "./codex-reset-actions.ts";
|
|
106
108
|
|
|
107
109
|
// ---------------------------------------------------------------------------
|
|
108
110
|
// Feishu 平台适配器
|
|
@@ -464,6 +466,15 @@ async function startBotServiceCore(): Promise<void> {
|
|
|
464
466
|
return;
|
|
465
467
|
}
|
|
466
468
|
|
|
469
|
+
const handledCodexReset = await handleCodexResetCardAction(data, {
|
|
470
|
+
getTenantAccessToken,
|
|
471
|
+
sendRawCard,
|
|
472
|
+
sendCardReply,
|
|
473
|
+
updateCardMessage,
|
|
474
|
+
consumeCodexRateLimitResetCredit,
|
|
475
|
+
});
|
|
476
|
+
if (handledCodexReset) return;
|
|
477
|
+
|
|
467
478
|
const result = parseCardAction(data);
|
|
468
479
|
if (!result) return;
|
|
469
480
|
console.log(`[BTN] chat=${result.chatId} text="${result.text}"`);
|
package/src/orchestrator.ts
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
buildSessionsCard,
|
|
45
45
|
buildQueuedCard,
|
|
46
46
|
buildQueueFullCard,
|
|
47
|
+
buildCodexUsageCard,
|
|
47
48
|
} from "./cards.ts";
|
|
48
49
|
import {
|
|
49
50
|
formatGitResult,
|
|
@@ -80,6 +81,7 @@ import {
|
|
|
80
81
|
} from "./session-chat-binding.ts";
|
|
81
82
|
import { getCodexUsageSummary, getTenantAccessToken, sendPostMessage } from "./feishu-platform.ts";
|
|
82
83
|
import { getCursorUsageSummary, type CursorUsageSummary } from "./cursor-usage.ts";
|
|
84
|
+
import { applySharedPrefix } from "./shared-prefix.ts";
|
|
83
85
|
export { type PlatformAdapter } from "./platform-adapter.ts";
|
|
84
86
|
import type { PlatformAdapter } from "./platform-adapter.ts";
|
|
85
87
|
import type { CodexUsageSummary } from "./feishu-api.ts";
|
|
@@ -159,9 +161,42 @@ function formatCodexUsageSummary(usage: CodexUsageSummary): string {
|
|
|
159
161
|
].join("\n");
|
|
160
162
|
};
|
|
161
163
|
|
|
164
|
+
const formatResetCredits = () => {
|
|
165
|
+
if (usage.rateLimitResetCreditsAvailable === null) return "**主动重置:** 暂无数据";
|
|
166
|
+
const lines = [`**主动重置:** 剩余 ${usage.rateLimitResetCreditsAvailable} 次`];
|
|
167
|
+
const credits = usage.rateLimitResetCredits ?? [];
|
|
168
|
+
if (credits.length > 0) {
|
|
169
|
+
const pad = (value: number) => String(value).padStart(2, "0");
|
|
170
|
+
const formatExpiresAt = (value: string) => {
|
|
171
|
+
const date = new Date(value);
|
|
172
|
+
if (!Number.isFinite(date.getTime())) return value;
|
|
173
|
+
return [
|
|
174
|
+
date.getFullYear(),
|
|
175
|
+
"-",
|
|
176
|
+
pad(date.getMonth() + 1),
|
|
177
|
+
"-",
|
|
178
|
+
pad(date.getDate()),
|
|
179
|
+
" ",
|
|
180
|
+
pad(date.getHours()),
|
|
181
|
+
":",
|
|
182
|
+
pad(date.getMinutes()),
|
|
183
|
+
":",
|
|
184
|
+
pad(date.getSeconds()),
|
|
185
|
+
].join("");
|
|
186
|
+
};
|
|
187
|
+
lines.push("**过期时间:**");
|
|
188
|
+
for (const credit of credits) {
|
|
189
|
+
lines.push(`- ${formatExpiresAt(credit.expiresAt)}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return lines.join("\n");
|
|
193
|
+
};
|
|
194
|
+
|
|
162
195
|
return [
|
|
163
196
|
"Codex 用量:",
|
|
164
197
|
"",
|
|
198
|
+
formatResetCredits(),
|
|
199
|
+
"",
|
|
165
200
|
formatWindow("5h", usage.fiveHour),
|
|
166
201
|
formatWindow("周", usage.weekly),
|
|
167
202
|
].join("\n");
|
|
@@ -224,7 +259,7 @@ function formatCursorUsageSummary(usage: CursorUsageSummary): string {
|
|
|
224
259
|
}
|
|
225
260
|
|
|
226
261
|
function usageHelpLine(tool: string): string {
|
|
227
|
-
if (tool === "codex") return "\n发送 **/usage** 查看 Codex 5h
|
|
262
|
+
if (tool === "codex") return "\n发送 **/usage** 查看 Codex 5h/周用量,以及查询/使用主动重置卡。";
|
|
228
263
|
if (tool === "cursor") return "\n发送 **/usage** 查看 Cursor 用量。";
|
|
229
264
|
return "";
|
|
230
265
|
}
|
|
@@ -249,9 +284,12 @@ async function sendUsageSummary(platform: PlatformAdapter, chatId: string, tool:
|
|
|
249
284
|
return;
|
|
250
285
|
}
|
|
251
286
|
|
|
252
|
-
const
|
|
287
|
+
const usage = await getCodexUsageSummary();
|
|
288
|
+
const content = formatCodexUsageSummary(usage);
|
|
253
289
|
if (platform.kind === "wechat") {
|
|
254
290
|
await platform.sendText(chatId, content).catch(() => {});
|
|
291
|
+
} else if (platform.kind === "feishu") {
|
|
292
|
+
await platform.sendRawCard(chatId, buildCodexUsageCard(content, usage.rateLimitResetCreditsAvailable));
|
|
255
293
|
} else {
|
|
256
294
|
await platform.sendCard(chatId, "Codex Usage", content, "blue");
|
|
257
295
|
}
|
|
@@ -273,11 +311,10 @@ function isUntitledSessionChatName(name: string): boolean {
|
|
|
273
311
|
|
|
274
312
|
function shouldSendWechatProcessingAck(
|
|
275
313
|
platform: PlatformAdapter,
|
|
276
|
-
|
|
314
|
+
isCommandText: boolean,
|
|
277
315
|
chatType: string,
|
|
278
|
-
text: string,
|
|
279
316
|
): boolean {
|
|
280
|
-
return platform.kind === "wechat" && chatType === "p2p" && !
|
|
317
|
+
return platform.kind === "wechat" && chatType === "p2p" && !isCommandText;
|
|
281
318
|
}
|
|
282
319
|
|
|
283
320
|
function isNonWechatP2p(platform: PlatformAdapter, chatType: string): boolean {
|
|
@@ -410,11 +447,15 @@ export async function handleCommand(
|
|
|
410
447
|
traceId?: string,
|
|
411
448
|
): Promise<void> {
|
|
412
449
|
const tid = traceId ?? makeTraceId();
|
|
450
|
+
const sharedPrefix = applySharedPrefix(text);
|
|
451
|
+
const promptText = sharedPrefix.text;
|
|
452
|
+
text = sharedPrefix.body;
|
|
413
453
|
const textLower = text.toLowerCase();
|
|
454
|
+
const isCommandText = !sharedPrefix.matched && textLower.startsWith("/");
|
|
414
455
|
recordChatPlatform(chatId, platform);
|
|
415
456
|
await cleanupNonWechatP2pBinding(platform, chatId, chatType, tid);
|
|
416
457
|
|
|
417
|
-
if (textLower === "/restart") {
|
|
458
|
+
if (isCommandText && textLower === "/restart") {
|
|
418
459
|
logTrace(tid, "BRANCH", { cmd: "/restart" });
|
|
419
460
|
await platform.sendText(chatId, "重启中...请几秒后发消息唤醒我").catch(() => {});
|
|
420
461
|
logTrace(tid, "DONE", { outcome: "restart" });
|
|
@@ -449,7 +490,7 @@ export async function handleCommand(
|
|
|
449
490
|
return;
|
|
450
491
|
}
|
|
451
492
|
|
|
452
|
-
if (textLower === "/update") {
|
|
493
|
+
if (isCommandText && textLower === "/update") {
|
|
453
494
|
logTrace(tid, "BRANCH", { cmd: "/update" });
|
|
454
495
|
const isGlobal = isRunningFromGlobalNpm();
|
|
455
496
|
appendStartupTrace("update: command received", { isGlobal, chatId });
|
|
@@ -466,7 +507,7 @@ export async function handleCommand(
|
|
|
466
507
|
return;
|
|
467
508
|
}
|
|
468
509
|
|
|
469
|
-
if (textLower === "/usage") {
|
|
510
|
+
if (isCommandText && textLower === "/usage") {
|
|
470
511
|
const usageTool = await resolveUsageTool(chatId);
|
|
471
512
|
logTrace(tid, "BRANCH", { cmd: "/usage", tool: usageTool });
|
|
472
513
|
try {
|
|
@@ -479,7 +520,7 @@ export async function handleCommand(
|
|
|
479
520
|
return;
|
|
480
521
|
}
|
|
481
522
|
|
|
482
|
-
if (textLower === "/cd" || textLower.startsWith("/cd ")) {
|
|
523
|
+
if (isCommandText && (textLower === "/cd" || textLower.startsWith("/cd "))) {
|
|
483
524
|
logTrace(tid, "BRANCH", {
|
|
484
525
|
cmd: "/cd",
|
|
485
526
|
arg: text.slice(3).trim() || "(none)",
|
|
@@ -602,7 +643,7 @@ export async function handleCommand(
|
|
|
602
643
|
return;
|
|
603
644
|
}
|
|
604
645
|
|
|
605
|
-
if (textLower === "/new" || textLower.startsWith("/new ")) {
|
|
646
|
+
if (isCommandText && (textLower === "/new" || textLower.startsWith("/new "))) {
|
|
606
647
|
const toolArg = text.slice(5).trim().toLowerCase();
|
|
607
648
|
const tool = toolArg || resolveDefaultAgentTool();
|
|
608
649
|
logTrace(tid, "BRANCH", { cmd: "/new", tool });
|
|
@@ -863,7 +904,7 @@ export async function handleCommand(
|
|
|
863
904
|
if (sessionId && descriptionTool && toolLabel) {
|
|
864
905
|
// 有会话上下文 — 路由到命令处理或 prompt
|
|
865
906
|
logTrace(tid, "BRANCH", { sessionId, tool: descriptionTool });
|
|
866
|
-
const routeKind =
|
|
907
|
+
const routeKind = isCommandText ? "command" : "prompt";
|
|
867
908
|
const chatKind = chatType === "p2p" ? "p2p chat" : "session group";
|
|
868
909
|
console.log(
|
|
869
910
|
`[${ts()}] [ROUTE] ${toolLabel} ${chatKind} ${routeKind} detected, session=${sessionId} tool=${descriptionTool}`,
|
|
@@ -872,7 +913,7 @@ export async function handleCommand(
|
|
|
872
913
|
if (
|
|
873
914
|
chatType !== "p2p" &&
|
|
874
915
|
isUntitledSessionChatName(chatInfo!.name) &&
|
|
875
|
-
!
|
|
916
|
+
!isCommandText
|
|
876
917
|
) {
|
|
877
918
|
const MAX_PREFIX = 10;
|
|
878
919
|
const prefix = text.slice(0, MAX_PREFIX);
|
|
@@ -907,7 +948,7 @@ export async function handleCommand(
|
|
|
907
948
|
if (
|
|
908
949
|
chatType === "p2p" &&
|
|
909
950
|
platform.kind === "wechat" &&
|
|
910
|
-
!
|
|
951
|
+
!isCommandText
|
|
911
952
|
) {
|
|
912
953
|
try {
|
|
913
954
|
const reg = await loadSessionRegistryForBinding();
|
|
@@ -946,7 +987,7 @@ export async function handleCommand(
|
|
|
946
987
|
}
|
|
947
988
|
}
|
|
948
989
|
|
|
949
|
-
if (textLower === "/stop") {
|
|
990
|
+
if (isCommandText && textLower === "/stop") {
|
|
950
991
|
logTrace(tid, "BRANCH", { cmd: "/stop" });
|
|
951
992
|
if (stopSession(sessionId)) {
|
|
952
993
|
console.log(`[${ts()}] [STOP] User sent /stop, session=${sessionId}`);
|
|
@@ -960,7 +1001,7 @@ export async function handleCommand(
|
|
|
960
1001
|
return;
|
|
961
1002
|
}
|
|
962
1003
|
|
|
963
|
-
if (textLower === "/cancel") {
|
|
1004
|
+
if (isCommandText && textLower === "/cancel") {
|
|
964
1005
|
logTrace(tid, "BRANCH", { cmd: "/cancel" });
|
|
965
1006
|
if (cancelQueuedMessage(sessionId)) {
|
|
966
1007
|
console.log(`[${ts()}] [CANCEL] Queue cancelled for session=${sessionId}`);
|
|
@@ -973,7 +1014,7 @@ export async function handleCommand(
|
|
|
973
1014
|
return;
|
|
974
1015
|
}
|
|
975
1016
|
|
|
976
|
-
if (textLower === "/test") {
|
|
1017
|
+
if (isCommandText && textLower === "/test") {
|
|
977
1018
|
logTrace(tid, "BRANCH", { cmd: "/test" });
|
|
978
1019
|
const tableHeaders = ["名称", "版本", "状态"];
|
|
979
1020
|
const tableRows = [
|
|
@@ -1010,7 +1051,7 @@ export async function handleCommand(
|
|
|
1010
1051
|
return;
|
|
1011
1052
|
}
|
|
1012
1053
|
|
|
1013
|
-
if (textLower === "/state") {
|
|
1054
|
+
if (isCommandText && textLower === "/state") {
|
|
1014
1055
|
logTrace(tid, "BRANCH", { cmd: "/state" });
|
|
1015
1056
|
const status = await getSessionStatus(chatId);
|
|
1016
1057
|
const isActive = isSessionRunning(sessionId);
|
|
@@ -1049,7 +1090,7 @@ export async function handleCommand(
|
|
|
1049
1090
|
return;
|
|
1050
1091
|
}
|
|
1051
1092
|
|
|
1052
|
-
if (textLower === "/sessions") {
|
|
1093
|
+
if (isCommandText && textLower === "/sessions") {
|
|
1053
1094
|
logTrace(tid, "BRANCH", { cmd: "/sessions" });
|
|
1054
1095
|
const allSessions = await getAllSessionsStatus();
|
|
1055
1096
|
const now = Date.now();
|
|
@@ -1074,7 +1115,7 @@ export async function handleCommand(
|
|
|
1074
1115
|
return;
|
|
1075
1116
|
}
|
|
1076
1117
|
|
|
1077
|
-
if (textLower === "/newh") {
|
|
1118
|
+
if (isCommandText && textLower === "/newh") {
|
|
1078
1119
|
logTrace(tid, "BRANCH", { cmd: "/newh" });
|
|
1079
1120
|
const adapter = getAdapterForTool(descriptionTool, sessionId);
|
|
1080
1121
|
let cwd: string;
|
|
@@ -1161,7 +1202,7 @@ export async function handleCommand(
|
|
|
1161
1202
|
return;
|
|
1162
1203
|
}
|
|
1163
1204
|
|
|
1164
|
-
if (textLower === "/deleteg") {
|
|
1205
|
+
if (isCommandText && textLower === "/deleteg") {
|
|
1165
1206
|
logTrace(tid, "BRANCH", { cmd: "/deleteg" });
|
|
1166
1207
|
if (chatType === "p2p") {
|
|
1167
1208
|
await platform
|
|
@@ -1199,7 +1240,7 @@ export async function handleCommand(
|
|
|
1199
1240
|
}
|
|
1200
1241
|
|
|
1201
1242
|
// /session <number>:切换到 /sessions 列表中的指定会话
|
|
1202
|
-
const sessionMatch = textLower.match(/^\/session\s+(\d+)$/);
|
|
1243
|
+
const sessionMatch = isCommandText ? textLower.match(/^\/session\s+(\d+)$/) : null;
|
|
1203
1244
|
if (sessionMatch) {
|
|
1204
1245
|
const index = parseInt(sessionMatch[1], 10) - 1;
|
|
1205
1246
|
logTrace(tid, "BRANCH", { cmd: "/session", index: index + 1 });
|
|
@@ -1325,7 +1366,7 @@ export async function handleCommand(
|
|
|
1325
1366
|
}
|
|
1326
1367
|
|
|
1327
1368
|
// /model clear — 清除当前 session 的模型覆盖
|
|
1328
|
-
if (textLower === "/model clear") {
|
|
1369
|
+
if (isCommandText && textLower === "/model clear") {
|
|
1329
1370
|
logTrace(tid, "BRANCH", { cmd: "/model clear", sessionId });
|
|
1330
1371
|
clearSessionModelOverride(sessionId);
|
|
1331
1372
|
const defaultModel = getEffectiveModelForTool(descriptionTool);
|
|
@@ -1340,7 +1381,7 @@ export async function handleCommand(
|
|
|
1340
1381
|
}
|
|
1341
1382
|
|
|
1342
1383
|
// /model <name> — 切换当前 session 的模型(支持所有 agent,模糊匹配)
|
|
1343
|
-
if (textLower.startsWith("/model ")) {
|
|
1384
|
+
if (isCommandText && textLower.startsWith("/model ")) {
|
|
1344
1385
|
const modelArg = text.slice(7).trim();
|
|
1345
1386
|
if (!modelArg) return; // 纯 "/model " 不处理,交给上面的 /model 分支
|
|
1346
1387
|
logTrace(tid, "BRANCH", { cmd: "/model", arg: modelArg, sessionId, tool: descriptionTool });
|
|
@@ -1374,7 +1415,7 @@ export async function handleCommand(
|
|
|
1374
1415
|
}
|
|
1375
1416
|
|
|
1376
1417
|
// /model — 查看当前会话的可用模型(根据会话 Agent 类型)
|
|
1377
|
-
if (textLower === "/model") {
|
|
1418
|
+
if (isCommandText && textLower === "/model") {
|
|
1378
1419
|
logTrace(tid, "BRANCH", { cmd: "/model", sessionId, tool: descriptionTool });
|
|
1379
1420
|
const models = getAllModelsForTool(descriptionTool as AgentTool);
|
|
1380
1421
|
const currentModel = getEffectiveModelForTool(descriptionTool, sessionId);
|
|
@@ -1398,7 +1439,7 @@ export async function handleCommand(
|
|
|
1398
1439
|
}
|
|
1399
1440
|
|
|
1400
1441
|
// /git <args>:在「当前会话工作目录」执行 git 命令
|
|
1401
|
-
if (textLower.startsWith("/git ") || textLower === "/git") {
|
|
1442
|
+
if (isCommandText && (textLower.startsWith("/git ") || textLower === "/git")) {
|
|
1402
1443
|
const args = text === "/git" ? "" : text.slice(5).trim();
|
|
1403
1444
|
logTrace(tid, "BRANCH", { cmd: "/git", args: args || "(none)" });
|
|
1404
1445
|
if (!args) {
|
|
@@ -1468,7 +1509,7 @@ export async function handleCommand(
|
|
|
1468
1509
|
// 并发检查:同一 session 只能有一个活跃 prompt,多余消息进入队列
|
|
1469
1510
|
if (isSessionRunning(sessionId)) {
|
|
1470
1511
|
const queued = enqueueMessage(sessionId, {
|
|
1471
|
-
text, chatId, openId, msgTimestamp, chatType, traceId: tid,
|
|
1512
|
+
text: promptText, chatId, openId, msgTimestamp, chatType, traceId: tid,
|
|
1472
1513
|
});
|
|
1473
1514
|
if (queued) {
|
|
1474
1515
|
logTrace(tid, "QUEUED", { sessionId });
|
|
@@ -1494,7 +1535,7 @@ export async function handleCommand(
|
|
|
1494
1535
|
return;
|
|
1495
1536
|
}
|
|
1496
1537
|
|
|
1497
|
-
if (shouldSendWechatProcessingAck(platform,
|
|
1538
|
+
if (shouldSendWechatProcessingAck(platform, isCommandText, chatType)) {
|
|
1498
1539
|
await platform.sendText(chatId, "生成中...").catch(() => {});
|
|
1499
1540
|
}
|
|
1500
1541
|
|
|
@@ -1502,7 +1543,7 @@ export async function handleCommand(
|
|
|
1502
1543
|
logTrace(tid, "RESUME", { sessionId, tool: descriptionTool });
|
|
1503
1544
|
await resumeAndPrompt(
|
|
1504
1545
|
sessionId,
|
|
1505
|
-
|
|
1546
|
+
promptText,
|
|
1506
1547
|
platform,
|
|
1507
1548
|
chatId,
|
|
1508
1549
|
msgTimestamp,
|
|
@@ -1529,7 +1570,7 @@ export async function handleCommand(
|
|
|
1529
1570
|
}
|
|
1530
1571
|
|
|
1531
1572
|
// 无会话上下文 → 检查是否是 /model 查询
|
|
1532
|
-
if (textLower === "/model") {
|
|
1573
|
+
if (isCommandText && textLower === "/model") {
|
|
1533
1574
|
const defaultTool = resolveDefaultAgentTool();
|
|
1534
1575
|
const models = getAllModelsForTool(defaultTool);
|
|
1535
1576
|
let currentModel = "";
|
|
@@ -1556,7 +1597,7 @@ export async function handleCommand(
|
|
|
1556
1597
|
}
|
|
1557
1598
|
|
|
1558
1599
|
// 无会话上下文 → /sessions 仍是有效指令,不触发飞书私聊自动建群。
|
|
1559
|
-
if (textLower === "/sessions") {
|
|
1600
|
+
if (isCommandText && textLower === "/sessions") {
|
|
1560
1601
|
logTrace(tid, "BRANCH", { cmd: "/sessions", scope: "global" });
|
|
1561
1602
|
const allSessions = await getAllSessionsStatus();
|
|
1562
1603
|
const now = Date.now();
|
|
@@ -1582,7 +1623,7 @@ export async function handleCommand(
|
|
|
1582
1623
|
}
|
|
1583
1624
|
|
|
1584
1625
|
// 飞书私聊普通消息:不再绑定私聊本身,而是自动创建会话群并把私聊内容作为首轮 prompt。
|
|
1585
|
-
if (isNonWechatP2p(platform, chatType) && !
|
|
1626
|
+
if (isNonWechatP2p(platform, chatType) && !isCommandText) {
|
|
1586
1627
|
const tool = resolveDefaultAgentTool();
|
|
1587
1628
|
const toolLabel = toolDisplayName(tool);
|
|
1588
1629
|
logTrace(tid, "BRANCH", { cmd: "auto_new_from_p2p", tool });
|
|
@@ -1701,7 +1742,7 @@ export async function handleCommand(
|
|
|
1701
1742
|
logTrace(tid, "RESUME", { sessionId, tool, trigger: "auto_new_from_p2p" });
|
|
1702
1743
|
await resumeAndPrompt(
|
|
1703
1744
|
sessionId,
|
|
1704
|
-
|
|
1745
|
+
promptText,
|
|
1705
1746
|
platform,
|
|
1706
1747
|
newChatId,
|
|
1707
1748
|
msgTimestamp,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const ABD_PREFIX = "/abd";
|
|
2
|
+
|
|
3
|
+
export const ABD_APPEND_PROMPT =
|
|
4
|
+
"请从第一性原理出发挖掘我的真实需求。你觉得我的需求合理吗?开始实现之前有什么问题要问我的?";
|
|
5
|
+
|
|
6
|
+
export const ABD_HELP_LINE =
|
|
7
|
+
`发送 **${ABD_PREFIX}** 前缀消息,将以第一性原理追问真实需求、合理性和实现前问题`;
|
|
8
|
+
|
|
9
|
+
export interface SharedPrefixResult {
|
|
10
|
+
matched: boolean;
|
|
11
|
+
text: string;
|
|
12
|
+
body: string;
|
|
13
|
+
prefix?: typeof ABD_PREFIX;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function applySharedPrefix(text: string): SharedPrefixResult {
|
|
17
|
+
if (!text.toLowerCase().startsWith(ABD_PREFIX)) {
|
|
18
|
+
return { matched: false, text, body: text };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const body = text.slice(ABD_PREFIX.length).replace(/^\s+/, "");
|
|
22
|
+
const appendix = ["---", ABD_APPEND_PROMPT].join("\n");
|
|
23
|
+
return {
|
|
24
|
+
matched: true,
|
|
25
|
+
prefix: ABD_PREFIX,
|
|
26
|
+
body,
|
|
27
|
+
text: body ? [body, appendix].join("\n\n") : appendix,
|
|
28
|
+
};
|
|
29
|
+
}
|
package/src/sim-platform.ts
CHANGED
|
@@ -123,19 +123,25 @@ export const SimulatedPlatform: FeishuPlatform = {
|
|
|
123
123
|
},
|
|
124
124
|
|
|
125
125
|
// ---- 头像 ----
|
|
126
|
-
async setChatAvatar(_token, _chatId, _tool, _status) {
|
|
127
|
-
// 模拟模式不需要头像
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
async getCodexUsageSummary() {
|
|
131
|
-
return {
|
|
132
|
-
fiveHour: { usedPercent: 0, remainingPercent: 100, resetAtEpochSeconds: null, resetAfterSeconds: null },
|
|
133
|
-
weekly: { usedPercent: 0, remainingPercent: 100, resetAtEpochSeconds: null, resetAfterSeconds: null },
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
126
|
+
async setChatAvatar(_token, _chatId, _tool, _status) {
|
|
127
|
+
// 模拟模式不需要头像
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
async getCodexUsageSummary() {
|
|
131
|
+
return {
|
|
132
|
+
fiveHour: { usedPercent: 0, remainingPercent: 100, resetAtEpochSeconds: null, resetAfterSeconds: null },
|
|
133
|
+
weekly: { usedPercent: 0, remainingPercent: 100, resetAtEpochSeconds: null, resetAfterSeconds: null },
|
|
134
|
+
rateLimitResetCreditsAvailable: null,
|
|
135
|
+
rateLimitResetCredits: null,
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
async consumeCodexRateLimitResetCredit(_redeemRequestId) {
|
|
140
|
+
return { code: "no_credit" as const, windowsReset: 0 };
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// ---- 图片下载 ----
|
|
144
|
+
async getOrDownloadImage(_token, _messageId, fileKey) {
|
|
139
145
|
return join(SIM_DIR, "images", fileKey);
|
|
140
146
|
},
|
|
141
147
|
|
|
@@ -168,4 +174,4 @@ export const SimulatedPlatform: FeishuPlatform = {
|
|
|
168
174
|
};
|
|
169
175
|
|
|
170
176
|
/** 模拟模式下的默认 chat_id(重新导出以保持向后兼容) */
|
|
171
|
-
export { SIM_DEFAULT_CHAT_ID } from "./sim-store.ts";
|
|
177
|
+
export { SIM_DEFAULT_CHAT_ID } from "./sim-store.ts";
|