chatccc 0.2.223 → 0.2.225
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/config.sample.json +40 -39
- package/package.json +1 -1
- package/src/__tests__/builtin-chat-session.test.ts +33 -0
- package/src/__tests__/builtin-config.test.ts +1 -0
- package/src/__tests__/ccc-adapter.test.ts +20 -0
- package/src/__tests__/config-reload.test.ts +52 -52
- package/src/__tests__/config-sample.test.ts +41 -40
- package/src/__tests__/orchestrator.test.ts +836 -803
- package/src/__tests__/session.test.ts +1183 -1173
- package/src/__tests__/terminal-renderer.test.ts +104 -0
- package/src/__tests__/web-ui.test.ts +3 -0
- package/src/adapters/ccc-adapter.ts +1 -0
- package/src/builtin/cli.ts +63 -17
- package/src/builtin/index.ts +10 -0
- package/src/config.ts +203 -192
- package/src/progress/terminal-renderer.ts +112 -8
- package/src/session.ts +5 -2
- package/src/web-ui.ts +626 -607
package/src/web-ui.ts
CHANGED
|
@@ -12,12 +12,12 @@ import { readFile, writeFile, stat } from "node:fs/promises";
|
|
|
12
12
|
import { homedir } from "node:os";
|
|
13
13
|
import { join, dirname } from "node:path";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
|
-
import { spawn, execSync } from "node:child_process";
|
|
16
|
-
import {
|
|
17
|
-
buildWebUiUrl,
|
|
18
|
-
createInternalRestartEnv,
|
|
19
|
-
openWebUiInDefaultBrowser,
|
|
20
|
-
} from "./startup-lifecycle.ts";
|
|
15
|
+
import { spawn, execSync } from "node:child_process";
|
|
16
|
+
import {
|
|
17
|
+
buildWebUiUrl,
|
|
18
|
+
createInternalRestartEnv,
|
|
19
|
+
openWebUiInDefaultBrowser,
|
|
20
|
+
} from "./startup-lifecycle.ts";
|
|
21
21
|
|
|
22
22
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
23
|
const PROJECT_ROOT = join(__dirname, "..");
|
|
@@ -31,16 +31,16 @@ const ILINK_AUTH_PATH = join(USER_DATA_DIR, "state", "ilink-auth.json");
|
|
|
31
31
|
// Helpers — config.json parsing & generation
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
33
|
|
|
34
|
-
interface AppConfig {
|
|
35
|
-
feishu?: { appId?: string; appSecret?: string };
|
|
36
|
-
platforms?: {
|
|
37
|
-
feishu?: { enabled?: boolean; platformType?: string };
|
|
38
|
-
ilink?: { enabled?: boolean; reuseTokenOnStart?: boolean };
|
|
39
|
-
};
|
|
40
|
-
webUi?: { openOnStart?: boolean };
|
|
41
|
-
chromeDevtools?: { enabled?: boolean; port?: number; chromePath?: string };
|
|
42
|
-
port?: number;
|
|
43
|
-
gitTimeoutSeconds?: number;
|
|
34
|
+
interface AppConfig {
|
|
35
|
+
feishu?: { appId?: string; appSecret?: string };
|
|
36
|
+
platforms?: {
|
|
37
|
+
feishu?: { enabled?: boolean; platformType?: string };
|
|
38
|
+
ilink?: { enabled?: boolean; reuseTokenOnStart?: boolean };
|
|
39
|
+
};
|
|
40
|
+
webUi?: { openOnStart?: boolean };
|
|
41
|
+
chromeDevtools?: { enabled?: boolean; port?: number; chromePath?: string };
|
|
42
|
+
port?: number;
|
|
43
|
+
gitTimeoutSeconds?: number;
|
|
44
44
|
claude?: { enabled?: boolean; defaultAgent?: boolean; model?: string; subagentModel?: string; effort?: string; apiKey?: string; baseUrl?: string; maxTurn?: number };
|
|
45
45
|
// `command` 是已废弃的旧字段名,保留只读以兼容升级前的 config.json
|
|
46
46
|
cursor?: {
|
|
@@ -52,17 +52,17 @@ interface AppConfig {
|
|
|
52
52
|
alternativeModel?: string;
|
|
53
53
|
avatarBatteryMode?: string;
|
|
54
54
|
onDemandMonthlyBudget?: number;
|
|
55
|
-
};
|
|
56
|
-
codex?: { enabled?: boolean; defaultAgent?: boolean; path?: string; command?: string; model?: string; alternativeModel?: string; effort?: string; fastMode?: boolean };
|
|
57
|
-
ccc?: {
|
|
58
|
-
enabled?: boolean;
|
|
59
|
-
defaultAgent?: boolean;
|
|
60
|
-
DEEPSEEK_API_KEY?: string;
|
|
61
|
-
DEEPSEEK_BASE_URL?: string;
|
|
62
|
-
model?: string;
|
|
63
|
-
alternativeModel?: string;
|
|
64
|
-
};
|
|
65
|
-
}
|
|
55
|
+
};
|
|
56
|
+
codex?: { enabled?: boolean; defaultAgent?: boolean; path?: string; command?: string; model?: string; alternativeModel?: string; effort?: string; fastMode?: boolean };
|
|
57
|
+
ccc?: {
|
|
58
|
+
enabled?: boolean;
|
|
59
|
+
defaultAgent?: boolean;
|
|
60
|
+
DEEPSEEK_API_KEY?: string;
|
|
61
|
+
DEEPSEEK_BASE_URL?: string;
|
|
62
|
+
model?: string;
|
|
63
|
+
alternativeModel?: string;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
66
|
|
|
67
67
|
// ---------------------------------------------------------------------------
|
|
68
68
|
// /api/start 路径选择(纯函数,便于单测护栏)
|
|
@@ -118,76 +118,76 @@ function loadConfig(): AppConfig {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function saveConfig(cfg: AppConfig): void {
|
|
122
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), "utf8");
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
type ConfigApplyMode = "saved" | "reload" | "restart-required";
|
|
126
|
-
|
|
127
|
-
interface ConfigApplyResult {
|
|
128
|
-
mode: ConfigApplyMode;
|
|
129
|
-
restartRequired: boolean;
|
|
130
|
-
restartReasons: string[];
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function stringValue(value: unknown): string {
|
|
134
|
-
return typeof value === "string" ? value.trim() : "";
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function portValue(cfg: AppConfig): number {
|
|
138
|
-
return Number.isInteger(cfg.port) && cfg.port! >= 1 && cfg.port! <= 65535 ? cfg.port! : 18080;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function platformEnabled(cfg: AppConfig, platform: "feishu" | "ilink"): boolean {
|
|
142
|
-
return cfg.platforms?.[platform]?.enabled !== false;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function feishuPlatformType(cfg: AppConfig): string {
|
|
146
|
-
return cfg.platforms?.feishu?.platformType === "lark" ? "lark" : "feishu";
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function ilinkReuseTokenOnStart(cfg: AppConfig): boolean {
|
|
150
|
-
return cfg.platforms?.ilink?.reuseTokenOnStart !== false;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export function getRestartRequiredReasons(before: AppConfig, after: AppConfig): string[] {
|
|
154
|
-
const reasons: string[] = [];
|
|
155
|
-
|
|
156
|
-
if (portValue(before) !== portValue(after)) reasons.push("port");
|
|
157
|
-
if (stringValue(before.feishu?.appId) !== stringValue(after.feishu?.appId)) reasons.push("feishu.appId");
|
|
158
|
-
if (stringValue(before.feishu?.appSecret) !== stringValue(after.feishu?.appSecret)) reasons.push("feishu.appSecret");
|
|
159
|
-
if (feishuPlatformType(before) !== feishuPlatformType(after)) reasons.push("platforms.feishu.platformType");
|
|
160
|
-
if (platformEnabled(before, "feishu") !== platformEnabled(after, "feishu")) reasons.push("platforms.feishu.enabled");
|
|
161
|
-
if (platformEnabled(before, "ilink") !== platformEnabled(after, "ilink")) reasons.push("platforms.ilink.enabled");
|
|
162
|
-
if (ilinkReuseTokenOnStart(before) !== ilinkReuseTokenOnStart(after)) {
|
|
163
|
-
reasons.push("platforms.ilink.reuseTokenOnStart");
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return reasons;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async function applySavedConfigIfPossible(before: AppConfig, after: AppConfig): Promise<ConfigApplyResult> {
|
|
170
|
-
const setupMode = Boolean(setupActivateHook && setupHttpServer);
|
|
171
|
-
if (setupMode) {
|
|
172
|
-
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const running = isServiceRunning();
|
|
176
|
-
if (!running) {
|
|
177
|
-
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const restartReasons = getRestartRequiredReasons(before, after);
|
|
181
|
-
if (restartReasons.length > 0) {
|
|
182
|
-
return { mode: "restart-required", restartRequired: true, restartReasons };
|
|
183
|
-
}
|
|
184
|
-
if (!reloadConfigHook) {
|
|
185
|
-
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
await reloadConfigHook();
|
|
189
|
-
return { mode: "reload", restartRequired: false, restartReasons: [] };
|
|
190
|
-
}
|
|
121
|
+
function saveConfig(cfg: AppConfig): void {
|
|
122
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), "utf8");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
type ConfigApplyMode = "saved" | "reload" | "restart-required";
|
|
126
|
+
|
|
127
|
+
interface ConfigApplyResult {
|
|
128
|
+
mode: ConfigApplyMode;
|
|
129
|
+
restartRequired: boolean;
|
|
130
|
+
restartReasons: string[];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function stringValue(value: unknown): string {
|
|
134
|
+
return typeof value === "string" ? value.trim() : "";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function portValue(cfg: AppConfig): number {
|
|
138
|
+
return Number.isInteger(cfg.port) && cfg.port! >= 1 && cfg.port! <= 65535 ? cfg.port! : 18080;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function platformEnabled(cfg: AppConfig, platform: "feishu" | "ilink"): boolean {
|
|
142
|
+
return cfg.platforms?.[platform]?.enabled !== false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function feishuPlatformType(cfg: AppConfig): string {
|
|
146
|
+
return cfg.platforms?.feishu?.platformType === "lark" ? "lark" : "feishu";
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function ilinkReuseTokenOnStart(cfg: AppConfig): boolean {
|
|
150
|
+
return cfg.platforms?.ilink?.reuseTokenOnStart !== false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function getRestartRequiredReasons(before: AppConfig, after: AppConfig): string[] {
|
|
154
|
+
const reasons: string[] = [];
|
|
155
|
+
|
|
156
|
+
if (portValue(before) !== portValue(after)) reasons.push("port");
|
|
157
|
+
if (stringValue(before.feishu?.appId) !== stringValue(after.feishu?.appId)) reasons.push("feishu.appId");
|
|
158
|
+
if (stringValue(before.feishu?.appSecret) !== stringValue(after.feishu?.appSecret)) reasons.push("feishu.appSecret");
|
|
159
|
+
if (feishuPlatformType(before) !== feishuPlatformType(after)) reasons.push("platforms.feishu.platformType");
|
|
160
|
+
if (platformEnabled(before, "feishu") !== platformEnabled(after, "feishu")) reasons.push("platforms.feishu.enabled");
|
|
161
|
+
if (platformEnabled(before, "ilink") !== platformEnabled(after, "ilink")) reasons.push("platforms.ilink.enabled");
|
|
162
|
+
if (ilinkReuseTokenOnStart(before) !== ilinkReuseTokenOnStart(after)) {
|
|
163
|
+
reasons.push("platforms.ilink.reuseTokenOnStart");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return reasons;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function applySavedConfigIfPossible(before: AppConfig, after: AppConfig): Promise<ConfigApplyResult> {
|
|
170
|
+
const setupMode = Boolean(setupActivateHook && setupHttpServer);
|
|
171
|
+
if (setupMode) {
|
|
172
|
+
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const running = isServiceRunning();
|
|
176
|
+
if (!running) {
|
|
177
|
+
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const restartReasons = getRestartRequiredReasons(before, after);
|
|
181
|
+
if (restartReasons.length > 0) {
|
|
182
|
+
return { mode: "restart-required", restartRequired: true, restartReasons };
|
|
183
|
+
}
|
|
184
|
+
if (!reloadConfigHook) {
|
|
185
|
+
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
await reloadConfigHook();
|
|
189
|
+
return { mode: "reload", restartRequired: false, restartReasons: [] };
|
|
190
|
+
}
|
|
191
191
|
|
|
192
192
|
function maskSecret(value: string | undefined): string {
|
|
193
193
|
if (!value) return "(未设置)";
|
|
@@ -239,12 +239,12 @@ function spawnService(): { ok: boolean; pid?: number; error?: string } {
|
|
|
239
239
|
return { ok: false, error: `Entry not found: ${indexPath}` };
|
|
240
240
|
}
|
|
241
241
|
try {
|
|
242
|
-
const child = spawn("npx", ["tsx", "src/index.ts"], {
|
|
243
|
-
cwd: PROJECT_ROOT,
|
|
244
|
-
detached: true,
|
|
245
|
-
stdio: "ignore",
|
|
246
|
-
shell: true,
|
|
247
|
-
env: createInternalRestartEnv(),
|
|
242
|
+
const child = spawn("npx", ["tsx", "src/index.ts"], {
|
|
243
|
+
cwd: PROJECT_ROOT,
|
|
244
|
+
detached: true,
|
|
245
|
+
stdio: "ignore",
|
|
246
|
+
shell: true,
|
|
247
|
+
env: createInternalRestartEnv(),
|
|
248
248
|
});
|
|
249
249
|
child.unref();
|
|
250
250
|
return { ok: true, pid: child.pid ?? undefined };
|
|
@@ -262,19 +262,19 @@ function scheduleRestart(): void {
|
|
|
262
262
|
if (!existsSync(indexPath)) return;
|
|
263
263
|
if (process.platform === "win32") {
|
|
264
264
|
// Windows: ping 作为 sleep(ping 自己 3 次 ≈ 2 秒延迟)
|
|
265
|
-
spawn("cmd.exe", ["/c", "ping -n 3 127.0.0.1 > nul && npx tsx src/index.ts"], {
|
|
266
|
-
cwd: PROJECT_ROOT,
|
|
267
|
-
detached: true,
|
|
268
|
-
stdio: "ignore",
|
|
269
|
-
windowsHide: true,
|
|
270
|
-
env: createInternalRestartEnv(),
|
|
265
|
+
spawn("cmd.exe", ["/c", "ping -n 3 127.0.0.1 > nul && npx tsx src/index.ts"], {
|
|
266
|
+
cwd: PROJECT_ROOT,
|
|
267
|
+
detached: true,
|
|
268
|
+
stdio: "ignore",
|
|
269
|
+
windowsHide: true,
|
|
270
|
+
env: createInternalRestartEnv(),
|
|
271
271
|
}).unref();
|
|
272
272
|
} else {
|
|
273
|
-
spawn("bash", ["-c", "sleep 2 && npx tsx src/index.ts"], {
|
|
274
|
-
cwd: PROJECT_ROOT,
|
|
275
|
-
detached: true,
|
|
276
|
-
stdio: "ignore",
|
|
277
|
-
env: createInternalRestartEnv(),
|
|
273
|
+
spawn("bash", ["-c", "sleep 2 && npx tsx src/index.ts"], {
|
|
274
|
+
cwd: PROJECT_ROOT,
|
|
275
|
+
detached: true,
|
|
276
|
+
stdio: "ignore",
|
|
277
|
+
env: createInternalRestartEnv(),
|
|
278
278
|
}).unref();
|
|
279
279
|
}
|
|
280
280
|
}
|
|
@@ -383,14 +383,14 @@ async function handlePostConfig(req: IncomingMessage, res: ServerResponse): Prom
|
|
|
383
383
|
existing as unknown as Record<string, unknown>,
|
|
384
384
|
unflattenConfig(updates),
|
|
385
385
|
) as AppConfig;
|
|
386
|
-
try {
|
|
387
|
-
saveConfig(merged);
|
|
388
|
-
const applyResult = await applySavedConfigIfPossible(existing, merged);
|
|
389
|
-
jsonReply(res, 200, { ok: true, ...applyResult });
|
|
390
|
-
} catch (err) {
|
|
391
|
-
jsonReply(res, 500, { ok: false, saved: true, error: (err as Error).message });
|
|
392
|
-
}
|
|
393
|
-
}
|
|
386
|
+
try {
|
|
387
|
+
saveConfig(merged);
|
|
388
|
+
const applyResult = await applySavedConfigIfPossible(existing, merged);
|
|
389
|
+
jsonReply(res, 200, { ok: true, ...applyResult });
|
|
390
|
+
} catch (err) {
|
|
391
|
+
jsonReply(res, 500, { ok: false, saved: true, error: (err as Error).message });
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
394
|
|
|
395
395
|
function deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {
|
|
396
396
|
const result = { ...target };
|
|
@@ -422,14 +422,14 @@ export function unflattenConfig(flat: Record<string, unknown>): Record<string, u
|
|
|
422
422
|
result.platforms = result.platforms || {};
|
|
423
423
|
(result.platforms as Record<string, unknown>).feishu = (result.platforms as Record<string, unknown>).feishu || {};
|
|
424
424
|
((result.platforms as Record<string, unknown>).feishu as Record<string, unknown>).platformType = val;
|
|
425
|
-
} else if (key === "CHATCCC_ILINK_ENABLED") {
|
|
426
|
-
result.platforms = result.platforms || {};
|
|
427
|
-
(result.platforms as Record<string, unknown>).ilink = (result.platforms as Record<string, unknown>).ilink || {};
|
|
428
|
-
((result.platforms as Record<string, unknown>).ilink as Record<string, unknown>).enabled = val === true || val === "true";
|
|
429
|
-
} else if (key === "CHATCCC_WEB_UI_OPEN_ON_START") {
|
|
430
|
-
result.webUi = result.webUi || {};
|
|
431
|
-
(result.webUi as Record<string, unknown>).openOnStart = val === true || val === "true";
|
|
432
|
-
} else if (key === "CHATCCC_CHROME_DEVTOOLS_ENABLED") {
|
|
425
|
+
} else if (key === "CHATCCC_ILINK_ENABLED") {
|
|
426
|
+
result.platforms = result.platforms || {};
|
|
427
|
+
(result.platforms as Record<string, unknown>).ilink = (result.platforms as Record<string, unknown>).ilink || {};
|
|
428
|
+
((result.platforms as Record<string, unknown>).ilink as Record<string, unknown>).enabled = val === true || val === "true";
|
|
429
|
+
} else if (key === "CHATCCC_WEB_UI_OPEN_ON_START") {
|
|
430
|
+
result.webUi = result.webUi || {};
|
|
431
|
+
(result.webUi as Record<string, unknown>).openOnStart = val === true || val === "true";
|
|
432
|
+
} else if (key === "CHATCCC_CHROME_DEVTOOLS_ENABLED") {
|
|
433
433
|
result.chromeDevtools = result.chromeDevtools || {};
|
|
434
434
|
(result.chromeDevtools as Record<string, unknown>).enabled = val === true || val === "true";
|
|
435
435
|
} else if (key === "CHATCCC_CHROME_DEVTOOLS_PORT") {
|
|
@@ -496,37 +496,40 @@ export function unflattenConfig(flat: Record<string, unknown>): Record<string, u
|
|
|
496
496
|
} else if (key === "CHATCCC_CODEX_ALTERNATIVE_MODEL") {
|
|
497
497
|
result.codex = result.codex || {};
|
|
498
498
|
(result.codex as Record<string, unknown>).alternativeModel = val;
|
|
499
|
-
} else if (key === "CHATCCC_CODEX_EFFORT") {
|
|
500
|
-
result.codex = result.codex || {};
|
|
501
|
-
(result.codex as Record<string, unknown>).effort = val;
|
|
502
|
-
} else if (key === "CHATCCC_CODEX_FAST_MODE") {
|
|
503
|
-
result.codex = result.codex || {};
|
|
504
|
-
(result.codex as Record<string, unknown>).fastMode = val === true || val === "true";
|
|
505
|
-
} else if (key === "CHATCCC_CODEX_ENABLED") {
|
|
499
|
+
} else if (key === "CHATCCC_CODEX_EFFORT") {
|
|
500
|
+
result.codex = result.codex || {};
|
|
501
|
+
(result.codex as Record<string, unknown>).effort = val;
|
|
502
|
+
} else if (key === "CHATCCC_CODEX_FAST_MODE") {
|
|
503
|
+
result.codex = result.codex || {};
|
|
504
|
+
(result.codex as Record<string, unknown>).fastMode = val === true || val === "true";
|
|
505
|
+
} else if (key === "CHATCCC_CODEX_ENABLED") {
|
|
506
506
|
result.codex = result.codex || {};
|
|
507
507
|
(result.codex as Record<string, unknown>).enabled = val === true || val === "true";
|
|
508
|
-
} else if (key === "CHATCCC_CODEX_DEFAULT_AGENT") {
|
|
509
|
-
result.codex = result.codex || {};
|
|
510
|
-
(result.codex as Record<string, unknown>).defaultAgent = val === true || val === "true";
|
|
511
|
-
} else if (key === "CHATCCC_CCC_API_KEY") {
|
|
512
|
-
result.ccc = result.ccc || {};
|
|
513
|
-
(result.ccc as Record<string, unknown>).DEEPSEEK_API_KEY = val;
|
|
514
|
-
} else if (key === "CHATCCC_CCC_BASE_URL") {
|
|
515
|
-
result.ccc = result.ccc || {};
|
|
516
|
-
(result.ccc as Record<string, unknown>).DEEPSEEK_BASE_URL = val;
|
|
517
|
-
} else if (key === "CHATCCC_CCC_MODEL") {
|
|
518
|
-
result.ccc = result.ccc || {};
|
|
519
|
-
(result.ccc as Record<string, unknown>).model = val;
|
|
520
|
-
} else if (key === "CHATCCC_CCC_ALTERNATIVE_MODEL") {
|
|
521
|
-
result.ccc = result.ccc || {};
|
|
522
|
-
(result.ccc as Record<string, unknown>).alternativeModel = val;
|
|
523
|
-
} else if (key === "
|
|
524
|
-
result.ccc = result.ccc || {};
|
|
525
|
-
(result.ccc as Record<string, unknown>).
|
|
526
|
-
} else if (key === "
|
|
527
|
-
result.ccc = result.ccc || {};
|
|
528
|
-
(result.ccc as Record<string, unknown>).
|
|
529
|
-
}
|
|
508
|
+
} else if (key === "CHATCCC_CODEX_DEFAULT_AGENT") {
|
|
509
|
+
result.codex = result.codex || {};
|
|
510
|
+
(result.codex as Record<string, unknown>).defaultAgent = val === true || val === "true";
|
|
511
|
+
} else if (key === "CHATCCC_CCC_API_KEY") {
|
|
512
|
+
result.ccc = result.ccc || {};
|
|
513
|
+
(result.ccc as Record<string, unknown>).DEEPSEEK_API_KEY = val;
|
|
514
|
+
} else if (key === "CHATCCC_CCC_BASE_URL") {
|
|
515
|
+
result.ccc = result.ccc || {};
|
|
516
|
+
(result.ccc as Record<string, unknown>).DEEPSEEK_BASE_URL = val;
|
|
517
|
+
} else if (key === "CHATCCC_CCC_MODEL") {
|
|
518
|
+
result.ccc = result.ccc || {};
|
|
519
|
+
(result.ccc as Record<string, unknown>).model = val;
|
|
520
|
+
} else if (key === "CHATCCC_CCC_ALTERNATIVE_MODEL") {
|
|
521
|
+
result.ccc = result.ccc || {};
|
|
522
|
+
(result.ccc as Record<string, unknown>).alternativeModel = val;
|
|
523
|
+
} else if (key === "CHATCCC_CCC_EFFORT") {
|
|
524
|
+
result.ccc = result.ccc || {};
|
|
525
|
+
(result.ccc as Record<string, unknown>).effort = val;
|
|
526
|
+
} else if (key === "CHATCCC_CCC_ENABLED") {
|
|
527
|
+
result.ccc = result.ccc || {};
|
|
528
|
+
(result.ccc as Record<string, unknown>).enabled = val === true || val === "true";
|
|
529
|
+
} else if (key === "CHATCCC_CCC_DEFAULT_AGENT") {
|
|
530
|
+
result.ccc = result.ccc || {};
|
|
531
|
+
(result.ccc as Record<string, unknown>).defaultAgent = val === true || val === "true";
|
|
532
|
+
}
|
|
530
533
|
}
|
|
531
534
|
return result;
|
|
532
535
|
}
|
|
@@ -562,18 +565,18 @@ async function handleStartService(_req: IncomingMessage, res: ServerResponse): P
|
|
|
562
565
|
return;
|
|
563
566
|
}
|
|
564
567
|
|
|
565
|
-
if (path === "reload") {
|
|
566
|
-
// service 已经在跑:只刷新进程内配置,让后续消息/新会话使用新值。
|
|
567
|
-
if (!reloadConfigHook) {
|
|
568
|
-
jsonReply(res, 200, { ok: true, pid: process.pid, mode: "saved" });
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
try {
|
|
572
|
-
await reloadConfigHook();
|
|
573
|
-
jsonReply(res, 200, { ok: true, pid: process.pid, mode: "reload" });
|
|
574
|
-
} catch (err) {
|
|
575
|
-
jsonReply(res, 500, { ok: false, error: (err as Error).message });
|
|
576
|
-
}
|
|
568
|
+
if (path === "reload") {
|
|
569
|
+
// service 已经在跑:只刷新进程内配置,让后续消息/新会话使用新值。
|
|
570
|
+
if (!reloadConfigHook) {
|
|
571
|
+
jsonReply(res, 200, { ok: true, pid: process.pid, mode: "saved" });
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
try {
|
|
575
|
+
await reloadConfigHook();
|
|
576
|
+
jsonReply(res, 200, { ok: true, pid: process.pid, mode: "reload" });
|
|
577
|
+
} catch (err) {
|
|
578
|
+
jsonReply(res, 500, { ok: false, error: (err as Error).message });
|
|
579
|
+
}
|
|
577
580
|
return;
|
|
578
581
|
}
|
|
579
582
|
|
|
@@ -626,29 +629,29 @@ async function handleForgetIlink(_req: IncomingMessage, res: ServerResponse): Pr
|
|
|
626
629
|
}
|
|
627
630
|
}
|
|
628
631
|
|
|
629
|
-
// ---------------------------------------------------------------------------
|
|
630
|
-
// HTML page (embedded template)
|
|
631
|
-
// ---------------------------------------------------------------------------
|
|
632
|
-
|
|
633
|
-
/** Agent Team 的首个占位页面:按产品约定只展示标题,不提前放入功能内容。 */
|
|
634
|
-
export const AGENT_TEAM_PAGE_HTML = `<!DOCTYPE html>
|
|
635
|
-
<html lang="zh-CN">
|
|
636
|
-
<head>
|
|
637
|
-
<meta charset="UTF-8">
|
|
638
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
639
|
-
<title>Agent Team</title>
|
|
640
|
-
<style>
|
|
641
|
-
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
642
|
-
body{min-height:100vh;display:grid;place-items:center;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:radial-gradient(circle at 50% 20%,#312e81 0,#111827 42%,#020617 100%);color:#fff}
|
|
643
|
-
h1{font-size:clamp(40px,8vw,88px);font-weight:750;letter-spacing:-.04em;text-shadow:0 12px 42px rgba(129,140,248,.45)}
|
|
644
|
-
</style>
|
|
645
|
-
</head>
|
|
646
|
-
<body>
|
|
647
|
-
<h1>Agent Team</h1>
|
|
648
|
-
</body>
|
|
649
|
-
</html>`;
|
|
650
|
-
|
|
651
|
-
export const PAGE_HTML = `<!DOCTYPE html>
|
|
632
|
+
// ---------------------------------------------------------------------------
|
|
633
|
+
// HTML page (embedded template)
|
|
634
|
+
// ---------------------------------------------------------------------------
|
|
635
|
+
|
|
636
|
+
/** Agent Team 的首个占位页面:按产品约定只展示标题,不提前放入功能内容。 */
|
|
637
|
+
export const AGENT_TEAM_PAGE_HTML = `<!DOCTYPE html>
|
|
638
|
+
<html lang="zh-CN">
|
|
639
|
+
<head>
|
|
640
|
+
<meta charset="UTF-8">
|
|
641
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
642
|
+
<title>Agent Team</title>
|
|
643
|
+
<style>
|
|
644
|
+
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
645
|
+
body{min-height:100vh;display:grid;place-items:center;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:radial-gradient(circle at 50% 20%,#312e81 0,#111827 42%,#020617 100%);color:#fff}
|
|
646
|
+
h1{font-size:clamp(40px,8vw,88px);font-weight:750;letter-spacing:-.04em;text-shadow:0 12px 42px rgba(129,140,248,.45)}
|
|
647
|
+
</style>
|
|
648
|
+
</head>
|
|
649
|
+
<body>
|
|
650
|
+
<h1>Agent Team</h1>
|
|
651
|
+
</body>
|
|
652
|
+
</html>`;
|
|
653
|
+
|
|
654
|
+
export const PAGE_HTML = `<!DOCTYPE html>
|
|
652
655
|
<html lang="zh-CN">
|
|
653
656
|
<head>
|
|
654
657
|
<meta charset="UTF-8">
|
|
@@ -657,14 +660,14 @@ export const PAGE_HTML = `<!DOCTYPE html>
|
|
|
657
660
|
<style>
|
|
658
661
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
659
662
|
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f1f5f9;color:#1e293b;line-height:1.6}
|
|
660
|
-
header{background:#0f172a;color:#fff;padding:16px 24px;display:flex;align-items:center;justify-content:space-between}
|
|
661
|
-
header h1{font-size:20px;font-weight:600}
|
|
662
|
-
header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500}
|
|
663
|
-
.header-actions{display:flex;align-items:center;gap:12px}
|
|
664
|
-
.agent-team-entry{display:inline-flex;align-items:center;gap:8px;padding:9px 16px;border:1px solid rgba(255,255,255,.2);border-radius:999px;background:linear-gradient(135deg,#6366f1,#8b5cf6 55%,#d946ef);color:#fff;text-decoration:none;font-size:14px;font-weight:700;letter-spacing:.01em;box-shadow:0 8px 24px rgba(99,102,241,.38);transition:transform .18s ease,box-shadow .18s ease,filter .18s ease}
|
|
665
|
-
.agent-team-entry:hover{transform:translateY(-2px);box-shadow:0 12px 30px rgba(139,92,246,.52);filter:saturate(1.16)}
|
|
666
|
-
.agent-team-entry:focus-visible{outline:3px solid rgba(196,181,253,.65);outline-offset:3px}
|
|
667
|
-
.agent-team-entry .agent-team-icon{font-size:16px;line-height:1}
|
|
663
|
+
header{background:#0f172a;color:#fff;padding:16px 24px;display:flex;align-items:center;justify-content:space-between}
|
|
664
|
+
header h1{font-size:20px;font-weight:600}
|
|
665
|
+
header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500}
|
|
666
|
+
.header-actions{display:flex;align-items:center;gap:12px}
|
|
667
|
+
.agent-team-entry{display:inline-flex;align-items:center;gap:8px;padding:9px 16px;border:1px solid rgba(255,255,255,.2);border-radius:999px;background:linear-gradient(135deg,#6366f1,#8b5cf6 55%,#d946ef);color:#fff;text-decoration:none;font-size:14px;font-weight:700;letter-spacing:.01em;box-shadow:0 8px 24px rgba(99,102,241,.38);transition:transform .18s ease,box-shadow .18s ease,filter .18s ease}
|
|
668
|
+
.agent-team-entry:hover{transform:translateY(-2px);box-shadow:0 12px 30px rgba(139,92,246,.52);filter:saturate(1.16)}
|
|
669
|
+
.agent-team-entry:focus-visible{outline:3px solid rgba(196,181,253,.65);outline-offset:3px}
|
|
670
|
+
.agent-team-entry .agent-team-icon{font-size:16px;line-height:1}
|
|
668
671
|
.badge-running{background:#16a34a;color:#fff}
|
|
669
672
|
.badge-stopped{background:#94a3b8;color:#fff}
|
|
670
673
|
/* container 完全不限宽、不留左右内边距 —— step-2 是三列卡片需要尽量利用屏幕;
|
|
@@ -732,18 +735,18 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
732
735
|
.toast-error{background:#ef4444}
|
|
733
736
|
@keyframes slideIn{from{transform:translateX(100%);opacity:0}to{transform:translateX(0);opacity:1}}
|
|
734
737
|
.spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite}
|
|
735
|
-
@keyframes spin{to{transform:rotate(360deg)}}
|
|
736
|
-
@media(max-width:520px){header{padding:12px 14px}.header-actions{gap:8px}.agent-team-entry{padding:8px 11px;font-size:13px}header .badge{padding:4px 8px}}
|
|
737
|
-
</style>
|
|
738
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
739
|
+
@media(max-width:520px){header{padding:12px 14px}.header-actions{gap:8px}.agent-team-entry{padding:8px 11px;font-size:13px}header .badge{padding:4px 8px}}
|
|
740
|
+
</style>
|
|
738
741
|
</head>
|
|
739
742
|
<body>
|
|
740
|
-
<header>
|
|
741
|
-
<h1>ChatCCC</h1>
|
|
742
|
-
<div class="header-actions">
|
|
743
|
-
<a href="/agent-team" class="agent-team-entry"><span class="agent-team-icon" aria-hidden="true">✦</span>Agent Team <span aria-hidden="true">→</span></a>
|
|
744
|
-
<span id="header-badge" class="badge badge-stopped">未启动</span>
|
|
745
|
-
</div>
|
|
746
|
-
</header>
|
|
743
|
+
<header>
|
|
744
|
+
<h1>ChatCCC</h1>
|
|
745
|
+
<div class="header-actions">
|
|
746
|
+
<a href="/agent-team" class="agent-team-entry"><span class="agent-team-icon" aria-hidden="true">✦</span>Agent Team <span aria-hidden="true">→</span></a>
|
|
747
|
+
<span id="header-badge" class="badge badge-stopped">未启动</span>
|
|
748
|
+
</div>
|
|
749
|
+
</header>
|
|
747
750
|
<div class="container">
|
|
748
751
|
|
|
749
752
|
<!-- ===== Setup Wizard ===== -->
|
|
@@ -791,7 +794,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
791
794
|
</div>
|
|
792
795
|
</div>
|
|
793
796
|
|
|
794
|
-
<!-- 微信 iLink -->
|
|
797
|
+
<!-- 微信 iLink -->
|
|
795
798
|
<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:14px;margin-bottom:12px" id="platform-block-ilink">
|
|
796
799
|
<div style="display:flex;align-items:center;justify-content:space-between">
|
|
797
800
|
<div>
|
|
@@ -800,20 +803,20 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
800
803
|
</div>
|
|
801
804
|
<input type="checkbox" class="agent-toggle" id="platform-enable-ilink" checked onchange="onWizardPlatformToggle('ilink', this.checked)">
|
|
802
805
|
</div>
|
|
803
|
-
</div>
|
|
804
|
-
|
|
805
|
-
<!-- Web UI 启动行为 -->
|
|
806
|
-
<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:14px;margin-bottom:12px" id="web-ui-startup-block">
|
|
807
|
-
<div style="display:flex;align-items:center;justify-content:space-between;gap:16px">
|
|
808
|
-
<div>
|
|
809
|
-
<div style="font-weight:600;font-size:14px">启动时打开 Web UI</div>
|
|
810
|
-
<div style="font-size:12px;color:#64748b">直接运行 ChatCCC 时用系统默认浏览器打开管理页面;内部重启始终不打开</div>
|
|
811
|
-
</div>
|
|
812
|
-
<input type="checkbox" class="agent-toggle" id="field-CHATCCC_WEB_UI_OPEN_ON_START" checked>
|
|
813
|
-
</div>
|
|
814
|
-
</div>
|
|
815
|
-
|
|
816
|
-
<!-- Chrome CDP -->
|
|
806
|
+
</div>
|
|
807
|
+
|
|
808
|
+
<!-- Web UI 启动行为 -->
|
|
809
|
+
<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:14px;margin-bottom:12px" id="web-ui-startup-block">
|
|
810
|
+
<div style="display:flex;align-items:center;justify-content:space-between;gap:16px">
|
|
811
|
+
<div>
|
|
812
|
+
<div style="font-weight:600;font-size:14px">启动时打开 Web UI</div>
|
|
813
|
+
<div style="font-size:12px;color:#64748b">直接运行 ChatCCC 时用系统默认浏览器打开管理页面;内部重启始终不打开</div>
|
|
814
|
+
</div>
|
|
815
|
+
<input type="checkbox" class="agent-toggle" id="field-CHATCCC_WEB_UI_OPEN_ON_START" checked>
|
|
816
|
+
</div>
|
|
817
|
+
</div>
|
|
818
|
+
|
|
819
|
+
<!-- Chrome CDP -->
|
|
817
820
|
<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:14px;margin-bottom:12px" id="chrome-devtools-block">
|
|
818
821
|
<div style="display:flex;align-items:center;justify-content:space-between">
|
|
819
822
|
<div>
|
|
@@ -931,7 +934,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
931
934
|
</fieldset>
|
|
932
935
|
</div>
|
|
933
936
|
|
|
934
|
-
<!-- Codex 卡片 -->
|
|
937
|
+
<!-- Codex 卡片 -->
|
|
935
938
|
<div class="agent-card" id="agent-card-codex">
|
|
936
939
|
<div class="agent-card-header">
|
|
937
940
|
<input type="checkbox" class="agent-toggle" id="agent-enable-codex" onchange="onAgentToggle('codex', this.checked)">
|
|
@@ -957,54 +960,67 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
957
960
|
<label>备选模型(选填)</label>
|
|
958
961
|
<input type="text" id="field-CHATCCC_CODEX_ALTERNATIVE_MODEL" placeholder="加入 /model 列表,便于会话内切换">
|
|
959
962
|
</div>
|
|
960
|
-
<div class="form-group">
|
|
961
|
-
<label>努力程度 (Effort)</label>
|
|
962
|
-
<input type="text" id="field-CHATCCC_CODEX_EFFORT" placeholder="留空由 codex config.toml 决定">
|
|
963
|
-
</div>
|
|
964
|
-
<div class="form-group">
|
|
965
|
-
<label style="display:flex;align-items:center;gap:8px">
|
|
966
|
-
<input type="checkbox" id="field-CHATCCC_CODEX_FAST_MODE"> Fast 模式
|
|
967
|
-
</label>
|
|
968
|
-
</div>
|
|
969
|
-
<button class="btn btn-outline" onclick="validateCli('codex')" style="margin-bottom:12px">检测 Codex CLI</button>
|
|
963
|
+
<div class="form-group">
|
|
964
|
+
<label>努力程度 (Effort)</label>
|
|
965
|
+
<input type="text" id="field-CHATCCC_CODEX_EFFORT" placeholder="留空由 codex config.toml 决定">
|
|
966
|
+
</div>
|
|
967
|
+
<div class="form-group">
|
|
968
|
+
<label style="display:flex;align-items:center;gap:8px">
|
|
969
|
+
<input type="checkbox" id="field-CHATCCC_CODEX_FAST_MODE"> Fast 模式
|
|
970
|
+
</label>
|
|
971
|
+
</div>
|
|
972
|
+
<button class="btn btn-outline" onclick="validateCli('codex')" style="margin-bottom:12px">检测 Codex CLI</button>
|
|
970
973
|
<div id="codex-validate-result"></div>
|
|
971
974
|
</fieldset>
|
|
972
|
-
</div>
|
|
973
|
-
|
|
974
|
-
<!-- CCC Agent 卡片 -->
|
|
975
|
-
<div class="agent-card" id="agent-card-ccc">
|
|
976
|
-
<div class="agent-card-header">
|
|
977
|
-
<input type="checkbox" class="agent-toggle" id="agent-enable-ccc" onchange="onAgentToggle('ccc', this.checked)">
|
|
978
|
-
<div class="meta">
|
|
979
|
-
<div class="name">CCC Agent</div>
|
|
980
|
-
<div class="desc">ChatCCC 内置 Agent<br>使用 DeepSeek 兼容 API</div>
|
|
981
|
-
</div>
|
|
982
|
-
</div>
|
|
983
|
-
<label class="agent-default-row">
|
|
984
|
-
<input type="checkbox" id="agent-default-ccc" onchange="onDefaultAgentToggle('ccc', this.checked)">
|
|
985
|
-
设为默认 Agent
|
|
986
|
-
</label>
|
|
987
|
-
<fieldset class="agent-body" id="agent-body-ccc" disabled>
|
|
988
|
-
<div class="form-group">
|
|
989
|
-
<label>API Key</label>
|
|
990
|
-
<input type="password" id="field-CHATCCC_CCC_API_KEY" placeholder="DeepSeek 兼容 API Key">
|
|
991
|
-
</div>
|
|
992
|
-
<div class="form-group">
|
|
993
|
-
<label>Base URL</label>
|
|
994
|
-
<input type="text" id="field-CHATCCC_CCC_BASE_URL" placeholder="https://api.deepseek.com/v1">
|
|
995
|
-
</div>
|
|
996
|
-
<div class="form-group">
|
|
997
|
-
<label>模型</label>
|
|
998
|
-
<input type="text" id="field-CHATCCC_CCC_MODEL" placeholder="deepseek-v4-pro">
|
|
999
|
-
</div>
|
|
1000
|
-
<div class="form-group">
|
|
1001
|
-
<label>备选模型(选填)</label>
|
|
1002
|
-
<input type="text" id="field-CHATCCC_CCC_ALTERNATIVE_MODEL" placeholder="加入 /model 列表,便于会话内切换">
|
|
1003
|
-
</div>
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
975
|
+
</div>
|
|
976
|
+
|
|
977
|
+
<!-- CCC Agent 卡片 -->
|
|
978
|
+
<div class="agent-card" id="agent-card-ccc">
|
|
979
|
+
<div class="agent-card-header">
|
|
980
|
+
<input type="checkbox" class="agent-toggle" id="agent-enable-ccc" onchange="onAgentToggle('ccc', this.checked)">
|
|
981
|
+
<div class="meta">
|
|
982
|
+
<div class="name">CCC Agent</div>
|
|
983
|
+
<div class="desc">ChatCCC 内置 Agent<br>使用 DeepSeek 兼容 API</div>
|
|
984
|
+
</div>
|
|
985
|
+
</div>
|
|
986
|
+
<label class="agent-default-row">
|
|
987
|
+
<input type="checkbox" id="agent-default-ccc" onchange="onDefaultAgentToggle('ccc', this.checked)">
|
|
988
|
+
设为默认 Agent
|
|
989
|
+
</label>
|
|
990
|
+
<fieldset class="agent-body" id="agent-body-ccc" disabled>
|
|
991
|
+
<div class="form-group">
|
|
992
|
+
<label>API Key</label>
|
|
993
|
+
<input type="password" id="field-CHATCCC_CCC_API_KEY" placeholder="DeepSeek 兼容 API Key">
|
|
994
|
+
</div>
|
|
995
|
+
<div class="form-group">
|
|
996
|
+
<label>Base URL</label>
|
|
997
|
+
<input type="text" id="field-CHATCCC_CCC_BASE_URL" placeholder="https://api.deepseek.com/v1">
|
|
998
|
+
</div>
|
|
999
|
+
<div class="form-group">
|
|
1000
|
+
<label>模型</label>
|
|
1001
|
+
<input type="text" id="field-CHATCCC_CCC_MODEL" placeholder="deepseek-v4-pro">
|
|
1002
|
+
</div>
|
|
1003
|
+
<div class="form-group">
|
|
1004
|
+
<label>备选模型(选填)</label>
|
|
1005
|
+
<input type="text" id="field-CHATCCC_CCC_ALTERNATIVE_MODEL" placeholder="加入 /model 列表,便于会话内切换">
|
|
1006
|
+
</div>
|
|
1007
|
+
<div class="form-group">
|
|
1008
|
+
<label>Effort(推理强度,选填)</label>
|
|
1009
|
+
<select id="field-CHATCCC_CCC_EFFORT">
|
|
1010
|
+
<option value="">(留空/默认,服务端 medium)</option>
|
|
1011
|
+
<option value="none">none - 直接作答,最省 token</option>
|
|
1012
|
+
<option value="minimal">minimal</option>
|
|
1013
|
+
<option value="low">low</option>
|
|
1014
|
+
<option value="medium">medium</option>
|
|
1015
|
+
<option value="high">high</option>
|
|
1016
|
+
<option value="xhigh">xhigh</option>
|
|
1017
|
+
<option value="max">max - 最强推理</option>
|
|
1018
|
+
</select>
|
|
1019
|
+
</div>
|
|
1020
|
+
</fieldset>
|
|
1021
|
+
</div>
|
|
1022
|
+
|
|
1023
|
+
</div>
|
|
1008
1024
|
|
|
1009
1025
|
<div class="btn-group" style="justify-content:space-between">
|
|
1010
1026
|
<button class="btn btn-outline" onclick="goStep(1)">返回</button>
|
|
@@ -1047,16 +1063,16 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1047
1063
|
<span id="dash-platform-feishu-label">已启用</span>
|
|
1048
1064
|
</label>
|
|
1049
1065
|
</div>
|
|
1050
|
-
<div class="config-row"><span class="key">App ID</span><span class="val" id="cfg-APP_ID">-</span></div>
|
|
1051
|
-
<div class="config-row"><span class="key">App Secret</span><span class="val" id="cfg-APP_SECRET">-</span></div>
|
|
1052
|
-
<div class="config-row"><span class="key">平台类型</span><span class="val" id="cfg-FEISHU_PLATFORM_TYPE">-</span></div>
|
|
1053
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:飞书开关、App ID、App Secret 或平台类型变更需要重启 ChatCCC。</div>
|
|
1054
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('feishu')">编辑</button>
|
|
1066
|
+
<div class="config-row"><span class="key">App ID</span><span class="val" id="cfg-APP_ID">-</span></div>
|
|
1067
|
+
<div class="config-row"><span class="key">App Secret</span><span class="val" id="cfg-APP_SECRET">-</span></div>
|
|
1068
|
+
<div class="config-row"><span class="key">平台类型</span><span class="val" id="cfg-FEISHU_PLATFORM_TYPE">-</span></div>
|
|
1069
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:飞书开关、App ID、App Secret 或平台类型变更需要重启 ChatCCC。</div>
|
|
1070
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('feishu')">编辑</button>
|
|
1055
1071
|
</div>
|
|
1056
1072
|
</details>
|
|
1057
1073
|
|
|
1058
|
-
<details class="card config-section">
|
|
1059
|
-
<summary>微信 iLink</summary>
|
|
1074
|
+
<details class="card config-section">
|
|
1075
|
+
<summary>微信 iLink</summary>
|
|
1060
1076
|
<div class="section-detail">
|
|
1061
1077
|
<div class="config-row">
|
|
1062
1078
|
<span class="key">状态</span>
|
|
@@ -1065,30 +1081,30 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1065
1081
|
<span id="dash-platform-ilink-label">未启用</span>
|
|
1066
1082
|
</label>
|
|
1067
1083
|
</div>
|
|
1068
|
-
<div id="ilink-forget-row" style="margin-top:6px;display:none">
|
|
1069
|
-
<button class="btn btn-outline" style="font-size:12px" onclick="forgetIlink()">忘记扫码</button>
|
|
1070
|
-
<span style="font-size:11px;color:#94a3b8;margin-left:8px">清除登录状态,重启后重新扫码</span>
|
|
1071
|
-
</div>
|
|
1072
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:微信 iLink 开关变更需要重启 ChatCCC。</div>
|
|
1073
|
-
</div>
|
|
1074
|
-
</details>
|
|
1075
|
-
|
|
1076
|
-
<details class="card config-section">
|
|
1077
|
-
<summary>Web UI</summary>
|
|
1078
|
-
<div class="section-detail">
|
|
1079
|
-
<div class="config-row"><span class="key">直接启动时自动打开</span><span class="val" id="cfg-WEB_UI_OPEN_ON_START">-</span></div>
|
|
1080
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:下次直接启动生效;内部重启始终不打开。Linux 无图形桌面时会跳过打开并输出 SSH 隧道提示。</div>
|
|
1081
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('webUi')">编辑</button>
|
|
1082
|
-
</div>
|
|
1083
|
-
</details>
|
|
1084
|
-
|
|
1085
|
-
<details class="card config-section">
|
|
1084
|
+
<div id="ilink-forget-row" style="margin-top:6px;display:none">
|
|
1085
|
+
<button class="btn btn-outline" style="font-size:12px" onclick="forgetIlink()">忘记扫码</button>
|
|
1086
|
+
<span style="font-size:11px;color:#94a3b8;margin-left:8px">清除登录状态,重启后重新扫码</span>
|
|
1087
|
+
</div>
|
|
1088
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:微信 iLink 开关变更需要重启 ChatCCC。</div>
|
|
1089
|
+
</div>
|
|
1090
|
+
</details>
|
|
1091
|
+
|
|
1092
|
+
<details class="card config-section">
|
|
1093
|
+
<summary>Web UI</summary>
|
|
1094
|
+
<div class="section-detail">
|
|
1095
|
+
<div class="config-row"><span class="key">直接启动时自动打开</span><span class="val" id="cfg-WEB_UI_OPEN_ON_START">-</span></div>
|
|
1096
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:下次直接启动生效;内部重启始终不打开。Linux 无图形桌面时会跳过打开并输出 SSH 隧道提示。</div>
|
|
1097
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('webUi')">编辑</button>
|
|
1098
|
+
</div>
|
|
1099
|
+
</details>
|
|
1100
|
+
|
|
1101
|
+
<details class="card config-section">
|
|
1086
1102
|
<summary>Chrome CDP(选填)</summary>
|
|
1087
1103
|
<div class="section-detail">
|
|
1088
1104
|
<div class="config-row"><span class="key">状态</span><span class="val" id="cfg-CHROME_DEVTOOLS_ENABLED">-</span></div>
|
|
1089
1105
|
<div class="config-row" id="cfg-CHROME_DEVTOOLS_PORT_ROW"><span class="key">CDP 端口</span><span class="val" id="cfg-CHROME_DEVTOOLS_PORT">-</span></div>
|
|
1090
1106
|
<div class="config-row" id="cfg-CHROME_DEVTOOLS_PATH_ROW"><span class="key">Chrome 路径</span><span class="val" id="cfg-CHROME_DEVTOOLS_PATH">-</span></div>
|
|
1091
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后立即应用到 Chrome CDP 守护进程。依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。</div>
|
|
1107
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后立即应用到 Chrome CDP 守护进程。依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。</div>
|
|
1092
1108
|
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('chromeDevtools')">编辑</button>
|
|
1093
1109
|
</div>
|
|
1094
1110
|
</details>
|
|
@@ -1101,10 +1117,10 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1101
1117
|
<div class="config-row"><span class="key">Effort</span><span class="val" id="cfg-ANTHROPIC_EFFORT">-</span></div>
|
|
1102
1118
|
<div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-ANTHROPIC_API_KEY">-</span></div>
|
|
1103
1119
|
<div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-ANTHROPIC_BASE_URL">-</span></div>
|
|
1104
|
-
<div class="config-row"><span class="key">Max Turns</span><span class="val" id="cfg-ANTHROPIC_MAX_TURN">-</span><span class="hint">(0=无限制)</span></div>
|
|
1105
|
-
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-claude" onchange="setDashboardDefaultAgent('claude', this.checked)"> 设为默认 Agent</label>
|
|
1106
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1107
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('claude')">编辑</button>
|
|
1120
|
+
<div class="config-row"><span class="key">Max Turns</span><span class="val" id="cfg-ANTHROPIC_MAX_TURN">-</span><span class="hint">(0=无限制)</span></div>
|
|
1121
|
+
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-claude" onchange="setDashboardDefaultAgent('claude', this.checked)"> 设为默认 Agent</label>
|
|
1122
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1123
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('claude')">编辑</button>
|
|
1108
1124
|
</div>
|
|
1109
1125
|
</details>
|
|
1110
1126
|
|
|
@@ -1115,39 +1131,39 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1115
1131
|
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-CURSOR_MODEL">-</span></div>
|
|
1116
1132
|
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CURSOR_ALTERNATIVE_MODEL">-</span></div>
|
|
1117
1133
|
<div class="config-row"><span class="key">头像电池电量</span><span class="val" id="cfg-CURSOR_AVATAR_BATTERY_MODE">-</span></div>
|
|
1118
|
-
<div class="config-row" id="cfg-CURSOR_ON_DEMAND_MONTHLY_BUDGET_ROW"><span class="key">每月On demand use预算</span><span class="val" id="cfg-CURSOR_ON_DEMAND_MONTHLY_BUDGET">-</span></div>
|
|
1119
|
-
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-cursor" onchange="setDashboardDefaultAgent('cursor', this.checked)"> 设为默认 Agent</label>
|
|
1120
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1121
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('cursor')">编辑</button>
|
|
1134
|
+
<div class="config-row" id="cfg-CURSOR_ON_DEMAND_MONTHLY_BUDGET_ROW"><span class="key">每月On demand use预算</span><span class="val" id="cfg-CURSOR_ON_DEMAND_MONTHLY_BUDGET">-</span></div>
|
|
1135
|
+
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-cursor" onchange="setDashboardDefaultAgent('cursor', this.checked)"> 设为默认 Agent</label>
|
|
1136
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1137
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('cursor')">编辑</button>
|
|
1122
1138
|
</div>
|
|
1123
1139
|
</details>
|
|
1124
1140
|
|
|
1125
|
-
<details class="card config-section" id="dash-codex">
|
|
1141
|
+
<details class="card config-section" id="dash-codex">
|
|
1126
1142
|
<summary>Codex Agent</summary>
|
|
1127
1143
|
<div class="section-detail">
|
|
1128
1144
|
<div class="config-row"><span class="key">CLI 路径</span><span class="val" id="cfg-CODEX_PATH">-</span></div>
|
|
1129
1145
|
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-CODEX_MODEL">-</span></div>
|
|
1130
|
-
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CODEX_ALTERNATIVE_MODEL">-</span></div>
|
|
1131
|
-
<div class="config-row"><span class="key">Effort</span><span class="val" id="cfg-CODEX_EFFORT">-</span></div>
|
|
1132
|
-
<div class="config-row"><span class="key">Fast 模式</span><span class="val" id="cfg-CODEX_FAST_MODE">-</span></div>
|
|
1133
|
-
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-codex" onchange="setDashboardDefaultAgent('codex', this.checked)"> 设为默认 Agent</label>
|
|
1134
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1135
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('codex')">编辑</button>
|
|
1146
|
+
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CODEX_ALTERNATIVE_MODEL">-</span></div>
|
|
1147
|
+
<div class="config-row"><span class="key">Effort</span><span class="val" id="cfg-CODEX_EFFORT">-</span></div>
|
|
1148
|
+
<div class="config-row"><span class="key">Fast 模式</span><span class="val" id="cfg-CODEX_FAST_MODE">-</span></div>
|
|
1149
|
+
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-codex" onchange="setDashboardDefaultAgent('codex', this.checked)"> 设为默认 Agent</label>
|
|
1150
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1151
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('codex')">编辑</button>
|
|
1136
1152
|
</div>
|
|
1137
|
-
</details>
|
|
1138
|
-
|
|
1139
|
-
<details class="card config-section" id="dash-ccc">
|
|
1140
|
-
<summary>CCC Agent</summary>
|
|
1141
|
-
<div class="section-detail">
|
|
1142
|
-
<div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-CCC_API_KEY">-</span></div>
|
|
1143
|
-
<div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-CCC_BASE_URL">-</span></div>
|
|
1144
|
-
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-CCC_MODEL">-</span></div>
|
|
1145
|
-
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CCC_ALTERNATIVE_MODEL">-</span></div>
|
|
1146
|
-
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-ccc" onchange="setDashboardDefaultAgent('ccc', this.checked)"> 设为默认 Agent</label>
|
|
1147
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">备选模型仅加入 /model 人工切换列表;保存后下一条消息或下个新会话生效。</div>
|
|
1148
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('ccc')">编辑</button>
|
|
1149
|
-
</div>
|
|
1150
|
-
</details>
|
|
1153
|
+
</details>
|
|
1154
|
+
|
|
1155
|
+
<details class="card config-section" id="dash-ccc">
|
|
1156
|
+
<summary>CCC Agent</summary>
|
|
1157
|
+
<div class="section-detail">
|
|
1158
|
+
<div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-CCC_API_KEY">-</span></div>
|
|
1159
|
+
<div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-CCC_BASE_URL">-</span></div>
|
|
1160
|
+
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-CCC_MODEL">-</span></div>
|
|
1161
|
+
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CCC_ALTERNATIVE_MODEL">-</span></div>
|
|
1162
|
+
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-ccc" onchange="setDashboardDefaultAgent('ccc', this.checked)"> 设为默认 Agent</label>
|
|
1163
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">备选模型仅加入 /model 人工切换列表;保存后下一条消息或下个新会话生效。</div>
|
|
1164
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('ccc')">编辑</button>
|
|
1165
|
+
</div>
|
|
1166
|
+
</details>
|
|
1151
1167
|
|
|
1152
1168
|
<div class="card" id="dash-no-agent-hint" style="text-align:center;color:#94a3b8;display:none">
|
|
1153
1169
|
未启用任何 AI Agent。点击下方按钮重新运行配置向导启用。
|
|
@@ -1161,8 +1177,8 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1161
1177
|
<!-- Edit Modal -->
|
|
1162
1178
|
<div id="edit-modal" class="card hidden" style="position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90%;max-width:480px;z-index:200;max-height:80vh;overflow-y:auto">
|
|
1163
1179
|
<h2 id="edit-modal-title">编辑配置</h2>
|
|
1164
|
-
<div id="edit-modal-effect" class="hint" style="margin-bottom:12px;line-height:1.6"></div>
|
|
1165
|
-
<div id="edit-modal-fields"></div>
|
|
1180
|
+
<div id="edit-modal-effect" class="hint" style="margin-bottom:12px;line-height:1.6"></div>
|
|
1181
|
+
<div id="edit-modal-fields"></div>
|
|
1166
1182
|
<div class="btn-group" style="justify-content:flex-end;margin-top:16px">
|
|
1167
1183
|
<button class="btn btn-outline" onclick="closeEditModal()">取消</button>
|
|
1168
1184
|
<button class="btn btn-primary" onclick="saveEdit()">保存</button>
|
|
@@ -1175,8 +1191,8 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1175
1191
|
<script>
|
|
1176
1192
|
let state = {
|
|
1177
1193
|
view: 'loading',
|
|
1178
|
-
// 四个 Agent 各自的启用开关;初始全 false,renderStep2() 会按已存在 config 自动打开
|
|
1179
|
-
agentsEnabled: { claude: false, cursor: false, codex: false, ccc: false },
|
|
1194
|
+
// 四个 Agent 各自的启用开关;初始全 false,renderStep2() 会按已存在 config 自动打开
|
|
1195
|
+
agentsEnabled: { claude: false, cursor: false, codex: false, ccc: false },
|
|
1180
1196
|
defaultAgent: 'claude',
|
|
1181
1197
|
wizardStep: 1,
|
|
1182
1198
|
config: {},
|
|
@@ -1192,39 +1208,39 @@ var step2InputBound = false;
|
|
|
1192
1208
|
const AGENT_FIELDS = {
|
|
1193
1209
|
claude: ['CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_SUBAGENT_MODEL','CHATCCC_ANTHROPIC_EFFORT','CHATCCC_ANTHROPIC_API_KEY','CHATCCC_ANTHROPIC_BASE_URL','CHATCCC_ANTHROPIC_MAX_TURN'],
|
|
1194
1210
|
cursor: ['CHATCCC_CURSOR_PATH','CHATCCC_CURSOR_MODEL','CHATCCC_CURSOR_ALTERNATIVE_MODEL','CHATCCC_CURSOR_AVATAR_BATTERY_MODE','CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'],
|
|
1195
|
-
codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_ALTERNATIVE_MODEL','CHATCCC_CODEX_EFFORT','CHATCCC_CODEX_FAST_MODE'],
|
|
1196
|
-
ccc: ['CHATCCC_CCC_API_KEY','CHATCCC_CCC_BASE_URL','CHATCCC_CCC_MODEL','CHATCCC_CCC_ALTERNATIVE_MODEL']
|
|
1211
|
+
codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_ALTERNATIVE_MODEL','CHATCCC_CODEX_EFFORT','CHATCCC_CODEX_FAST_MODE'],
|
|
1212
|
+
ccc: ['CHATCCC_CCC_API_KEY','CHATCCC_CCC_BASE_URL','CHATCCC_CCC_MODEL','CHATCCC_CCC_ALTERNATIVE_MODEL','CHATCCC_CCC_EFFORT']
|
|
1197
1213
|
};
|
|
1198
|
-
const FEISHU_FIELDS = ['CHATCCC_APP_ID','CHATCCC_APP_SECRET'];
|
|
1199
|
-
const WEB_UI_FIELDS = ['CHATCCC_WEB_UI_OPEN_ON_START'];
|
|
1200
|
-
const CHROME_DEVTOOLS_FIELDS = ['CHATCCC_CHROME_DEVTOOLS_ENABLED','CHATCCC_CHROME_DEVTOOLS_PORT','CHATCCC_CHROME_DEVTOOLS_PATH'];
|
|
1214
|
+
const FEISHU_FIELDS = ['CHATCCC_APP_ID','CHATCCC_APP_SECRET'];
|
|
1215
|
+
const WEB_UI_FIELDS = ['CHATCCC_WEB_UI_OPEN_ON_START'];
|
|
1216
|
+
const CHROME_DEVTOOLS_FIELDS = ['CHATCCC_CHROME_DEVTOOLS_ENABLED','CHATCCC_CHROME_DEVTOOLS_PORT','CHATCCC_CHROME_DEVTOOLS_PATH'];
|
|
1201
1217
|
|
|
1202
1218
|
function cursorBatteryModeLabel(value) {
|
|
1203
1219
|
return value === 'onDemandUse' ? 'On demand use 金额' : 'API 使用比例';
|
|
1204
1220
|
}
|
|
1205
1221
|
|
|
1206
|
-
function configEffectHint(section) {
|
|
1207
|
-
if (section === 'feishu') return '生效范围:App ID、App Secret、平台类型或飞书开关变更需要重启 ChatCCC;其它未变更项仅保存。';
|
|
1208
|
-
if (section === 'webUi') return '生效范围:下次直接启动生效;内部重启始终不打开。';
|
|
1209
|
-
if (section === 'chromeDevtools') return '生效范围:保存后立即应用到 Chrome CDP 守护进程。';
|
|
1210
|
-
if (section === 'claude') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1211
|
-
if (section === 'cursor') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1212
|
-
if (section === 'codex') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1213
|
-
return '生效范围:保存后按配置类型应用。';
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
function toastConfigApplyResult(result, savedText) {
|
|
1217
|
-
var text = savedText || '配置已保存';
|
|
1218
|
-
if (result && result.mode === 'reload') {
|
|
1219
|
-
toast(text + ',后续消息/新会话已生效。');
|
|
1220
|
-
} else if (result && result.mode === 'restart-required') {
|
|
1221
|
-
toast(text + ',需重启服务生效。');
|
|
1222
|
-
} else {
|
|
1223
|
-
toast(text);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
function onCursorBatteryModeChange(prefix, value) {
|
|
1222
|
+
function configEffectHint(section) {
|
|
1223
|
+
if (section === 'feishu') return '生效范围:App ID、App Secret、平台类型或飞书开关变更需要重启 ChatCCC;其它未变更项仅保存。';
|
|
1224
|
+
if (section === 'webUi') return '生效范围:下次直接启动生效;内部重启始终不打开。';
|
|
1225
|
+
if (section === 'chromeDevtools') return '生效范围:保存后立即应用到 Chrome CDP 守护进程。';
|
|
1226
|
+
if (section === 'claude') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1227
|
+
if (section === 'cursor') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1228
|
+
if (section === 'codex') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1229
|
+
return '生效范围:保存后按配置类型应用。';
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function toastConfigApplyResult(result, savedText) {
|
|
1233
|
+
var text = savedText || '配置已保存';
|
|
1234
|
+
if (result && result.mode === 'reload') {
|
|
1235
|
+
toast(text + ',后续消息/新会话已生效。');
|
|
1236
|
+
} else if (result && result.mode === 'restart-required') {
|
|
1237
|
+
toast(text + ',需重启服务生效。');
|
|
1238
|
+
} else {
|
|
1239
|
+
toast(text);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
function onCursorBatteryModeChange(prefix, value) {
|
|
1228
1244
|
var rowId = prefix === 'edit-' ? 'edit-cursor-on-demand-budget-row' : 'field-cursor-on-demand-budget-row';
|
|
1229
1245
|
var row = document.getElementById(rowId);
|
|
1230
1246
|
if (row) row.style.display = value === 'onDemandUse' ? '' : 'none';
|
|
@@ -1258,10 +1274,10 @@ async function onPlatformToggle(platform, enabled) {
|
|
|
1258
1274
|
if (result.ok) {
|
|
1259
1275
|
state.config.platforms = state.config.platforms || {};
|
|
1260
1276
|
state.config.platforms[platform] = state.config.platforms[platform] || {};
|
|
1261
|
-
state.config.platforms[platform].enabled = enabled;
|
|
1262
|
-
var label = document.getElementById('dash-platform-' + platform + '-label');
|
|
1263
|
-
if (label) label.textContent = enabled ? '已启用' : '未启用';
|
|
1264
|
-
toastConfigApplyResult(result, (platform === 'feishu' ? '飞书' : '微信 iLink') + (enabled ? ' 已启用' : ' 已禁用'));
|
|
1277
|
+
state.config.platforms[platform].enabled = enabled;
|
|
1278
|
+
var label = document.getElementById('dash-platform-' + platform + '-label');
|
|
1279
|
+
if (label) label.textContent = enabled ? '已启用' : '未启用';
|
|
1280
|
+
toastConfigApplyResult(result, (platform === 'feishu' ? '飞书' : '微信 iLink') + (enabled ? ' 已启用' : ' 已禁用'));
|
|
1265
1281
|
} else {
|
|
1266
1282
|
toast('保存失败: ' + (result.error || '未知错误'), 'error');
|
|
1267
1283
|
// 还原 toggle
|
|
@@ -1298,25 +1314,25 @@ function onAgentToggle(agent, enabled) {
|
|
|
1298
1314
|
function firstEnabledAgent() {
|
|
1299
1315
|
if (state.agentsEnabled.claude) return 'claude';
|
|
1300
1316
|
if (state.agentsEnabled.cursor) return 'cursor';
|
|
1301
|
-
if (state.agentsEnabled.codex) return 'codex';
|
|
1302
|
-
if (state.agentsEnabled.ccc) return 'ccc';
|
|
1317
|
+
if (state.agentsEnabled.codex) return 'codex';
|
|
1318
|
+
if (state.agentsEnabled.ccc) return 'ccc';
|
|
1303
1319
|
return null;
|
|
1304
1320
|
}
|
|
1305
1321
|
|
|
1306
|
-
function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn) {
|
|
1322
|
+
function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn) {
|
|
1307
1323
|
if (claudeOn && c.claude && c.claude.defaultAgent === true) return 'claude';
|
|
1308
1324
|
if (cursorOn && c.cursor && c.cursor.defaultAgent === true) return 'cursor';
|
|
1309
|
-
if (codexOn && c.codex && c.codex.defaultAgent === true) return 'codex';
|
|
1310
|
-
if (cccOn && c.ccc && c.ccc.defaultAgent === true) return 'ccc';
|
|
1325
|
+
if (codexOn && c.codex && c.codex.defaultAgent === true) return 'codex';
|
|
1326
|
+
if (cccOn && c.ccc && c.ccc.defaultAgent === true) return 'ccc';
|
|
1311
1327
|
if (claudeOn) return 'claude';
|
|
1312
1328
|
if (cursorOn) return 'cursor';
|
|
1313
|
-
if (codexOn) return 'codex';
|
|
1314
|
-
if (cccOn) return 'ccc';
|
|
1329
|
+
if (codexOn) return 'codex';
|
|
1330
|
+
if (cccOn) return 'ccc';
|
|
1315
1331
|
return 'claude';
|
|
1316
1332
|
}
|
|
1317
1333
|
|
|
1318
1334
|
function updateDefaultAgentToggles() {
|
|
1319
|
-
['claude','cursor','codex','ccc'].forEach(function(agent){
|
|
1335
|
+
['claude','cursor','codex','ccc'].forEach(function(agent){
|
|
1320
1336
|
var el = document.getElementById('agent-default-' + agent);
|
|
1321
1337
|
if (el) {
|
|
1322
1338
|
el.checked = state.defaultAgent === agent;
|
|
@@ -1347,8 +1363,8 @@ function updateStep2NextBtn() {
|
|
|
1347
1363
|
var validCount = 0;
|
|
1348
1364
|
if (state.agentsEnabled.claude) validCount++;
|
|
1349
1365
|
if (state.agentsEnabled.cursor) validCount++;
|
|
1350
|
-
if (state.agentsEnabled.codex) validCount++;
|
|
1351
|
-
if (state.agentsEnabled.ccc) validCount++;
|
|
1366
|
+
if (state.agentsEnabled.codex) validCount++;
|
|
1367
|
+
if (state.agentsEnabled.ccc) validCount++;
|
|
1352
1368
|
if (validCount > 0 && !state.defaultAgent) {
|
|
1353
1369
|
state.defaultAgent = firstEnabledAgent();
|
|
1354
1370
|
updateDefaultAgentToggles();
|
|
@@ -1459,11 +1475,11 @@ function renderStep1() {
|
|
|
1459
1475
|
// 飞书凭证字段初始显隐
|
|
1460
1476
|
var credFields = document.getElementById('feishu-cred-fields');
|
|
1461
1477
|
if (credFields) credFields.style.display = feishuEnabled ? '' : 'none';
|
|
1462
|
-
var ilToggle = document.getElementById('platform-enable-ilink');
|
|
1463
|
-
if (ilToggle) ilToggle.checked = ilinkEnabled;
|
|
1464
|
-
var webUiOpenOnStart = document.getElementById('field-CHATCCC_WEB_UI_OPEN_ON_START');
|
|
1465
|
-
if (webUiOpenOnStart) webUiOpenOnStart.checked = !c.webUi || c.webUi.openOnStart !== false;
|
|
1466
|
-
var cd = c.chromeDevtools || {};
|
|
1478
|
+
var ilToggle = document.getElementById('platform-enable-ilink');
|
|
1479
|
+
if (ilToggle) ilToggle.checked = ilinkEnabled;
|
|
1480
|
+
var webUiOpenOnStart = document.getElementById('field-CHATCCC_WEB_UI_OPEN_ON_START');
|
|
1481
|
+
if (webUiOpenOnStart) webUiOpenOnStart.checked = !c.webUi || c.webUi.openOnStart !== false;
|
|
1482
|
+
var cd = c.chromeDevtools || {};
|
|
1467
1483
|
var cdpEnabled = document.getElementById('field-CHATCCC_CHROME_DEVTOOLS_ENABLED');
|
|
1468
1484
|
if (cdpEnabled) cdpEnabled.checked = cd.enabled === true;
|
|
1469
1485
|
prefillNested('field-CHATCCC_CHROME_DEVTOOLS_PORT', cd.port || 15166);
|
|
@@ -1488,9 +1504,9 @@ function isAgentEnabled(node, keys) {
|
|
|
1488
1504
|
|
|
1489
1505
|
var CLAUDE_FALLBACK_KEYS = ['model','subagentModel','effort','maxTurn'];
|
|
1490
1506
|
var CURSOR_FALLBACK_KEYS = ['path','command','model','alternativeModel'];
|
|
1491
|
-
var CODEX_FALLBACK_KEYS = ['path','command','model','alternativeModel','effort','fastMode'];
|
|
1492
|
-
// 旧版 CCC 配置没有 enabled;只有 API Key 才表示用户实际完成了配置。
|
|
1493
|
-
var CCC_FALLBACK_KEYS = ['DEEPSEEK_API_KEY'];
|
|
1507
|
+
var CODEX_FALLBACK_KEYS = ['path','command','model','alternativeModel','effort','fastMode'];
|
|
1508
|
+
// 旧版 CCC 配置没有 enabled;只有 API Key 才表示用户实际完成了配置。
|
|
1509
|
+
var CCC_FALLBACK_KEYS = ['DEEPSEEK_API_KEY'];
|
|
1494
1510
|
|
|
1495
1511
|
function renderStep2() {
|
|
1496
1512
|
var c = state.config || {};
|
|
@@ -1515,35 +1531,36 @@ function renderStep2() {
|
|
|
1515
1531
|
var cursorBatteryModeEl = document.getElementById('field-CHATCCC_CURSOR_AVATAR_BATTERY_MODE');
|
|
1516
1532
|
if (cursorBatteryModeEl && !cursorBatteryModeEl.value) cursorBatteryModeEl.value = 'apiPercent';
|
|
1517
1533
|
onCursorBatteryModeChange('field-', cursorBatteryModeEl ? cursorBatteryModeEl.value : 'apiPercent');
|
|
1518
|
-
if (c.codex) {
|
|
1519
|
-
prefillNested('field-CHATCCC_CODEX_PATH', c.codex.path || c.codex.command);
|
|
1520
|
-
prefillNested('field-CHATCCC_CODEX_MODEL', c.codex.model);
|
|
1521
|
-
prefillNested('field-CHATCCC_CODEX_ALTERNATIVE_MODEL', c.codex.alternativeModel);
|
|
1522
|
-
prefillNested('field-CHATCCC_CODEX_EFFORT', c.codex.effort);
|
|
1523
|
-
}
|
|
1524
|
-
var codexFastModeEl = document.getElementById('field-CHATCCC_CODEX_FAST_MODE');
|
|
1525
|
-
if (codexFastModeEl) codexFastModeEl.checked = !!(c.codex && c.codex.fastMode === true);
|
|
1526
|
-
if (c.ccc) {
|
|
1527
|
-
prefillNested('field-CHATCCC_CCC_API_KEY', c.ccc.DEEPSEEK_API_KEY);
|
|
1528
|
-
prefillNested('field-CHATCCC_CCC_BASE_URL', c.ccc.DEEPSEEK_BASE_URL);
|
|
1529
|
-
prefillNested('field-CHATCCC_CCC_MODEL', c.ccc.model);
|
|
1530
|
-
prefillNested('field-CHATCCC_CCC_ALTERNATIVE_MODEL', c.ccc.alternativeModel);
|
|
1531
|
-
|
|
1534
|
+
if (c.codex) {
|
|
1535
|
+
prefillNested('field-CHATCCC_CODEX_PATH', c.codex.path || c.codex.command);
|
|
1536
|
+
prefillNested('field-CHATCCC_CODEX_MODEL', c.codex.model);
|
|
1537
|
+
prefillNested('field-CHATCCC_CODEX_ALTERNATIVE_MODEL', c.codex.alternativeModel);
|
|
1538
|
+
prefillNested('field-CHATCCC_CODEX_EFFORT', c.codex.effort);
|
|
1539
|
+
}
|
|
1540
|
+
var codexFastModeEl = document.getElementById('field-CHATCCC_CODEX_FAST_MODE');
|
|
1541
|
+
if (codexFastModeEl) codexFastModeEl.checked = !!(c.codex && c.codex.fastMode === true);
|
|
1542
|
+
if (c.ccc) {
|
|
1543
|
+
prefillNested('field-CHATCCC_CCC_API_KEY', c.ccc.DEEPSEEK_API_KEY);
|
|
1544
|
+
prefillNested('field-CHATCCC_CCC_BASE_URL', c.ccc.DEEPSEEK_BASE_URL);
|
|
1545
|
+
prefillNested('field-CHATCCC_CCC_MODEL', c.ccc.model);
|
|
1546
|
+
prefillNested('field-CHATCCC_CCC_ALTERNATIVE_MODEL', c.ccc.alternativeModel);
|
|
1547
|
+
prefillNested('field-CHATCCC_CCC_EFFORT', c.ccc.effort);
|
|
1548
|
+
}
|
|
1532
1549
|
|
|
1533
1550
|
// 按已有 config 决定每个 Agent 默认是否开启:优先 enabled 字段,缺省时按"任一字段非空"
|
|
1534
1551
|
var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
|
|
1535
1552
|
var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
|
|
1536
|
-
var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
|
|
1537
|
-
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
1538
|
-
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn);
|
|
1553
|
+
var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
|
|
1554
|
+
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
1555
|
+
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn);
|
|
1539
1556
|
document.getElementById('agent-enable-claude').checked = claudeOn;
|
|
1540
1557
|
document.getElementById('agent-enable-cursor').checked = cursorOn;
|
|
1541
|
-
document.getElementById('agent-enable-codex').checked = codexOn;
|
|
1542
|
-
document.getElementById('agent-enable-ccc').checked = cccOn;
|
|
1558
|
+
document.getElementById('agent-enable-codex').checked = codexOn;
|
|
1559
|
+
document.getElementById('agent-enable-ccc').checked = cccOn;
|
|
1543
1560
|
onAgentToggle('claude', claudeOn);
|
|
1544
1561
|
onAgentToggle('cursor', cursorOn);
|
|
1545
|
-
onAgentToggle('codex', codexOn);
|
|
1546
|
-
onAgentToggle('ccc', cccOn);
|
|
1562
|
+
onAgentToggle('codex', codexOn);
|
|
1563
|
+
onAgentToggle('ccc', cccOn);
|
|
1547
1564
|
updateDefaultAgentToggles();
|
|
1548
1565
|
|
|
1549
1566
|
// Cursor path placeholder/hint:把已探测到的路径显示为占位
|
|
@@ -1567,7 +1584,7 @@ function renderStep2() {
|
|
|
1567
1584
|
* 收集"待落地到 config.json 的扁平 vars"。
|
|
1568
1585
|
*
|
|
1569
1586
|
* - 飞书字段始终收集
|
|
1570
|
-
* - 四个 Agent 的 enabled 状态都显式下发,让 config.json 持久化用户的最新开关偏好
|
|
1587
|
+
* - 四个 Agent 的 enabled 状态都显式下发,让 config.json 持久化用户的最新开关偏好
|
|
1571
1588
|
* - Agent 字段仅在该 Agent 开关启用时收集;未启用的 Agent 不下发其它字段,
|
|
1572
1589
|
* 服务端 deepMerge 会保留 config.json 中已有值(避免关闭开关时误清空旧配置)
|
|
1573
1590
|
*/
|
|
@@ -1580,11 +1597,11 @@ function collectAllFields() {
|
|
|
1580
1597
|
// 平台类型(下拉选择框,始终发送以保持与服务端同步)
|
|
1581
1598
|
var ptEl = document.getElementById('field-CHATCCC_FEISHU_PLATFORM_TYPE');
|
|
1582
1599
|
if (ptEl && ptEl.value.trim()) vars['CHATCCC_FEISHU_PLATFORM_TYPE'] = ptEl.value.trim();
|
|
1583
|
-
vars.CHATCCC_FEISHU_ENABLED = !!state.platformsEnabled.feishu;
|
|
1584
|
-
vars.CHATCCC_ILINK_ENABLED = !!state.platformsEnabled.ilink;
|
|
1585
|
-
var webUiOpenOnStartEl = document.getElementById('field-CHATCCC_WEB_UI_OPEN_ON_START');
|
|
1586
|
-
vars.CHATCCC_WEB_UI_OPEN_ON_START = !webUiOpenOnStartEl || !!webUiOpenOnStartEl.checked;
|
|
1587
|
-
var cdpEnabledEl = document.getElementById('field-CHATCCC_CHROME_DEVTOOLS_ENABLED');
|
|
1600
|
+
vars.CHATCCC_FEISHU_ENABLED = !!state.platformsEnabled.feishu;
|
|
1601
|
+
vars.CHATCCC_ILINK_ENABLED = !!state.platformsEnabled.ilink;
|
|
1602
|
+
var webUiOpenOnStartEl = document.getElementById('field-CHATCCC_WEB_UI_OPEN_ON_START');
|
|
1603
|
+
vars.CHATCCC_WEB_UI_OPEN_ON_START = !webUiOpenOnStartEl || !!webUiOpenOnStartEl.checked;
|
|
1604
|
+
var cdpEnabledEl = document.getElementById('field-CHATCCC_CHROME_DEVTOOLS_ENABLED');
|
|
1588
1605
|
vars.CHATCCC_CHROME_DEVTOOLS_ENABLED = !!(cdpEnabledEl && cdpEnabledEl.checked);
|
|
1589
1606
|
var cdpPortEl = document.getElementById('field-CHATCCC_CHROME_DEVTOOLS_PORT');
|
|
1590
1607
|
vars.CHATCCC_CHROME_DEVTOOLS_PORT = (cdpPortEl && cdpPortEl.value.trim()) ? cdpPortEl.value.trim() : '15166';
|
|
@@ -1592,15 +1609,15 @@ function collectAllFields() {
|
|
|
1592
1609
|
if (cdpPathEl && cdpPathEl.value.trim()) vars.CHATCCC_CHROME_DEVTOOLS_PATH = cdpPathEl.value.trim();
|
|
1593
1610
|
vars.CHATCCC_CLAUDE_ENABLED = !!state.agentsEnabled.claude;
|
|
1594
1611
|
vars.CHATCCC_CURSOR_ENABLED = !!state.agentsEnabled.cursor;
|
|
1595
|
-
vars.CHATCCC_CODEX_ENABLED = !!state.agentsEnabled.codex;
|
|
1596
|
-
vars.CHATCCC_CCC_ENABLED = !!state.agentsEnabled.ccc;
|
|
1612
|
+
vars.CHATCCC_CODEX_ENABLED = !!state.agentsEnabled.codex;
|
|
1613
|
+
vars.CHATCCC_CCC_ENABLED = !!state.agentsEnabled.ccc;
|
|
1597
1614
|
if (!state.defaultAgent || !state.agentsEnabled[state.defaultAgent]) {
|
|
1598
1615
|
state.defaultAgent = firstEnabledAgent();
|
|
1599
1616
|
}
|
|
1600
1617
|
vars.CHATCCC_CLAUDE_DEFAULT_AGENT = state.defaultAgent === 'claude';
|
|
1601
1618
|
vars.CHATCCC_CURSOR_DEFAULT_AGENT = state.defaultAgent === 'cursor';
|
|
1602
|
-
vars.CHATCCC_CODEX_DEFAULT_AGENT = state.defaultAgent === 'codex';
|
|
1603
|
-
vars.CHATCCC_CCC_DEFAULT_AGENT = state.defaultAgent === 'ccc';
|
|
1619
|
+
vars.CHATCCC_CODEX_DEFAULT_AGENT = state.defaultAgent === 'codex';
|
|
1620
|
+
vars.CHATCCC_CCC_DEFAULT_AGENT = state.defaultAgent === 'ccc';
|
|
1604
1621
|
if (state.agentsEnabled.claude) {
|
|
1605
1622
|
AGENT_FIELDS.claude.forEach(function(key){
|
|
1606
1623
|
var el = document.getElementById('field-' + key);
|
|
@@ -1613,21 +1630,21 @@ function collectAllFields() {
|
|
|
1613
1630
|
if (el && el.value.trim()) vars[key] = el.value.trim();
|
|
1614
1631
|
});
|
|
1615
1632
|
}
|
|
1616
|
-
if (state.agentsEnabled.codex) {
|
|
1617
|
-
AGENT_FIELDS.codex.forEach(function(key){
|
|
1618
|
-
var el = document.getElementById('field-' + key);
|
|
1619
|
-
if (!el) return;
|
|
1620
|
-
if (key === 'CHATCCC_CODEX_FAST_MODE') vars[key] = !!el.checked;
|
|
1621
|
-
else if (el.value.trim()) vars[key] = el.value.trim();
|
|
1622
|
-
});
|
|
1623
|
-
}
|
|
1624
|
-
if (state.agentsEnabled.ccc) {
|
|
1625
|
-
AGENT_FIELDS.ccc.forEach(function(key){
|
|
1626
|
-
var el = document.getElementById('field-' + key);
|
|
1627
|
-
if (el && el.value.trim()) vars[key] = el.value.trim();
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
|
-
return vars;
|
|
1633
|
+
if (state.agentsEnabled.codex) {
|
|
1634
|
+
AGENT_FIELDS.codex.forEach(function(key){
|
|
1635
|
+
var el = document.getElementById('field-' + key);
|
|
1636
|
+
if (!el) return;
|
|
1637
|
+
if (key === 'CHATCCC_CODEX_FAST_MODE') vars[key] = !!el.checked;
|
|
1638
|
+
else if (el.value.trim()) vars[key] = el.value.trim();
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
if (state.agentsEnabled.ccc) {
|
|
1642
|
+
AGENT_FIELDS.ccc.forEach(function(key){
|
|
1643
|
+
var el = document.getElementById('field-' + key);
|
|
1644
|
+
if (el && el.value.trim()) vars[key] = el.value.trim();
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
return vars;
|
|
1631
1648
|
}
|
|
1632
1649
|
|
|
1633
1650
|
function renderStep3() {
|
|
@@ -1643,16 +1660,16 @@ function renderStep3() {
|
|
|
1643
1660
|
lines.push('<div class="config-row"><span class="key">平台类型</span><span class="val">' + ptLabel + '</span></div>');
|
|
1644
1661
|
}
|
|
1645
1662
|
|
|
1646
|
-
lines.push('<h3 style="margin:16px 0 8px">微信 iLink</h3>');
|
|
1647
|
-
lines.push('<div class="config-row"><span class="key">状态</span><span class="val">' + (state.platformsEnabled.ilink ? '已启用' : '已禁用') + '</span></div>');
|
|
1663
|
+
lines.push('<h3 style="margin:16px 0 8px">微信 iLink</h3>');
|
|
1664
|
+
lines.push('<div class="config-row"><span class="key">状态</span><span class="val">' + (state.platformsEnabled.ilink ? '已启用' : '已禁用') + '</span></div>');
|
|
1648
1665
|
|
|
1649
1666
|
if (!state.platformsEnabled.feishu && !state.platformsEnabled.ilink) {
|
|
1650
|
-
lines.push('<div style="color:#ef4444;margin-top:8px">未启用任何平台</div>');
|
|
1651
|
-
}
|
|
1652
|
-
lines.push('<h3 style="margin:16px 0 8px">Web UI</h3>');
|
|
1653
|
-
lines.push('<div class="config-row"><span class="key">直接启动时自动打开</span><span class="val">' + (vars.CHATCCC_WEB_UI_OPEN_ON_START ? '已启用' : '已禁用') + '</span></div>');
|
|
1654
|
-
|
|
1655
|
-
lines.push('<h3 style="margin:16px 0 8px">Chrome CDP</h3>');
|
|
1667
|
+
lines.push('<div style="color:#ef4444;margin-top:8px">未启用任何平台</div>');
|
|
1668
|
+
}
|
|
1669
|
+
lines.push('<h3 style="margin:16px 0 8px">Web UI</h3>');
|
|
1670
|
+
lines.push('<div class="config-row"><span class="key">直接启动时自动打开</span><span class="val">' + (vars.CHATCCC_WEB_UI_OPEN_ON_START ? '已启用' : '已禁用') + '</span></div>');
|
|
1671
|
+
|
|
1672
|
+
lines.push('<h3 style="margin:16px 0 8px">Chrome CDP</h3>');
|
|
1656
1673
|
lines.push('<div class="config-row"><span class="key">状态</span><span class="val">' + (vars.CHATCCC_CHROME_DEVTOOLS_ENABLED ? '已启用' : '已禁用') + '</span></div>');
|
|
1657
1674
|
if (vars.CHATCCC_CHROME_DEVTOOLS_ENABLED) {
|
|
1658
1675
|
lines.push('<div class="config-row"><span class="key">CDP 端口</span><span class="val">' + (vars.CHATCCC_CHROME_DEVTOOLS_PORT || '15166') + '</span></div>');
|
|
@@ -1665,12 +1682,12 @@ function renderStep3() {
|
|
|
1665
1682
|
var enabledList = [];
|
|
1666
1683
|
if (state.agentsEnabled.claude) enabledList.push('claude');
|
|
1667
1684
|
if (state.agentsEnabled.cursor) enabledList.push('cursor');
|
|
1668
|
-
if (state.agentsEnabled.codex) enabledList.push('codex');
|
|
1669
|
-
if (state.agentsEnabled.ccc) enabledList.push('ccc');
|
|
1685
|
+
if (state.agentsEnabled.codex) enabledList.push('codex');
|
|
1686
|
+
if (state.agentsEnabled.ccc) enabledList.push('ccc');
|
|
1670
1687
|
if (enabledList.length === 0) {
|
|
1671
1688
|
lines.push('<div style="color:#ef4444">未启用任何 AI Agent</div>');
|
|
1672
1689
|
} else {
|
|
1673
|
-
var defaultLabel = state.defaultAgent === 'cursor' ? 'Cursor' : state.defaultAgent === 'codex' ? 'Codex' : state.defaultAgent === 'ccc' ? 'CCC Agent' : 'Claude Code';
|
|
1690
|
+
var defaultLabel = state.defaultAgent === 'cursor' ? 'Cursor' : state.defaultAgent === 'codex' ? 'Codex' : state.defaultAgent === 'ccc' ? 'CCC Agent' : 'Claude Code';
|
|
1674
1691
|
lines.push('<div class="config-row"><span class="key">/new 默认 Agent</span><span class="val">' + defaultLabel + '</span></div>');
|
|
1675
1692
|
}
|
|
1676
1693
|
enabledList.forEach(function(t){
|
|
@@ -1690,35 +1707,36 @@ function renderStep3() {
|
|
|
1690
1707
|
if (vars.CHATCCC_CURSOR_AVATAR_BATTERY_MODE === 'onDemandUse') {
|
|
1691
1708
|
lines.push('<div class="config-row"><span class="key">每月On demand use预算</span><span class="val">' + (vars.CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET || '1000') + '</span></div>');
|
|
1692
1709
|
}
|
|
1693
|
-
} else if (t === 'codex') {
|
|
1710
|
+
} else if (t === 'codex') {
|
|
1694
1711
|
lines.push('<h4 style="margin:10px 0 4px;color:#334155">Codex</h4>');
|
|
1695
1712
|
if (vars.CHATCCC_CODEX_PATH) lines.push('<div class="config-row"><span class="key">CLI 路径</span><span class="val">' + vars.CHATCCC_CODEX_PATH + '</span></div>');
|
|
1696
1713
|
lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_CODEX_MODEL || '(留空)') + '</span></div>');
|
|
1697
|
-
lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CODEX_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
|
|
1698
|
-
lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CODEX_EFFORT || '(留空)') + '</span></div>');
|
|
1699
|
-
lines.push('<div class="config-row"><span class="key">Fast 模式</span><span class="val">' + (vars.CHATCCC_CODEX_FAST_MODE ? '已启用' : '已禁用') + '</span></div>');
|
|
1700
|
-
} else if (t === 'ccc') {
|
|
1701
|
-
lines.push('<h4 style="margin:10px 0 4px;color:#334155">CCC Agent</h4>');
|
|
1702
|
-
lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_CCC_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
|
|
1703
|
-
lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_CCC_BASE_URL || '(留空)') + '</span></div>');
|
|
1704
|
-
lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_CCC_MODEL || '(留空)') + '</span></div>');
|
|
1705
|
-
lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CCC_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
|
|
1714
|
+
lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CODEX_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
|
|
1715
|
+
lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CODEX_EFFORT || '(留空)') + '</span></div>');
|
|
1716
|
+
lines.push('<div class="config-row"><span class="key">Fast 模式</span><span class="val">' + (vars.CHATCCC_CODEX_FAST_MODE ? '已启用' : '已禁用') + '</span></div>');
|
|
1717
|
+
} else if (t === 'ccc') {
|
|
1718
|
+
lines.push('<h4 style="margin:10px 0 4px;color:#334155">CCC Agent</h4>');
|
|
1719
|
+
lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_CCC_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
|
|
1720
|
+
lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_CCC_BASE_URL || '(留空)') + '</span></div>');
|
|
1721
|
+
lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_CCC_MODEL || '(留空)') + '</span></div>');
|
|
1722
|
+
lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CCC_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
|
|
1723
|
+
lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CCC_EFFORT || '(留空)') + '</span></div>');
|
|
1706
1724
|
}
|
|
1707
1725
|
});
|
|
1708
1726
|
document.getElementById('review-content').innerHTML = lines.join('');
|
|
1709
1727
|
}
|
|
1710
1728
|
|
|
1711
|
-
async function saveConfig(vars, options) {
|
|
1712
|
-
options = options || {};
|
|
1729
|
+
async function saveConfig(vars, options) {
|
|
1730
|
+
options = options || {};
|
|
1713
1731
|
var result = await api('/api/config', 'POST', { vars: vars });
|
|
1714
|
-
if (result.ok) {
|
|
1715
|
-
state.config = Object.assign({}, state.config, vars);
|
|
1716
|
-
if (!options.quiet) toastConfigApplyResult(result, '配置已保存');
|
|
1732
|
+
if (result.ok) {
|
|
1733
|
+
state.config = Object.assign({}, state.config, vars);
|
|
1734
|
+
if (!options.quiet) toastConfigApplyResult(result, '配置已保存');
|
|
1717
1735
|
} else {
|
|
1718
1736
|
toast('保存失败: ' + (result.error || '未知错误'), 'error');
|
|
1719
1737
|
}
|
|
1720
|
-
return result;
|
|
1721
|
-
}
|
|
1738
|
+
return result;
|
|
1739
|
+
}
|
|
1722
1740
|
|
|
1723
1741
|
async function setDashboardDefaultAgent(agent, enabled) {
|
|
1724
1742
|
if (!enabled) {
|
|
@@ -1728,22 +1746,22 @@ async function setDashboardDefaultAgent(agent, enabled) {
|
|
|
1728
1746
|
state.defaultAgent = agent;
|
|
1729
1747
|
var vars = {
|
|
1730
1748
|
CHATCCC_CLAUDE_DEFAULT_AGENT: agent === 'claude',
|
|
1731
|
-
CHATCCC_CURSOR_DEFAULT_AGENT: agent === 'cursor',
|
|
1732
|
-
CHATCCC_CODEX_DEFAULT_AGENT: agent === 'codex',
|
|
1733
|
-
CHATCCC_CCC_DEFAULT_AGENT: agent === 'ccc'
|
|
1749
|
+
CHATCCC_CURSOR_DEFAULT_AGENT: agent === 'cursor',
|
|
1750
|
+
CHATCCC_CODEX_DEFAULT_AGENT: agent === 'codex',
|
|
1751
|
+
CHATCCC_CCC_DEFAULT_AGENT: agent === 'ccc'
|
|
1734
1752
|
};
|
|
1735
1753
|
var result = await api('/api/config', 'POST', { vars: vars });
|
|
1736
1754
|
if (result.ok) {
|
|
1737
1755
|
state.config.claude = state.config.claude || {};
|
|
1738
1756
|
state.config.cursor = state.config.cursor || {};
|
|
1739
|
-
state.config.codex = state.config.codex || {};
|
|
1740
|
-
state.config.ccc = state.config.ccc || {};
|
|
1757
|
+
state.config.codex = state.config.codex || {};
|
|
1758
|
+
state.config.ccc = state.config.ccc || {};
|
|
1741
1759
|
state.config.claude.defaultAgent = agent === 'claude';
|
|
1742
|
-
state.config.cursor.defaultAgent = agent === 'cursor';
|
|
1743
|
-
state.config.codex.defaultAgent = agent === 'codex';
|
|
1744
|
-
state.config.ccc.defaultAgent = agent === 'ccc';
|
|
1745
|
-
updateDefaultAgentToggles();
|
|
1746
|
-
toastConfigApplyResult(result, '默认 Agent 已更新');
|
|
1760
|
+
state.config.cursor.defaultAgent = agent === 'cursor';
|
|
1761
|
+
state.config.codex.defaultAgent = agent === 'codex';
|
|
1762
|
+
state.config.ccc.defaultAgent = agent === 'ccc';
|
|
1763
|
+
updateDefaultAgentToggles();
|
|
1764
|
+
toastConfigApplyResult(result, '默认 Agent 已更新');
|
|
1747
1765
|
} else {
|
|
1748
1766
|
toast('保存失败: ' + (result.error || '未知错误'), 'error');
|
|
1749
1767
|
updateDefaultAgentToggles();
|
|
@@ -1756,36 +1774,36 @@ async function saveAndStart() {
|
|
|
1756
1774
|
toast('飞书已启用,请先填写 App ID 和 App Secret', 'error');
|
|
1757
1775
|
return;
|
|
1758
1776
|
}
|
|
1759
|
-
var saved = await saveConfig(vars, { quiet: true });
|
|
1760
|
-
if (saved.ok !== true) return;
|
|
1761
|
-
document.getElementById('btn-save-start').disabled = true;
|
|
1762
|
-
document.getElementById('btn-save-start').innerHTML = '<span class="spinner"></span> 应用中...';
|
|
1763
|
-
|
|
1764
|
-
if (state.running && saved.restartRequired === true) {
|
|
1765
|
-
await api('/api/restart', 'POST');
|
|
1766
|
-
toast('配置已保存,服务正在重启…');
|
|
1767
|
-
pollUntilRunning();
|
|
1768
|
-
return;
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
if (state.running && saved.mode === 'reload') {
|
|
1772
|
-
toastConfigApplyResult(saved, '配置已保存');
|
|
1773
|
-
setTimeout(function(){ location.reload(); }, 1000);
|
|
1774
|
-
return;
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
var result = await api('/api/start', 'POST');
|
|
1778
|
-
if (result.ok) {
|
|
1779
|
-
var msg;
|
|
1780
|
-
if (result.mode === 'reload') {
|
|
1781
|
-
msg = '配置已保存,后续消息/新会话已生效。';
|
|
1782
|
-
} else if (result.mode === 'inplace') {
|
|
1783
|
-
msg = '服务已启动! PID: ' + result.pid;
|
|
1784
|
-
} else {
|
|
1785
|
-
msg = '服务已启动! PID: ' + (result.pid || '?');
|
|
1786
|
-
}
|
|
1787
|
-
toast(msg);
|
|
1788
|
-
setTimeout(function(){ location.reload(); }, 1500);
|
|
1777
|
+
var saved = await saveConfig(vars, { quiet: true });
|
|
1778
|
+
if (saved.ok !== true) return;
|
|
1779
|
+
document.getElementById('btn-save-start').disabled = true;
|
|
1780
|
+
document.getElementById('btn-save-start').innerHTML = '<span class="spinner"></span> 应用中...';
|
|
1781
|
+
|
|
1782
|
+
if (state.running && saved.restartRequired === true) {
|
|
1783
|
+
await api('/api/restart', 'POST');
|
|
1784
|
+
toast('配置已保存,服务正在重启…');
|
|
1785
|
+
pollUntilRunning();
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
if (state.running && saved.mode === 'reload') {
|
|
1790
|
+
toastConfigApplyResult(saved, '配置已保存');
|
|
1791
|
+
setTimeout(function(){ location.reload(); }, 1000);
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
var result = await api('/api/start', 'POST');
|
|
1796
|
+
if (result.ok) {
|
|
1797
|
+
var msg;
|
|
1798
|
+
if (result.mode === 'reload') {
|
|
1799
|
+
msg = '配置已保存,后续消息/新会话已生效。';
|
|
1800
|
+
} else if (result.mode === 'inplace') {
|
|
1801
|
+
msg = '服务已启动! PID: ' + result.pid;
|
|
1802
|
+
} else {
|
|
1803
|
+
msg = '服务已启动! PID: ' + (result.pid || '?');
|
|
1804
|
+
}
|
|
1805
|
+
toast(msg);
|
|
1806
|
+
setTimeout(function(){ location.reload(); }, 1500);
|
|
1789
1807
|
} else {
|
|
1790
1808
|
toast('保存失败: ' + (result.error || '未知错误'), 'error');
|
|
1791
1809
|
document.getElementById('btn-save-start').disabled = false;
|
|
@@ -1857,10 +1875,10 @@ function updateDashboardUI() {
|
|
|
1857
1875
|
// 只显示已启用的 Agent 卡片(按 enabled 字段;缺省时退回到"任一字段非空"兼容旧 config)
|
|
1858
1876
|
var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
|
|
1859
1877
|
var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
|
|
1860
|
-
var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
|
|
1861
|
-
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
1862
|
-
state.agentsEnabled = { claude: claudeOn, cursor: cursorOn, codex: codexOn, ccc: cccOn };
|
|
1863
|
-
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn);
|
|
1878
|
+
var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
|
|
1879
|
+
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
1880
|
+
state.agentsEnabled = { claude: claudeOn, cursor: cursorOn, codex: codexOn, ccc: cccOn };
|
|
1881
|
+
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn);
|
|
1864
1882
|
|
|
1865
1883
|
// 微信 iLink 平台开关:同步复选框和标签
|
|
1866
1884
|
var ilinkEnabled = c.platforms && c.platforms.ilink ? c.platforms.ilink.enabled !== false : true;
|
|
@@ -1872,14 +1890,14 @@ function updateDashboardUI() {
|
|
|
1872
1890
|
|
|
1873
1891
|
// 微信 iLink "忘记扫码" 按钮:仅在 ilink 已启用且有已保存 token 时显示
|
|
1874
1892
|
var ilinkForgetRow = document.getElementById('ilink-forget-row');
|
|
1875
|
-
if (ilinkForgetRow) {
|
|
1876
|
-
ilinkForgetRow.style.display = (ilinkEnabled && state.ilinkAuthExists) ? '' : 'none';
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
var webUi = c.webUi || {};
|
|
1880
|
-
document.getElementById('cfg-WEB_UI_OPEN_ON_START').textContent = webUi.openOnStart !== false ? '已启用' : '已禁用';
|
|
1881
|
-
|
|
1882
|
-
var chromeDevtools = c.chromeDevtools || {};
|
|
1893
|
+
if (ilinkForgetRow) {
|
|
1894
|
+
ilinkForgetRow.style.display = (ilinkEnabled && state.ilinkAuthExists) ? '' : 'none';
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
var webUi = c.webUi || {};
|
|
1898
|
+
document.getElementById('cfg-WEB_UI_OPEN_ON_START').textContent = webUi.openOnStart !== false ? '已启用' : '已禁用';
|
|
1899
|
+
|
|
1900
|
+
var chromeDevtools = c.chromeDevtools || {};
|
|
1883
1901
|
var cdpPort = chromeDevtools.port || 15166;
|
|
1884
1902
|
document.getElementById('cfg-CHROME_DEVTOOLS_ENABLED').textContent = chromeDevtools.enabled ? '已启用' : '已禁用';
|
|
1885
1903
|
document.getElementById('cfg-CHROME_DEVTOOLS_PORT').textContent = String(cdpPort);
|
|
@@ -1891,12 +1909,12 @@ function updateDashboardUI() {
|
|
|
1891
1909
|
|
|
1892
1910
|
document.getElementById('dash-claude').style.display = claudeOn ? '' : 'none';
|
|
1893
1911
|
document.getElementById('dash-cursor').style.display = cursorOn ? '' : 'none';
|
|
1894
|
-
document.getElementById('dash-codex').style.display = codexOn ? '' : 'none';
|
|
1895
|
-
document.getElementById('dash-ccc').style.display = cccOn ? '' : 'none';
|
|
1912
|
+
document.getElementById('dash-codex').style.display = codexOn ? '' : 'none';
|
|
1913
|
+
document.getElementById('dash-ccc').style.display = cccOn ? '' : 'none';
|
|
1896
1914
|
updateDefaultAgentToggles();
|
|
1897
|
-
// 四个都未启用时给一个空态提示,引导用户去配置向导启用
|
|
1915
|
+
// 四个都未启用时给一个空态提示,引导用户去配置向导启用
|
|
1898
1916
|
var emptyHint = document.getElementById('dash-no-agent-hint');
|
|
1899
|
-
if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn && !cccOn) ? '' : 'none';
|
|
1917
|
+
if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn && !cccOn) ? '' : 'none';
|
|
1900
1918
|
|
|
1901
1919
|
document.getElementById('cfg-ANTHROPIC_MODEL').textContent = (c.claude && c.claude.model) || '(留空)';
|
|
1902
1920
|
document.getElementById('cfg-ANTHROPIC_SUBAGENT_MODEL').textContent = (c.claude && c.claude.subagentModel) || '(留空)';
|
|
@@ -1914,13 +1932,13 @@ function updateDashboardUI() {
|
|
|
1914
1932
|
document.getElementById('cfg-CURSOR_ON_DEMAND_MONTHLY_BUDGET').textContent = String((c.cursor && c.cursor.onDemandMonthlyBudget) || 1000);
|
|
1915
1933
|
document.getElementById('cfg-CODEX_PATH').textContent = (c.codex && (c.codex.path || c.codex.command)) || 'codex';
|
|
1916
1934
|
document.getElementById('cfg-CODEX_MODEL').textContent = (c.codex && c.codex.model) || '(留空)';
|
|
1917
|
-
document.getElementById('cfg-CODEX_ALTERNATIVE_MODEL').textContent = (c.codex && c.codex.alternativeModel) || '(留空)';
|
|
1918
|
-
document.getElementById('cfg-CODEX_EFFORT').textContent = (c.codex && c.codex.effort) || '(留空)';
|
|
1919
|
-
document.getElementById('cfg-CODEX_FAST_MODE').textContent = c.codex && c.codex.fastMode === true ? '已启用' : '已禁用';
|
|
1920
|
-
document.getElementById('cfg-CCC_API_KEY').textContent = (c.ccc && c.ccc.DEEPSEEK_API_KEY) ? '***已设置***' : '(留空)';
|
|
1921
|
-
document.getElementById('cfg-CCC_BASE_URL').textContent = (c.ccc && c.ccc.DEEPSEEK_BASE_URL) || '(留空)';
|
|
1922
|
-
document.getElementById('cfg-CCC_MODEL').textContent = (c.ccc && c.ccc.model) || '(留空)';
|
|
1923
|
-
document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
|
|
1935
|
+
document.getElementById('cfg-CODEX_ALTERNATIVE_MODEL').textContent = (c.codex && c.codex.alternativeModel) || '(留空)';
|
|
1936
|
+
document.getElementById('cfg-CODEX_EFFORT').textContent = (c.codex && c.codex.effort) || '(留空)';
|
|
1937
|
+
document.getElementById('cfg-CODEX_FAST_MODE').textContent = c.codex && c.codex.fastMode === true ? '已启用' : '已禁用';
|
|
1938
|
+
document.getElementById('cfg-CCC_API_KEY').textContent = (c.ccc && c.ccc.DEEPSEEK_API_KEY) ? '***已设置***' : '(留空)';
|
|
1939
|
+
document.getElementById('cfg-CCC_BASE_URL').textContent = (c.ccc && c.ccc.DEEPSEEK_BASE_URL) || '(留空)';
|
|
1940
|
+
document.getElementById('cfg-CCC_MODEL').textContent = (c.ccc && c.ccc.model) || '(留空)';
|
|
1941
|
+
document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
|
|
1924
1942
|
}
|
|
1925
1943
|
|
|
1926
1944
|
function pollStatus() {
|
|
@@ -1968,24 +1986,24 @@ async function pollUntilRunning() {
|
|
|
1968
1986
|
// ---- Edit Modal ----
|
|
1969
1987
|
var editSectionType = null;
|
|
1970
1988
|
|
|
1971
|
-
function editSection(section) {
|
|
1989
|
+
function editSection(section) {
|
|
1972
1990
|
editSectionType = section;
|
|
1973
|
-
var fields;
|
|
1974
|
-
if (section === 'feishu') fields = FEISHU_FIELDS;
|
|
1975
|
-
else if (section === 'webUi') fields = WEB_UI_FIELDS;
|
|
1976
|
-
else if (section === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
|
|
1991
|
+
var fields;
|
|
1992
|
+
if (section === 'feishu') fields = FEISHU_FIELDS;
|
|
1993
|
+
else if (section === 'webUi') fields = WEB_UI_FIELDS;
|
|
1994
|
+
else if (section === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
|
|
1977
1995
|
else fields = AGENT_FIELDS[section] || [];
|
|
1978
1996
|
|
|
1979
|
-
var titleMap = { feishu: '飞书', webUi: 'Web UI', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent', ccc: 'CCC Agent' };
|
|
1997
|
+
var titleMap = { feishu: '飞书', webUi: 'Web UI', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent', ccc: 'CCC Agent' };
|
|
1980
1998
|
document.getElementById('edit-modal-title').textContent = '编辑 ' + (titleMap[section] || section);
|
|
1981
1999
|
|
|
1982
|
-
document.getElementById('edit-modal-effect').textContent = configEffectHint(section);
|
|
1983
|
-
|
|
1984
|
-
var html = '';
|
|
1985
|
-
var labelMap = {
|
|
1986
|
-
'CHATCCC_APP_ID': 'App ID', 'CHATCCC_APP_SECRET': 'App Secret',
|
|
1987
|
-
'CHATCCC_WEB_UI_OPEN_ON_START': '直接启动 ChatCCC 时自动打开 Web UI',
|
|
1988
|
-
'CHATCCC_CHROME_DEVTOOLS_ENABLED': '启用常驻 Chrome CDP(选填)',
|
|
2000
|
+
document.getElementById('edit-modal-effect').textContent = configEffectHint(section);
|
|
2001
|
+
|
|
2002
|
+
var html = '';
|
|
2003
|
+
var labelMap = {
|
|
2004
|
+
'CHATCCC_APP_ID': 'App ID', 'CHATCCC_APP_SECRET': 'App Secret',
|
|
2005
|
+
'CHATCCC_WEB_UI_OPEN_ON_START': '直接启动 ChatCCC 时自动打开 Web UI',
|
|
2006
|
+
'CHATCCC_CHROME_DEVTOOLS_ENABLED': '启用常驻 Chrome CDP(选填)',
|
|
1989
2007
|
'CHATCCC_CHROME_DEVTOOLS_PORT': 'CDP 端口',
|
|
1990
2008
|
'CHATCCC_CHROME_DEVTOOLS_PATH': 'Chrome 路径(选填)',
|
|
1991
2009
|
'CHATCCC_ANTHROPIC_MODEL': '模型', 'CHATCCC_ANTHROPIC_SUBAGENT_MODEL': 'Subagent 模型', 'CHATCCC_ANTHROPIC_EFFORT': 'Effort',
|
|
@@ -1993,14 +2011,14 @@ function editSection(section) {
|
|
|
1993
2011
|
'CHATCCC_CURSOR_PATH': 'CLI 路径', 'CHATCCC_CURSOR_MODEL': '模型', 'CHATCCC_CURSOR_ALTERNATIVE_MODEL': '备选模型',
|
|
1994
2012
|
'CHATCCC_CURSOR_AVATAR_BATTERY_MODE': '头像电池电量',
|
|
1995
2013
|
'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET': '每月On demand use预算',
|
|
1996
|
-
'CHATCCC_CODEX_PATH': 'CLI 路径', 'CHATCCC_CODEX_MODEL': '模型', 'CHATCCC_CODEX_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CODEX_EFFORT': 'Effort',
|
|
1997
|
-
'CHATCCC_CODEX_FAST_MODE': 'Fast 模式',
|
|
1998
|
-
'CHATCCC_CCC_API_KEY': 'API Key', 'CHATCCC_CCC_BASE_URL': 'Base URL',
|
|
1999
|
-
'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型'
|
|
2014
|
+
'CHATCCC_CODEX_PATH': 'CLI 路径', 'CHATCCC_CODEX_MODEL': '模型', 'CHATCCC_CODEX_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CODEX_EFFORT': 'Effort',
|
|
2015
|
+
'CHATCCC_CODEX_FAST_MODE': 'Fast 模式',
|
|
2016
|
+
'CHATCCC_CCC_API_KEY': 'API Key', 'CHATCCC_CCC_BASE_URL': 'Base URL',
|
|
2017
|
+
'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CCC_EFFORT': 'Effort'
|
|
2000
2018
|
};
|
|
2001
|
-
var hintMap = {
|
|
2002
|
-
'CHATCCC_WEB_UI_OPEN_ON_START': '关闭后可继续手动访问 http://localhost:<端口>/;/restart、/update 和 Web UI 重启无论此项为何值都不会自动打开。',
|
|
2003
|
-
'CHATCCC_CHROME_DEVTOOLS_ENABLED': '依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。',
|
|
2019
|
+
var hintMap = {
|
|
2020
|
+
'CHATCCC_WEB_UI_OPEN_ON_START': '关闭后可继续手动访问 http://localhost:<端口>/;/restart、/update 和 Web UI 重启无论此项为何值都不会自动打开。',
|
|
2021
|
+
'CHATCCC_CHROME_DEVTOOLS_ENABLED': '依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。',
|
|
2004
2022
|
'CHATCCC_CHROME_DEVTOOLS_PORT': '默认 15166,健康检查端点为 http://127.0.0.1:15166/json/version。',
|
|
2005
2023
|
'CHATCCC_CHROME_DEVTOOLS_PATH': '选填。留空时自动探测 Google Chrome。'
|
|
2006
2024
|
};
|
|
@@ -2013,12 +2031,12 @@ function editSection(section) {
|
|
|
2013
2031
|
var val = state.config[key] || '';
|
|
2014
2032
|
// Also check nested config
|
|
2015
2033
|
if (!val) {
|
|
2016
|
-
if (section === 'feishu') {
|
|
2034
|
+
if (section === 'feishu') {
|
|
2017
2035
|
if (key === 'CHATCCC_APP_ID' && state.config.feishu) val = state.config.feishu.appId || '';
|
|
2018
2036
|
else if (key === 'CHATCCC_APP_SECRET' && state.config.feishu) val = state.config.feishu.appSecret || '';
|
|
2019
|
-
} else if (section === 'webUi') {
|
|
2020
|
-
val = !state.config.webUi || state.config.webUi.openOnStart !== false ? 'true' : 'false';
|
|
2021
|
-
} else if (section === 'chromeDevtools' && state.config.chromeDevtools) {
|
|
2037
|
+
} else if (section === 'webUi') {
|
|
2038
|
+
val = !state.config.webUi || state.config.webUi.openOnStart !== false ? 'true' : 'false';
|
|
2039
|
+
} else if (section === 'chromeDevtools' && state.config.chromeDevtools) {
|
|
2022
2040
|
if (key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED') val = state.config.chromeDevtools.enabled === true ? 'true' : 'false';
|
|
2023
2041
|
else if (key === 'CHATCCC_CHROME_DEVTOOLS_PORT') val = state.config.chromeDevtools.port != null ? String(state.config.chromeDevtools.port) : '15166';
|
|
2024
2042
|
else if (key === 'CHATCCC_CHROME_DEVTOOLS_PATH') val = state.config.chromeDevtools.chromePath || '';
|
|
@@ -2035,24 +2053,25 @@ function editSection(section) {
|
|
|
2035
2053
|
else if (key === 'CHATCCC_CURSOR_ALTERNATIVE_MODEL') val = state.config.cursor.alternativeModel || '';
|
|
2036
2054
|
else if (key === 'CHATCCC_CURSOR_AVATAR_BATTERY_MODE') val = state.config.cursor.avatarBatteryMode || 'apiPercent';
|
|
2037
2055
|
else if (key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET') val = (state.config.cursor.onDemandMonthlyBudget != null) ? String(state.config.cursor.onDemandMonthlyBudget) : '1000';
|
|
2038
|
-
} else if (section === 'codex' && state.config.codex) {
|
|
2056
|
+
} else if (section === 'codex' && state.config.codex) {
|
|
2039
2057
|
if (key === 'CHATCCC_CODEX_PATH') val = state.config.codex.path || state.config.codex.command || '';
|
|
2040
|
-
else if (key === 'CHATCCC_CODEX_MODEL') val = state.config.codex.model || '';
|
|
2041
|
-
else if (key === 'CHATCCC_CODEX_ALTERNATIVE_MODEL') val = state.config.codex.alternativeModel || '';
|
|
2042
|
-
else if (key === 'CHATCCC_CODEX_EFFORT') val = state.config.codex.effort || '';
|
|
2043
|
-
else if (key === 'CHATCCC_CODEX_FAST_MODE') val = state.config.codex.fastMode === true ? 'true' : 'false';
|
|
2044
|
-
} else if (section === 'ccc' && state.config.ccc) {
|
|
2045
|
-
if (key === 'CHATCCC_CCC_API_KEY') val = state.config.ccc.DEEPSEEK_API_KEY || '';
|
|
2046
|
-
else if (key === 'CHATCCC_CCC_BASE_URL') val = state.config.ccc.DEEPSEEK_BASE_URL || '';
|
|
2047
|
-
else if (key === 'CHATCCC_CCC_MODEL') val = state.config.ccc.model || '';
|
|
2048
|
-
else if (key === 'CHATCCC_CCC_ALTERNATIVE_MODEL') val = state.config.ccc.alternativeModel || '';
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
var
|
|
2055
|
-
|
|
2058
|
+
else if (key === 'CHATCCC_CODEX_MODEL') val = state.config.codex.model || '';
|
|
2059
|
+
else if (key === 'CHATCCC_CODEX_ALTERNATIVE_MODEL') val = state.config.codex.alternativeModel || '';
|
|
2060
|
+
else if (key === 'CHATCCC_CODEX_EFFORT') val = state.config.codex.effort || '';
|
|
2061
|
+
else if (key === 'CHATCCC_CODEX_FAST_MODE') val = state.config.codex.fastMode === true ? 'true' : 'false';
|
|
2062
|
+
} else if (section === 'ccc' && state.config.ccc) {
|
|
2063
|
+
if (key === 'CHATCCC_CCC_API_KEY') val = state.config.ccc.DEEPSEEK_API_KEY || '';
|
|
2064
|
+
else if (key === 'CHATCCC_CCC_BASE_URL') val = state.config.ccc.DEEPSEEK_BASE_URL || '';
|
|
2065
|
+
else if (key === 'CHATCCC_CCC_MODEL') val = state.config.ccc.model || '';
|
|
2066
|
+
else if (key === 'CHATCCC_CCC_ALTERNATIVE_MODEL') val = state.config.ccc.alternativeModel || '';
|
|
2067
|
+
else if (key === 'CHATCCC_CCC_EFFORT') val = state.config.ccc.effort || '';
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
var isSecret = key.includes('SECRET') || key.includes('API_KEY');
|
|
2071
|
+
if (key === 'CHATCCC_WEB_UI_OPEN_ON_START' || key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED' || key === 'CHATCCC_CODEX_FAST_MODE') {
|
|
2072
|
+
var checked = val === true || val === 'true';
|
|
2073
|
+
var changeHandler = key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED' ? ' onchange="toggleEditChromeDevtoolsFields(this.checked)"' : '';
|
|
2074
|
+
html += '<div class="form-group"><label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="edit-' + key + '"' + (checked ? ' checked' : '') + changeHandler + '> ' + (labelMap[key] || key) + '</label>';
|
|
2056
2075
|
if (hintMap[key]) html += '<div class="hint" style="margin-top:6px;line-height:1.5">' + hintMap[key] + '</div>';
|
|
2057
2076
|
html += '</div>';
|
|
2058
2077
|
} else if (key === 'CHATCCC_CURSOR_AVATAR_BATTERY_MODE') {
|
|
@@ -2106,18 +2125,18 @@ function closeEditModal() {
|
|
|
2106
2125
|
editSectionType = null;
|
|
2107
2126
|
}
|
|
2108
2127
|
|
|
2109
|
-
async function saveEdit() {
|
|
2110
|
-
var fields;
|
|
2111
|
-
if (editSectionType === 'feishu') fields = FEISHU_FIELDS;
|
|
2112
|
-
else if (editSectionType === 'webUi') fields = WEB_UI_FIELDS;
|
|
2113
|
-
else if (editSectionType === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
|
|
2128
|
+
async function saveEdit() {
|
|
2129
|
+
var fields;
|
|
2130
|
+
if (editSectionType === 'feishu') fields = FEISHU_FIELDS;
|
|
2131
|
+
else if (editSectionType === 'webUi') fields = WEB_UI_FIELDS;
|
|
2132
|
+
else if (editSectionType === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
|
|
2114
2133
|
else fields = AGENT_FIELDS[editSectionType] || [];
|
|
2115
2134
|
|
|
2116
2135
|
var vars = {};
|
|
2117
2136
|
fields.forEach(function(key){
|
|
2118
2137
|
var el = document.getElementById('edit-' + key);
|
|
2119
2138
|
if (!el) return;
|
|
2120
|
-
if (key === 'CHATCCC_WEB_UI_OPEN_ON_START' || key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED' || key === 'CHATCCC_CODEX_FAST_MODE') vars[key] = !!el.checked;
|
|
2139
|
+
if (key === 'CHATCCC_WEB_UI_OPEN_ON_START' || key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED' || key === 'CHATCCC_CODEX_FAST_MODE') vars[key] = !!el.checked;
|
|
2121
2140
|
else vars[key] = el.value.trim();
|
|
2122
2141
|
});
|
|
2123
2142
|
if (editSectionType === 'chromeDevtools' && !vars.CHATCCC_CHROME_DEVTOOLS_PORT) {
|
|
@@ -2131,16 +2150,16 @@ async function saveEdit() {
|
|
|
2131
2150
|
var ptEl = document.getElementById('edit-CHATCCC_FEISHU_PLATFORM_TYPE');
|
|
2132
2151
|
if (ptEl && ptEl.value.trim()) vars['CHATCCC_FEISHU_PLATFORM_TYPE'] = ptEl.value.trim();
|
|
2133
2152
|
}
|
|
2134
|
-
var result = await saveConfig(vars, { quiet: true });
|
|
2135
|
-
if (result.ok !== true) return;
|
|
2136
|
-
try {
|
|
2137
|
-
var fresh = await api('/api/config');
|
|
2138
|
-
state.config = fresh.vars || state.config;
|
|
2139
|
-
} catch(e) {}
|
|
2140
|
-
closeEditModal();
|
|
2141
|
-
updateDashboardUI();
|
|
2142
|
-
toastConfigApplyResult(result, '修改已保存');
|
|
2143
|
-
}
|
|
2153
|
+
var result = await saveConfig(vars, { quiet: true });
|
|
2154
|
+
if (result.ok !== true) return;
|
|
2155
|
+
try {
|
|
2156
|
+
var fresh = await api('/api/config');
|
|
2157
|
+
state.config = fresh.vars || state.config;
|
|
2158
|
+
} catch(e) {}
|
|
2159
|
+
closeEditModal();
|
|
2160
|
+
updateDashboardUI();
|
|
2161
|
+
toastConfigApplyResult(result, '修改已保存');
|
|
2162
|
+
}
|
|
2144
2163
|
|
|
2145
2164
|
// ---- Other actions ----
|
|
2146
2165
|
function reconfigure() {
|
|
@@ -2173,10 +2192,10 @@ init();
|
|
|
2173
2192
|
// Router
|
|
2174
2193
|
// ---------------------------------------------------------------------------
|
|
2175
2194
|
|
|
2176
|
-
async function handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
|
|
2177
|
-
const url = req.url ?? "/";
|
|
2178
|
-
const method = req.method ?? "GET";
|
|
2179
|
-
const pathname = url.split("?", 1)[0];
|
|
2195
|
+
async function handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
|
|
2196
|
+
const url = req.url ?? "/";
|
|
2197
|
+
const method = req.method ?? "GET";
|
|
2198
|
+
const pathname = url.split("?", 1)[0];
|
|
2180
2199
|
|
|
2181
2200
|
if (extraApiHandler && await extraApiHandler(req, res)) return;
|
|
2182
2201
|
|
|
@@ -2189,13 +2208,13 @@ async function handleRequest(req: IncomingMessage, res: ServerResponse): Promise
|
|
|
2189
2208
|
if (url === "/api/stop" && method === "POST") return handleStopService(req, res);
|
|
2190
2209
|
if (url === "/api/restart" && method === "POST") return handleRestartService(req, res);
|
|
2191
2210
|
if (url === "/api/validate" && method === "POST") return handleValidate(req, res);
|
|
2192
|
-
if (url === "/api/ilink/forget" && method === "POST") return handleForgetIlink(req, res);
|
|
2193
|
-
|
|
2194
|
-
if (method === "GET" && (pathname === "/agent-team" || pathname === "/agent-team/")) {
|
|
2195
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2196
|
-
res.end(AGENT_TEAM_PAGE_HTML);
|
|
2197
|
-
return;
|
|
2198
|
-
}
|
|
2211
|
+
if (url === "/api/ilink/forget" && method === "POST") return handleForgetIlink(req, res);
|
|
2212
|
+
|
|
2213
|
+
if (method === "GET" && (pathname === "/agent-team" || pathname === "/agent-team/")) {
|
|
2214
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2215
|
+
res.end(AGENT_TEAM_PAGE_HTML);
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2199
2218
|
|
|
2200
2219
|
// Serve HTML page for all other GET requests
|
|
2201
2220
|
if (method === "GET") {
|
|
@@ -2220,7 +2239,7 @@ export function createUiRouter(): (req: IncomingMessage, res: ServerResponse) =>
|
|
|
2220
2239
|
// Setup mode entry point — called from index.ts when no credentials
|
|
2221
2240
|
// ---------------------------------------------------------------------------
|
|
2222
2241
|
|
|
2223
|
-
/**
|
|
2242
|
+
/**
|
|
2224
2243
|
* setup → service「在线切换」回调签名:
|
|
2225
2244
|
* - 入参 httpServer:setup 模式当前监听的 HTTP server,会被复用为 service 的
|
|
2226
2245
|
* relay server(避免 close + recreate 的端口竞态)。
|
|
@@ -2232,10 +2251,10 @@ export type SetupActivateHook = (
|
|
|
2232
2251
|
httpServer: ReturnType<typeof createServer>,
|
|
2233
2252
|
) => Promise<{ ok: true } | { ok: false; error: string }>;
|
|
2234
2253
|
|
|
2235
|
-
export interface StartSetupModeOptions {
|
|
2236
|
-
onActivate?: SetupActivateHook;
|
|
2237
|
-
/** 内部重启进入 setup 模式时设为 false,避免重复弹出系统浏览器。 */
|
|
2238
|
-
openBrowser?: boolean;
|
|
2254
|
+
export interface StartSetupModeOptions {
|
|
2255
|
+
onActivate?: SetupActivateHook;
|
|
2256
|
+
/** 内部重启进入 setup 模式时设为 false,避免重复弹出系统浏览器。 */
|
|
2257
|
+
openBrowser?: boolean;
|
|
2239
2258
|
}
|
|
2240
2259
|
|
|
2241
2260
|
// setup HTTP server + onActivate 回调通过模块级变量暴露给 handleStartService。
|
|
@@ -2266,10 +2285,10 @@ export function setExtraApiHandler(handler: ExtraApiHandler): void {
|
|
|
2266
2285
|
extraApiHandler = handler;
|
|
2267
2286
|
}
|
|
2268
2287
|
|
|
2269
|
-
export function startSetupMode(
|
|
2270
|
-
port: number,
|
|
2271
|
-
options: StartSetupModeOptions = {},
|
|
2272
|
-
): ReturnType<typeof createServer> {
|
|
2288
|
+
export function startSetupMode(
|
|
2289
|
+
port: number,
|
|
2290
|
+
options: StartSetupModeOptions = {},
|
|
2291
|
+
): ReturnType<typeof createServer> {
|
|
2273
2292
|
const router = createUiRouter();
|
|
2274
2293
|
const server = createServer(router);
|
|
2275
2294
|
setupHttpServer = server;
|
|
@@ -2285,25 +2304,25 @@ export function startSetupMode(
|
|
|
2285
2304
|
process.exit(1);
|
|
2286
2305
|
});
|
|
2287
2306
|
|
|
2288
|
-
server.listen(port, "127.0.0.1", () => {
|
|
2289
|
-
const url = buildWebUiUrl(port);
|
|
2307
|
+
server.listen(port, "127.0.0.1", () => {
|
|
2308
|
+
const url = buildWebUiUrl(port);
|
|
2290
2309
|
console.log("");
|
|
2291
2310
|
console.log("=".repeat(60));
|
|
2292
2311
|
console.log(" ChatCCC — 首次配置向导");
|
|
2293
2312
|
console.log("=".repeat(60));
|
|
2294
2313
|
console.log(" 未检测到已配置的飞书凭证,已启动配置界面。");
|
|
2295
|
-
if (options.openBrowser !== false) {
|
|
2296
|
-
console.log(` 已启用启动时自动打开浏览器: ${url}`);
|
|
2297
|
-
} else {
|
|
2298
|
-
console.log(` 内部重启不会自动打开浏览器,请按需访问: ${url}`);
|
|
2299
|
-
}
|
|
2314
|
+
if (options.openBrowser !== false) {
|
|
2315
|
+
console.log(` 已启用启动时自动打开浏览器: ${url}`);
|
|
2316
|
+
} else {
|
|
2317
|
+
console.log(` 内部重启不会自动打开浏览器,请按需访问: ${url}`);
|
|
2318
|
+
}
|
|
2300
2319
|
console.log(" 若浏览器未自动弹出,请手动访问上面的地址。");
|
|
2301
2320
|
console.log("");
|
|
2302
2321
|
console.log(" 在向导里填好 App ID / App Secret 后点「保存并启动」,");
|
|
2303
2322
|
console.log(" 服务会在当前进程内直接激活,不需要重新运行 chatccc。");
|
|
2304
2323
|
console.log("=".repeat(60));
|
|
2305
2324
|
console.log("");
|
|
2306
|
-
if (options.openBrowser !== false) openWebUiInDefaultBrowser(port);
|
|
2307
|
-
});
|
|
2308
|
-
return server;
|
|
2309
|
-
}
|
|
2325
|
+
if (options.openBrowser !== false) openWebUiInDefaultBrowser(port);
|
|
2326
|
+
});
|
|
2327
|
+
return server;
|
|
2328
|
+
}
|