chatccc 0.2.181 → 0.2.183
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 +69 -69
- package/package.json +1 -1
- package/src/__tests__/card-plain-text.test.ts +5 -5
- package/src/__tests__/cards.test.ts +77 -77
- package/src/__tests__/codex-reset-actions.test.ts +40 -7
- package/src/__tests__/feishu-avatar.test.ts +115 -115
- package/src/__tests__/feishu-platform.test.ts +22 -22
- package/src/__tests__/format-message.test.ts +316 -273
- package/src/__tests__/orchestrator.test.ts +117 -117
- package/src/__tests__/shared-prefix.test.ts +36 -36
- package/src/cards.ts +89 -89
- package/src/codex-reset-actions.ts +19 -6
- package/src/feishu-api.ts +184 -184
- package/src/feishu-platform.ts +20 -20
- package/src/format-message.ts +293 -214
- package/src/index.ts +2 -0
- package/src/orchestrator.ts +120 -120
- package/src/shared-prefix.ts +29 -29
- package/src/sim-platform.ts +20 -20
package/src/orchestrator.ts
CHANGED
|
@@ -38,14 +38,14 @@ import {
|
|
|
38
38
|
import {
|
|
39
39
|
buildHelpCard,
|
|
40
40
|
buildModelCard,
|
|
41
|
-
buildStatusCard,
|
|
42
|
-
buildCdContent,
|
|
43
|
-
buildCdCard,
|
|
44
|
-
buildSessionsCard,
|
|
45
|
-
buildQueuedCard,
|
|
46
|
-
buildQueueFullCard,
|
|
47
|
-
buildCodexUsageCard,
|
|
48
|
-
} from "./cards.ts";
|
|
41
|
+
buildStatusCard,
|
|
42
|
+
buildCdContent,
|
|
43
|
+
buildCdCard,
|
|
44
|
+
buildSessionsCard,
|
|
45
|
+
buildQueuedCard,
|
|
46
|
+
buildQueueFullCard,
|
|
47
|
+
buildCodexUsageCard,
|
|
48
|
+
} from "./cards.ts";
|
|
49
49
|
import {
|
|
50
50
|
formatGitResult,
|
|
51
51
|
gitResultHeaderTemplate,
|
|
@@ -79,10 +79,10 @@ import {
|
|
|
79
79
|
enqueueMessage,
|
|
80
80
|
cancelQueuedMessage,
|
|
81
81
|
} from "./session-chat-binding.ts";
|
|
82
|
-
import { getCodexUsageSummary, getTenantAccessToken, sendPostMessage } from "./feishu-platform.ts";
|
|
83
|
-
import { getCursorUsageSummary, type CursorUsageSummary } from "./cursor-usage.ts";
|
|
84
|
-
import { applySharedPrefix } from "./shared-prefix.ts";
|
|
85
|
-
export { type PlatformAdapter } from "./platform-adapter.ts";
|
|
82
|
+
import { getCodexUsageSummary, getTenantAccessToken, sendPostMessage } from "./feishu-platform.ts";
|
|
83
|
+
import { getCursorUsageSummary, type CursorUsageSummary } from "./cursor-usage.ts";
|
|
84
|
+
import { applySharedPrefix } from "./shared-prefix.ts";
|
|
85
|
+
export { type PlatformAdapter } from "./platform-adapter.ts";
|
|
86
86
|
import type { PlatformAdapter } from "./platform-adapter.ts";
|
|
87
87
|
import type { CodexUsageSummary } from "./feishu-api.ts";
|
|
88
88
|
|
|
@@ -114,7 +114,7 @@ function findModelMatch(input: string, models: string[]): string | null {
|
|
|
114
114
|
return candidates[0] ?? null;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
function formatCodexUsageSummary(usage: CodexUsageSummary): string {
|
|
117
|
+
function formatCodexUsageSummary(usage: CodexUsageSummary): string {
|
|
118
118
|
const progressBar = (usedPercent: number) => {
|
|
119
119
|
const width = 20;
|
|
120
120
|
const usedBlocks = Math.max(0, Math.min(width, Math.round((usedPercent / 100) * width)));
|
|
@@ -153,54 +153,54 @@ function formatCodexUsageSummary(usage: CodexUsageSummary): string {
|
|
|
153
153
|
return `${absolute}${formatDuration(balance.resetAfterSeconds)}`;
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
-
const formatWindow = (label: string, balance: CodexUsageSummary["fiveHour"] | null) => {
|
|
157
|
-
if (!balance) return `**${label}:** 暂无数据`;
|
|
158
|
-
return [
|
|
159
|
-
`**${label}:** 已用 ${balance.usedPercent}%,剩余 ${balance.remainingPercent}%,重置: ${formatResetTime(balance)}`,
|
|
160
|
-
progressBar(balance.usedPercent),
|
|
161
|
-
].join("\n");
|
|
162
|
-
};
|
|
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
|
-
|
|
195
|
-
return [
|
|
196
|
-
"Codex 用量:",
|
|
197
|
-
"",
|
|
198
|
-
formatResetCredits(),
|
|
199
|
-
"",
|
|
200
|
-
formatWindow("5h", usage.fiveHour),
|
|
201
|
-
formatWindow("周", usage.weekly),
|
|
202
|
-
].join("\n");
|
|
203
|
-
}
|
|
156
|
+
const formatWindow = (label: string, balance: CodexUsageSummary["fiveHour"] | null) => {
|
|
157
|
+
if (!balance) return `**${label}:** 暂无数据`;
|
|
158
|
+
return [
|
|
159
|
+
`**${label}:** 已用 ${balance.usedPercent}%,剩余 ${balance.remainingPercent}%,重置: ${formatResetTime(balance)}`,
|
|
160
|
+
progressBar(balance.usedPercent),
|
|
161
|
+
].join("\n");
|
|
162
|
+
};
|
|
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
|
+
|
|
195
|
+
return [
|
|
196
|
+
"Codex 用量:",
|
|
197
|
+
"",
|
|
198
|
+
formatResetCredits(),
|
|
199
|
+
"",
|
|
200
|
+
formatWindow("5h", usage.fiveHour),
|
|
201
|
+
formatWindow("周", usage.weekly),
|
|
202
|
+
].join("\n");
|
|
203
|
+
}
|
|
204
204
|
|
|
205
205
|
function formatCursorUsageSummary(usage: CursorUsageSummary): string {
|
|
206
206
|
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
@@ -259,7 +259,7 @@ function formatCursorUsageSummary(usage: CursorUsageSummary): string {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
function usageHelpLine(tool: string): string {
|
|
262
|
-
if (tool === "codex") return "\n发送 **/usage** 查看 Codex 5h/周用量,以及查询/使用主动重置卡。";
|
|
262
|
+
if (tool === "codex") return "\n发送 **/usage** 查看 Codex 5h/周用量,以及查询/使用主动重置卡。";
|
|
263
263
|
if (tool === "cursor") return "\n发送 **/usage** 查看 Cursor 用量。";
|
|
264
264
|
return "";
|
|
265
265
|
}
|
|
@@ -284,16 +284,16 @@ async function sendUsageSummary(platform: PlatformAdapter, chatId: string, tool:
|
|
|
284
284
|
return;
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
-
const usage = await getCodexUsageSummary();
|
|
288
|
-
const content = formatCodexUsageSummary(usage);
|
|
289
|
-
if (platform.kind === "wechat") {
|
|
290
|
-
await platform.sendText(chatId, content).catch(() => {});
|
|
291
|
-
} else if (platform.kind === "feishu") {
|
|
292
|
-
await platform.sendRawCard(chatId, buildCodexUsageCard(content, usage.rateLimitResetCreditsAvailable));
|
|
293
|
-
} else {
|
|
294
|
-
await platform.sendCard(chatId, "Codex Usage", content, "blue");
|
|
295
|
-
}
|
|
296
|
-
}
|
|
287
|
+
const usage = await getCodexUsageSummary();
|
|
288
|
+
const content = formatCodexUsageSummary(usage);
|
|
289
|
+
if (platform.kind === "wechat") {
|
|
290
|
+
await platform.sendText(chatId, content).catch(() => {});
|
|
291
|
+
} else if (platform.kind === "feishu") {
|
|
292
|
+
await platform.sendRawCard(chatId, buildCodexUsageCard(content, usage.rateLimitResetCreditsAvailable));
|
|
293
|
+
} else {
|
|
294
|
+
await platform.sendCard(chatId, "Codex Usage", content, "blue");
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
297
|
|
|
298
298
|
async function sendUsageError(platform: PlatformAdapter, chatId: string, tool: "codex" | "cursor", err: unknown): Promise<void> {
|
|
299
299
|
const toolLabel = tool === "cursor" ? "Cursor" : "Codex";
|
|
@@ -309,13 +309,13 @@ function isUntitledSessionChatName(name: string): boolean {
|
|
|
309
309
|
return name === "新会话" || name.startsWith("新会话-");
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
function shouldSendWechatProcessingAck(
|
|
313
|
-
platform: PlatformAdapter,
|
|
314
|
-
isCommandText: boolean,
|
|
315
|
-
chatType: string,
|
|
316
|
-
): boolean {
|
|
317
|
-
return platform.kind === "wechat" && chatType === "p2p" && !isCommandText;
|
|
318
|
-
}
|
|
312
|
+
function shouldSendWechatProcessingAck(
|
|
313
|
+
platform: PlatformAdapter,
|
|
314
|
+
isCommandText: boolean,
|
|
315
|
+
chatType: string,
|
|
316
|
+
): boolean {
|
|
317
|
+
return platform.kind === "wechat" && chatType === "p2p" && !isCommandText;
|
|
318
|
+
}
|
|
319
319
|
|
|
320
320
|
function isNonWechatP2p(platform: PlatformAdapter, chatType: string): boolean {
|
|
321
321
|
return chatType === "p2p" && platform.kind !== "wechat";
|
|
@@ -445,17 +445,17 @@ export async function handleCommand(
|
|
|
445
445
|
msgTimestamp: number,
|
|
446
446
|
chatType = "group",
|
|
447
447
|
traceId?: string,
|
|
448
|
-
): Promise<void> {
|
|
449
|
-
const tid = traceId ?? makeTraceId();
|
|
450
|
-
const sharedPrefix = applySharedPrefix(text);
|
|
451
|
-
const promptText = sharedPrefix.text;
|
|
452
|
-
text = sharedPrefix.body;
|
|
453
|
-
const textLower = text.toLowerCase();
|
|
454
|
-
const isCommandText = !sharedPrefix.matched && textLower.startsWith("/");
|
|
455
|
-
recordChatPlatform(chatId, platform);
|
|
448
|
+
): Promise<void> {
|
|
449
|
+
const tid = traceId ?? makeTraceId();
|
|
450
|
+
const sharedPrefix = applySharedPrefix(text);
|
|
451
|
+
const promptText = sharedPrefix.text;
|
|
452
|
+
text = sharedPrefix.body;
|
|
453
|
+
const textLower = text.toLowerCase();
|
|
454
|
+
const isCommandText = !sharedPrefix.matched && textLower.startsWith("/");
|
|
455
|
+
recordChatPlatform(chatId, platform);
|
|
456
456
|
await cleanupNonWechatP2pBinding(platform, chatId, chatType, tid);
|
|
457
457
|
|
|
458
|
-
if (isCommandText && textLower === "/restart") {
|
|
458
|
+
if (isCommandText && textLower === "/restart") {
|
|
459
459
|
logTrace(tid, "BRANCH", { cmd: "/restart" });
|
|
460
460
|
await platform.sendText(chatId, "重启中...请几秒后发消息唤醒我").catch(() => {});
|
|
461
461
|
logTrace(tid, "DONE", { outcome: "restart" });
|
|
@@ -490,7 +490,7 @@ export async function handleCommand(
|
|
|
490
490
|
return;
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
-
if (isCommandText && textLower === "/update") {
|
|
493
|
+
if (isCommandText && textLower === "/update") {
|
|
494
494
|
logTrace(tid, "BRANCH", { cmd: "/update" });
|
|
495
495
|
const isGlobal = isRunningFromGlobalNpm();
|
|
496
496
|
appendStartupTrace("update: command received", { isGlobal, chatId });
|
|
@@ -507,7 +507,7 @@ export async function handleCommand(
|
|
|
507
507
|
return;
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
-
if (isCommandText && textLower === "/usage") {
|
|
510
|
+
if (isCommandText && textLower === "/usage") {
|
|
511
511
|
const usageTool = await resolveUsageTool(chatId);
|
|
512
512
|
logTrace(tid, "BRANCH", { cmd: "/usage", tool: usageTool });
|
|
513
513
|
try {
|
|
@@ -520,7 +520,7 @@ export async function handleCommand(
|
|
|
520
520
|
return;
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
-
if (isCommandText && (textLower === "/cd" || textLower.startsWith("/cd "))) {
|
|
523
|
+
if (isCommandText && (textLower === "/cd" || textLower.startsWith("/cd "))) {
|
|
524
524
|
logTrace(tid, "BRANCH", {
|
|
525
525
|
cmd: "/cd",
|
|
526
526
|
arg: text.slice(3).trim() || "(none)",
|
|
@@ -643,7 +643,7 @@ export async function handleCommand(
|
|
|
643
643
|
return;
|
|
644
644
|
}
|
|
645
645
|
|
|
646
|
-
if (isCommandText && (textLower === "/new" || textLower.startsWith("/new "))) {
|
|
646
|
+
if (isCommandText && (textLower === "/new" || textLower.startsWith("/new "))) {
|
|
647
647
|
const toolArg = text.slice(5).trim().toLowerCase();
|
|
648
648
|
const tool = toolArg || resolveDefaultAgentTool();
|
|
649
649
|
logTrace(tid, "BRANCH", { cmd: "/new", tool });
|
|
@@ -904,7 +904,7 @@ export async function handleCommand(
|
|
|
904
904
|
if (sessionId && descriptionTool && toolLabel) {
|
|
905
905
|
// 有会话上下文 — 路由到命令处理或 prompt
|
|
906
906
|
logTrace(tid, "BRANCH", { sessionId, tool: descriptionTool });
|
|
907
|
-
const routeKind = isCommandText ? "command" : "prompt";
|
|
907
|
+
const routeKind = isCommandText ? "command" : "prompt";
|
|
908
908
|
const chatKind = chatType === "p2p" ? "p2p chat" : "session group";
|
|
909
909
|
console.log(
|
|
910
910
|
`[${ts()}] [ROUTE] ${toolLabel} ${chatKind} ${routeKind} detected, session=${sessionId} tool=${descriptionTool}`,
|
|
@@ -913,7 +913,7 @@ export async function handleCommand(
|
|
|
913
913
|
if (
|
|
914
914
|
chatType !== "p2p" &&
|
|
915
915
|
isUntitledSessionChatName(chatInfo!.name) &&
|
|
916
|
-
!isCommandText
|
|
916
|
+
!isCommandText
|
|
917
917
|
) {
|
|
918
918
|
const MAX_PREFIX = 10;
|
|
919
919
|
const prefix = text.slice(0, MAX_PREFIX);
|
|
@@ -948,7 +948,7 @@ export async function handleCommand(
|
|
|
948
948
|
if (
|
|
949
949
|
chatType === "p2p" &&
|
|
950
950
|
platform.kind === "wechat" &&
|
|
951
|
-
!isCommandText
|
|
951
|
+
!isCommandText
|
|
952
952
|
) {
|
|
953
953
|
try {
|
|
954
954
|
const reg = await loadSessionRegistryForBinding();
|
|
@@ -987,7 +987,7 @@ export async function handleCommand(
|
|
|
987
987
|
}
|
|
988
988
|
}
|
|
989
989
|
|
|
990
|
-
if (isCommandText && textLower === "/stop") {
|
|
990
|
+
if (isCommandText && textLower === "/stop") {
|
|
991
991
|
logTrace(tid, "BRANCH", { cmd: "/stop" });
|
|
992
992
|
if (stopSession(sessionId)) {
|
|
993
993
|
console.log(`[${ts()}] [STOP] User sent /stop, session=${sessionId}`);
|
|
@@ -1001,7 +1001,7 @@ export async function handleCommand(
|
|
|
1001
1001
|
return;
|
|
1002
1002
|
}
|
|
1003
1003
|
|
|
1004
|
-
if (isCommandText && textLower === "/cancel") {
|
|
1004
|
+
if (isCommandText && textLower === "/cancel") {
|
|
1005
1005
|
logTrace(tid, "BRANCH", { cmd: "/cancel" });
|
|
1006
1006
|
if (cancelQueuedMessage(sessionId)) {
|
|
1007
1007
|
console.log(`[${ts()}] [CANCEL] Queue cancelled for session=${sessionId}`);
|
|
@@ -1014,7 +1014,7 @@ export async function handleCommand(
|
|
|
1014
1014
|
return;
|
|
1015
1015
|
}
|
|
1016
1016
|
|
|
1017
|
-
if (isCommandText && textLower === "/test") {
|
|
1017
|
+
if (isCommandText && textLower === "/test") {
|
|
1018
1018
|
logTrace(tid, "BRANCH", { cmd: "/test" });
|
|
1019
1019
|
const tableHeaders = ["名称", "版本", "状态"];
|
|
1020
1020
|
const tableRows = [
|
|
@@ -1051,7 +1051,7 @@ export async function handleCommand(
|
|
|
1051
1051
|
return;
|
|
1052
1052
|
}
|
|
1053
1053
|
|
|
1054
|
-
if (isCommandText && textLower === "/state") {
|
|
1054
|
+
if (isCommandText && textLower === "/state") {
|
|
1055
1055
|
logTrace(tid, "BRANCH", { cmd: "/state" });
|
|
1056
1056
|
const status = await getSessionStatus(chatId);
|
|
1057
1057
|
const isActive = isSessionRunning(sessionId);
|
|
@@ -1090,7 +1090,7 @@ export async function handleCommand(
|
|
|
1090
1090
|
return;
|
|
1091
1091
|
}
|
|
1092
1092
|
|
|
1093
|
-
if (isCommandText && textLower === "/sessions") {
|
|
1093
|
+
if (isCommandText && textLower === "/sessions") {
|
|
1094
1094
|
logTrace(tid, "BRANCH", { cmd: "/sessions" });
|
|
1095
1095
|
const allSessions = await getAllSessionsStatus();
|
|
1096
1096
|
const now = Date.now();
|
|
@@ -1115,7 +1115,7 @@ export async function handleCommand(
|
|
|
1115
1115
|
return;
|
|
1116
1116
|
}
|
|
1117
1117
|
|
|
1118
|
-
if (isCommandText && textLower === "/newh") {
|
|
1118
|
+
if (isCommandText && textLower === "/newh") {
|
|
1119
1119
|
logTrace(tid, "BRANCH", { cmd: "/newh" });
|
|
1120
1120
|
const adapter = getAdapterForTool(descriptionTool, sessionId);
|
|
1121
1121
|
let cwd: string;
|
|
@@ -1202,7 +1202,7 @@ export async function handleCommand(
|
|
|
1202
1202
|
return;
|
|
1203
1203
|
}
|
|
1204
1204
|
|
|
1205
|
-
if (isCommandText && textLower === "/deleteg") {
|
|
1205
|
+
if (isCommandText && textLower === "/deleteg") {
|
|
1206
1206
|
logTrace(tid, "BRANCH", { cmd: "/deleteg" });
|
|
1207
1207
|
if (chatType === "p2p") {
|
|
1208
1208
|
await platform
|
|
@@ -1240,7 +1240,7 @@ export async function handleCommand(
|
|
|
1240
1240
|
}
|
|
1241
1241
|
|
|
1242
1242
|
// /session <number>:切换到 /sessions 列表中的指定会话
|
|
1243
|
-
const sessionMatch = isCommandText ? textLower.match(/^\/session\s+(\d+)$/) : null;
|
|
1243
|
+
const sessionMatch = isCommandText ? textLower.match(/^\/session\s+(\d+)$/) : null;
|
|
1244
1244
|
if (sessionMatch) {
|
|
1245
1245
|
const index = parseInt(sessionMatch[1], 10) - 1;
|
|
1246
1246
|
logTrace(tid, "BRANCH", { cmd: "/session", index: index + 1 });
|
|
@@ -1366,7 +1366,7 @@ export async function handleCommand(
|
|
|
1366
1366
|
}
|
|
1367
1367
|
|
|
1368
1368
|
// /model clear — 清除当前 session 的模型覆盖
|
|
1369
|
-
if (isCommandText && textLower === "/model clear") {
|
|
1369
|
+
if (isCommandText && textLower === "/model clear") {
|
|
1370
1370
|
logTrace(tid, "BRANCH", { cmd: "/model clear", sessionId });
|
|
1371
1371
|
clearSessionModelOverride(sessionId);
|
|
1372
1372
|
const defaultModel = getEffectiveModelForTool(descriptionTool);
|
|
@@ -1381,7 +1381,7 @@ export async function handleCommand(
|
|
|
1381
1381
|
}
|
|
1382
1382
|
|
|
1383
1383
|
// /model <name> — 切换当前 session 的模型(支持所有 agent,模糊匹配)
|
|
1384
|
-
if (isCommandText && textLower.startsWith("/model ")) {
|
|
1384
|
+
if (isCommandText && textLower.startsWith("/model ")) {
|
|
1385
1385
|
const modelArg = text.slice(7).trim();
|
|
1386
1386
|
if (!modelArg) return; // 纯 "/model " 不处理,交给上面的 /model 分支
|
|
1387
1387
|
logTrace(tid, "BRANCH", { cmd: "/model", arg: modelArg, sessionId, tool: descriptionTool });
|
|
@@ -1415,7 +1415,7 @@ export async function handleCommand(
|
|
|
1415
1415
|
}
|
|
1416
1416
|
|
|
1417
1417
|
// /model — 查看当前会话的可用模型(根据会话 Agent 类型)
|
|
1418
|
-
if (isCommandText && textLower === "/model") {
|
|
1418
|
+
if (isCommandText && textLower === "/model") {
|
|
1419
1419
|
logTrace(tid, "BRANCH", { cmd: "/model", sessionId, tool: descriptionTool });
|
|
1420
1420
|
const models = getAllModelsForTool(descriptionTool as AgentTool);
|
|
1421
1421
|
const currentModel = getEffectiveModelForTool(descriptionTool, sessionId);
|
|
@@ -1439,7 +1439,7 @@ export async function handleCommand(
|
|
|
1439
1439
|
}
|
|
1440
1440
|
|
|
1441
1441
|
// /git <args>:在「当前会话工作目录」执行 git 命令
|
|
1442
|
-
if (isCommandText && (textLower.startsWith("/git ") || textLower === "/git")) {
|
|
1442
|
+
if (isCommandText && (textLower.startsWith("/git ") || textLower === "/git")) {
|
|
1443
1443
|
const args = text === "/git" ? "" : text.slice(5).trim();
|
|
1444
1444
|
logTrace(tid, "BRANCH", { cmd: "/git", args: args || "(none)" });
|
|
1445
1445
|
if (!args) {
|
|
@@ -1508,9 +1508,9 @@ export async function handleCommand(
|
|
|
1508
1508
|
|
|
1509
1509
|
// 并发检查:同一 session 只能有一个活跃 prompt,多余消息进入队列
|
|
1510
1510
|
if (isSessionRunning(sessionId)) {
|
|
1511
|
-
const queued = enqueueMessage(sessionId, {
|
|
1512
|
-
text: promptText, chatId, openId, msgTimestamp, chatType, traceId: tid,
|
|
1513
|
-
});
|
|
1511
|
+
const queued = enqueueMessage(sessionId, {
|
|
1512
|
+
text: promptText, chatId, openId, msgTimestamp, chatType, traceId: tid,
|
|
1513
|
+
});
|
|
1514
1514
|
if (queued) {
|
|
1515
1515
|
logTrace(tid, "QUEUED", { sessionId });
|
|
1516
1516
|
console.log(
|
|
@@ -1535,16 +1535,16 @@ export async function handleCommand(
|
|
|
1535
1535
|
return;
|
|
1536
1536
|
}
|
|
1537
1537
|
|
|
1538
|
-
if (shouldSendWechatProcessingAck(platform, isCommandText, chatType)) {
|
|
1538
|
+
if (shouldSendWechatProcessingAck(platform, isCommandText, chatType)) {
|
|
1539
1539
|
await platform.sendText(chatId, "生成中...").catch(() => {});
|
|
1540
1540
|
}
|
|
1541
1541
|
|
|
1542
1542
|
try {
|
|
1543
1543
|
logTrace(tid, "RESUME", { sessionId, tool: descriptionTool });
|
|
1544
1544
|
await resumeAndPrompt(
|
|
1545
|
-
sessionId,
|
|
1546
|
-
promptText,
|
|
1547
|
-
platform,
|
|
1545
|
+
sessionId,
|
|
1546
|
+
promptText,
|
|
1547
|
+
platform,
|
|
1548
1548
|
chatId,
|
|
1549
1549
|
msgTimestamp,
|
|
1550
1550
|
descriptionTool,
|
|
@@ -1570,7 +1570,7 @@ export async function handleCommand(
|
|
|
1570
1570
|
}
|
|
1571
1571
|
|
|
1572
1572
|
// 无会话上下文 → 检查是否是 /model 查询
|
|
1573
|
-
if (isCommandText && textLower === "/model") {
|
|
1573
|
+
if (isCommandText && textLower === "/model") {
|
|
1574
1574
|
const defaultTool = resolveDefaultAgentTool();
|
|
1575
1575
|
const models = getAllModelsForTool(defaultTool);
|
|
1576
1576
|
let currentModel = "";
|
|
@@ -1597,7 +1597,7 @@ export async function handleCommand(
|
|
|
1597
1597
|
}
|
|
1598
1598
|
|
|
1599
1599
|
// 无会话上下文 → /sessions 仍是有效指令,不触发飞书私聊自动建群。
|
|
1600
|
-
if (isCommandText && textLower === "/sessions") {
|
|
1600
|
+
if (isCommandText && textLower === "/sessions") {
|
|
1601
1601
|
logTrace(tid, "BRANCH", { cmd: "/sessions", scope: "global" });
|
|
1602
1602
|
const allSessions = await getAllSessionsStatus();
|
|
1603
1603
|
const now = Date.now();
|
|
@@ -1623,7 +1623,7 @@ export async function handleCommand(
|
|
|
1623
1623
|
}
|
|
1624
1624
|
|
|
1625
1625
|
// 飞书私聊普通消息:不再绑定私聊本身,而是自动创建会话群并把私聊内容作为首轮 prompt。
|
|
1626
|
-
if (isNonWechatP2p(platform, chatType) && !isCommandText) {
|
|
1626
|
+
if (isNonWechatP2p(platform, chatType) && !isCommandText) {
|
|
1627
1627
|
const tool = resolveDefaultAgentTool();
|
|
1628
1628
|
const toolLabel = toolDisplayName(tool);
|
|
1629
1629
|
logTrace(tid, "BRANCH", { cmd: "auto_new_from_p2p", tool });
|
|
@@ -1741,9 +1741,9 @@ export async function handleCommand(
|
|
|
1741
1741
|
try {
|
|
1742
1742
|
logTrace(tid, "RESUME", { sessionId, tool, trigger: "auto_new_from_p2p" });
|
|
1743
1743
|
await resumeAndPrompt(
|
|
1744
|
-
sessionId,
|
|
1745
|
-
promptText,
|
|
1746
|
-
platform,
|
|
1744
|
+
sessionId,
|
|
1745
|
+
promptText,
|
|
1746
|
+
platform,
|
|
1747
1747
|
newChatId,
|
|
1748
1748
|
msgTimestamp,
|
|
1749
1749
|
tool,
|
package/src/shared-prefix.ts
CHANGED
|
@@ -1,29 +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
|
-
}
|
|
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,25 +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
|
-
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) {
|
|
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) {
|
|
145
145
|
return join(SIM_DIR, "images", fileKey);
|
|
146
146
|
},
|
|
147
147
|
|
|
@@ -174,4 +174,4 @@ export const SimulatedPlatform: FeishuPlatform = {
|
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
/** 模拟模式下的默认 chat_id(重新导出以保持向后兼容) */
|
|
177
|
-
export { SIM_DEFAULT_CHAT_ID } from "./sim-store.ts";
|
|
177
|
+
export { SIM_DEFAULT_CHAT_ID } from "./sim-store.ts";
|