chatccc 0.2.128 → 0.2.130
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 +8 -1
- package/config.sample.json +2 -1
- package/package.json +1 -1
- package/src/adapters/adapter-interface.ts +2 -0
- package/src/adapters/claude-adapter.ts +12 -3
- package/src/config.ts +7 -1
- package/src/orchestrator.ts +25 -7
- package/src/session.ts +6 -1
- package/src/web-ui.ts +10 -4
package/README.md
CHANGED
|
@@ -205,7 +205,7 @@ ChatCCC 只负责把聊天消息转给本地 AI 工具,不捆绑这些 CLI。
|
|
|
205
205
|
|
|
206
206
|
#### Claude Code
|
|
207
207
|
|
|
208
|
-
本机完成 Claude CLI 登录后即可使用。同时支持官方和第三方 Anthropic 兼容 API:使用官方服务无需额外配置,使用第三方 API 则填写 `claude.apiKey` 和 `claude.baseUrl`。`claude.model`、`claude.subagentModel`、`claude.apiKey`、`claude.baseUrl`
|
|
208
|
+
本机完成 Claude CLI 登录后即可使用。同时支持官方和第三方 Anthropic 兼容 API:使用官方服务无需额外配置,使用第三方 API 则填写 `claude.apiKey` 和 `claude.baseUrl`。`claude.model`、`claude.subagentModel`、`claude.effort`、`claude.apiKey`、`claude.baseUrl` 均为选填;`claude.maxTurn` 控制每次对话的最大轮数(默认 25)。填写后会把对应配置传给 Claude CLI 子进程;留空则以 `~/.claude/settings.json` 为准。
|
|
209
209
|
|
|
210
210
|
#### Cursor Agent CLI
|
|
211
211
|
|
|
@@ -285,6 +285,7 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
285
285
|
| `cursor.path` / `codex.path` | CLI 可执行文件路径;留空时自动探测或使用 PATH |
|
|
286
286
|
| `claude.model` / `claude.subagentModel` / `claude.effort` | 选填;设置后传给 Claude CLI 子进程,留空以 `~/.claude/settings.json` 为准 |
|
|
287
287
|
| `claude.apiKey` / `claude.baseUrl` | 选填;设置后传给 Claude CLI 子进程,留空以 `~/.claude/settings.json` 为准 |
|
|
288
|
+
| `claude.maxTurn` | 选填;Claude CLI 最大对话轮数,默认 25,可在 Web UI 编辑 |
|
|
288
289
|
|
|
289
290
|
> 当前 ChatCCC 以 `bypassPermissions` 模式运行,会跳过 Agent 操作确认。请只在可信环境中使用。
|
|
290
291
|
|
|
@@ -310,6 +311,12 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
310
311
|
| `/sessions` | 查看所有会话状态 |
|
|
311
312
|
| `/git <子命令>` | 在当前会话工作目录执行 `git ...` 并回传输出 |
|
|
312
313
|
| `/restart` | 重启机器人进程 |
|
|
314
|
+
| `/plan [内容]` | 以 plan 模式传递给 Agent,**不跳过权限检查**(无 `--dangerously-skip-permissions`) |
|
|
315
|
+
| `/ask [内容]` | 以 plan 模式传递给 Agent,**不跳过权限检查**(同 `/plan`) |
|
|
316
|
+
|
|
317
|
+
> **`/plan` 与 `/ask`**:这两个指令不会被 ChatCCC 当作内置命令处理,而是把用户原话(含指令前缀)完整传给 Agent。与普通消息的区别是:调用时不添加 `--dangerously-skip-permissions`,改为 `--permission-mode plan`,Agent 会先在权限沙盒内运行,遇到命令执行等操作时需要确认。适合需要安全审查的任务场景。
|
|
318
|
+
>
|
|
319
|
+
> **群名**:`/plan` / `/ask` 消息如果是群内第一条消息,也会触发群名更新。为美观起见,群名会自动去除 `/plan` 或 `/ask` 前缀。
|
|
313
320
|
|
|
314
321
|
> **模型切换**:`/model` 查看可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。当前仅 Claude Code 支持模型切换(需同时填写 `claude.model` 和 `claude.subagentModel`),Cursor / Codex 切换正在开发中。
|
|
315
322
|
|
package/config.sample.json
CHANGED
package/package.json
CHANGED
|
@@ -128,6 +128,8 @@ export interface ToolPromptOptions {
|
|
|
128
128
|
onProcessStart?: (info: ToolProcessInfo) => void;
|
|
129
129
|
/** Called when the adapter leaves the prompt process scope normally or by abort. */
|
|
130
130
|
onProcessExit?: (info: ToolProcessInfo) => void;
|
|
131
|
+
/** 是否为 /plan 或 /ask 模式:不开启 dangerous skip permissions */
|
|
132
|
+
planMode?: boolean;
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
// ---------------------------------------------------------------------------
|
|
@@ -127,6 +127,8 @@ export interface ClaudeAdapterOptions {
|
|
|
127
127
|
isEmpty: (value: string) => boolean;
|
|
128
128
|
/** 注入自定义 meta 持久化 store(测试用);未提供时使用全局默认实例。 */
|
|
129
129
|
metaStore?: ClaudeSessionMetaStore;
|
|
130
|
+
/** Claude CLI maxTurns 设置,默认 25 */
|
|
131
|
+
maxTurn?: number;
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
// ---------------------------------------------------------------------------
|
|
@@ -281,16 +283,18 @@ function buildCliArgs(
|
|
|
281
283
|
isEmpty: (value: string) => boolean,
|
|
282
284
|
mcpConfigJson: string | null,
|
|
283
285
|
extraArgs: string[],
|
|
286
|
+
maxTurn: number,
|
|
287
|
+
planMode?: boolean,
|
|
284
288
|
): string[] {
|
|
285
289
|
const args = [
|
|
286
290
|
"-p",
|
|
287
291
|
"--output-format", "stream-json",
|
|
288
292
|
"--verbose",
|
|
289
293
|
"--setting-sources", "user,project,local",
|
|
290
|
-
"--permission-mode", "bypassPermissions",
|
|
291
|
-
"--
|
|
292
|
-
"--settings", "{\"maxTurns\":999999999,\"maxBudget\":999999999}",
|
|
294
|
+
"--permission-mode", planMode ? "plan" : "bypassPermissions",
|
|
295
|
+
"--settings", `{"maxTurns":${maxTurn},"maxBudget":999999999}`,
|
|
293
296
|
];
|
|
297
|
+
if (!planMode) args.push("--dangerously-skip-permissions");
|
|
294
298
|
|
|
295
299
|
if (!isEmpty(model)) args.push("--model", model);
|
|
296
300
|
if (!isEmpty(effort)) args.push("--effort", effort);
|
|
@@ -416,6 +420,7 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
416
420
|
private baseUrl: string | undefined;
|
|
417
421
|
private isEmpty: (value: string) => boolean;
|
|
418
422
|
private metaStore: ClaudeSessionMetaStore;
|
|
423
|
+
private maxTurn: number;
|
|
419
424
|
|
|
420
425
|
constructor(options: ClaudeAdapterOptions) {
|
|
421
426
|
this.model = options.model;
|
|
@@ -425,6 +430,7 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
425
430
|
this.baseUrl = options.baseUrl;
|
|
426
431
|
this.isEmpty = options.isEmpty;
|
|
427
432
|
this.metaStore = options.metaStore ?? defaultClaudeSessionMetaStore;
|
|
433
|
+
this.maxTurn = options.maxTurn ?? 25;
|
|
428
434
|
}
|
|
429
435
|
|
|
430
436
|
async createSession(cwd: string): Promise<CreateSessionResult> {
|
|
@@ -434,6 +440,7 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
434
440
|
const args = buildCliArgs(
|
|
435
441
|
this.model, this.effort, this.isEmpty, mcpConfigJson,
|
|
436
442
|
["ok"],
|
|
443
|
+
this.maxTurn,
|
|
437
444
|
);
|
|
438
445
|
|
|
439
446
|
const proc = spawnCli(args, cwd, env, false);
|
|
@@ -465,6 +472,8 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
465
472
|
const args = buildCliArgs(
|
|
466
473
|
this.model, this.effort, this.isEmpty, mcpConfigJson,
|
|
467
474
|
["--resume", sessionId, "--input-format", "stream-json", "--replay-user-messages"],
|
|
475
|
+
this.maxTurn,
|
|
476
|
+
options?.planMode,
|
|
468
477
|
);
|
|
469
478
|
|
|
470
479
|
const proc = spawnCli(args, cwd, env, true);
|
package/src/config.ts
CHANGED
|
@@ -69,6 +69,8 @@ export interface ClaudeConfig {
|
|
|
69
69
|
apiKey: string;
|
|
70
70
|
/** Anthropic 兼容 API Base URL(选填,留空则使用默认端点) */
|
|
71
71
|
baseUrl: string;
|
|
72
|
+
/** Claude CLI maxTurns 设置,默认 25 */
|
|
73
|
+
maxTurn: number;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export interface CursorConfig {
|
|
@@ -324,7 +326,7 @@ function loadConfig(): AppConfig {
|
|
|
324
326
|
port: 18080,
|
|
325
327
|
gitTimeoutSeconds: 180,
|
|
326
328
|
allowInterrupt: false,
|
|
327
|
-
claude: { enabled: false, defaultAgent: true, model: "", subagentModel: "", effort: "", apiKey: "", baseUrl: "" },
|
|
329
|
+
claude: { enabled: false, defaultAgent: true, model: "", subagentModel: "", effort: "", apiKey: "", baseUrl: "", maxTurn: 25 },
|
|
328
330
|
cursor: { enabled: false, defaultAgent: false, path: "", model: "claude-opus-4-7-max" },
|
|
329
331
|
codex: { enabled: false, defaultAgent: false, path: "", model: "", effort: "" },
|
|
330
332
|
};
|
|
@@ -468,6 +470,9 @@ function loadConfig(): AppConfig {
|
|
|
468
470
|
effort: normalizeOptionalConfigField(claude.effort, { label: "claude.effort" }),
|
|
469
471
|
apiKey: normalizeOptionalConfigField(claude.apiKey, { label: "claude.apiKey" }),
|
|
470
472
|
baseUrl: normalizeOptionalConfigField(claude.baseUrl, { label: "claude.baseUrl" }),
|
|
473
|
+
maxTurn: typeof (claude as Record<string, unknown>).maxTurn === "number"
|
|
474
|
+
? (claude as Record<string, unknown>).maxTurn as number
|
|
475
|
+
: 25,
|
|
471
476
|
},
|
|
472
477
|
cursor: {
|
|
473
478
|
enabled: cursorEnabled,
|
|
@@ -521,6 +526,7 @@ export const LOCAL_RELAY_URL = `ws://127.0.0.1:${CHATCCC_PORT}`;
|
|
|
521
526
|
export let CLAUDE_MODEL = config.claude.model;
|
|
522
527
|
export let CLAUDE_SUBAGENT_MODEL = config.claude.subagentModel;
|
|
523
528
|
export let CLAUDE_EFFORT = config.claude.effort;
|
|
529
|
+
export let CLAUDE_MAX_TURN = config.claude.maxTurn;
|
|
524
530
|
export let CLAUDE_API_KEY = config.claude.apiKey;
|
|
525
531
|
export let CLAUDE_BASE_URL = config.claude.baseUrl;
|
|
526
532
|
|
package/src/orchestrator.ts
CHANGED
|
@@ -94,6 +94,21 @@ export function sessionChatName(left: string, cwd: string): string {
|
|
|
94
94
|
return `${left}-${cwdDisplayName(cwd)}`;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/** 判断用户消息是否以 /plan 或 /ask 开头(忽略大小写和尾部空格) */
|
|
98
|
+
function isPlanOrAsk(text: string): boolean {
|
|
99
|
+
const t = text.toLowerCase().trim();
|
|
100
|
+
return t === "/plan" || t.startsWith("/plan ") || t === "/ask" || t.startsWith("/ask ");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** 剥离 /plan 或 /ask 前缀,用于群名等展示场景 */
|
|
104
|
+
function stripPlanOrAskPrefix(text: string): string {
|
|
105
|
+
if (isPlanOrAsk(text)) {
|
|
106
|
+
const match = text.match(/^\/(?:plan|ask)\s*(.*)/i);
|
|
107
|
+
return (match?.[1] ?? "").trim() || text.trim();
|
|
108
|
+
}
|
|
109
|
+
return text;
|
|
110
|
+
}
|
|
111
|
+
|
|
97
112
|
/** 模型模糊匹配:精确匹配优先,否则找子串匹配(模型名越短越优先) */
|
|
98
113
|
function findModelMatch(input: string, models: string[]): string | null {
|
|
99
114
|
if (models.length === 0) return null;
|
|
@@ -117,8 +132,9 @@ function shouldSendWechatProcessingAck(
|
|
|
117
132
|
platform: PlatformAdapter,
|
|
118
133
|
textLower: string,
|
|
119
134
|
chatType: string,
|
|
135
|
+
text: string,
|
|
120
136
|
): boolean {
|
|
121
|
-
return platform.kind === "wechat" && chatType === "p2p" && !textLower.startsWith("/");
|
|
137
|
+
return platform.kind === "wechat" && chatType === "p2p" && (!textLower.startsWith("/") || isPlanOrAsk(text));
|
|
122
138
|
}
|
|
123
139
|
|
|
124
140
|
function isNonWechatP2p(platform: PlatformAdapter, chatType: string): boolean {
|
|
@@ -600,10 +616,10 @@ export async function handleCommand(
|
|
|
600
616
|
if (
|
|
601
617
|
chatType !== "p2p" &&
|
|
602
618
|
isUntitledSessionChatName(chatInfo!.name) &&
|
|
603
|
-
!textLower.startsWith("/")
|
|
619
|
+
(!textLower.startsWith("/") || isPlanOrAsk(text))
|
|
604
620
|
) {
|
|
605
621
|
const MAX_PREFIX = 10;
|
|
606
|
-
const prefix = text.slice(0, MAX_PREFIX);
|
|
622
|
+
const prefix = stripPlanOrAskPrefix(text).slice(0, MAX_PREFIX);
|
|
607
623
|
const adapter = getAdapterForTool(descriptionTool, sessionId);
|
|
608
624
|
const info = await adapter
|
|
609
625
|
.getSessionInfo(sessionId)
|
|
@@ -635,7 +651,7 @@ export async function handleCommand(
|
|
|
635
651
|
if (
|
|
636
652
|
chatType === "p2p" &&
|
|
637
653
|
platform.kind === "wechat" &&
|
|
638
|
-
!textLower.startsWith("/")
|
|
654
|
+
(!textLower.startsWith("/") || isPlanOrAsk(text))
|
|
639
655
|
) {
|
|
640
656
|
try {
|
|
641
657
|
const reg = await loadSessionRegistryForBinding();
|
|
@@ -1223,7 +1239,7 @@ export async function handleCommand(
|
|
|
1223
1239
|
return;
|
|
1224
1240
|
}
|
|
1225
1241
|
|
|
1226
|
-
if (shouldSendWechatProcessingAck(platform, textLower, chatType)) {
|
|
1242
|
+
if (shouldSendWechatProcessingAck(platform, textLower, chatType, text)) {
|
|
1227
1243
|
await platform.sendText(chatId, "生成中...").catch(() => {});
|
|
1228
1244
|
}
|
|
1229
1245
|
|
|
@@ -1237,6 +1253,7 @@ export async function handleCommand(
|
|
|
1237
1253
|
msgTimestamp,
|
|
1238
1254
|
descriptionTool,
|
|
1239
1255
|
tid,
|
|
1256
|
+
isPlanOrAsk(text),
|
|
1240
1257
|
);
|
|
1241
1258
|
logTrace(tid, "DONE", { outcome: "resume_done", sessionId });
|
|
1242
1259
|
console.log(`[${ts()}] [RESUME] Session ${sessionId} done`);
|
|
@@ -1311,7 +1328,7 @@ export async function handleCommand(
|
|
|
1311
1328
|
}
|
|
1312
1329
|
|
|
1313
1330
|
// 飞书私聊普通消息:不再绑定私聊本身,而是自动创建会话群并把私聊内容作为首轮 prompt。
|
|
1314
|
-
if (isNonWechatP2p(platform, chatType) && !textLower.startsWith("/")) {
|
|
1331
|
+
if (isNonWechatP2p(platform, chatType) && (!textLower.startsWith("/") || isPlanOrAsk(text))) {
|
|
1315
1332
|
const tool = resolveDefaultAgentTool();
|
|
1316
1333
|
const toolLabel = toolDisplayName(tool);
|
|
1317
1334
|
logTrace(tid, "BRANCH", { cmd: "auto_new_from_p2p", tool });
|
|
@@ -1352,7 +1369,7 @@ export async function handleCommand(
|
|
|
1352
1369
|
}
|
|
1353
1370
|
|
|
1354
1371
|
const cwd = sessionCwd;
|
|
1355
|
-
const initialName = sessionChatName(text.slice(0, 10) || "新会话", cwd);
|
|
1372
|
+
const initialName = sessionChatName(stripPlanOrAskPrefix(text).slice(0, 10) || "新会话", cwd);
|
|
1356
1373
|
let newChatId: string;
|
|
1357
1374
|
try {
|
|
1358
1375
|
newChatId = await platform.createGroup(initialName, [openId]);
|
|
@@ -1436,6 +1453,7 @@ export async function handleCommand(
|
|
|
1436
1453
|
msgTimestamp,
|
|
1437
1454
|
tool,
|
|
1438
1455
|
tid,
|
|
1456
|
+
isPlanOrAsk(text),
|
|
1439
1457
|
);
|
|
1440
1458
|
console.log(`[${ts()}] [AUTO-P2P 5/5] First prompt sent → OK`);
|
|
1441
1459
|
logTrace(tid, "DONE", { outcome: "auto_new_p2p_prompt_done", newChatId, sessionId, tool });
|
package/src/session.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CLAUDE_API_KEY,
|
|
6
6
|
CLAUDE_BASE_URL,
|
|
7
7
|
CLAUDE_EFFORT,
|
|
8
|
+
CLAUDE_MAX_TURN,
|
|
8
9
|
CLAUDE_MODEL,
|
|
9
10
|
CLAUDE_SUBAGENT_MODEL,
|
|
10
11
|
CHATCCC_PORT,
|
|
@@ -367,6 +368,7 @@ export function getAdapterForTool(tool: string, sessionId?: string): ToolAdapter
|
|
|
367
368
|
apiKey: CLAUDE_API_KEY,
|
|
368
369
|
baseUrl: CLAUDE_BASE_URL,
|
|
369
370
|
isEmpty: isAnthropicConfigEmpty,
|
|
371
|
+
maxTurn: CLAUDE_MAX_TURN,
|
|
370
372
|
});
|
|
371
373
|
}
|
|
372
374
|
adapterCache.set(cacheKey, adapter);
|
|
@@ -832,8 +834,9 @@ export async function resumeAndPrompt(
|
|
|
832
834
|
msgTimestamp: number,
|
|
833
835
|
tool: string,
|
|
834
836
|
traceId?: string,
|
|
837
|
+
planMode?: boolean,
|
|
835
838
|
): Promise<void> {
|
|
836
|
-
return runAgentSession(sessionId, userText, platform, chatId, msgTimestamp, tool, traceId);
|
|
839
|
+
return runAgentSession(sessionId, userText, platform, chatId, msgTimestamp, tool, traceId, planMode);
|
|
837
840
|
}
|
|
838
841
|
|
|
839
842
|
// ---------------------------------------------------------------------------
|
|
@@ -848,6 +851,7 @@ export async function runAgentSession(
|
|
|
848
851
|
msgTimestamp: number,
|
|
849
852
|
tool: string,
|
|
850
853
|
traceId?: string,
|
|
854
|
+
planMode?: boolean,
|
|
851
855
|
): Promise<void> {
|
|
852
856
|
const tid = traceId ?? "";
|
|
853
857
|
|
|
@@ -1106,6 +1110,7 @@ export async function runAgentSession(
|
|
|
1106
1110
|
clearPromptProcessMonitor(sessionId);
|
|
1107
1111
|
if (exitInfo.pid !== undefined) unregisterProcess(exitInfo.pid);
|
|
1108
1112
|
},
|
|
1113
|
+
planMode,
|
|
1109
1114
|
})) {
|
|
1110
1115
|
for (const block of unifiedMsg.blocks) {
|
|
1111
1116
|
accumulateBlockContent(block, state, toolCallMap);
|
package/src/web-ui.ts
CHANGED
|
@@ -31,7 +31,7 @@ interface AppConfig {
|
|
|
31
31
|
platforms?: { feishu?: { enabled?: boolean }; ilink?: { enabled?: boolean } };
|
|
32
32
|
port?: number;
|
|
33
33
|
gitTimeoutSeconds?: number;
|
|
34
|
-
claude?: { enabled?: boolean; defaultAgent?: boolean; model?: string; subagentModel?: string; effort?: string; apiKey?: string; baseUrl?: string };
|
|
34
|
+
claude?: { enabled?: boolean; defaultAgent?: boolean; model?: string; subagentModel?: string; effort?: string; apiKey?: string; baseUrl?: string; maxTurn?: number };
|
|
35
35
|
// `command` 是已废弃的旧字段名,保留只读以兼容升级前的 config.json
|
|
36
36
|
cursor?: { enabled?: boolean; defaultAgent?: boolean; path?: string; command?: string; model?: string };
|
|
37
37
|
codex?: { enabled?: boolean; defaultAgent?: boolean; path?: string; command?: string; model?: string; effort?: string };
|
|
@@ -319,6 +319,9 @@ export function unflattenConfig(flat: Record<string, unknown>): Record<string, u
|
|
|
319
319
|
} else if (key === "CHATCCC_ANTHROPIC_BASE_URL") {
|
|
320
320
|
result.claude = result.claude || {};
|
|
321
321
|
(result.claude as Record<string, unknown>).baseUrl = val;
|
|
322
|
+
} else if (key === "CHATCCC_ANTHROPIC_MAX_TURN") {
|
|
323
|
+
result.claude = result.claude || {};
|
|
324
|
+
(result.claude as Record<string, unknown>).maxTurn = parseInt(val as string, 10) || 25;
|
|
322
325
|
} else if (key === "CHATCCC_CLAUDE_ENABLED") {
|
|
323
326
|
result.claude = result.claude || {};
|
|
324
327
|
(result.claude as Record<string, unknown>).enabled = val === true || val === "true";
|
|
@@ -785,6 +788,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
785
788
|
<div class="config-row"><span class="key">Effort</span><span class="val" id="cfg-ANTHROPIC_EFFORT">-</span></div>
|
|
786
789
|
<div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-ANTHROPIC_API_KEY">-</span></div>
|
|
787
790
|
<div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-ANTHROPIC_BASE_URL">-</span></div>
|
|
791
|
+
<div class="config-row"><span class="key">Max Turns</span><span class="val" id="cfg-ANTHROPIC_MAX_TURN">-</span></div>
|
|
788
792
|
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-claude" onchange="setDashboardDefaultAgent('claude', this.checked)"> 设为默认 Agent</label>
|
|
789
793
|
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('claude')">编辑</button>
|
|
790
794
|
</div>
|
|
@@ -851,7 +855,7 @@ let state = {
|
|
|
851
855
|
var step2InputBound = false;
|
|
852
856
|
|
|
853
857
|
const AGENT_FIELDS = {
|
|
854
|
-
claude: ['CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_SUBAGENT_MODEL','CHATCCC_ANTHROPIC_EFFORT','CHATCCC_ANTHROPIC_API_KEY','CHATCCC_ANTHROPIC_BASE_URL'],
|
|
858
|
+
claude: ['CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_SUBAGENT_MODEL','CHATCCC_ANTHROPIC_EFFORT','CHATCCC_ANTHROPIC_API_KEY','CHATCCC_ANTHROPIC_BASE_URL','CHATCCC_ANTHROPIC_MAX_TURN'],
|
|
855
859
|
cursor: ['CHATCCC_CURSOR_PATH','CHATCCC_CURSOR_MODEL'],
|
|
856
860
|
codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_EFFORT']
|
|
857
861
|
};
|
|
@@ -1087,7 +1091,7 @@ function isAgentEnabled(node, keys) {
|
|
|
1087
1091
|
return false;
|
|
1088
1092
|
}
|
|
1089
1093
|
|
|
1090
|
-
var CLAUDE_FALLBACK_KEYS = ['model','subagentModel','effort'];
|
|
1094
|
+
var CLAUDE_FALLBACK_KEYS = ['model','subagentModel','effort','maxTurn'];
|
|
1091
1095
|
var CURSOR_FALLBACK_KEYS = ['path','command','model'];
|
|
1092
1096
|
var CODEX_FALLBACK_KEYS = ['path','command','model','effort'];
|
|
1093
1097
|
|
|
@@ -1399,6 +1403,7 @@ function updateDashboardUI() {
|
|
|
1399
1403
|
document.getElementById('cfg-ANTHROPIC_EFFORT').textContent = (c.claude && c.claude.effort) || '(留空)';
|
|
1400
1404
|
document.getElementById('cfg-ANTHROPIC_API_KEY').textContent = (c.claude && c.claude.apiKey) ? '***已设置***' : '(留空)';
|
|
1401
1405
|
document.getElementById('cfg-ANTHROPIC_BASE_URL').textContent = (c.claude && c.claude.baseUrl) || '(留空)';
|
|
1406
|
+
document.getElementById('cfg-ANTHROPIC_MAX_TURN').textContent = (c.claude && c.claude.maxTurn != null) ? String(c.claude.maxTurn) : '25';
|
|
1402
1407
|
document.getElementById('cfg-CURSOR_PATH').textContent = (c.cursor && (c.cursor.path || c.cursor.command)) || '-';
|
|
1403
1408
|
document.getElementById('cfg-CURSOR_MODEL').textContent = (c.cursor && c.cursor.model) || '(留空)';
|
|
1404
1409
|
document.getElementById('cfg-CODEX_PATH').textContent = (c.codex && (c.codex.path || c.codex.command)) || 'codex';
|
|
@@ -1459,7 +1464,7 @@ function editSection(section) {
|
|
|
1459
1464
|
var labelMap = {
|
|
1460
1465
|
'CHATCCC_APP_ID': 'App ID', 'CHATCCC_APP_SECRET': 'App Secret',
|
|
1461
1466
|
'CHATCCC_ANTHROPIC_MODEL': '模型', 'CHATCCC_ANTHROPIC_SUBAGENT_MODEL': 'Subagent 模型', 'CHATCCC_ANTHROPIC_EFFORT': 'Effort',
|
|
1462
|
-
'CHATCCC_ANTHROPIC_API_KEY': 'API Key', 'CHATCCC_ANTHROPIC_BASE_URL': 'Base URL',
|
|
1467
|
+
'CHATCCC_ANTHROPIC_API_KEY': 'API Key', 'CHATCCC_ANTHROPIC_BASE_URL': 'Base URL', 'CHATCCC_ANTHROPIC_MAX_TURN': 'Max Turns',
|
|
1463
1468
|
'CHATCCC_CURSOR_PATH': 'CLI 路径', 'CHATCCC_CURSOR_MODEL': '模型',
|
|
1464
1469
|
'CHATCCC_CODEX_PATH': 'CLI 路径', 'CHATCCC_CODEX_MODEL': '模型', 'CHATCCC_CODEX_EFFORT': 'Effort'
|
|
1465
1470
|
};
|
|
@@ -1477,6 +1482,7 @@ function editSection(section) {
|
|
|
1477
1482
|
else if (key === 'CHATCCC_ANTHROPIC_EFFORT') val = state.config.claude.effort || '';
|
|
1478
1483
|
else if (key === 'CHATCCC_ANTHROPIC_API_KEY') val = state.config.claude.apiKey || '';
|
|
1479
1484
|
else if (key === 'CHATCCC_ANTHROPIC_BASE_URL') val = state.config.claude.baseUrl || '';
|
|
1485
|
+
else if (key === 'CHATCCC_ANTHROPIC_MAX_TURN') val = (state.config.claude.maxTurn != null) ? String(state.config.claude.maxTurn) : '25';
|
|
1480
1486
|
} else if (section === 'cursor' && state.config.cursor) {
|
|
1481
1487
|
if (key === 'CHATCCC_CURSOR_PATH') val = state.config.cursor.path || state.config.cursor.command || '';
|
|
1482
1488
|
else if (key === 'CHATCCC_CURSOR_MODEL') val = state.config.cursor.model || '';
|