chatccc 0.2.219 → 0.2.220
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 +36 -16
- package/config.sample.json +4 -1
- package/package.json +1 -1
- package/src/__tests__/builtin-config.test.ts +3 -0
- package/src/__tests__/cards.test.ts +6 -6
- package/src/__tests__/config-reload.test.ts +19 -8
- package/src/__tests__/config-sample.test.ts +6 -2
- package/src/__tests__/orchestrator.test.ts +84 -5
- package/src/__tests__/sim-platform.test.ts +10 -0
- package/src/__tests__/web-ui.test.ts +40 -0
- package/src/cards.ts +7 -5
- package/src/config.ts +41 -10
- package/src/index.ts +13 -0
- package/src/orchestrator.ts +1553 -1488
- package/src/web-ui.ts +164 -46
package/src/config.ts
CHANGED
|
@@ -104,12 +104,18 @@ export interface CodexConfig {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
export interface CccConfig {
|
|
107
|
+
/** Whether the built-in CCC Agent is available for new sessions. */
|
|
108
|
+
enabled: boolean;
|
|
109
|
+
/** Whether /new without an explicit tool should use CCC Agent. */
|
|
110
|
+
defaultAgent: boolean;
|
|
107
111
|
/** DeepSeek API Key for the ChatCCC self-developed agent. */
|
|
108
112
|
DEEPSEEK_API_KEY: string;
|
|
109
113
|
/** DeepSeek-compatible API Base URL for the ChatCCC self-developed agent. */
|
|
110
114
|
DEEPSEEK_BASE_URL: string;
|
|
111
115
|
/** Model used by the ChatCCC self-developed agent. */
|
|
112
116
|
model: string;
|
|
117
|
+
/** Optional model exposed through /model for manual per-session switching. */
|
|
118
|
+
alternativeModel: string;
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
export interface FeishuConfig {
|
|
@@ -168,8 +174,8 @@ export interface AppConfig {
|
|
|
168
174
|
ccc: CccConfig;
|
|
169
175
|
}
|
|
170
176
|
|
|
171
|
-
export type AgentTool = "claude" | "cursor" | "codex";
|
|
172
|
-
export const AGENT_TOOLS: AgentTool[] = ["claude", "cursor", "codex"];
|
|
177
|
+
export type AgentTool = "claude" | "cursor" | "codex" | "ccc";
|
|
178
|
+
export const AGENT_TOOLS: AgentTool[] = ["claude", "cursor", "codex", "ccc"];
|
|
173
179
|
export type CursorAvatarBatteryMode = "apiPercent" | "onDemandUse";
|
|
174
180
|
|
|
175
181
|
/** 获取指定 agent 配置中所有模型相关的值(最多 100 个,去重) */
|
|
@@ -190,6 +196,7 @@ export function getAllModelsForTool(tool: string, cfg: AppConfig = config): stri
|
|
|
190
196
|
collect(cfg.codex.alternativeModel);
|
|
191
197
|
} else if (tool === "ccc") {
|
|
192
198
|
collect(cfg.ccc.model);
|
|
199
|
+
collect(cfg.ccc.alternativeModel);
|
|
193
200
|
}
|
|
194
201
|
|
|
195
202
|
return Array.from(seen).slice(0, 100);
|
|
@@ -445,7 +452,14 @@ function loadConfig(): AppConfig {
|
|
|
445
452
|
onDemandMonthlyBudget: 1000,
|
|
446
453
|
},
|
|
447
454
|
codex: { enabled: false, defaultAgent: false, path: "", model: "", alternativeModel: "", effort: "", fastMode: false },
|
|
448
|
-
ccc: {
|
|
455
|
+
ccc: {
|
|
456
|
+
enabled: false,
|
|
457
|
+
defaultAgent: false,
|
|
458
|
+
DEEPSEEK_API_KEY: "",
|
|
459
|
+
DEEPSEEK_BASE_URL: DEFAULT_CCC_DEEPSEEK_BASE_URL,
|
|
460
|
+
model: DEFAULT_CCC_MODEL,
|
|
461
|
+
alternativeModel: "",
|
|
462
|
+
},
|
|
449
463
|
};
|
|
450
464
|
|
|
451
465
|
if (!IS_TEST_ENV) {
|
|
@@ -498,7 +512,14 @@ function loadConfig(): AppConfig {
|
|
|
498
512
|
onDemandMonthlyBudget?: unknown;
|
|
499
513
|
};
|
|
500
514
|
codex?: { enabled?: unknown; defaultAgent?: unknown; path?: unknown; command?: unknown; model?: unknown; alternativeModel?: unknown; effort?: unknown; fastMode?: unknown };
|
|
501
|
-
ccc?: {
|
|
515
|
+
ccc?: {
|
|
516
|
+
enabled?: unknown;
|
|
517
|
+
defaultAgent?: unknown;
|
|
518
|
+
DEEPSEEK_API_KEY?: unknown;
|
|
519
|
+
DEEPSEEK_BASE_URL?: unknown;
|
|
520
|
+
model?: unknown;
|
|
521
|
+
alternativeModel?: unknown;
|
|
522
|
+
};
|
|
502
523
|
webUi?: { openOnStart?: unknown };
|
|
503
524
|
chromeDevtools?: { enabled?: unknown; port?: unknown; chromePath?: unknown };
|
|
504
525
|
rawStreamLogs?: unknown;
|
|
@@ -553,7 +574,7 @@ function loadConfig(): AppConfig {
|
|
|
553
574
|
(typeof cursorRaw.model === "string" && (cursorRaw.model as string).trim()) ||
|
|
554
575
|
(typeof cursorRaw.alternativeModel === "string" && (cursorRaw.alternativeModel as string).trim()),
|
|
555
576
|
);
|
|
556
|
-
const codexNonEmpty = (): boolean =>
|
|
577
|
+
const codexNonEmpty = (): boolean =>
|
|
557
578
|
Boolean(
|
|
558
579
|
(typeof codexRaw.path === "string" && codexRaw.path.trim()) ||
|
|
559
580
|
(typeof codexRaw.command === "string" && (codexRaw.command as string).trim()) ||
|
|
@@ -561,21 +582,28 @@ function loadConfig(): AppConfig {
|
|
|
561
582
|
(typeof codexRaw.alternativeModel === "string" && (codexRaw.alternativeModel as string).trim()) ||
|
|
562
583
|
(typeof codexRaw.effort === "string" && (codexRaw.effort as string).trim()) ||
|
|
563
584
|
codexRaw.fastMode === true,
|
|
564
|
-
);
|
|
585
|
+
);
|
|
586
|
+
// 旧版 ccc 配置没有 enabled。只用 API Key 推断启用,避免 sample 中自带的
|
|
587
|
+
// 默认 Base URL / model 让升级用户在未配置凭证时意外启用 CCC Agent。
|
|
588
|
+
const cccNonEmpty = (): boolean =>
|
|
589
|
+
Boolean(typeof cccRaw.DEEPSEEK_API_KEY === "string" && cccRaw.DEEPSEEK_API_KEY.trim());
|
|
565
590
|
|
|
566
591
|
const claudeEnabled = resolveEnabled(claude.enabled, claudeNonEmpty);
|
|
567
592
|
const cursorEnabled = resolveEnabled(cursorRaw.enabled, cursorNonEmpty);
|
|
568
|
-
const codexEnabled = resolveEnabled(codexRaw.enabled, codexNonEmpty);
|
|
593
|
+
const codexEnabled = resolveEnabled(codexRaw.enabled, codexNonEmpty);
|
|
594
|
+
const cccEnabled = resolveEnabled(cccRaw.enabled, cccNonEmpty);
|
|
569
595
|
const chromeDevtoolsPort = Number(chromeDevtoolsRaw.port);
|
|
570
596
|
const explicitDefaultTool: AgentTool | null =
|
|
571
597
|
typeof claude.defaultAgent === "boolean" && claude.defaultAgent && claudeEnabled ? "claude" :
|
|
572
598
|
typeof cursorRaw.defaultAgent === "boolean" && cursorRaw.defaultAgent && cursorEnabled ? "cursor" :
|
|
573
|
-
typeof codexRaw.defaultAgent === "boolean" && codexRaw.defaultAgent && codexEnabled ? "codex" :
|
|
599
|
+
typeof codexRaw.defaultAgent === "boolean" && codexRaw.defaultAgent && codexEnabled ? "codex" :
|
|
600
|
+
typeof cccRaw.defaultAgent === "boolean" && cccRaw.defaultAgent && cccEnabled ? "ccc" :
|
|
574
601
|
null;
|
|
575
602
|
const fallbackDefaultTool: AgentTool =
|
|
576
603
|
claudeEnabled ? "claude" :
|
|
577
604
|
cursorEnabled ? "cursor" :
|
|
578
|
-
codexEnabled ? "codex" :
|
|
605
|
+
codexEnabled ? "codex" :
|
|
606
|
+
cccEnabled ? "ccc" :
|
|
579
607
|
"claude";
|
|
580
608
|
const defaultTool = explicitDefaultTool ?? fallbackDefaultTool;
|
|
581
609
|
|
|
@@ -655,12 +683,15 @@ function loadConfig(): AppConfig {
|
|
|
655
683
|
fastMode: codexRaw.fastMode === true,
|
|
656
684
|
},
|
|
657
685
|
ccc: {
|
|
686
|
+
enabled: cccEnabled,
|
|
687
|
+
defaultAgent: defaultTool === "ccc",
|
|
658
688
|
DEEPSEEK_API_KEY: normalizeOptionalConfigField(cccRaw.DEEPSEEK_API_KEY, { label: "ccc.DEEPSEEK_API_KEY" }),
|
|
659
689
|
DEEPSEEK_BASE_URL: normalizeOptionalConfigField(cccRaw.DEEPSEEK_BASE_URL, {
|
|
660
690
|
label: "ccc.DEEPSEEK_BASE_URL",
|
|
661
691
|
fallback: DEFAULT_CCC_DEEPSEEK_BASE_URL,
|
|
662
692
|
}),
|
|
663
693
|
model: normalizeOptionalConfigField(cccRaw.model, { label: "ccc.model", fallback: DEFAULT_CCC_MODEL }),
|
|
694
|
+
alternativeModel: normalizeOptionalConfigField(cccRaw.alternativeModel, { label: "ccc.alternativeModel" }),
|
|
664
695
|
},
|
|
665
696
|
};
|
|
666
697
|
}
|
|
@@ -981,7 +1012,7 @@ export const CLAUDE_SESSION_PREFIX = "Claude Code Session:";
|
|
|
981
1012
|
export const CURSOR_SESSION_PREFIX = "Cursor Session:";
|
|
982
1013
|
/** 群描述中用于识别 Codex 会话的前缀 */
|
|
983
1014
|
export const CODEX_SESSION_PREFIX = "Codex Session:";
|
|
984
|
-
/** 群描述中用于识别
|
|
1015
|
+
/** 群描述中用于识别 CCC Agent 会话的前缀 */
|
|
985
1016
|
export const CCC_SESSION_PREFIX = "CCC Session:";
|
|
986
1017
|
|
|
987
1018
|
/** 根据 tool 名称返回对应的群描述前缀 */
|
package/src/index.ts
CHANGED
|
@@ -333,6 +333,19 @@ async function processFeishuMessageEvent(data: Evt): Promise<void> {
|
|
|
333
333
|
if (delayNotice) {
|
|
334
334
|
const delayToken = await getTenantAccessToken();
|
|
335
335
|
await sendCardReply(delayToken, chatId, "延迟送达", delayNotice, "yellow").catch(() => {});
|
|
336
|
+
// 延迟送达的旧消息只发提醒、不转发给 agent 执行:
|
|
337
|
+
// 断线重连或补推时,早已过期的消息不应未经确认就触发新任务。
|
|
338
|
+
// 用户如需执行可自行重发。
|
|
339
|
+
logTrace(traceId, "DONE", {
|
|
340
|
+
outcome: "skip_delayed_feishu_message",
|
|
341
|
+
chatId,
|
|
342
|
+
msgTimestamp,
|
|
343
|
+
delayMinutes: Math.floor((Date.now() - msgTimestamp) / 60000),
|
|
344
|
+
});
|
|
345
|
+
console.log(
|
|
346
|
+
`[${ts()}] [SKIP] Delayed Feishu message not forwarded to agent: messageId=${messageId ?? "(missing id)"} chatId=${chatId} createTime=${msgTimestamp}`,
|
|
347
|
+
);
|
|
348
|
+
return;
|
|
336
349
|
}
|
|
337
350
|
|
|
338
351
|
await handleCommand(
|