chatccc 0.2.200 → 0.2.202
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 +17 -15
- package/agent-prompts/claude_specific.md +45 -45
- package/agent-prompts/codex_specific.md +2 -2
- package/agent-prompts/cursor_specific.md +13 -13
- package/bin/cccagent.mjs +17 -17
- package/config.sample.json +27 -27
- package/im-skills/feishu-skill/receive-send-file.md +63 -63
- package/im-skills/feishu-skill/receive-send-image.md +24 -24
- package/im-skills/feishu-skill/skill.md +3 -3
- package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
- package/im-skills/wechat-file-skill/send-file.mjs +83 -83
- package/im-skills/wechat-file-skill/skill.md +10 -10
- package/im-skills/wechat-image-skill/skill.md +10 -10
- package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
- package/im-skills/wechat-video-skill/send-video.mjs +79 -79
- package/im-skills/wechat-video-skill/skill.md +10 -10
- package/package.json +1 -1
- package/scripts/postinstall-sharp-check.mjs +58 -58
- package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
- package/src/__tests__/agent-reload-config-rpc.test.ts +99 -99
- package/src/__tests__/builtin-chat-session.test.ts +277 -277
- package/src/__tests__/builtin-cli-json.test.ts +39 -39
- package/src/__tests__/builtin-config.test.ts +33 -33
- package/src/__tests__/builtin-context.test.ts +163 -163
- package/src/__tests__/builtin-file-tools.test.ts +224 -224
- package/src/__tests__/builtin-session-select.test.ts +116 -116
- package/src/__tests__/builtin-sigint.test.ts +56 -56
- package/src/__tests__/card-plain-text.test.ts +5 -5
- package/src/__tests__/cardkit.test.ts +60 -60
- package/src/__tests__/cards.test.ts +133 -122
- package/src/__tests__/ccc-adapter.test.ts +114 -114
- package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
- package/src/__tests__/chatgpt-subscription.test.ts +135 -135
- package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
- package/src/__tests__/claude-adapter.test.ts +592 -592
- package/src/__tests__/claude-raw-stream-log.test.ts +87 -87
- package/src/__tests__/codex-raw-stream-log.test.ts +163 -163
- package/src/__tests__/codex-reset-actions.test.ts +146 -146
- package/src/__tests__/config-reload.test.ts +10 -10
- package/src/__tests__/config-sample.test.ts +18 -18
- package/src/__tests__/cursor-adapter.test.ts +226 -226
- package/src/__tests__/feishu-api.test.ts +60 -60
- package/src/__tests__/feishu-avatar.test.ts +90 -90
- package/src/__tests__/feishu-platform.test.ts +22 -22
- package/src/__tests__/format-message.test.ts +47 -47
- package/src/__tests__/jsonl-stream.test.ts +79 -79
- package/src/__tests__/orchestrator.test.ts +464 -301
- package/src/__tests__/privacy.test.ts +198 -198
- package/src/__tests__/raw-stream-log.test.ts +106 -106
- package/src/__tests__/session.test.ts +46 -44
- package/src/__tests__/shared-prefix.test.ts +36 -36
- package/src/__tests__/sim-platform.test.ts +12 -12
- package/src/__tests__/stream-state.test.ts +42 -42
- package/src/__tests__/web-ui.test.ts +209 -209
- package/src/adapters/ccc-adapter.ts +121 -121
- package/src/adapters/claude-adapter.ts +603 -603
- package/src/adapters/claude-session-meta-store.ts +120 -120
- package/src/adapters/codex-adapter.ts +380 -380
- package/src/adapters/cursor-adapter.ts +673 -673
- package/src/adapters/jsonl-stream.ts +157 -157
- package/src/adapters/raw-stream-log.ts +124 -124
- package/src/adapters/resource-monitor.ts +140 -140
- package/src/agent-delegate-task-rpc.ts +153 -153
- package/src/agent-delegate-task.ts +81 -81
- package/src/agent-reload-config-rpc.ts +34 -34
- package/src/agent-stop-stuck.ts +129 -129
- package/src/builtin/cli.ts +473 -473
- package/src/builtin/context.ts +323 -323
- package/src/builtin/file-tools.ts +1072 -1072
- package/src/builtin/index.ts +404 -404
- package/src/builtin/session-select.ts +48 -48
- package/src/builtin/sigint.ts +50 -50
- package/src/cards.ts +222 -210
- package/src/chatgpt-subscription-rpc.ts +27 -27
- package/src/chatgpt-subscription.ts +299 -299
- package/src/chrome-devtools-guard.ts +318 -318
- package/src/codex-reset-actions.ts +184 -184
- package/src/config.ts +125 -125
- package/src/feishu-api.ts +118 -118
- package/src/feishu-platform.ts +20 -20
- package/src/format-message.ts +293 -293
- package/src/litellm-proxy.ts +374 -374
- package/src/orchestrator.ts +348 -329
- package/src/privacy.ts +118 -118
- package/src/runtime-reload.ts +34 -34
- package/src/session-chat-binding.ts +6 -6
- package/src/session-name.ts +8 -8
- package/src/session.ts +177 -169
- package/src/shared-prefix.ts +29 -29
- package/src/sim-platform.ts +20 -20
- package/src/turn-cards.ts +117 -117
- package/src/web-ui.ts +205 -205
package/src/cards.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Button helpers
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
import { ABD_HELP_LINE } from "./shared-prefix.ts";
|
|
6
|
-
|
|
7
|
-
export interface ButtonDef {
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Button helpers
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import { ABD_HELP_LINE } from "./shared-prefix.ts";
|
|
6
|
+
|
|
7
|
+
export interface ButtonDef {
|
|
8
8
|
text: string;
|
|
9
9
|
value: string;
|
|
10
10
|
type?: "primary" | "default" | "danger";
|
|
@@ -137,11 +137,11 @@ export function buildHelpCard(
|
|
|
137
137
|
"发送 **/newh** 重置当前会话(沿用当前工作目录,不切换)",
|
|
138
138
|
"发送 **/plan** 以规划模式提问(只读,不执行写操作)",
|
|
139
139
|
"发送 **/ask** 以问答模式提问(只读,不执行写操作)",
|
|
140
|
-
"发送 **/usage** 查看 Codex 实际存在的 5h/7天用量窗口,以及查询/使用主动重置卡",
|
|
141
|
-
"发送 **/restart** 重启 ChatCCC 进程",
|
|
142
|
-
"发送 **/update** 更新并重启(仅 npm 全局安装可用)",
|
|
143
|
-
ABD_HELP_LINE,
|
|
144
|
-
].join("\n");
|
|
140
|
+
"发送 **/usage** 查看 Codex 实际存在的 5h/7天用量窗口,以及查询/使用主动重置卡",
|
|
141
|
+
"发送 **/restart** 重启 ChatCCC 进程",
|
|
142
|
+
"发送 **/update** 更新并重启(仅 npm 全局安装可用)",
|
|
143
|
+
ABD_HELP_LINE,
|
|
144
|
+
].join("\n");
|
|
145
145
|
return JSON.stringify({
|
|
146
146
|
config: { wide_screen_mode: true },
|
|
147
147
|
header: { template: "blue", title: { content: "ChatCCC", tag: "plain_text" } },
|
|
@@ -177,9 +177,9 @@ export function buildCdContent(
|
|
|
177
177
|
? `**当前会话工作路径:** \`${currentCwd}\``
|
|
178
178
|
: "";
|
|
179
179
|
|
|
180
|
-
const statusLine = isUpdate
|
|
181
|
-
?
|
|
182
|
-
:
|
|
180
|
+
const statusLine = isUpdate
|
|
181
|
+
? `**后续新建会话默认工作路径(已切换):** \`${dirPath}\``
|
|
182
|
+
: `**后续新建会话默认工作路径:** \`${dirPath}\``;
|
|
183
183
|
|
|
184
184
|
const lines: string[] = [];
|
|
185
185
|
if (currentLine) lines.push(currentLine, "");
|
|
@@ -226,10 +226,10 @@ export function buildCdCard(
|
|
|
226
226
|
text: { tag: "lark_md", content: `**当前会话工作路径:** \`${sessionCwd}\`` },
|
|
227
227
|
});
|
|
228
228
|
}
|
|
229
|
-
elements.push({
|
|
230
|
-
tag: "div",
|
|
231
|
-
text: { tag: "lark_md", content:
|
|
232
|
-
});
|
|
229
|
+
elements.push({
|
|
230
|
+
tag: "div",
|
|
231
|
+
text: { tag: "lark_md", content: `**后续新建会话默认工作路径:** \`${dirPath}\`` },
|
|
232
|
+
});
|
|
233
233
|
|
|
234
234
|
if (recentDirs.length > 0) {
|
|
235
235
|
elements.push({ tag: "hr" });
|
|
@@ -274,74 +274,78 @@ export function buildCdCard(
|
|
|
274
274
|
});
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
function sessionToolLabel(tool: string): string {
|
|
278
|
-
if (tool === "cursor") return "Cursor";
|
|
279
|
-
if (tool === "codex") return "Codex";
|
|
280
|
-
if (tool === "ccc") return "CCC Agent";
|
|
281
|
-
return "Claude Code";
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function pushSessionGroup(
|
|
285
|
-
lines: string[],
|
|
286
|
-
title: string,
|
|
287
|
-
sessions: Array<{
|
|
288
|
-
sessionId: string;
|
|
289
|
-
chatName: string;
|
|
290
|
-
chatId: string;
|
|
291
|
-
active: boolean;
|
|
292
|
-
turnCount: number;
|
|
293
|
-
elapsedSeconds: number | null;
|
|
294
|
-
model: string;
|
|
295
|
-
tool: string;
|
|
296
|
-
}>,
|
|
297
|
-
formatSession: (session: {
|
|
298
|
-
sessionId: string;
|
|
299
|
-
chatName: string;
|
|
300
|
-
chatId: string;
|
|
301
|
-
active: boolean;
|
|
302
|
-
turnCount: number;
|
|
303
|
-
elapsedSeconds: number | null;
|
|
304
|
-
model: string;
|
|
305
|
-
tool: string;
|
|
306
|
-
}, index: number) => string,
|
|
307
|
-
index: { value: number },
|
|
308
|
-
): void {
|
|
309
|
-
if (sessions.length === 0) return;
|
|
310
|
-
if (index.value > 0) lines.push("", `**${title}:**`, "");
|
|
311
|
-
else lines.push(`**${title}:**`, "");
|
|
312
|
-
for (const session of sessions) {
|
|
313
|
-
lines.push(formatSession(session, index.value++));
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// 所有会话列表卡片(Claude Code 优先,然后 Cursor)
|
|
318
|
-
export function buildSessionsCard(sessions: Array<{
|
|
319
|
-
sessionId: string;
|
|
320
|
-
chatName: string;
|
|
321
|
-
chatId: string;
|
|
322
|
-
|
|
277
|
+
function sessionToolLabel(tool: string): string {
|
|
278
|
+
if (tool === "cursor") return "Cursor";
|
|
279
|
+
if (tool === "codex") return "Codex";
|
|
280
|
+
if (tool === "ccc") return "CCC Agent";
|
|
281
|
+
return "Claude Code";
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function pushSessionGroup(
|
|
285
|
+
lines: string[],
|
|
286
|
+
title: string,
|
|
287
|
+
sessions: Array<{
|
|
288
|
+
sessionId: string;
|
|
289
|
+
chatName: string;
|
|
290
|
+
chatId: string;
|
|
291
|
+
active: boolean;
|
|
292
|
+
turnCount: number;
|
|
293
|
+
elapsedSeconds: number | null;
|
|
294
|
+
model: string;
|
|
295
|
+
tool: string;
|
|
296
|
+
}>,
|
|
297
|
+
formatSession: (session: {
|
|
298
|
+
sessionId: string;
|
|
299
|
+
chatName: string;
|
|
300
|
+
chatId: string;
|
|
301
|
+
active: boolean;
|
|
302
|
+
turnCount: number;
|
|
303
|
+
elapsedSeconds: number | null;
|
|
304
|
+
model: string;
|
|
305
|
+
tool: string;
|
|
306
|
+
}, index: number) => string,
|
|
307
|
+
index: { value: number },
|
|
308
|
+
): void {
|
|
309
|
+
if (sessions.length === 0) return;
|
|
310
|
+
if (index.value > 0) lines.push("", `**${title}:**`, "");
|
|
311
|
+
else lines.push(`**${title}:**`, "");
|
|
312
|
+
for (const session of sessions) {
|
|
313
|
+
lines.push(formatSession(session, index.value++));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 所有会话列表卡片(Claude Code 优先,然后 Cursor)
|
|
318
|
+
export function buildSessionsCard(sessions: Array<{
|
|
319
|
+
sessionId: string;
|
|
320
|
+
chatName: string;
|
|
321
|
+
chatId: string;
|
|
322
|
+
chatType?: string;
|
|
323
|
+
active: boolean;
|
|
323
324
|
turnCount: number;
|
|
324
325
|
elapsedSeconds: number | null;
|
|
325
326
|
model: string;
|
|
326
327
|
tool: string;
|
|
327
|
-
}>, opts: { defaultToolLabel?: string } = {}): string {
|
|
328
|
-
const defaultToolLabel = opts.defaultToolLabel ?? "Claude Code";
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
const
|
|
328
|
+
}>, opts: { defaultToolLabel?: string; fixedPrivateSession?: boolean } = {}): string {
|
|
329
|
+
const defaultToolLabel = opts.defaultToolLabel ?? "Claude Code";
|
|
330
|
+
const fixedPrivateSession = opts.fixedPrivateSession ?? false;
|
|
331
|
+
// 按 tool 分组排序:Claude Code 在前,Cursor 其次,Codex 最后
|
|
332
|
+
const claudeCodeSessions = sessions.filter(s => s.tool !== "cursor" && s.tool !== "codex" && s.tool !== "ccc");
|
|
333
|
+
const cursorSessions = sessions.filter(s => s.tool === "cursor");
|
|
334
|
+
const codexSessions = sessions.filter(s => s.tool === "codex");
|
|
335
|
+
const cccSessions = sessions.filter(s => s.tool === "ccc");
|
|
336
|
+
const hasClaudeCode = claudeCodeSessions.length > 0;
|
|
337
|
+
const hasCursor = cursorSessions.length > 0;
|
|
338
|
+
const hasCodex = codexSessions.length > 0;
|
|
339
|
+
const hasCcc = cccSessions.length > 0;
|
|
338
340
|
|
|
339
341
|
if (sessions.length === 0) {
|
|
340
342
|
return JSON.stringify({
|
|
341
343
|
config: { wide_screen_mode: true },
|
|
342
344
|
header: { template: "blue", title: { content: "所有会话", tag: "plain_text" } },
|
|
343
345
|
elements: [
|
|
344
|
-
{ tag: "div", text: { tag: "lark_md", content:
|
|
346
|
+
{ tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
|
|
347
|
+
? `当前没有会话记录。\n\n直接发送普通消息即可创建飞书私聊专属 ${defaultToolLabel} 会话;发送 **/new**、**/new claude**、**/new cursor** 或 **/new codex** 会另外创建会话群。`
|
|
348
|
+
: `当前没有会话记录。\n\n使用 **/new**(默认 ${defaultToolLabel})、**/new claude**、**/new cursor** 或 **/new codex** 创建新会话。\n创建后可在任意会话群内发送 **/sessions** 查看列表,用 **/session 数字** 切换会话。` } },
|
|
345
349
|
{ tag: "hr" },
|
|
346
350
|
{ tag: "action", actions: [{ tag: "button", text: { tag: "plain_text", content: "收起" }, type: "default", value: { action: "close" } }] },
|
|
347
351
|
],
|
|
@@ -357,18 +361,24 @@ export function buildSessionsCard(sessions: Array<{
|
|
|
357
361
|
const secs = s.elapsedSeconds % 60;
|
|
358
362
|
extra = ` | 本轮: ${mins}分${secs}秒`;
|
|
359
363
|
}
|
|
360
|
-
const toolLabel = sessionToolLabel(s.tool);
|
|
364
|
+
const toolLabel = sessionToolLabel(s.tool);
|
|
361
365
|
const namePart = s.chatName ? `**${s.chatName}** ` : "";
|
|
362
|
-
const chatTag = !s.chatId
|
|
366
|
+
const chatTag = !s.chatId
|
|
367
|
+
? " (chat id缺失)"
|
|
368
|
+
: s.chatType === "p2p"
|
|
369
|
+
? " (私聊)"
|
|
370
|
+
: s.chatType === "group" || (s.chatType == null && s.chatId.startsWith("oc_"))
|
|
371
|
+
? " (群聊)"
|
|
372
|
+
: "";
|
|
363
373
|
return `**${i + 1}.** ${namePart}${chatTag} \`${shortId}\` ${status} | 工具: ${toolLabel} | 轮数: ${s.turnCount} | ${s.model}${extra}`;
|
|
364
374
|
};
|
|
365
375
|
|
|
366
376
|
const lines: string[] = [`共 **${sessions.length}** 个会话:`, ""];
|
|
367
|
-
const idx = { value: 0 };
|
|
368
|
-
if (hasClaudeCode) pushSessionGroup(lines, "Claude Code 会话", claudeCodeSessions, formatSession, idx);
|
|
369
|
-
if (hasCursor) pushSessionGroup(lines, "Cursor 会话", cursorSessions, formatSession, idx);
|
|
370
|
-
if (hasCodex) pushSessionGroup(lines, "Codex 会话", codexSessions, formatSession, idx);
|
|
371
|
-
if (hasCcc) pushSessionGroup(lines, "CCC Agent 会话", cccSessions, formatSession, idx);
|
|
377
|
+
const idx = { value: 0 };
|
|
378
|
+
if (hasClaudeCode) pushSessionGroup(lines, "Claude Code 会话", claudeCodeSessions, formatSession, idx);
|
|
379
|
+
if (hasCursor) pushSessionGroup(lines, "Cursor 会话", cursorSessions, formatSession, idx);
|
|
380
|
+
if (hasCodex) pushSessionGroup(lines, "Codex 会话", codexSessions, formatSession, idx);
|
|
381
|
+
if (hasCcc) pushSessionGroup(lines, "CCC Agent 会话", cccSessions, formatSession, idx);
|
|
372
382
|
|
|
373
383
|
return JSON.stringify({
|
|
374
384
|
config: { wide_screen_mode: true },
|
|
@@ -376,7 +386,9 @@ export function buildSessionsCard(sessions: Array<{
|
|
|
376
386
|
elements: [
|
|
377
387
|
{ tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
|
|
378
388
|
{ tag: "hr" },
|
|
379
|
-
{ tag: "div", text: { tag: "lark_md", content:
|
|
389
|
+
{ tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
|
|
390
|
+
? "当前飞书私聊使用固定的专属会话;发送 **/newh** 可在私聊中原地重置。群聊会话请回到对应群聊继续,私聊不支持 **/session** 切换。"
|
|
391
|
+
: "在会话群内发送 **/newh** 可重置当前会话(创建新 Session,保留工作目录和群聊)。\n发送 **/session 数字**(如 `/session 1`)可将当前群聊切换到列表中对应编号的会话。" } },
|
|
380
392
|
{ tag: "hr" },
|
|
381
393
|
{
|
|
382
394
|
tag: "action",
|
|
@@ -433,11 +445,11 @@ export function buildQueueFullCard(): string {
|
|
|
433
445
|
}
|
|
434
446
|
|
|
435
447
|
// 状态卡片(带关闭按钮)
|
|
436
|
-
export function buildStatusCard(statusText: string, template = "blue"): string {
|
|
437
|
-
return JSON.stringify({
|
|
438
|
-
config: { wide_screen_mode: true },
|
|
439
|
-
header: { template, title: { content: "会话状态", tag: "plain_text" } },
|
|
440
|
-
elements: [
|
|
448
|
+
export function buildStatusCard(statusText: string, template = "blue"): string {
|
|
449
|
+
return JSON.stringify({
|
|
450
|
+
config: { wide_screen_mode: true },
|
|
451
|
+
header: { template, title: { content: "会话状态", tag: "plain_text" } },
|
|
452
|
+
elements: [
|
|
441
453
|
{ tag: "div", text: { tag: "lark_md", content: statusText } },
|
|
442
454
|
{ tag: "hr" },
|
|
443
455
|
{
|
|
@@ -449,82 +461,82 @@ export function buildStatusCard(statusText: string, template = "blue"): string {
|
|
|
449
461
|
value: { action: "close" },
|
|
450
462
|
}],
|
|
451
463
|
},
|
|
452
|
-
],
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
export function buildCodexUsageCard(content: string, resetCreditsAvailable: number | null): string {
|
|
457
|
-
const elements: object[] = [
|
|
458
|
-
{ tag: "div", text: { tag: "lark_md", content } },
|
|
459
|
-
];
|
|
460
|
-
if (resetCreditsAvailable !== null && resetCreditsAvailable > 0) {
|
|
461
|
-
elements.push({ tag: "hr" });
|
|
462
|
-
elements.push({
|
|
463
|
-
tag: "action",
|
|
464
|
-
actions: [{
|
|
465
|
-
tag: "button",
|
|
466
|
-
text: { tag: "plain_text", content: "发起重置" },
|
|
467
|
-
type: "primary",
|
|
468
|
-
value: { action: "codex_reset_request", availableCount: resetCreditsAvailable },
|
|
469
|
-
}],
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return JSON.stringify({
|
|
474
|
-
config: { wide_screen_mode: true },
|
|
475
|
-
header: { template: "blue", title: { content: "Codex Usage", tag: "plain_text" } },
|
|
476
|
-
elements,
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
export function buildCodexResetConfirmCard(params: {
|
|
481
|
-
availableCount: number;
|
|
482
|
-
parentMessageId: string;
|
|
483
|
-
requestId: string;
|
|
484
|
-
}): string {
|
|
485
|
-
const valueBase = {
|
|
486
|
-
parentMessageId: params.parentMessageId,
|
|
487
|
-
requestId: params.requestId,
|
|
488
|
-
};
|
|
489
|
-
return JSON.stringify({
|
|
490
|
-
config: { wide_screen_mode: true },
|
|
491
|
-
header: { template: "yellow", title: { content: "确认 Codex 主动重置", tag: "plain_text" } },
|
|
492
|
-
elements: [
|
|
493
|
-
{
|
|
494
|
-
tag: "div",
|
|
495
|
-
text: {
|
|
496
|
-
tag: "lark_md",
|
|
497
|
-
content: `当前可用主动重置次数:**${params.availableCount}**。\n\n确认后会消耗 1 次主动重置,并重置当前可重置的 Codex 用量窗口。`,
|
|
498
|
-
},
|
|
499
|
-
},
|
|
500
|
-
{ tag: "hr" },
|
|
501
|
-
{
|
|
502
|
-
tag: "action",
|
|
503
|
-
actions: [
|
|
504
|
-
{
|
|
505
|
-
tag: "button",
|
|
506
|
-
text: { tag: "plain_text", content: "是,发起重置" },
|
|
507
|
-
type: "danger",
|
|
508
|
-
value: { action: "codex_reset_confirm", decision: "yes", ...valueBase },
|
|
509
|
-
},
|
|
510
|
-
{
|
|
511
|
-
tag: "button",
|
|
512
|
-
text: { tag: "plain_text", content: "否" },
|
|
513
|
-
type: "default",
|
|
514
|
-
value: { action: "codex_reset_confirm", decision: "no", ...valueBase },
|
|
515
|
-
},
|
|
516
|
-
],
|
|
517
|
-
},
|
|
518
|
-
],
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/** 模型切换卡片(/model 命令,飞书专用,含切换按钮) */
|
|
523
|
-
export function buildModelCard(
|
|
524
|
-
currentModel: string,
|
|
525
|
-
models: string[],
|
|
526
|
-
tool?: string,
|
|
527
|
-
): string {
|
|
464
|
+
],
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export function buildCodexUsageCard(content: string, resetCreditsAvailable: number | null): string {
|
|
469
|
+
const elements: object[] = [
|
|
470
|
+
{ tag: "div", text: { tag: "lark_md", content } },
|
|
471
|
+
];
|
|
472
|
+
if (resetCreditsAvailable !== null && resetCreditsAvailable > 0) {
|
|
473
|
+
elements.push({ tag: "hr" });
|
|
474
|
+
elements.push({
|
|
475
|
+
tag: "action",
|
|
476
|
+
actions: [{
|
|
477
|
+
tag: "button",
|
|
478
|
+
text: { tag: "plain_text", content: "发起重置" },
|
|
479
|
+
type: "primary",
|
|
480
|
+
value: { action: "codex_reset_request", availableCount: resetCreditsAvailable },
|
|
481
|
+
}],
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return JSON.stringify({
|
|
486
|
+
config: { wide_screen_mode: true },
|
|
487
|
+
header: { template: "blue", title: { content: "Codex Usage", tag: "plain_text" } },
|
|
488
|
+
elements,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export function buildCodexResetConfirmCard(params: {
|
|
493
|
+
availableCount: number;
|
|
494
|
+
parentMessageId: string;
|
|
495
|
+
requestId: string;
|
|
496
|
+
}): string {
|
|
497
|
+
const valueBase = {
|
|
498
|
+
parentMessageId: params.parentMessageId,
|
|
499
|
+
requestId: params.requestId,
|
|
500
|
+
};
|
|
501
|
+
return JSON.stringify({
|
|
502
|
+
config: { wide_screen_mode: true },
|
|
503
|
+
header: { template: "yellow", title: { content: "确认 Codex 主动重置", tag: "plain_text" } },
|
|
504
|
+
elements: [
|
|
505
|
+
{
|
|
506
|
+
tag: "div",
|
|
507
|
+
text: {
|
|
508
|
+
tag: "lark_md",
|
|
509
|
+
content: `当前可用主动重置次数:**${params.availableCount}**。\n\n确认后会消耗 1 次主动重置,并重置当前可重置的 Codex 用量窗口。`,
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
{ tag: "hr" },
|
|
513
|
+
{
|
|
514
|
+
tag: "action",
|
|
515
|
+
actions: [
|
|
516
|
+
{
|
|
517
|
+
tag: "button",
|
|
518
|
+
text: { tag: "plain_text", content: "是,发起重置" },
|
|
519
|
+
type: "danger",
|
|
520
|
+
value: { action: "codex_reset_confirm", decision: "yes", ...valueBase },
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
tag: "button",
|
|
524
|
+
text: { tag: "plain_text", content: "否" },
|
|
525
|
+
type: "default",
|
|
526
|
+
value: { action: "codex_reset_confirm", decision: "no", ...valueBase },
|
|
527
|
+
},
|
|
528
|
+
],
|
|
529
|
+
},
|
|
530
|
+
],
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** 模型切换卡片(/model 命令,飞书专用,含切换按钮) */
|
|
535
|
+
export function buildModelCard(
|
|
536
|
+
currentModel: string,
|
|
537
|
+
models: string[],
|
|
538
|
+
tool?: string,
|
|
539
|
+
): string {
|
|
528
540
|
const toolLabel = tool ? ` (${tool})` : "";
|
|
529
541
|
const currentLine = currentModel
|
|
530
542
|
? `**当前模型:** \`${currentModel}\``
|
|
@@ -558,48 +570,48 @@ export function buildModelCard(
|
|
|
558
570
|
{ tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
|
|
559
571
|
{ tag: "hr" },
|
|
560
572
|
buildButtons(buttons),
|
|
561
|
-
],
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
export function buildEffortCard(
|
|
566
|
-
currentEffort: string,
|
|
567
|
-
efforts: string[],
|
|
568
|
-
tool?: string,
|
|
569
|
-
): string {
|
|
570
|
-
const toolLabel = tool ? ` (${tool})` : "";
|
|
571
|
-
const currentLine = currentEffort
|
|
572
|
-
? `**当前 effort:** \`${currentEffort}\``
|
|
573
|
-
: "**当前 effort:** 未指定";
|
|
574
|
-
|
|
575
|
-
const lines: string[] = [currentLine];
|
|
576
|
-
if (efforts.length > 0) {
|
|
577
|
-
lines.push("", "**可切换 effort**");
|
|
578
|
-
for (const effort of efforts) {
|
|
579
|
-
lines.push(`- \`${effort}\``);
|
|
580
|
-
}
|
|
581
|
-
lines.push("", "点击按钮切换 effort,或输入 `/effort clear` 恢复默认");
|
|
582
|
-
} else {
|
|
583
|
-
lines.push("", "当前 agent 不支持 effort 切换。");
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
const buttons: ButtonDef[] = [];
|
|
587
|
-
for (const effort of efforts.slice(0, 20)) {
|
|
588
|
-
buttons.push({
|
|
589
|
-
text: `/effort ${effort}`,
|
|
590
|
-
value: JSON.stringify({ cmd: `/effort ${effort}` }),
|
|
591
|
-
type: "primary",
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
return JSON.stringify({
|
|
596
|
-
config: { wide_screen_mode: true },
|
|
597
|
-
header: { template: "blue", title: { content: `Effort 切换${toolLabel}`, tag: "plain_text" } },
|
|
598
|
-
elements: [
|
|
599
|
-
{ tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
|
|
600
|
-
{ tag: "hr" },
|
|
601
|
-
buildButtons(buttons),
|
|
602
|
-
],
|
|
603
|
-
});
|
|
604
|
-
}
|
|
573
|
+
],
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
export function buildEffortCard(
|
|
578
|
+
currentEffort: string,
|
|
579
|
+
efforts: string[],
|
|
580
|
+
tool?: string,
|
|
581
|
+
): string {
|
|
582
|
+
const toolLabel = tool ? ` (${tool})` : "";
|
|
583
|
+
const currentLine = currentEffort
|
|
584
|
+
? `**当前 effort:** \`${currentEffort}\``
|
|
585
|
+
: "**当前 effort:** 未指定";
|
|
586
|
+
|
|
587
|
+
const lines: string[] = [currentLine];
|
|
588
|
+
if (efforts.length > 0) {
|
|
589
|
+
lines.push("", "**可切换 effort**");
|
|
590
|
+
for (const effort of efforts) {
|
|
591
|
+
lines.push(`- \`${effort}\``);
|
|
592
|
+
}
|
|
593
|
+
lines.push("", "点击按钮切换 effort,或输入 `/effort clear` 恢复默认");
|
|
594
|
+
} else {
|
|
595
|
+
lines.push("", "当前 agent 不支持 effort 切换。");
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const buttons: ButtonDef[] = [];
|
|
599
|
+
for (const effort of efforts.slice(0, 20)) {
|
|
600
|
+
buttons.push({
|
|
601
|
+
text: `/effort ${effort}`,
|
|
602
|
+
value: JSON.stringify({ cmd: `/effort ${effort}` }),
|
|
603
|
+
type: "primary",
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return JSON.stringify({
|
|
608
|
+
config: { wide_screen_mode: true },
|
|
609
|
+
header: { template: "blue", title: { content: `Effort 切换${toolLabel}`, tag: "plain_text" } },
|
|
610
|
+
elements: [
|
|
611
|
+
{ tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
|
|
612
|
+
{ tag: "hr" },
|
|
613
|
+
buildButtons(buttons),
|
|
614
|
+
],
|
|
615
|
+
});
|
|
616
|
+
}
|
|
605
617
|
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
|
|
3
|
-
import { getChatGptSubscriptionStatus } from "./chatgpt-subscription.ts";
|
|
4
|
-
|
|
5
|
-
export const CHATGPT_SUBSCRIPTION_PATH = "/api/chatgpt/subscription";
|
|
6
|
-
|
|
7
|
-
function jsonReply(res: ServerResponse, code: number, data: unknown): void {
|
|
8
|
-
res.writeHead(code, { "Content-Type": "application/json; charset=utf-8" });
|
|
9
|
-
res.end(JSON.stringify(data));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export async function handleChatGptSubscriptionRequest(
|
|
13
|
-
req: IncomingMessage,
|
|
14
|
-
res: ServerResponse,
|
|
15
|
-
): Promise<boolean> {
|
|
16
|
-
const method = req.method ?? "GET";
|
|
17
|
-
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
18
|
-
if (url.pathname !== CHATGPT_SUBSCRIPTION_PATH) return false;
|
|
19
|
-
|
|
20
|
-
if (method !== "POST") {
|
|
21
|
-
jsonReply(res, 405, { ok: false, code: "method_not_allowed", reason: "Use POST." });
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
jsonReply(res, 200, await getChatGptSubscriptionStatus());
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
|
|
3
|
+
import { getChatGptSubscriptionStatus } from "./chatgpt-subscription.ts";
|
|
4
|
+
|
|
5
|
+
export const CHATGPT_SUBSCRIPTION_PATH = "/api/chatgpt/subscription";
|
|
6
|
+
|
|
7
|
+
function jsonReply(res: ServerResponse, code: number, data: unknown): void {
|
|
8
|
+
res.writeHead(code, { "Content-Type": "application/json; charset=utf-8" });
|
|
9
|
+
res.end(JSON.stringify(data));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function handleChatGptSubscriptionRequest(
|
|
13
|
+
req: IncomingMessage,
|
|
14
|
+
res: ServerResponse,
|
|
15
|
+
): Promise<boolean> {
|
|
16
|
+
const method = req.method ?? "GET";
|
|
17
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
18
|
+
if (url.pathname !== CHATGPT_SUBSCRIPTION_PATH) return false;
|
|
19
|
+
|
|
20
|
+
if (method !== "POST") {
|
|
21
|
+
jsonReply(res, 405, { ok: false, code: "method_not_allowed", reason: "Use POST." });
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
jsonReply(res, 200, await getChatGptSubscriptionStatus());
|
|
26
|
+
return true;
|
|
27
|
+
}
|