chatccc 0.2.198 → 0.2.199
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/agent-prompts/cursor_specific.md +13 -13
- package/bin/cccagent.mjs +17 -17
- package/config.sample.json +27 -27
- package/package.json +1 -1
- 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__/cards.test.ts +109 -109
- 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-raw-stream-log.test.ts +87 -87
- package/src/__tests__/codex-raw-stream-log.test.ts +163 -163
- 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 +66 -7
- package/src/__tests__/feishu-avatar.test.ts +40 -40
- package/src/__tests__/jsonl-stream.test.ts +79 -79
- package/src/__tests__/orchestrator.test.ts +200 -200
- package/src/__tests__/raw-stream-log.test.ts +106 -106
- package/src/__tests__/session.test.ts +40 -40
- package/src/__tests__/sim-platform.test.ts +12 -12
- 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/codex-adapter.ts +380 -380
- package/src/adapters/cursor-adapter.ts +39 -6
- package/src/adapters/jsonl-stream.ts +157 -157
- package/src/adapters/raw-stream-log.ts +124 -124
- package/src/agent-reload-config-rpc.ts +34 -34
- 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 +195 -195
- 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/config.ts +125 -125
- package/src/feishu-api.ts +49 -49
- package/src/orchestrator.ts +179 -179
- package/src/runtime-reload.ts +34 -34
- package/src/session.ts +141 -141
- package/src/web-ui.ts +205 -205
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEFAULT_BUILTIN_CONTEXT_DIR,
|
|
3
|
-
getBuiltinContextSession,
|
|
4
|
-
latestBuiltinSessionForCwd,
|
|
5
|
-
newBuiltinSessionId,
|
|
6
|
-
normalizeBuiltinSessionId,
|
|
7
|
-
} from "./context.js";
|
|
8
|
-
|
|
9
|
-
export type BuiltinResumeRequest = true | string | undefined;
|
|
10
|
-
|
|
11
|
-
export interface ResolveBuiltinSessionOptions {
|
|
12
|
-
cwd: string;
|
|
13
|
-
contextDir?: string;
|
|
14
|
-
resume?: BuiltinResumeRequest;
|
|
15
|
-
now?: Date;
|
|
16
|
-
randomSuffix?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface ResolvedBuiltinSession {
|
|
20
|
-
mode: "new" | "resumed";
|
|
21
|
-
sessionId: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function resolveBuiltinSession(options: ResolveBuiltinSessionOptions): ResolvedBuiltinSession {
|
|
25
|
-
const contextDir = options.contextDir ?? DEFAULT_BUILTIN_CONTEXT_DIR;
|
|
26
|
-
|
|
27
|
-
if (options.resume === true) {
|
|
28
|
-
const latest = latestBuiltinSessionForCwd(options.cwd, contextDir);
|
|
29
|
-
if (!latest) {
|
|
30
|
-
throw new Error(`未找到当前目录可恢复的 ccc 会话: ${options.cwd}`);
|
|
31
|
-
}
|
|
32
|
-
return { mode: "resumed", sessionId: latest.sessionId };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (typeof options.resume === "string") {
|
|
36
|
-
const sessionId = normalizeBuiltinSessionId(options.resume);
|
|
37
|
-
const existing = getBuiltinContextSession(sessionId, contextDir);
|
|
38
|
-
if (!existing) {
|
|
39
|
-
throw new Error(`未找到 ccc 会话: ${sessionId}`);
|
|
40
|
-
}
|
|
41
|
-
return { mode: "resumed", sessionId };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
mode: "new",
|
|
46
|
-
sessionId: newBuiltinSessionId(options.now, options.randomSuffix),
|
|
47
|
-
};
|
|
48
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_BUILTIN_CONTEXT_DIR,
|
|
3
|
+
getBuiltinContextSession,
|
|
4
|
+
latestBuiltinSessionForCwd,
|
|
5
|
+
newBuiltinSessionId,
|
|
6
|
+
normalizeBuiltinSessionId,
|
|
7
|
+
} from "./context.js";
|
|
8
|
+
|
|
9
|
+
export type BuiltinResumeRequest = true | string | undefined;
|
|
10
|
+
|
|
11
|
+
export interface ResolveBuiltinSessionOptions {
|
|
12
|
+
cwd: string;
|
|
13
|
+
contextDir?: string;
|
|
14
|
+
resume?: BuiltinResumeRequest;
|
|
15
|
+
now?: Date;
|
|
16
|
+
randomSuffix?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ResolvedBuiltinSession {
|
|
20
|
+
mode: "new" | "resumed";
|
|
21
|
+
sessionId: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolveBuiltinSession(options: ResolveBuiltinSessionOptions): ResolvedBuiltinSession {
|
|
25
|
+
const contextDir = options.contextDir ?? DEFAULT_BUILTIN_CONTEXT_DIR;
|
|
26
|
+
|
|
27
|
+
if (options.resume === true) {
|
|
28
|
+
const latest = latestBuiltinSessionForCwd(options.cwd, contextDir);
|
|
29
|
+
if (!latest) {
|
|
30
|
+
throw new Error(`未找到当前目录可恢复的 ccc 会话: ${options.cwd}`);
|
|
31
|
+
}
|
|
32
|
+
return { mode: "resumed", sessionId: latest.sessionId };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof options.resume === "string") {
|
|
36
|
+
const sessionId = normalizeBuiltinSessionId(options.resume);
|
|
37
|
+
const existing = getBuiltinContextSession(sessionId, contextDir);
|
|
38
|
+
if (!existing) {
|
|
39
|
+
throw new Error(`未找到 ccc 会话: ${sessionId}`);
|
|
40
|
+
}
|
|
41
|
+
return { mode: "resumed", sessionId };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
mode: "new",
|
|
46
|
+
sessionId: newBuiltinSessionId(options.now, options.randomSuffix),
|
|
47
|
+
};
|
|
48
|
+
}
|
package/src/builtin/sigint.ts
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
export const CTRL_C_CONFIRM_WINDOW_MS = 2000;
|
|
2
|
-
|
|
3
|
-
export type CtrlCAction =
|
|
4
|
-
| "arm-interrupt"
|
|
5
|
-
| "interrupt"
|
|
6
|
-
| "arm-exit"
|
|
7
|
-
| "exit";
|
|
8
|
-
|
|
9
|
-
type PendingCtrlCAction = "interrupt" | "exit";
|
|
10
|
-
|
|
11
|
-
export interface CtrlCStateOptions {
|
|
12
|
-
windowMs?: number;
|
|
13
|
-
now?: () => number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface CtrlCState {
|
|
17
|
-
press(isGenerating: boolean): CtrlCAction;
|
|
18
|
-
reset(): void;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function createCtrlCState(options: CtrlCStateOptions = {}): CtrlCState {
|
|
22
|
-
const windowMs = options.windowMs ?? CTRL_C_CONFIRM_WINDOW_MS;
|
|
23
|
-
const now = options.now ?? Date.now;
|
|
24
|
-
|
|
25
|
-
let pending: PendingCtrlCAction | null = null;
|
|
26
|
-
let lastPressAt = 0;
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
press(isGenerating: boolean): CtrlCAction {
|
|
30
|
-
const action: PendingCtrlCAction = isGenerating ? "interrupt" : "exit";
|
|
31
|
-
const current = now();
|
|
32
|
-
const isConfirmed = pending === action && current - lastPressAt <= windowMs;
|
|
33
|
-
|
|
34
|
-
if (isConfirmed) {
|
|
35
|
-
pending = null;
|
|
36
|
-
lastPressAt = 0;
|
|
37
|
-
return action;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
pending = action;
|
|
41
|
-
lastPressAt = current;
|
|
42
|
-
return isGenerating ? "arm-interrupt" : "arm-exit";
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
reset(): void {
|
|
46
|
-
pending = null;
|
|
47
|
-
lastPressAt = 0;
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
1
|
+
export const CTRL_C_CONFIRM_WINDOW_MS = 2000;
|
|
2
|
+
|
|
3
|
+
export type CtrlCAction =
|
|
4
|
+
| "arm-interrupt"
|
|
5
|
+
| "interrupt"
|
|
6
|
+
| "arm-exit"
|
|
7
|
+
| "exit";
|
|
8
|
+
|
|
9
|
+
type PendingCtrlCAction = "interrupt" | "exit";
|
|
10
|
+
|
|
11
|
+
export interface CtrlCStateOptions {
|
|
12
|
+
windowMs?: number;
|
|
13
|
+
now?: () => number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface CtrlCState {
|
|
17
|
+
press(isGenerating: boolean): CtrlCAction;
|
|
18
|
+
reset(): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createCtrlCState(options: CtrlCStateOptions = {}): CtrlCState {
|
|
22
|
+
const windowMs = options.windowMs ?? CTRL_C_CONFIRM_WINDOW_MS;
|
|
23
|
+
const now = options.now ?? Date.now;
|
|
24
|
+
|
|
25
|
+
let pending: PendingCtrlCAction | null = null;
|
|
26
|
+
let lastPressAt = 0;
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
press(isGenerating: boolean): CtrlCAction {
|
|
30
|
+
const action: PendingCtrlCAction = isGenerating ? "interrupt" : "exit";
|
|
31
|
+
const current = now();
|
|
32
|
+
const isConfirmed = pending === action && current - lastPressAt <= windowMs;
|
|
33
|
+
|
|
34
|
+
if (isConfirmed) {
|
|
35
|
+
pending = null;
|
|
36
|
+
lastPressAt = 0;
|
|
37
|
+
return action;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pending = action;
|
|
41
|
+
lastPressAt = current;
|
|
42
|
+
return isGenerating ? "arm-interrupt" : "arm-exit";
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
reset(): void {
|
|
46
|
+
pending = null;
|
|
47
|
+
lastPressAt = 0;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
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/周用量,以及查询/使用主动重置卡",
|
|
141
|
-
"发送 **/restart** 重启 ChatCCC 进程",
|
|
142
|
-
"发送 **/update** 更新并重启(仅 npm 全局安装可用)",
|
|
143
|
-
ABD_HELP_LINE,
|
|
144
|
-
].join("\n");
|
|
140
|
+
"发送 **/usage** 查看 Codex 5h/周用量,以及查询/使用主动重置卡",
|
|
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" } },
|
|
@@ -274,48 +274,48 @@ 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<{
|
|
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
319
|
sessionId: string;
|
|
320
320
|
chatName: string;
|
|
321
321
|
chatId: string;
|
|
@@ -325,16 +325,16 @@ export function buildSessionsCard(sessions: Array<{
|
|
|
325
325
|
model: string;
|
|
326
326
|
tool: string;
|
|
327
327
|
}>, opts: { defaultToolLabel?: string } = {}): string {
|
|
328
|
-
const defaultToolLabel = opts.defaultToolLabel ?? "Claude Code";
|
|
329
|
-
// 按 tool 分组排序:Claude Code 在前,Cursor 其次,Codex 最后
|
|
330
|
-
const claudeCodeSessions = sessions.filter(s => s.tool !== "cursor" && s.tool !== "codex" && s.tool !== "ccc");
|
|
331
|
-
const cursorSessions = sessions.filter(s => s.tool === "cursor");
|
|
332
|
-
const codexSessions = sessions.filter(s => s.tool === "codex");
|
|
333
|
-
const cccSessions = sessions.filter(s => s.tool === "ccc");
|
|
334
|
-
const hasClaudeCode = claudeCodeSessions.length > 0;
|
|
335
|
-
const hasCursor = cursorSessions.length > 0;
|
|
336
|
-
const hasCodex = codexSessions.length > 0;
|
|
337
|
-
const hasCcc = cccSessions.length > 0;
|
|
328
|
+
const defaultToolLabel = opts.defaultToolLabel ?? "Claude Code";
|
|
329
|
+
// 按 tool 分组排序:Claude Code 在前,Cursor 其次,Codex 最后
|
|
330
|
+
const claudeCodeSessions = sessions.filter(s => s.tool !== "cursor" && s.tool !== "codex" && s.tool !== "ccc");
|
|
331
|
+
const cursorSessions = sessions.filter(s => s.tool === "cursor");
|
|
332
|
+
const codexSessions = sessions.filter(s => s.tool === "codex");
|
|
333
|
+
const cccSessions = sessions.filter(s => s.tool === "ccc");
|
|
334
|
+
const hasClaudeCode = claudeCodeSessions.length > 0;
|
|
335
|
+
const hasCursor = cursorSessions.length > 0;
|
|
336
|
+
const hasCodex = codexSessions.length > 0;
|
|
337
|
+
const hasCcc = cccSessions.length > 0;
|
|
338
338
|
|
|
339
339
|
if (sessions.length === 0) {
|
|
340
340
|
return JSON.stringify({
|
|
@@ -357,18 +357,18 @@ export function buildSessionsCard(sessions: Array<{
|
|
|
357
357
|
const secs = s.elapsedSeconds % 60;
|
|
358
358
|
extra = ` | 本轮: ${mins}分${secs}秒`;
|
|
359
359
|
}
|
|
360
|
-
const toolLabel = sessionToolLabel(s.tool);
|
|
360
|
+
const toolLabel = sessionToolLabel(s.tool);
|
|
361
361
|
const namePart = s.chatName ? `**${s.chatName}** ` : "";
|
|
362
362
|
const chatTag = !s.chatId ? " (chat id缺失)" : s.chatId.startsWith("oc_") ? " (群聊)" : "";
|
|
363
363
|
return `**${i + 1}.** ${namePart}${chatTag} \`${shortId}\` ${status} | 工具: ${toolLabel} | 轮数: ${s.turnCount} | ${s.model}${extra}`;
|
|
364
364
|
};
|
|
365
365
|
|
|
366
366
|
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);
|
|
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);
|
|
372
372
|
|
|
373
373
|
return JSON.stringify({
|
|
374
374
|
config: { wide_screen_mode: true },
|
|
@@ -433,11 +433,11 @@ export function buildQueueFullCard(): string {
|
|
|
433
433
|
}
|
|
434
434
|
|
|
435
435
|
// 状态卡片(带关闭按钮)
|
|
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: [
|
|
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: [
|
|
441
441
|
{ tag: "div", text: { tag: "lark_md", content: statusText } },
|
|
442
442
|
{ tag: "hr" },
|
|
443
443
|
{
|
|
@@ -449,82 +449,82 @@ export function buildStatusCard(statusText: string, template = "blue"): string {
|
|
|
449
449
|
value: { action: "close" },
|
|
450
450
|
}],
|
|
451
451
|
},
|
|
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 {
|
|
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 {
|
|
528
528
|
const toolLabel = tool ? ` (${tool})` : "";
|
|
529
529
|
const currentLine = currentModel
|
|
530
530
|
? `**当前模型:** \`${currentModel}\``
|
|
@@ -558,48 +558,48 @@ export function buildModelCard(
|
|
|
558
558
|
{ tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
|
|
559
559
|
{ tag: "hr" },
|
|
560
560
|
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
|
-
}
|
|
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
|
+
}
|
|
605
605
|
|