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.
@@ -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
- getOrDownloadImage: typeof realApi.getOrDownloadImage;
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 getOrDownloadImage(...args: Parameters<typeof realApi.getOrDownloadImage>): ReturnType<typeof realApi.getOrDownloadImage> {
121
- return _impl.getOrDownloadImage(...args);
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}"`);
@@ -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 content = formatCodexUsageSummary(await getCodexUsageSummary());
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
- textLower: string,
314
+ isCommandText: boolean,
277
315
  chatType: string,
278
- text: string,
279
316
  ): boolean {
280
- return platform.kind === "wechat" && chatType === "p2p" && !textLower.startsWith("/");
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 = textLower.startsWith("/") ? "command" : "prompt";
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
- !textLower.startsWith("/")
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
- !textLower.startsWith("/")
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, textLower, chatType, text)) {
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
- text,
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) && !textLower.startsWith("/")) {
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
- text,
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
+ }
@@ -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
- 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) {
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";