chatccc 0.2.190 → 0.2.192
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-prompts/claude_specific.md +45 -45
- package/agent-prompts/codex_specific.md +2 -2
- package/agent-prompts/cursor_specific.md +13 -13
- package/config.sample.json +14 -14
- package/im-skills/feishu-skill/receive-send-file.md +63 -63
- package/im-skills/feishu-skill/receive-send-image.md +24 -24
- package/im-skills/feishu-skill/skill.md +3 -3
- package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
- package/im-skills/wechat-file-skill/send-file.mjs +83 -83
- package/im-skills/wechat-file-skill/skill.md +10 -10
- package/im-skills/wechat-image-skill/skill.md +10 -10
- package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
- package/im-skills/wechat-video-skill/send-video.mjs +79 -79
- package/im-skills/wechat-video-skill/skill.md +10 -10
- package/package.json +1 -1
- package/scripts/postinstall-sharp-check.mjs +58 -58
- package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
- package/src/__tests__/builtin-chat-session.test.ts +76 -0
- package/src/__tests__/builtin-config.test.ts +33 -33
- package/src/__tests__/builtin-context.test.ts +126 -0
- package/src/__tests__/builtin-sigint.test.ts +56 -0
- package/src/__tests__/card-plain-text.test.ts +5 -5
- package/src/__tests__/cardkit.test.ts +60 -60
- package/src/__tests__/cards.test.ts +77 -77
- package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
- package/src/__tests__/chatgpt-subscription.test.ts +135 -135
- package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
- package/src/__tests__/claude-adapter.test.ts +592 -592
- package/src/__tests__/codex-reset-actions.test.ts +146 -146
- package/src/__tests__/config-reload.test.ts +4 -4
- package/src/__tests__/config-sample.test.ts +17 -17
- package/src/__tests__/feishu-api.test.ts +60 -60
- package/src/__tests__/feishu-platform.test.ts +22 -22
- package/src/__tests__/format-message.test.ts +47 -47
- package/src/__tests__/orchestrator.test.ts +120 -120
- package/src/__tests__/privacy.test.ts +198 -198
- package/src/__tests__/raw-stream-log.test.ts +106 -106
- package/src/__tests__/session.test.ts +17 -17
- package/src/__tests__/shared-prefix.test.ts +36 -36
- package/src/__tests__/stream-state.test.ts +42 -42
- package/src/__tests__/web-ui.test.ts +209 -209
- package/src/adapters/claude-adapter.ts +566 -566
- package/src/adapters/claude-session-meta-store.ts +120 -120
- package/src/adapters/codex-adapter.ts +30 -30
- package/src/adapters/cursor-adapter.ts +46 -46
- package/src/adapters/raw-stream-log.ts +124 -124
- package/src/adapters/resource-monitor.ts +140 -140
- package/src/agent-delegate-task-rpc.ts +153 -153
- package/src/agent-delegate-task.ts +81 -81
- package/src/agent-stop-stuck.ts +129 -129
- package/src/builtin/cli.ts +211 -189
- package/src/builtin/context.ts +225 -0
- package/src/builtin/index.ts +232 -170
- package/src/builtin/sigint.ts +50 -0
- package/src/cards.ts +137 -137
- package/src/chatgpt-subscription-rpc.ts +27 -27
- package/src/chatgpt-subscription.ts +299 -299
- package/src/chrome-devtools-guard.ts +318 -318
- package/src/codex-reset-actions.ts +184 -184
- package/src/config.ts +86 -86
- package/src/feishu-platform.ts +20 -20
- package/src/format-message.ts +293 -293
- package/src/litellm-proxy.ts +374 -374
- package/src/orchestrator.ts +143 -143
- package/src/privacy.ts +118 -118
- package/src/session-chat-binding.ts +6 -6
- package/src/session-name.ts +8 -8
- package/src/session.ts +98 -98
- package/src/shared-prefix.ts +29 -29
- package/src/sim-platform.ts +20 -20
- package/src/turn-cards.ts +117 -117
- package/src/web-ui.ts +205 -205
package/src/web-ui.ts
CHANGED
|
@@ -26,15 +26,15 @@ const ILINK_AUTH_PATH = join(USER_DATA_DIR, "state", "ilink-auth.json");
|
|
|
26
26
|
// Helpers — config.json parsing & generation
|
|
27
27
|
// ---------------------------------------------------------------------------
|
|
28
28
|
|
|
29
|
-
interface AppConfig {
|
|
30
|
-
feishu?: { appId?: string; appSecret?: string };
|
|
31
|
-
platforms?: {
|
|
32
|
-
feishu?: { enabled?: boolean; platformType?: string };
|
|
33
|
-
ilink?: { enabled?: boolean; reuseTokenOnStart?: boolean };
|
|
34
|
-
};
|
|
35
|
-
chromeDevtools?: { enabled?: boolean; port?: number; chromePath?: string };
|
|
36
|
-
port?: number;
|
|
37
|
-
gitTimeoutSeconds?: number;
|
|
29
|
+
interface AppConfig {
|
|
30
|
+
feishu?: { appId?: string; appSecret?: string };
|
|
31
|
+
platforms?: {
|
|
32
|
+
feishu?: { enabled?: boolean; platformType?: string };
|
|
33
|
+
ilink?: { enabled?: boolean; reuseTokenOnStart?: boolean };
|
|
34
|
+
};
|
|
35
|
+
chromeDevtools?: { enabled?: boolean; port?: number; chromePath?: string };
|
|
36
|
+
port?: number;
|
|
37
|
+
gitTimeoutSeconds?: number;
|
|
38
38
|
claude?: { enabled?: boolean; defaultAgent?: boolean; model?: string; subagentModel?: string; effort?: string; apiKey?: string; baseUrl?: string; maxTurn?: number };
|
|
39
39
|
// `command` 是已废弃的旧字段名,保留只读以兼容升级前的 config.json
|
|
40
40
|
cursor?: {
|
|
@@ -104,76 +104,76 @@ function loadConfig(): AppConfig {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
function saveConfig(cfg: AppConfig): void {
|
|
108
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), "utf8");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
type ConfigApplyMode = "saved" | "reload" | "restart-required";
|
|
112
|
-
|
|
113
|
-
interface ConfigApplyResult {
|
|
114
|
-
mode: ConfigApplyMode;
|
|
115
|
-
restartRequired: boolean;
|
|
116
|
-
restartReasons: string[];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function stringValue(value: unknown): string {
|
|
120
|
-
return typeof value === "string" ? value.trim() : "";
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function portValue(cfg: AppConfig): number {
|
|
124
|
-
return Number.isInteger(cfg.port) && cfg.port! >= 1 && cfg.port! <= 65535 ? cfg.port! : 18080;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function platformEnabled(cfg: AppConfig, platform: "feishu" | "ilink"): boolean {
|
|
128
|
-
return cfg.platforms?.[platform]?.enabled !== false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function feishuPlatformType(cfg: AppConfig): string {
|
|
132
|
-
return cfg.platforms?.feishu?.platformType === "lark" ? "lark" : "feishu";
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function ilinkReuseTokenOnStart(cfg: AppConfig): boolean {
|
|
136
|
-
return cfg.platforms?.ilink?.reuseTokenOnStart !== false;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function getRestartRequiredReasons(before: AppConfig, after: AppConfig): string[] {
|
|
140
|
-
const reasons: string[] = [];
|
|
141
|
-
|
|
142
|
-
if (portValue(before) !== portValue(after)) reasons.push("port");
|
|
143
|
-
if (stringValue(before.feishu?.appId) !== stringValue(after.feishu?.appId)) reasons.push("feishu.appId");
|
|
144
|
-
if (stringValue(before.feishu?.appSecret) !== stringValue(after.feishu?.appSecret)) reasons.push("feishu.appSecret");
|
|
145
|
-
if (feishuPlatformType(before) !== feishuPlatformType(after)) reasons.push("platforms.feishu.platformType");
|
|
146
|
-
if (platformEnabled(before, "feishu") !== platformEnabled(after, "feishu")) reasons.push("platforms.feishu.enabled");
|
|
147
|
-
if (platformEnabled(before, "ilink") !== platformEnabled(after, "ilink")) reasons.push("platforms.ilink.enabled");
|
|
148
|
-
if (ilinkReuseTokenOnStart(before) !== ilinkReuseTokenOnStart(after)) {
|
|
149
|
-
reasons.push("platforms.ilink.reuseTokenOnStart");
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return reasons;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async function applySavedConfigIfPossible(before: AppConfig, after: AppConfig): Promise<ConfigApplyResult> {
|
|
156
|
-
const setupMode = Boolean(setupActivateHook && setupHttpServer);
|
|
157
|
-
if (setupMode) {
|
|
158
|
-
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const running = isServiceRunning();
|
|
162
|
-
if (!running) {
|
|
163
|
-
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const restartReasons = getRestartRequiredReasons(before, after);
|
|
167
|
-
if (restartReasons.length > 0) {
|
|
168
|
-
return { mode: "restart-required", restartRequired: true, restartReasons };
|
|
169
|
-
}
|
|
170
|
-
if (!reloadConfigHook) {
|
|
171
|
-
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
await reloadConfigHook();
|
|
175
|
-
return { mode: "reload", restartRequired: false, restartReasons: [] };
|
|
176
|
-
}
|
|
107
|
+
function saveConfig(cfg: AppConfig): void {
|
|
108
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), "utf8");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type ConfigApplyMode = "saved" | "reload" | "restart-required";
|
|
112
|
+
|
|
113
|
+
interface ConfigApplyResult {
|
|
114
|
+
mode: ConfigApplyMode;
|
|
115
|
+
restartRequired: boolean;
|
|
116
|
+
restartReasons: string[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function stringValue(value: unknown): string {
|
|
120
|
+
return typeof value === "string" ? value.trim() : "";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function portValue(cfg: AppConfig): number {
|
|
124
|
+
return Number.isInteger(cfg.port) && cfg.port! >= 1 && cfg.port! <= 65535 ? cfg.port! : 18080;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function platformEnabled(cfg: AppConfig, platform: "feishu" | "ilink"): boolean {
|
|
128
|
+
return cfg.platforms?.[platform]?.enabled !== false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function feishuPlatformType(cfg: AppConfig): string {
|
|
132
|
+
return cfg.platforms?.feishu?.platformType === "lark" ? "lark" : "feishu";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function ilinkReuseTokenOnStart(cfg: AppConfig): boolean {
|
|
136
|
+
return cfg.platforms?.ilink?.reuseTokenOnStart !== false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function getRestartRequiredReasons(before: AppConfig, after: AppConfig): string[] {
|
|
140
|
+
const reasons: string[] = [];
|
|
141
|
+
|
|
142
|
+
if (portValue(before) !== portValue(after)) reasons.push("port");
|
|
143
|
+
if (stringValue(before.feishu?.appId) !== stringValue(after.feishu?.appId)) reasons.push("feishu.appId");
|
|
144
|
+
if (stringValue(before.feishu?.appSecret) !== stringValue(after.feishu?.appSecret)) reasons.push("feishu.appSecret");
|
|
145
|
+
if (feishuPlatformType(before) !== feishuPlatformType(after)) reasons.push("platforms.feishu.platformType");
|
|
146
|
+
if (platformEnabled(before, "feishu") !== platformEnabled(after, "feishu")) reasons.push("platforms.feishu.enabled");
|
|
147
|
+
if (platformEnabled(before, "ilink") !== platformEnabled(after, "ilink")) reasons.push("platforms.ilink.enabled");
|
|
148
|
+
if (ilinkReuseTokenOnStart(before) !== ilinkReuseTokenOnStart(after)) {
|
|
149
|
+
reasons.push("platforms.ilink.reuseTokenOnStart");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return reasons;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function applySavedConfigIfPossible(before: AppConfig, after: AppConfig): Promise<ConfigApplyResult> {
|
|
156
|
+
const setupMode = Boolean(setupActivateHook && setupHttpServer);
|
|
157
|
+
if (setupMode) {
|
|
158
|
+
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const running = isServiceRunning();
|
|
162
|
+
if (!running) {
|
|
163
|
+
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const restartReasons = getRestartRequiredReasons(before, after);
|
|
167
|
+
if (restartReasons.length > 0) {
|
|
168
|
+
return { mode: "restart-required", restartRequired: true, restartReasons };
|
|
169
|
+
}
|
|
170
|
+
if (!reloadConfigHook) {
|
|
171
|
+
return { mode: "saved", restartRequired: false, restartReasons: [] };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await reloadConfigHook();
|
|
175
|
+
return { mode: "reload", restartRequired: false, restartReasons: [] };
|
|
176
|
+
}
|
|
177
177
|
|
|
178
178
|
function maskSecret(value: string | undefined): string {
|
|
179
179
|
if (!value) return "(未设置)";
|
|
@@ -366,14 +366,14 @@ async function handlePostConfig(req: IncomingMessage, res: ServerResponse): Prom
|
|
|
366
366
|
existing as unknown as Record<string, unknown>,
|
|
367
367
|
unflattenConfig(updates),
|
|
368
368
|
) as AppConfig;
|
|
369
|
-
try {
|
|
370
|
-
saveConfig(merged);
|
|
371
|
-
const applyResult = await applySavedConfigIfPossible(existing, merged);
|
|
372
|
-
jsonReply(res, 200, { ok: true, ...applyResult });
|
|
373
|
-
} catch (err) {
|
|
374
|
-
jsonReply(res, 500, { ok: false, saved: true, error: (err as Error).message });
|
|
375
|
-
}
|
|
376
|
-
}
|
|
369
|
+
try {
|
|
370
|
+
saveConfig(merged);
|
|
371
|
+
const applyResult = await applySavedConfigIfPossible(existing, merged);
|
|
372
|
+
jsonReply(res, 200, { ok: true, ...applyResult });
|
|
373
|
+
} catch (err) {
|
|
374
|
+
jsonReply(res, 500, { ok: false, saved: true, error: (err as Error).message });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
377
|
|
|
378
378
|
function deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {
|
|
379
379
|
const result = { ...target };
|
|
@@ -521,18 +521,18 @@ async function handleStartService(_req: IncomingMessage, res: ServerResponse): P
|
|
|
521
521
|
return;
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
-
if (path === "reload") {
|
|
525
|
-
// service 已经在跑:只刷新进程内配置,让后续消息/新会话使用新值。
|
|
526
|
-
if (!reloadConfigHook) {
|
|
527
|
-
jsonReply(res, 200, { ok: true, pid: process.pid, mode: "saved" });
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
try {
|
|
531
|
-
await reloadConfigHook();
|
|
532
|
-
jsonReply(res, 200, { ok: true, pid: process.pid, mode: "reload" });
|
|
533
|
-
} catch (err) {
|
|
534
|
-
jsonReply(res, 500, { ok: false, error: (err as Error).message });
|
|
535
|
-
}
|
|
524
|
+
if (path === "reload") {
|
|
525
|
+
// service 已经在跑:只刷新进程内配置,让后续消息/新会话使用新值。
|
|
526
|
+
if (!reloadConfigHook) {
|
|
527
|
+
jsonReply(res, 200, { ok: true, pid: process.pid, mode: "saved" });
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
try {
|
|
531
|
+
await reloadConfigHook();
|
|
532
|
+
jsonReply(res, 200, { ok: true, pid: process.pid, mode: "reload" });
|
|
533
|
+
} catch (err) {
|
|
534
|
+
jsonReply(res, 500, { ok: false, error: (err as Error).message });
|
|
535
|
+
}
|
|
536
536
|
return;
|
|
537
537
|
}
|
|
538
538
|
|
|
@@ -930,11 +930,11 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
930
930
|
<span id="dash-platform-feishu-label">已启用</span>
|
|
931
931
|
</label>
|
|
932
932
|
</div>
|
|
933
|
-
<div class="config-row"><span class="key">App ID</span><span class="val" id="cfg-APP_ID">-</span></div>
|
|
934
|
-
<div class="config-row"><span class="key">App Secret</span><span class="val" id="cfg-APP_SECRET">-</span></div>
|
|
935
|
-
<div class="config-row"><span class="key">平台类型</span><span class="val" id="cfg-FEISHU_PLATFORM_TYPE">-</span></div>
|
|
936
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:飞书开关、App ID、App Secret 或平台类型变更需要重启 ChatCCC。</div>
|
|
937
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('feishu')">编辑</button>
|
|
933
|
+
<div class="config-row"><span class="key">App ID</span><span class="val" id="cfg-APP_ID">-</span></div>
|
|
934
|
+
<div class="config-row"><span class="key">App Secret</span><span class="val" id="cfg-APP_SECRET">-</span></div>
|
|
935
|
+
<div class="config-row"><span class="key">平台类型</span><span class="val" id="cfg-FEISHU_PLATFORM_TYPE">-</span></div>
|
|
936
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:飞书开关、App ID、App Secret 或平台类型变更需要重启 ChatCCC。</div>
|
|
937
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('feishu')">编辑</button>
|
|
938
938
|
</div>
|
|
939
939
|
</details>
|
|
940
940
|
|
|
@@ -948,13 +948,13 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
948
948
|
<span id="dash-platform-ilink-label">未启用</span>
|
|
949
949
|
</label>
|
|
950
950
|
</div>
|
|
951
|
-
<div id="ilink-forget-row" style="margin-top:6px;display:none">
|
|
952
|
-
<button class="btn btn-outline" style="font-size:12px" onclick="forgetIlink()">忘记扫码</button>
|
|
953
|
-
<span style="font-size:11px;color:#94a3b8;margin-left:8px">清除登录状态,重启后重新扫码</span>
|
|
954
|
-
</div>
|
|
955
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:微信 iLink 开关变更需要重启 ChatCCC。</div>
|
|
956
|
-
</div>
|
|
957
|
-
</details>
|
|
951
|
+
<div id="ilink-forget-row" style="margin-top:6px;display:none">
|
|
952
|
+
<button class="btn btn-outline" style="font-size:12px" onclick="forgetIlink()">忘记扫码</button>
|
|
953
|
+
<span style="font-size:11px;color:#94a3b8;margin-left:8px">清除登录状态,重启后重新扫码</span>
|
|
954
|
+
</div>
|
|
955
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:微信 iLink 开关变更需要重启 ChatCCC。</div>
|
|
956
|
+
</div>
|
|
957
|
+
</details>
|
|
958
958
|
|
|
959
959
|
<details class="card config-section">
|
|
960
960
|
<summary>Chrome CDP(选填)</summary>
|
|
@@ -962,7 +962,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
962
962
|
<div class="config-row"><span class="key">状态</span><span class="val" id="cfg-CHROME_DEVTOOLS_ENABLED">-</span></div>
|
|
963
963
|
<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>
|
|
964
964
|
<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>
|
|
965
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后立即应用到 Chrome CDP 守护进程。依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。</div>
|
|
965
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后立即应用到 Chrome CDP 守护进程。依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。</div>
|
|
966
966
|
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('chromeDevtools')">编辑</button>
|
|
967
967
|
</div>
|
|
968
968
|
</details>
|
|
@@ -975,10 +975,10 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
975
975
|
<div class="config-row"><span class="key">Effort</span><span class="val" id="cfg-ANTHROPIC_EFFORT">-</span></div>
|
|
976
976
|
<div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-ANTHROPIC_API_KEY">-</span></div>
|
|
977
977
|
<div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-ANTHROPIC_BASE_URL">-</span></div>
|
|
978
|
-
<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>
|
|
979
|
-
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-claude" onchange="setDashboardDefaultAgent('claude', this.checked)"> 设为默认 Agent</label>
|
|
980
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
981
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('claude')">编辑</button>
|
|
978
|
+
<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>
|
|
979
|
+
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-claude" onchange="setDashboardDefaultAgent('claude', this.checked)"> 设为默认 Agent</label>
|
|
980
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
981
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('claude')">编辑</button>
|
|
982
982
|
</div>
|
|
983
983
|
</details>
|
|
984
984
|
|
|
@@ -989,10 +989,10 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
989
989
|
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-CURSOR_MODEL">-</span></div>
|
|
990
990
|
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CURSOR_ALTERNATIVE_MODEL">-</span></div>
|
|
991
991
|
<div class="config-row"><span class="key">头像电池电量</span><span class="val" id="cfg-CURSOR_AVATAR_BATTERY_MODE">-</span></div>
|
|
992
|
-
<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>
|
|
993
|
-
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-cursor" onchange="setDashboardDefaultAgent('cursor', this.checked)"> 设为默认 Agent</label>
|
|
994
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
995
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('cursor')">编辑</button>
|
|
992
|
+
<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>
|
|
993
|
+
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-cursor" onchange="setDashboardDefaultAgent('cursor', this.checked)"> 设为默认 Agent</label>
|
|
994
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
995
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('cursor')">编辑</button>
|
|
996
996
|
</div>
|
|
997
997
|
</details>
|
|
998
998
|
|
|
@@ -1002,10 +1002,10 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1002
1002
|
<div class="config-row"><span class="key">CLI 路径</span><span class="val" id="cfg-CODEX_PATH">-</span></div>
|
|
1003
1003
|
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-CODEX_MODEL">-</span></div>
|
|
1004
1004
|
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CODEX_ALTERNATIVE_MODEL">-</span></div>
|
|
1005
|
-
<div class="config-row"><span class="key">Effort</span><span class="val" id="cfg-CODEX_EFFORT">-</span></div>
|
|
1006
|
-
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-codex" onchange="setDashboardDefaultAgent('codex', this.checked)"> 设为默认 Agent</label>
|
|
1007
|
-
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1008
|
-
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('codex')">编辑</button>
|
|
1005
|
+
<div class="config-row"><span class="key">Effort</span><span class="val" id="cfg-CODEX_EFFORT">-</span></div>
|
|
1006
|
+
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-codex" onchange="setDashboardDefaultAgent('codex', this.checked)"> 设为默认 Agent</label>
|
|
1007
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1008
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('codex')">编辑</button>
|
|
1009
1009
|
</div>
|
|
1010
1010
|
</details>
|
|
1011
1011
|
|
|
@@ -1021,8 +1021,8 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1021
1021
|
<!-- Edit Modal -->
|
|
1022
1022
|
<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">
|
|
1023
1023
|
<h2 id="edit-modal-title">编辑配置</h2>
|
|
1024
|
-
<div id="edit-modal-effect" class="hint" style="margin-bottom:12px;line-height:1.6"></div>
|
|
1025
|
-
<div id="edit-modal-fields"></div>
|
|
1024
|
+
<div id="edit-modal-effect" class="hint" style="margin-bottom:12px;line-height:1.6"></div>
|
|
1025
|
+
<div id="edit-modal-fields"></div>
|
|
1026
1026
|
<div class="btn-group" style="justify-content:flex-end;margin-top:16px">
|
|
1027
1027
|
<button class="btn btn-outline" onclick="closeEditModal()">取消</button>
|
|
1028
1028
|
<button class="btn btn-primary" onclick="saveEdit()">保存</button>
|
|
@@ -1061,27 +1061,27 @@ function cursorBatteryModeLabel(value) {
|
|
|
1061
1061
|
return value === 'onDemandUse' ? 'On demand use 金额' : 'API 使用比例';
|
|
1062
1062
|
}
|
|
1063
1063
|
|
|
1064
|
-
function configEffectHint(section) {
|
|
1065
|
-
if (section === 'feishu') return '生效范围:App ID、App Secret、平台类型或飞书开关变更需要重启 ChatCCC;其它未变更项仅保存。';
|
|
1066
|
-
if (section === 'chromeDevtools') return '生效范围:保存后立即应用到 Chrome CDP 守护进程。';
|
|
1067
|
-
if (section === 'claude') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1068
|
-
if (section === 'cursor') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1069
|
-
if (section === 'codex') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1070
|
-
return '生效范围:保存后按配置类型应用。';
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
function toastConfigApplyResult(result, savedText) {
|
|
1074
|
-
var text = savedText || '配置已保存';
|
|
1075
|
-
if (result && result.mode === 'reload') {
|
|
1076
|
-
toast(text + ',后续消息/新会话已生效。');
|
|
1077
|
-
} else if (result && result.mode === 'restart-required') {
|
|
1078
|
-
toast(text + ',需重启服务生效。');
|
|
1079
|
-
} else {
|
|
1080
|
-
toast(text);
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
function onCursorBatteryModeChange(prefix, value) {
|
|
1064
|
+
function configEffectHint(section) {
|
|
1065
|
+
if (section === 'feishu') return '生效范围:App ID、App Secret、平台类型或飞书开关变更需要重启 ChatCCC;其它未变更项仅保存。';
|
|
1066
|
+
if (section === 'chromeDevtools') return '生效范围:保存后立即应用到 Chrome CDP 守护进程。';
|
|
1067
|
+
if (section === 'claude') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1068
|
+
if (section === 'cursor') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1069
|
+
if (section === 'codex') return '生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。';
|
|
1070
|
+
return '生效范围:保存后按配置类型应用。';
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
function toastConfigApplyResult(result, savedText) {
|
|
1074
|
+
var text = savedText || '配置已保存';
|
|
1075
|
+
if (result && result.mode === 'reload') {
|
|
1076
|
+
toast(text + ',后续消息/新会话已生效。');
|
|
1077
|
+
} else if (result && result.mode === 'restart-required') {
|
|
1078
|
+
toast(text + ',需重启服务生效。');
|
|
1079
|
+
} else {
|
|
1080
|
+
toast(text);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
function onCursorBatteryModeChange(prefix, value) {
|
|
1085
1085
|
var rowId = prefix === 'edit-' ? 'edit-cursor-on-demand-budget-row' : 'field-cursor-on-demand-budget-row';
|
|
1086
1086
|
var row = document.getElementById(rowId);
|
|
1087
1087
|
if (row) row.style.display = value === 'onDemandUse' ? '' : 'none';
|
|
@@ -1115,10 +1115,10 @@ async function onPlatformToggle(platform, enabled) {
|
|
|
1115
1115
|
if (result.ok) {
|
|
1116
1116
|
state.config.platforms = state.config.platforms || {};
|
|
1117
1117
|
state.config.platforms[platform] = state.config.platforms[platform] || {};
|
|
1118
|
-
state.config.platforms[platform].enabled = enabled;
|
|
1119
|
-
var label = document.getElementById('dash-platform-' + platform + '-label');
|
|
1120
|
-
if (label) label.textContent = enabled ? '已启用' : '未启用';
|
|
1121
|
-
toastConfigApplyResult(result, (platform === 'feishu' ? '飞书' : '微信 iLink') + (enabled ? ' 已启用' : ' 已禁用'));
|
|
1118
|
+
state.config.platforms[platform].enabled = enabled;
|
|
1119
|
+
var label = document.getElementById('dash-platform-' + platform + '-label');
|
|
1120
|
+
if (label) label.textContent = enabled ? '已启用' : '未启用';
|
|
1121
|
+
toastConfigApplyResult(result, (platform === 'feishu' ? '飞书' : '微信 iLink') + (enabled ? ' 已启用' : ' 已禁用'));
|
|
1122
1122
|
} else {
|
|
1123
1123
|
toast('保存失败: ' + (result.error || '未知错误'), 'error');
|
|
1124
1124
|
// 还原 toggle
|
|
@@ -1524,17 +1524,17 @@ function renderStep3() {
|
|
|
1524
1524
|
document.getElementById('review-content').innerHTML = lines.join('');
|
|
1525
1525
|
}
|
|
1526
1526
|
|
|
1527
|
-
async function saveConfig(vars, options) {
|
|
1528
|
-
options = options || {};
|
|
1527
|
+
async function saveConfig(vars, options) {
|
|
1528
|
+
options = options || {};
|
|
1529
1529
|
var result = await api('/api/config', 'POST', { vars: vars });
|
|
1530
|
-
if (result.ok) {
|
|
1531
|
-
state.config = Object.assign({}, state.config, vars);
|
|
1532
|
-
if (!options.quiet) toastConfigApplyResult(result, '配置已保存');
|
|
1530
|
+
if (result.ok) {
|
|
1531
|
+
state.config = Object.assign({}, state.config, vars);
|
|
1532
|
+
if (!options.quiet) toastConfigApplyResult(result, '配置已保存');
|
|
1533
1533
|
} else {
|
|
1534
1534
|
toast('保存失败: ' + (result.error || '未知错误'), 'error');
|
|
1535
1535
|
}
|
|
1536
|
-
return result;
|
|
1537
|
-
}
|
|
1536
|
+
return result;
|
|
1537
|
+
}
|
|
1538
1538
|
|
|
1539
1539
|
async function setDashboardDefaultAgent(agent, enabled) {
|
|
1540
1540
|
if (!enabled) {
|
|
@@ -1553,10 +1553,10 @@ async function setDashboardDefaultAgent(agent, enabled) {
|
|
|
1553
1553
|
state.config.cursor = state.config.cursor || {};
|
|
1554
1554
|
state.config.codex = state.config.codex || {};
|
|
1555
1555
|
state.config.claude.defaultAgent = agent === 'claude';
|
|
1556
|
-
state.config.cursor.defaultAgent = agent === 'cursor';
|
|
1557
|
-
state.config.codex.defaultAgent = agent === 'codex';
|
|
1558
|
-
updateDefaultAgentToggles();
|
|
1559
|
-
toastConfigApplyResult(result, '默认 Agent 已更新');
|
|
1556
|
+
state.config.cursor.defaultAgent = agent === 'cursor';
|
|
1557
|
+
state.config.codex.defaultAgent = agent === 'codex';
|
|
1558
|
+
updateDefaultAgentToggles();
|
|
1559
|
+
toastConfigApplyResult(result, '默认 Agent 已更新');
|
|
1560
1560
|
} else {
|
|
1561
1561
|
toast('保存失败: ' + (result.error || '未知错误'), 'error');
|
|
1562
1562
|
updateDefaultAgentToggles();
|
|
@@ -1569,36 +1569,36 @@ async function saveAndStart() {
|
|
|
1569
1569
|
toast('飞书已启用,请先填写 App ID 和 App Secret', 'error');
|
|
1570
1570
|
return;
|
|
1571
1571
|
}
|
|
1572
|
-
var saved = await saveConfig(vars, { quiet: true });
|
|
1573
|
-
if (saved.ok !== true) return;
|
|
1574
|
-
document.getElementById('btn-save-start').disabled = true;
|
|
1575
|
-
document.getElementById('btn-save-start').innerHTML = '<span class="spinner"></span> 应用中...';
|
|
1576
|
-
|
|
1577
|
-
if (state.running && saved.restartRequired === true) {
|
|
1578
|
-
await api('/api/restart', 'POST');
|
|
1579
|
-
toast('配置已保存,服务正在重启…');
|
|
1580
|
-
pollUntilRunning();
|
|
1581
|
-
return;
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
if (state.running && saved.mode === 'reload') {
|
|
1585
|
-
toastConfigApplyResult(saved, '配置已保存');
|
|
1586
|
-
setTimeout(function(){ location.reload(); }, 1000);
|
|
1587
|
-
return;
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
var result = await api('/api/start', 'POST');
|
|
1591
|
-
if (result.ok) {
|
|
1592
|
-
var msg;
|
|
1593
|
-
if (result.mode === 'reload') {
|
|
1594
|
-
msg = '配置已保存,后续消息/新会话已生效。';
|
|
1595
|
-
} else if (result.mode === 'inplace') {
|
|
1596
|
-
msg = '服务已启动! PID: ' + result.pid;
|
|
1597
|
-
} else {
|
|
1598
|
-
msg = '服务已启动! PID: ' + (result.pid || '?');
|
|
1599
|
-
}
|
|
1600
|
-
toast(msg);
|
|
1601
|
-
setTimeout(function(){ location.reload(); }, 1500);
|
|
1572
|
+
var saved = await saveConfig(vars, { quiet: true });
|
|
1573
|
+
if (saved.ok !== true) return;
|
|
1574
|
+
document.getElementById('btn-save-start').disabled = true;
|
|
1575
|
+
document.getElementById('btn-save-start').innerHTML = '<span class="spinner"></span> 应用中...';
|
|
1576
|
+
|
|
1577
|
+
if (state.running && saved.restartRequired === true) {
|
|
1578
|
+
await api('/api/restart', 'POST');
|
|
1579
|
+
toast('配置已保存,服务正在重启…');
|
|
1580
|
+
pollUntilRunning();
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
if (state.running && saved.mode === 'reload') {
|
|
1585
|
+
toastConfigApplyResult(saved, '配置已保存');
|
|
1586
|
+
setTimeout(function(){ location.reload(); }, 1000);
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
var result = await api('/api/start', 'POST');
|
|
1591
|
+
if (result.ok) {
|
|
1592
|
+
var msg;
|
|
1593
|
+
if (result.mode === 'reload') {
|
|
1594
|
+
msg = '配置已保存,后续消息/新会话已生效。';
|
|
1595
|
+
} else if (result.mode === 'inplace') {
|
|
1596
|
+
msg = '服务已启动! PID: ' + result.pid;
|
|
1597
|
+
} else {
|
|
1598
|
+
msg = '服务已启动! PID: ' + (result.pid || '?');
|
|
1599
|
+
}
|
|
1600
|
+
toast(msg);
|
|
1601
|
+
setTimeout(function(){ location.reload(); }, 1500);
|
|
1602
1602
|
} else {
|
|
1603
1603
|
toast('保存失败: ' + (result.error || '未知错误'), 'error');
|
|
1604
1604
|
document.getElementById('btn-save-start').disabled = false;
|
|
@@ -1781,9 +1781,9 @@ function editSection(section) {
|
|
|
1781
1781
|
var titleMap = { feishu: '飞书', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent' };
|
|
1782
1782
|
document.getElementById('edit-modal-title').textContent = '编辑 ' + (titleMap[section] || section);
|
|
1783
1783
|
|
|
1784
|
-
document.getElementById('edit-modal-effect').textContent = configEffectHint(section);
|
|
1785
|
-
|
|
1786
|
-
var html = '';
|
|
1784
|
+
document.getElementById('edit-modal-effect').textContent = configEffectHint(section);
|
|
1785
|
+
|
|
1786
|
+
var html = '';
|
|
1787
1787
|
var labelMap = {
|
|
1788
1788
|
'CHATCCC_APP_ID': 'App ID', 'CHATCCC_APP_SECRET': 'App Secret',
|
|
1789
1789
|
'CHATCCC_CHROME_DEVTOOLS_ENABLED': '启用常驻 Chrome CDP(选填)',
|
|
@@ -1918,16 +1918,16 @@ async function saveEdit() {
|
|
|
1918
1918
|
var ptEl = document.getElementById('edit-CHATCCC_FEISHU_PLATFORM_TYPE');
|
|
1919
1919
|
if (ptEl && ptEl.value.trim()) vars['CHATCCC_FEISHU_PLATFORM_TYPE'] = ptEl.value.trim();
|
|
1920
1920
|
}
|
|
1921
|
-
var result = await saveConfig(vars, { quiet: true });
|
|
1922
|
-
if (result.ok !== true) return;
|
|
1923
|
-
try {
|
|
1924
|
-
var fresh = await api('/api/config');
|
|
1925
|
-
state.config = fresh.vars || state.config;
|
|
1926
|
-
} catch(e) {}
|
|
1927
|
-
closeEditModal();
|
|
1928
|
-
updateDashboardUI();
|
|
1929
|
-
toastConfigApplyResult(result, '修改已保存');
|
|
1930
|
-
}
|
|
1921
|
+
var result = await saveConfig(vars, { quiet: true });
|
|
1922
|
+
if (result.ok !== true) return;
|
|
1923
|
+
try {
|
|
1924
|
+
var fresh = await api('/api/config');
|
|
1925
|
+
state.config = fresh.vars || state.config;
|
|
1926
|
+
} catch(e) {}
|
|
1927
|
+
closeEditModal();
|
|
1928
|
+
updateDashboardUI();
|
|
1929
|
+
toastConfigApplyResult(result, '修改已保存');
|
|
1930
|
+
}
|
|
1931
1931
|
|
|
1932
1932
|
// ---- Other actions ----
|
|
1933
1933
|
function reconfigure() {
|