chatccc 0.2.184 → 0.2.186
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -69
- package/agent-prompts/cursor_specific.md +12 -1
- package/config.sample.json +29 -4
- package/im-skills/feishu-skill/skill.md +3 -3
- package/package.json +1 -1
- package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
- package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -0
- package/src/__tests__/chatgpt-subscription.test.ts +135 -0
- package/src/__tests__/chrome-devtools-guard.test.ts +125 -0
- package/src/__tests__/config-reload.test.ts +22 -4
- package/src/__tests__/config-sample.test.ts +31 -1
- package/src/__tests__/orchestrator.test.ts +162 -117
- package/src/__tests__/raw-stream-log.test.ts +106 -0
- package/src/__tests__/web-ui.test.ts +16 -0
- package/src/adapters/cursor-adapter.ts +46 -21
- package/src/adapters/raw-stream-log.ts +124 -0
- package/src/agent-delegate-task-rpc.ts +153 -153
- package/src/agent-delegate-task.ts +81 -81
- package/src/chatgpt-subscription-rpc.ts +27 -0
- package/src/chatgpt-subscription.ts +299 -0
- package/src/chrome-devtools-guard.ts +242 -0
- package/src/config.ts +99 -32
- package/src/index.ts +12 -4
- package/src/orchestrator.ts +41 -4
- package/src/session-name.ts +8 -8
- package/src/session.ts +6 -6
- package/src/web-ui.ts +136 -6
package/src/config.ts
CHANGED
|
@@ -39,9 +39,10 @@ export const USER_DATA_DIR = join(homedir(), ".chatccc");
|
|
|
39
39
|
export const PID_FILE = join(USER_DATA_DIR, "state", "runtime.pid");
|
|
40
40
|
|
|
41
41
|
export const LOG_DIR = join(USER_DATA_DIR, "logs");
|
|
42
|
-
export const fileLog = setupFileLogging(LOG_DIR, "index");
|
|
43
|
-
|
|
44
|
-
export const CHAT_LOGS_DIR = join(USER_DATA_DIR, "state", "chat_logs");
|
|
42
|
+
export const fileLog = setupFileLogging(LOG_DIR, "index");
|
|
43
|
+
|
|
44
|
+
export const CHAT_LOGS_DIR = join(USER_DATA_DIR, "state", "chat_logs");
|
|
45
|
+
export const RAW_STREAM_LOGS_DIR = join(LOG_DIR, "raw-streams");
|
|
45
46
|
|
|
46
47
|
export async function appendChatLog(chatId: string, sender: string, text: string): Promise<void> {
|
|
47
48
|
try {
|
|
@@ -113,17 +114,41 @@ export interface PlatformsConfig {
|
|
|
113
114
|
ilink: PlatformConfig;
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
export interface
|
|
117
|
+
export interface ChromeDevtoolsConfig {
|
|
118
|
+
/** 是否由 ChatCCC 守护一个常驻 Chrome CDP 实例 */
|
|
119
|
+
enabled: boolean;
|
|
120
|
+
/** Chrome remote debugging 端口,默认 15166 */
|
|
121
|
+
port: number;
|
|
122
|
+
/** Chrome 可执行文件路径;留空时按常见安装位置自动探测 */
|
|
123
|
+
chromePath: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface RawStreamAgentLogConfig {
|
|
127
|
+
enabled: boolean;
|
|
128
|
+
maxBytesPerTurn: number;
|
|
129
|
+
retentionDays: number;
|
|
130
|
+
keepCompleted: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface RawStreamLogsConfig {
|
|
134
|
+
claude: RawStreamAgentLogConfig;
|
|
135
|
+
cursor: RawStreamAgentLogConfig;
|
|
136
|
+
codex: RawStreamAgentLogConfig;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface AppConfig {
|
|
117
140
|
feishu: FeishuConfig;
|
|
118
141
|
platforms: PlatformsConfig;
|
|
142
|
+
chromeDevtools: ChromeDevtoolsConfig;
|
|
119
143
|
port: number;
|
|
120
|
-
gitTimeoutSeconds: number;
|
|
121
|
-
/** 若为 false,AI 生成过程中用户发送消息不会打断,须先点「停止」再发送新消息 */
|
|
122
|
-
allowInterrupt: boolean;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
144
|
+
gitTimeoutSeconds: number;
|
|
145
|
+
/** 若为 false,AI 生成过程中用户发送消息不会打断,须先点「停止」再发送新消息 */
|
|
146
|
+
allowInterrupt: boolean;
|
|
147
|
+
rawStreamLogs: RawStreamLogsConfig;
|
|
148
|
+
claude: ClaudeConfig;
|
|
149
|
+
cursor: CursorConfig;
|
|
150
|
+
codex: CodexConfig;
|
|
151
|
+
}
|
|
127
152
|
|
|
128
153
|
export type AgentTool = "claude" | "cursor" | "codex";
|
|
129
154
|
export const AGENT_TOOLS: AgentTool[] = ["claude", "cursor", "codex"];
|
|
@@ -333,19 +358,42 @@ function normalizeCursorAvatarBatteryMode(raw: unknown): CursorAvatarBatteryMode
|
|
|
333
358
|
return raw === "onDemandUse" ? "onDemandUse" : "apiPercent";
|
|
334
359
|
}
|
|
335
360
|
|
|
336
|
-
function normalizeCursorOnDemandMonthlyBudget(raw: unknown): number {
|
|
337
|
-
const value = typeof raw === "string" ? Number(raw.trim()) : Number(raw);
|
|
338
|
-
return Number.isFinite(value) && value > 0 ? value : 1000;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
361
|
+
function normalizeCursorOnDemandMonthlyBudget(raw: unknown): number {
|
|
362
|
+
const value = typeof raw === "string" ? Number(raw.trim()) : Number(raw);
|
|
363
|
+
return Number.isFinite(value) && value > 0 ? value : 1000;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function normalizePositiveInteger(raw: unknown, fallback: number): number {
|
|
367
|
+
const value = typeof raw === "string" ? Number(raw.trim()) : Number(raw);
|
|
368
|
+
return Number.isInteger(value) && value > 0 ? value : fallback;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function normalizeRawStreamAgentLogConfig(raw: unknown): RawStreamAgentLogConfig {
|
|
372
|
+
const obj = typeof raw === "object" && raw !== null
|
|
373
|
+
? raw as Record<string, unknown>
|
|
374
|
+
: {};
|
|
375
|
+
return {
|
|
376
|
+
enabled: typeof obj.enabled === "boolean" ? obj.enabled : false,
|
|
377
|
+
maxBytesPerTurn: normalizePositiveInteger(obj.maxBytesPerTurn, 50 * 1024 * 1024),
|
|
378
|
+
retentionDays: normalizePositiveInteger(obj.retentionDays, 7),
|
|
379
|
+
keepCompleted: typeof obj.keepCompleted === "boolean" ? obj.keepCompleted : false,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function loadConfig(): AppConfig {
|
|
384
|
+
const defaults: AppConfig = {
|
|
385
|
+
feishu: { appId: "", appSecret: "" },
|
|
386
|
+
platforms: { feishu: { enabled: true }, ilink: { enabled: true } },
|
|
387
|
+
chromeDevtools: { enabled: false, port: 15166, chromePath: "" },
|
|
388
|
+
port: 18080,
|
|
389
|
+
gitTimeoutSeconds: 180,
|
|
390
|
+
allowInterrupt: false,
|
|
391
|
+
rawStreamLogs: {
|
|
392
|
+
claude: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
393
|
+
cursor: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
394
|
+
codex: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
395
|
+
},
|
|
396
|
+
claude: { enabled: false, defaultAgent: true, model: "", subagentModel: "", effort: "", apiKey: "", baseUrl: "", maxTurn: 0 },
|
|
349
397
|
cursor: {
|
|
350
398
|
enabled: false,
|
|
351
399
|
defaultAgent: false,
|
|
@@ -405,8 +453,10 @@ function loadConfig(): AppConfig {
|
|
|
405
453
|
avatarBatteryMode?: unknown;
|
|
406
454
|
onDemandMonthlyBudget?: unknown;
|
|
407
455
|
};
|
|
408
|
-
codex?: { enabled?: unknown; defaultAgent?: unknown; path?: unknown; command?: unknown; model?: unknown; effort?: unknown };
|
|
409
|
-
|
|
456
|
+
codex?: { enabled?: unknown; defaultAgent?: unknown; path?: unknown; command?: unknown; model?: unknown; effort?: unknown };
|
|
457
|
+
chromeDevtools?: { enabled?: unknown; port?: unknown; chromePath?: unknown };
|
|
458
|
+
rawStreamLogs?: unknown;
|
|
459
|
+
};
|
|
410
460
|
try {
|
|
411
461
|
parsed = JSON.parse(raw);
|
|
412
462
|
} catch (err) {
|
|
@@ -416,8 +466,12 @@ function loadConfig(): AppConfig {
|
|
|
416
466
|
|
|
417
467
|
const feishu = parsed.feishu ?? { appId: "", appSecret: "" };
|
|
418
468
|
const claude = parsed.claude ?? {} as Partial<ClaudeConfig>;
|
|
419
|
-
const cursorRaw = (parsed.cursor ?? {}) as NonNullable<typeof parsed.cursor>;
|
|
420
|
-
const codexRaw = (parsed.codex ?? {}) as NonNullable<typeof parsed.codex>;
|
|
469
|
+
const cursorRaw = (parsed.cursor ?? {}) as NonNullable<typeof parsed.cursor>;
|
|
470
|
+
const codexRaw = (parsed.codex ?? {}) as NonNullable<typeof parsed.codex>;
|
|
471
|
+
const chromeDevtoolsRaw = (parsed.chromeDevtools ?? {}) as NonNullable<typeof parsed.chromeDevtools>;
|
|
472
|
+
const rawStreamLogsRaw = typeof parsed.rawStreamLogs === "object" && parsed.rawStreamLogs !== null
|
|
473
|
+
? parsed.rawStreamLogs as unknown as Record<string, unknown>
|
|
474
|
+
: {};
|
|
421
475
|
|
|
422
476
|
// 兼容旧字段 `command`:命中时打印一次性 warning 提示用户改名
|
|
423
477
|
const onLegacyField = (label: string, value: string): void => {
|
|
@@ -461,6 +515,7 @@ function loadConfig(): AppConfig {
|
|
|
461
515
|
const claudeEnabled = resolveEnabled(claude.enabled, claudeNonEmpty);
|
|
462
516
|
const cursorEnabled = resolveEnabled(cursorRaw.enabled, cursorNonEmpty);
|
|
463
517
|
const codexEnabled = resolveEnabled(codexRaw.enabled, codexNonEmpty);
|
|
518
|
+
const chromeDevtoolsPort = Number(chromeDevtoolsRaw.port);
|
|
464
519
|
const explicitDefaultTool: AgentTool | null =
|
|
465
520
|
typeof claude.defaultAgent === "boolean" && claude.defaultAgent && claudeEnabled ? "claude" :
|
|
466
521
|
typeof cursorRaw.defaultAgent === "boolean" && cursorRaw.defaultAgent && cursorEnabled ? "cursor" :
|
|
@@ -498,10 +553,22 @@ function loadConfig(): AppConfig {
|
|
|
498
553
|
: true,
|
|
499
554
|
},
|
|
500
555
|
},
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
556
|
+
chromeDevtools: {
|
|
557
|
+
enabled: typeof chromeDevtoolsRaw.enabled === "boolean" ? chromeDevtoolsRaw.enabled : false,
|
|
558
|
+
port: Number.isInteger(chromeDevtoolsPort) && chromeDevtoolsPort >= 1 && chromeDevtoolsPort <= 65535
|
|
559
|
+
? chromeDevtoolsPort
|
|
560
|
+
: 15166,
|
|
561
|
+
chromePath: normalizeOptionalConfigField(chromeDevtoolsRaw.chromePath, { label: "chromeDevtools.chromePath" }),
|
|
562
|
+
},
|
|
563
|
+
port: typeof parsed.port === "number" ? parsed.port : 18080,
|
|
564
|
+
gitTimeoutSeconds: typeof parsed.gitTimeoutSeconds === "number" ? parsed.gitTimeoutSeconds : 180,
|
|
565
|
+
allowInterrupt: typeof parsed.allowInterrupt === "boolean" ? parsed.allowInterrupt : false,
|
|
566
|
+
rawStreamLogs: {
|
|
567
|
+
claude: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.claude),
|
|
568
|
+
cursor: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.cursor),
|
|
569
|
+
codex: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.codex),
|
|
570
|
+
},
|
|
571
|
+
claude: {
|
|
505
572
|
enabled: claudeEnabled,
|
|
506
573
|
defaultAgent: defaultTool === "claude",
|
|
507
574
|
model: normalizeOptionalConfigField(claude.model, { label: "claude.model" }),
|
package/src/index.ts
CHANGED
|
@@ -82,6 +82,7 @@ import { handleAgentImageRequest } from "./agent-image-rpc.ts";
|
|
|
82
82
|
import { handleAgentFileRequest } from "./agent-file-rpc.ts";
|
|
83
83
|
import { handleAgentDelegateTaskRequest } from "./agent-delegate-task-rpc.ts";
|
|
84
84
|
import { handleAgentStopStuckRequest } from "./agent-stop-stuck.ts";
|
|
85
|
+
import { handleChatGptSubscriptionRequest } from "./chatgpt-subscription-rpc.ts";
|
|
85
86
|
import { applyPrivacy } from "./privacy.ts";
|
|
86
87
|
import {
|
|
87
88
|
createCardKitCard,
|
|
@@ -98,6 +99,7 @@ import {
|
|
|
98
99
|
setSessionPlatform,
|
|
99
100
|
startUnifiedDisplayLoop,
|
|
100
101
|
} from "./session.ts";
|
|
102
|
+
import { startChromeDevtoolsGuard, stopChromeDevtoolsGuard } from "./chrome-devtools-guard.ts";
|
|
101
103
|
import {
|
|
102
104
|
rebuildSessionChatsFromRegistry,
|
|
103
105
|
setQueueConsumer,
|
|
@@ -711,7 +713,8 @@ async function main(): Promise<void> {
|
|
|
711
713
|
return (await handleAgentImageRequest(req, res))
|
|
712
714
|
|| (await handleAgentFileRequest(req, res))
|
|
713
715
|
|| (await handleAgentDelegateTaskRequest(req, res, feishuPlatform))
|
|
714
|
-
|| (await handleAgentStopStuckRequest(req, res))
|
|
716
|
+
|| (await handleAgentStopStuckRequest(req, res))
|
|
717
|
+
|| (await handleChatGptSubscriptionRequest(req, res));
|
|
715
718
|
});
|
|
716
719
|
|
|
717
720
|
const simServer = createServer(createUiRouter());
|
|
@@ -765,6 +768,7 @@ async function main(): Promise<void> {
|
|
|
765
768
|
setReloadConfigHook(() => {
|
|
766
769
|
reloadConfigFromDisk();
|
|
767
770
|
clearAdapterCache();
|
|
771
|
+
startChromeDevtoolsGuard();
|
|
768
772
|
appendStartupTrace("reload-from-ui: config reloaded", {
|
|
769
773
|
appIdMask: maskAppId(APP_ID),
|
|
770
774
|
});
|
|
@@ -773,7 +777,8 @@ async function main(): Promise<void> {
|
|
|
773
777
|
return (await handleAgentImageRequest(req, res))
|
|
774
778
|
|| (await handleAgentFileRequest(req, res))
|
|
775
779
|
|| (await handleAgentDelegateTaskRequest(req, res, feishuPlatform))
|
|
776
|
-
|| (await handleAgentStopStuckRequest(req, res))
|
|
780
|
+
|| (await handleAgentStopStuckRequest(req, res))
|
|
781
|
+
|| (await handleChatGptSubscriptionRequest(req, res));
|
|
777
782
|
});
|
|
778
783
|
|
|
779
784
|
console.log(`[启动 2/7] 环境与凭证检查`);
|
|
@@ -793,6 +798,7 @@ async function main(): Promise<void> {
|
|
|
793
798
|
onActivate: async (httpServer: Server) => {
|
|
794
799
|
reloadConfigFromDisk();
|
|
795
800
|
clearAdapterCache();
|
|
801
|
+
startChromeDevtoolsGuard();
|
|
796
802
|
appendStartupTrace("setup-activate: reloaded config from disk", {
|
|
797
803
|
appIdMaskAfterReload: maskAppId(APP_ID),
|
|
798
804
|
});
|
|
@@ -817,6 +823,8 @@ async function main(): Promise<void> {
|
|
|
817
823
|
appendStartupTrace("main: feishu disabled", {});
|
|
818
824
|
}
|
|
819
825
|
|
|
826
|
+
startChromeDevtoolsGuard();
|
|
827
|
+
|
|
820
828
|
// 启动 HTTP server(同时挂 UI router,供 dashboard / setup / agent image/file 使用)
|
|
821
829
|
appendStartupTrace("main: before freeRelayListenPort", { CHATCCC_PORT });
|
|
822
830
|
const killed = freeRelayListenPort(CHATCCC_PORT);
|
|
@@ -877,8 +885,8 @@ async function listenWithRetry(
|
|
|
877
885
|
* 先写盘,再走这里。
|
|
878
886
|
*/
|
|
879
887
|
function installShutdownHandlers(httpServer: Server): void {
|
|
880
|
-
process.on("SIGINT", () => { console.log("\nShutting down..."); wechatSignal.stopped = true; httpServer.close(); process.exit(0); });
|
|
881
|
-
process.on("SIGTERM", () => { wechatSignal.stopped = true; httpServer.close(); process.exit(0); });
|
|
888
|
+
process.on("SIGINT", () => { console.log("\nShutting down..."); wechatSignal.stopped = true; stopChromeDevtoolsGuard(); httpServer.close(); process.exit(0); });
|
|
889
|
+
process.on("SIGTERM", () => { wechatSignal.stopped = true; stopChromeDevtoolsGuard(); httpServer.close(); process.exit(0); });
|
|
882
890
|
}
|
|
883
891
|
|
|
884
892
|
main().catch((err: Error) => {
|
package/src/orchestrator.ts
CHANGED
|
@@ -81,6 +81,7 @@ import {
|
|
|
81
81
|
} from "./session-chat-binding.ts";
|
|
82
82
|
import { getCodexUsageSummary, getTenantAccessToken, sendPostMessage } from "./feishu-platform.ts";
|
|
83
83
|
import { getCursorUsageSummary, type CursorUsageSummary } from "./cursor-usage.ts";
|
|
84
|
+
import { getChatGptSubscriptionStatus, type ChatGptSubscriptionResult } from "./chatgpt-subscription.ts";
|
|
84
85
|
import { delegateAgentTask } from "./agent-delegate-task.ts";
|
|
85
86
|
import { applySharedPrefix } from "./shared-prefix.ts";
|
|
86
87
|
import { cwdDisplayName, sessionChatName } from "./session-name.ts";
|
|
@@ -107,7 +108,7 @@ function findModelMatch(input: string, models: string[]): string | null {
|
|
|
107
108
|
return candidates[0] ?? null;
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
function formatCodexUsageSummary(usage: CodexUsageSummary): string {
|
|
111
|
+
function formatCodexUsageSummary(usage: CodexUsageSummary, chatGptSubscription: ChatGptSubscriptionResult | null = null): string {
|
|
111
112
|
const progressBar = (usedPercent: number) => {
|
|
112
113
|
const width = 20;
|
|
113
114
|
const usedBlocks = Math.max(0, Math.min(width, Math.round((usedPercent / 100) * width)));
|
|
@@ -185,14 +186,47 @@ function formatCodexUsageSummary(usage: CodexUsageSummary): string {
|
|
|
185
186
|
return lines.join("\n");
|
|
186
187
|
};
|
|
187
188
|
|
|
189
|
+
const formatChatGptSubscription = () => {
|
|
190
|
+
if (!chatGptSubscription?.ok || !chatGptSubscription.subscription) return "";
|
|
191
|
+
const subscription = chatGptSubscription.subscription;
|
|
192
|
+
const pad = (value: number) => String(value).padStart(2, "0");
|
|
193
|
+
const formatExpiresAt = (value: string | null) => {
|
|
194
|
+
if (!value) return "暂无数据";
|
|
195
|
+
const date = new Date(value);
|
|
196
|
+
if (!Number.isFinite(date.getTime())) return value;
|
|
197
|
+
return [
|
|
198
|
+
date.getFullYear(),
|
|
199
|
+
"-",
|
|
200
|
+
pad(date.getMonth() + 1),
|
|
201
|
+
"-",
|
|
202
|
+
pad(date.getDate()),
|
|
203
|
+
" ",
|
|
204
|
+
pad(date.getHours()),
|
|
205
|
+
":",
|
|
206
|
+
pad(date.getMinutes()),
|
|
207
|
+
].join("");
|
|
208
|
+
};
|
|
209
|
+
const remaining = typeof subscription.remainingDays === "number"
|
|
210
|
+
? `(剩余 ${subscription.remainingDays} 天)`
|
|
211
|
+
: "";
|
|
212
|
+
return [
|
|
213
|
+
"**ChatGPT 订阅:**",
|
|
214
|
+
`- 套餐: ${subscription.plan ?? "暂无数据"}`,
|
|
215
|
+
`- 到期: ${formatExpiresAt(subscription.expiresAt)}${remaining}`,
|
|
216
|
+
`- 自动续费: ${subscription.willRenew === null ? "暂无数据" : subscription.willRenew ? "是" : "否"}`,
|
|
217
|
+
].join("\n");
|
|
218
|
+
};
|
|
219
|
+
|
|
188
220
|
return [
|
|
189
221
|
"Codex 用量:",
|
|
190
222
|
"",
|
|
223
|
+
formatChatGptSubscription(),
|
|
224
|
+
chatGptSubscription?.ok ? "" : "",
|
|
191
225
|
formatResetCredits(),
|
|
192
226
|
"",
|
|
193
227
|
formatWindow("5h", usage.fiveHour),
|
|
194
228
|
formatWindow("周", usage.weekly),
|
|
195
|
-
].join("\n");
|
|
229
|
+
].filter((line, index, arr) => line !== "" || (index > 0 && arr[index - 1] !== "")).join("\n");
|
|
196
230
|
}
|
|
197
231
|
|
|
198
232
|
function formatCursorUsageSummary(usage: CursorUsageSummary): string {
|
|
@@ -277,8 +311,11 @@ async function sendUsageSummary(platform: PlatformAdapter, chatId: string, tool:
|
|
|
277
311
|
return;
|
|
278
312
|
}
|
|
279
313
|
|
|
280
|
-
const usage = await
|
|
281
|
-
|
|
314
|
+
const [usage, chatGptSubscription] = await Promise.all([
|
|
315
|
+
getCodexUsageSummary(),
|
|
316
|
+
getChatGptSubscriptionStatus().catch(() => null),
|
|
317
|
+
]);
|
|
318
|
+
const content = formatCodexUsageSummary(usage, chatGptSubscription);
|
|
282
319
|
if (platform.kind === "wechat") {
|
|
283
320
|
await platform.sendText(chatId, content).catch(() => {});
|
|
284
321
|
} else if (platform.kind === "feishu") {
|
package/src/session-name.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export function cwdDisplayName(cwd: string): string {
|
|
2
|
-
const trimmed = cwd.trim().replace(/[\\/]+$/, "");
|
|
3
|
-
return trimmed.split(/[\\/]/).filter(Boolean).pop() || trimmed || "cwd";
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export function sessionChatName(left: string, cwd: string): string {
|
|
7
|
-
return `${left}-${cwdDisplayName(cwd)}`;
|
|
8
|
-
}
|
|
1
|
+
export function cwdDisplayName(cwd: string): string {
|
|
2
|
+
const trimmed = cwd.trim().replace(/[\\/]+$/, "");
|
|
3
|
+
return trimmed.split(/[\\/]/).filter(Boolean).pop() || trimmed || "cwd";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function sessionChatName(left: string, cwd: string): string {
|
|
7
|
+
return `${left}-${cwdDisplayName(cwd)}`;
|
|
8
|
+
}
|
package/src/session.ts
CHANGED
|
@@ -955,12 +955,12 @@ export async function runAgentSession(
|
|
|
955
955
|
const imSkillsCacheDir = join(USER_DATA_DIR, "im-skills");
|
|
956
956
|
const skillVariables = {
|
|
957
957
|
cwd,
|
|
958
|
-
session_id: sessionId,
|
|
959
|
-
im_skills_cache_dir: imSkillsCacheDir,
|
|
960
|
-
delegate_task_url: `http://127.0.0.1:${CHATCCC_PORT}/api/agent/delegate-task`,
|
|
961
|
-
send_image_url: `http://127.0.0.1:${CHATCCC_PORT}/api/agent/send-image`,
|
|
962
|
-
send_file_url: `http://127.0.0.1:${CHATCCC_PORT}/api/agent/send-file`,
|
|
963
|
-
send_image_script: join(feishuSkillDir, "send-image.mjs"),
|
|
958
|
+
session_id: sessionId,
|
|
959
|
+
im_skills_cache_dir: imSkillsCacheDir,
|
|
960
|
+
delegate_task_url: `http://127.0.0.1:${CHATCCC_PORT}/api/agent/delegate-task`,
|
|
961
|
+
send_image_url: `http://127.0.0.1:${CHATCCC_PORT}/api/agent/send-image`,
|
|
962
|
+
send_file_url: `http://127.0.0.1:${CHATCCC_PORT}/api/agent/send-file`,
|
|
963
|
+
send_image_script: join(feishuSkillDir, "send-image.mjs"),
|
|
964
964
|
send_file_script: join(feishuSkillDir, "send-file.mjs"),
|
|
965
965
|
download_video_script: join(feishuSkillDir, "download-video.mjs"),
|
|
966
966
|
wechat_send_image_script: join(wechatImageSkillDir, "send-image.mjs"),
|
package/src/web-ui.ts
CHANGED
|
@@ -29,6 +29,7 @@ const ILINK_AUTH_PATH = join(USER_DATA_DIR, "state", "ilink-auth.json");
|
|
|
29
29
|
interface AppConfig {
|
|
30
30
|
feishu?: { appId?: string; appSecret?: string };
|
|
31
31
|
platforms?: { feishu?: { enabled?: boolean }; ilink?: { enabled?: boolean } };
|
|
32
|
+
chromeDevtools?: { enabled?: boolean; port?: number; chromePath?: string };
|
|
32
33
|
port?: number;
|
|
33
34
|
gitTimeoutSeconds?: number;
|
|
34
35
|
claude?: { enabled?: boolean; defaultAgent?: boolean; model?: string; subagentModel?: string; effort?: string; apiKey?: string; baseUrl?: string; maxTurn?: number };
|
|
@@ -336,6 +337,15 @@ export function unflattenConfig(flat: Record<string, unknown>): Record<string, u
|
|
|
336
337
|
result.platforms = result.platforms || {};
|
|
337
338
|
(result.platforms as Record<string, unknown>).ilink = (result.platforms as Record<string, unknown>).ilink || {};
|
|
338
339
|
((result.platforms as Record<string, unknown>).ilink as Record<string, unknown>).enabled = val === true || val === "true";
|
|
340
|
+
} else if (key === "CHATCCC_CHROME_DEVTOOLS_ENABLED") {
|
|
341
|
+
result.chromeDevtools = result.chromeDevtools || {};
|
|
342
|
+
(result.chromeDevtools as Record<string, unknown>).enabled = val === true || val === "true";
|
|
343
|
+
} else if (key === "CHATCCC_CHROME_DEVTOOLS_PORT") {
|
|
344
|
+
result.chromeDevtools = result.chromeDevtools || {};
|
|
345
|
+
(result.chromeDevtools as Record<string, unknown>).port = parseInt(val as string, 10) || 15166;
|
|
346
|
+
} else if (key === "CHATCCC_CHROME_DEVTOOLS_PATH") {
|
|
347
|
+
result.chromeDevtools = result.chromeDevtools || {};
|
|
348
|
+
(result.chromeDevtools as Record<string, unknown>).chromePath = val;
|
|
339
349
|
} else if (key === "CHATCCC_PORT") {
|
|
340
350
|
result.port = parseInt(val as string, 10) || 18080;
|
|
341
351
|
} else if (key === "CHATCCC_GIT_TIMEOUT_SECONDS") {
|
|
@@ -639,6 +649,32 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
639
649
|
</div>
|
|
640
650
|
</div>
|
|
641
651
|
|
|
652
|
+
<!-- Chrome CDP -->
|
|
653
|
+
<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:14px;margin-bottom:12px" id="chrome-devtools-block">
|
|
654
|
+
<div style="display:flex;align-items:center;justify-content:space-between">
|
|
655
|
+
<div>
|
|
656
|
+
<div style="font-weight:600;font-size:14px">常驻 Chrome CDP(选填)</div>
|
|
657
|
+
<div style="font-size:12px;color:#64748b">维护本机 Chrome DevTools Protocol 端口,用于 ChatGPT 订阅到期查询</div>
|
|
658
|
+
</div>
|
|
659
|
+
<input type="checkbox" class="agent-toggle" id="field-CHATCCC_CHROME_DEVTOOLS_ENABLED" onchange="toggleWizardChromeDevtoolsFields(this.checked)">
|
|
660
|
+
</div>
|
|
661
|
+
<div id="chrome-devtools-settings" style="margin-top:12px;padding-top:12px;border-top:1px solid #e2e8f0">
|
|
662
|
+
<div class="hint" style="margin-bottom:10px;line-height:1.6">
|
|
663
|
+
依赖:本机已安装 Google Chrome;查询 ChatGPT 订阅到期时间时,需要在这个 CDP 专用 Chrome 窗口中登录 ChatGPT。
|
|
664
|
+
</div>
|
|
665
|
+
<div class="form-group" style="margin-bottom:10px">
|
|
666
|
+
<label>CDP 端口</label>
|
|
667
|
+
<input type="number" id="field-CHATCCC_CHROME_DEVTOOLS_PORT" min="1" max="65535" step="1" placeholder="15166">
|
|
668
|
+
<div class="hint">默认 15166;健康检查端点:http://127.0.0.1:15166/json/version</div>
|
|
669
|
+
</div>
|
|
670
|
+
<div class="form-group">
|
|
671
|
+
<label>Chrome 路径(选填)</label>
|
|
672
|
+
<input type="text" id="field-CHATCCC_CHROME_DEVTOOLS_PATH" placeholder="留空自动探测 chrome.exe">
|
|
673
|
+
<div class="hint">选填;留空时自动探测 Google Chrome。</div>
|
|
674
|
+
</div>
|
|
675
|
+
</div>
|
|
676
|
+
</div>
|
|
677
|
+
|
|
642
678
|
<div class="btn-group" style="justify-content:flex-end">
|
|
643
679
|
<button class="btn btn-primary" id="btn-step1-next" onclick="goStep1Next()">下一步</button>
|
|
644
680
|
</div>
|
|
@@ -825,6 +861,17 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
825
861
|
</div>
|
|
826
862
|
</details>
|
|
827
863
|
|
|
864
|
+
<details class="card config-section">
|
|
865
|
+
<summary>Chrome CDP(选填)</summary>
|
|
866
|
+
<div class="section-detail">
|
|
867
|
+
<div class="config-row"><span class="key">状态</span><span class="val" id="cfg-CHROME_DEVTOOLS_ENABLED">-</span></div>
|
|
868
|
+
<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>
|
|
869
|
+
<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>
|
|
870
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。</div>
|
|
871
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('chromeDevtools')">编辑</button>
|
|
872
|
+
</div>
|
|
873
|
+
</details>
|
|
874
|
+
|
|
828
875
|
<details class="card config-section" id="dash-claude">
|
|
829
876
|
<summary>Claude Agent</summary>
|
|
830
877
|
<div class="section-detail">
|
|
@@ -907,6 +954,7 @@ const AGENT_FIELDS = {
|
|
|
907
954
|
codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_EFFORT']
|
|
908
955
|
};
|
|
909
956
|
const FEISHU_FIELDS = ['CHATCCC_APP_ID','CHATCCC_APP_SECRET'];
|
|
957
|
+
const CHROME_DEVTOOLS_FIELDS = ['CHATCCC_CHROME_DEVTOOLS_ENABLED','CHATCCC_CHROME_DEVTOOLS_PORT','CHATCCC_CHROME_DEVTOOLS_PATH'];
|
|
910
958
|
|
|
911
959
|
function cursorBatteryModeLabel(value) {
|
|
912
960
|
return value === 'onDemandUse' ? 'On demand use 金额' : 'API 使用比例';
|
|
@@ -918,6 +966,18 @@ function onCursorBatteryModeChange(prefix, value) {
|
|
|
918
966
|
if (row) row.style.display = value === 'onDemandUse' ? '' : 'none';
|
|
919
967
|
}
|
|
920
968
|
|
|
969
|
+
function toggleWizardChromeDevtoolsFields(enabled) {
|
|
970
|
+
var settings = document.getElementById('chrome-devtools-settings');
|
|
971
|
+
if (settings) settings.style.display = enabled ? '' : 'none';
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
function toggleEditChromeDevtoolsFields(enabled) {
|
|
975
|
+
['CHATCCC_CHROME_DEVTOOLS_PORT', 'CHATCCC_CHROME_DEVTOOLS_PATH'].forEach(function(key){
|
|
976
|
+
var row = document.getElementById('edit-row-' + key);
|
|
977
|
+
if (row) row.style.display = enabled ? '' : 'none';
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
|
|
921
981
|
function onWizardPlatformToggle(platform, enabled) {
|
|
922
982
|
state.platformsEnabled[platform] = enabled;
|
|
923
983
|
// 飞书凭证字段跟随开关显示/隐藏
|
|
@@ -1133,6 +1193,12 @@ function renderStep1() {
|
|
|
1133
1193
|
if (credFields) credFields.style.display = feishuEnabled ? '' : 'none';
|
|
1134
1194
|
var ilToggle = document.getElementById('platform-enable-ilink');
|
|
1135
1195
|
if (ilToggle) ilToggle.checked = ilinkEnabled;
|
|
1196
|
+
var cd = c.chromeDevtools || {};
|
|
1197
|
+
var cdpEnabled = document.getElementById('field-CHATCCC_CHROME_DEVTOOLS_ENABLED');
|
|
1198
|
+
if (cdpEnabled) cdpEnabled.checked = cd.enabled === true;
|
|
1199
|
+
prefillNested('field-CHATCCC_CHROME_DEVTOOLS_PORT', cd.port || 15166);
|
|
1200
|
+
prefillNested('field-CHATCCC_CHROME_DEVTOOLS_PATH', cd.chromePath);
|
|
1201
|
+
toggleWizardChromeDevtoolsFields(cd.enabled === true);
|
|
1136
1202
|
}
|
|
1137
1203
|
|
|
1138
1204
|
/**
|
|
@@ -1231,6 +1297,12 @@ function collectAllFields() {
|
|
|
1231
1297
|
if (ptEl && ptEl.value.trim()) vars['CHATCCC_FEISHU_PLATFORM_TYPE'] = ptEl.value.trim();
|
|
1232
1298
|
vars.CHATCCC_FEISHU_ENABLED = !!state.platformsEnabled.feishu;
|
|
1233
1299
|
vars.CHATCCC_ILINK_ENABLED = !!state.platformsEnabled.ilink;
|
|
1300
|
+
var cdpEnabledEl = document.getElementById('field-CHATCCC_CHROME_DEVTOOLS_ENABLED');
|
|
1301
|
+
vars.CHATCCC_CHROME_DEVTOOLS_ENABLED = !!(cdpEnabledEl && cdpEnabledEl.checked);
|
|
1302
|
+
var cdpPortEl = document.getElementById('field-CHATCCC_CHROME_DEVTOOLS_PORT');
|
|
1303
|
+
vars.CHATCCC_CHROME_DEVTOOLS_PORT = (cdpPortEl && cdpPortEl.value.trim()) ? cdpPortEl.value.trim() : '15166';
|
|
1304
|
+
var cdpPathEl = document.getElementById('field-CHATCCC_CHROME_DEVTOOLS_PATH');
|
|
1305
|
+
if (cdpPathEl && cdpPathEl.value.trim()) vars.CHATCCC_CHROME_DEVTOOLS_PATH = cdpPathEl.value.trim();
|
|
1234
1306
|
vars.CHATCCC_CLAUDE_ENABLED = !!state.agentsEnabled.claude;
|
|
1235
1307
|
vars.CHATCCC_CURSOR_ENABLED = !!state.agentsEnabled.cursor;
|
|
1236
1308
|
vars.CHATCCC_CODEX_ENABLED = !!state.agentsEnabled.codex;
|
|
@@ -1281,6 +1353,15 @@ function renderStep3() {
|
|
|
1281
1353
|
lines.push('<div style="color:#ef4444;margin-top:8px">未启用任何平台</div>');
|
|
1282
1354
|
}
|
|
1283
1355
|
|
|
1356
|
+
lines.push('<h3 style="margin:16px 0 8px">Chrome CDP</h3>');
|
|
1357
|
+
lines.push('<div class="config-row"><span class="key">状态</span><span class="val">' + (vars.CHATCCC_CHROME_DEVTOOLS_ENABLED ? '已启用' : '已禁用') + '</span></div>');
|
|
1358
|
+
if (vars.CHATCCC_CHROME_DEVTOOLS_ENABLED) {
|
|
1359
|
+
lines.push('<div class="config-row"><span class="key">CDP 端口</span><span class="val">' + (vars.CHATCCC_CHROME_DEVTOOLS_PORT || '15166') + '</span></div>');
|
|
1360
|
+
if (vars.CHATCCC_CHROME_DEVTOOLS_PATH) {
|
|
1361
|
+
lines.push('<div class="config-row"><span class="key">Chrome 路径</span><span class="val">' + vars.CHATCCC_CHROME_DEVTOOLS_PATH + '</span></div>');
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1284
1365
|
lines.push('<h3 style="margin:16px 0 8px">已启用的 AI Agent</h3>');
|
|
1285
1366
|
var enabledList = [];
|
|
1286
1367
|
if (state.agentsEnabled.claude) enabledList.push('claude');
|
|
@@ -1471,6 +1552,16 @@ function updateDashboardUI() {
|
|
|
1471
1552
|
ilinkForgetRow.style.display = (ilinkEnabled && state.ilinkAuthExists) ? '' : 'none';
|
|
1472
1553
|
}
|
|
1473
1554
|
|
|
1555
|
+
var chromeDevtools = c.chromeDevtools || {};
|
|
1556
|
+
var cdpPort = chromeDevtools.port || 15166;
|
|
1557
|
+
document.getElementById('cfg-CHROME_DEVTOOLS_ENABLED').textContent = chromeDevtools.enabled ? '已启用' : '已禁用';
|
|
1558
|
+
document.getElementById('cfg-CHROME_DEVTOOLS_PORT').textContent = String(cdpPort);
|
|
1559
|
+
document.getElementById('cfg-CHROME_DEVTOOLS_PATH').textContent = chromeDevtools.chromePath || '(自动探测)';
|
|
1560
|
+
var cdpPortRow = document.getElementById('cfg-CHROME_DEVTOOLS_PORT_ROW');
|
|
1561
|
+
var cdpPathRow = document.getElementById('cfg-CHROME_DEVTOOLS_PATH_ROW');
|
|
1562
|
+
if (cdpPortRow) cdpPortRow.style.display = chromeDevtools.enabled ? '' : 'none';
|
|
1563
|
+
if (cdpPathRow) cdpPathRow.style.display = chromeDevtools.enabled ? '' : 'none';
|
|
1564
|
+
|
|
1474
1565
|
document.getElementById('dash-claude').style.display = claudeOn ? '' : 'none';
|
|
1475
1566
|
document.getElementById('dash-cursor').style.display = cursorOn ? '' : 'none';
|
|
1476
1567
|
document.getElementById('dash-codex').style.display = codexOn ? '' : 'none';
|
|
@@ -1546,14 +1637,18 @@ function editSection(section) {
|
|
|
1546
1637
|
editSectionType = section;
|
|
1547
1638
|
var fields;
|
|
1548
1639
|
if (section === 'feishu') fields = FEISHU_FIELDS;
|
|
1640
|
+
else if (section === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
|
|
1549
1641
|
else fields = AGENT_FIELDS[section] || [];
|
|
1550
1642
|
|
|
1551
|
-
var titleMap = { feishu: '飞书', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent' };
|
|
1643
|
+
var titleMap = { feishu: '飞书', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent' };
|
|
1552
1644
|
document.getElementById('edit-modal-title').textContent = '编辑 ' + (titleMap[section] || section);
|
|
1553
1645
|
|
|
1554
1646
|
var html = '';
|
|
1555
1647
|
var labelMap = {
|
|
1556
1648
|
'CHATCCC_APP_ID': 'App ID', 'CHATCCC_APP_SECRET': 'App Secret',
|
|
1649
|
+
'CHATCCC_CHROME_DEVTOOLS_ENABLED': '启用常驻 Chrome CDP(选填)',
|
|
1650
|
+
'CHATCCC_CHROME_DEVTOOLS_PORT': 'CDP 端口',
|
|
1651
|
+
'CHATCCC_CHROME_DEVTOOLS_PATH': 'Chrome 路径(选填)',
|
|
1557
1652
|
'CHATCCC_ANTHROPIC_MODEL': '模型', 'CHATCCC_ANTHROPIC_SUBAGENT_MODEL': 'Subagent 模型', 'CHATCCC_ANTHROPIC_EFFORT': 'Effort',
|
|
1558
1653
|
'CHATCCC_ANTHROPIC_API_KEY': 'API Key', 'CHATCCC_ANTHROPIC_BASE_URL': 'Base URL', 'CHATCCC_ANTHROPIC_MAX_TURN': 'Max Turns (0=无限制)',
|
|
1559
1654
|
'CHATCCC_CURSOR_PATH': 'CLI 路径', 'CHATCCC_CURSOR_MODEL': '模型',
|
|
@@ -1561,6 +1656,15 @@ function editSection(section) {
|
|
|
1561
1656
|
'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET': '每月On demand use预算',
|
|
1562
1657
|
'CHATCCC_CODEX_PATH': 'CLI 路径', 'CHATCCC_CODEX_MODEL': '模型', 'CHATCCC_CODEX_EFFORT': 'Effort'
|
|
1563
1658
|
};
|
|
1659
|
+
var hintMap = {
|
|
1660
|
+
'CHATCCC_CHROME_DEVTOOLS_ENABLED': '依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。',
|
|
1661
|
+
'CHATCCC_CHROME_DEVTOOLS_PORT': '默认 15166,健康检查端点为 http://127.0.0.1:15166/json/version。',
|
|
1662
|
+
'CHATCCC_CHROME_DEVTOOLS_PATH': '选填。留空时自动探测 Google Chrome。'
|
|
1663
|
+
};
|
|
1664
|
+
|
|
1665
|
+
if (section === 'chromeDevtools') {
|
|
1666
|
+
html += '<div class="hint" style="margin-bottom:12px;line-height:1.6">常驻 Chrome CDP 用于维护本机 Chrome DevTools Protocol 端口。依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。</div>';
|
|
1667
|
+
}
|
|
1564
1668
|
|
|
1565
1669
|
fields.forEach(function(key){
|
|
1566
1670
|
var val = state.config[key] || '';
|
|
@@ -1569,6 +1673,10 @@ function editSection(section) {
|
|
|
1569
1673
|
if (section === 'feishu') {
|
|
1570
1674
|
if (key === 'CHATCCC_APP_ID' && state.config.feishu) val = state.config.feishu.appId || '';
|
|
1571
1675
|
else if (key === 'CHATCCC_APP_SECRET' && state.config.feishu) val = state.config.feishu.appSecret || '';
|
|
1676
|
+
} else if (section === 'chromeDevtools' && state.config.chromeDevtools) {
|
|
1677
|
+
if (key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED') val = state.config.chromeDevtools.enabled === true ? 'true' : 'false';
|
|
1678
|
+
else if (key === 'CHATCCC_CHROME_DEVTOOLS_PORT') val = state.config.chromeDevtools.port != null ? String(state.config.chromeDevtools.port) : '15166';
|
|
1679
|
+
else if (key === 'CHATCCC_CHROME_DEVTOOLS_PATH') val = state.config.chromeDevtools.chromePath || '';
|
|
1572
1680
|
} else if (section === 'claude' && state.config.claude) {
|
|
1573
1681
|
if (key === 'CHATCCC_ANTHROPIC_MODEL') val = state.config.claude.model || '';
|
|
1574
1682
|
else if (key === 'CHATCCC_ANTHROPIC_SUBAGENT_MODEL') val = state.config.claude.subagentModel || '';
|
|
@@ -1588,7 +1696,12 @@ function editSection(section) {
|
|
|
1588
1696
|
}
|
|
1589
1697
|
}
|
|
1590
1698
|
var isSecret = key.includes('SECRET') || key.includes('API_KEY');
|
|
1591
|
-
if (key === '
|
|
1699
|
+
if (key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED') {
|
|
1700
|
+
var checked = val === true || val === 'true';
|
|
1701
|
+
html += '<div class="form-group"><label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="edit-' + key + '"' + (checked ? ' checked' : '') + ' onchange="toggleEditChromeDevtoolsFields(this.checked)"> ' + (labelMap[key] || key) + '</label>';
|
|
1702
|
+
if (hintMap[key]) html += '<div class="hint" style="margin-top:6px;line-height:1.5">' + hintMap[key] + '</div>';
|
|
1703
|
+
html += '</div>';
|
|
1704
|
+
} else if (key === 'CHATCCC_CURSOR_AVATAR_BATTERY_MODE') {
|
|
1592
1705
|
var modeVal = val || 'apiPercent';
|
|
1593
1706
|
html += '<div class="form-group"><label>' + (labelMap[key] || key) + '</label>';
|
|
1594
1707
|
html += '<select id="edit-' + key + '" onchange="onCursorBatteryModeChange(\\'edit-\\', this.value)" style="width:100%;padding:8px 12px;border:1px solid #cbd5e1;border-radius:8px;font-size:14px;outline:none">';
|
|
@@ -1596,11 +1709,19 @@ function editSection(section) {
|
|
|
1596
1709
|
html += '<option value="onDemandUse"' + (modeVal === 'onDemandUse' ? ' selected' : '') + '>On demand use 金额</option>';
|
|
1597
1710
|
html += '</select></div>';
|
|
1598
1711
|
} else {
|
|
1599
|
-
var rowId = key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'
|
|
1600
|
-
|
|
1601
|
-
|
|
1712
|
+
var rowId = key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'
|
|
1713
|
+
? ' id="edit-cursor-on-demand-budget-row"'
|
|
1714
|
+
: (section === 'chromeDevtools' && key !== 'CHATCCC_CHROME_DEVTOOLS_ENABLED' ? ' id="edit-row-' + key + '"' : '');
|
|
1715
|
+
var isNumber = key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET' || key === 'CHATCCC_CHROME_DEVTOOLS_PORT';
|
|
1716
|
+
var inputType = isNumber ? 'number' : (isSecret ? 'password' : 'text');
|
|
1717
|
+
var attrs = key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'
|
|
1718
|
+
? ' min="1" step="1"'
|
|
1719
|
+
: key === 'CHATCCC_CHROME_DEVTOOLS_PORT'
|
|
1720
|
+
? ' min="1" max="65535" step="1" placeholder="15166"'
|
|
1721
|
+
: '';
|
|
1602
1722
|
html += '<div class="form-group"' + rowId + '><label>' + (labelMap[key] || key) + '</label>';
|
|
1603
1723
|
html += '<input type="' + inputType + '" id="edit-' + key + '"' + attrs + ' value="' + String(val).replace(/"/g,'"') + '">';
|
|
1724
|
+
if (hintMap[key]) html += '<div class="hint" style="margin-top:6px;line-height:1.5">' + hintMap[key] + '</div>';
|
|
1604
1725
|
html += '</div>';
|
|
1605
1726
|
}
|
|
1606
1727
|
});
|
|
@@ -1617,6 +1738,9 @@ function editSection(section) {
|
|
|
1617
1738
|
if (section === 'cursor') {
|
|
1618
1739
|
var editModeEl = document.getElementById('edit-CHATCCC_CURSOR_AVATAR_BATTERY_MODE');
|
|
1619
1740
|
onCursorBatteryModeChange('edit-', editModeEl ? editModeEl.value : 'apiPercent');
|
|
1741
|
+
} else if (section === 'chromeDevtools') {
|
|
1742
|
+
var editChromeDevtoolsEnabledEl = document.getElementById('edit-CHATCCC_CHROME_DEVTOOLS_ENABLED');
|
|
1743
|
+
toggleEditChromeDevtoolsFields(!!(editChromeDevtoolsEnabledEl && editChromeDevtoolsEnabledEl.checked));
|
|
1620
1744
|
}
|
|
1621
1745
|
document.getElementById('edit-modal').classList.remove('hidden');
|
|
1622
1746
|
document.getElementById('edit-overlay').classList.remove('hidden');
|
|
@@ -1631,13 +1755,19 @@ function closeEditModal() {
|
|
|
1631
1755
|
async function saveEdit() {
|
|
1632
1756
|
var fields;
|
|
1633
1757
|
if (editSectionType === 'feishu') fields = FEISHU_FIELDS;
|
|
1758
|
+
else if (editSectionType === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
|
|
1634
1759
|
else fields = AGENT_FIELDS[editSectionType] || [];
|
|
1635
1760
|
|
|
1636
1761
|
var vars = {};
|
|
1637
1762
|
fields.forEach(function(key){
|
|
1638
1763
|
var el = document.getElementById('edit-' + key);
|
|
1639
|
-
if (el)
|
|
1764
|
+
if (!el) return;
|
|
1765
|
+
if (key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED') vars[key] = !!el.checked;
|
|
1766
|
+
else vars[key] = el.value.trim();
|
|
1640
1767
|
});
|
|
1768
|
+
if (editSectionType === 'chromeDevtools' && !vars.CHATCCC_CHROME_DEVTOOLS_PORT) {
|
|
1769
|
+
vars.CHATCCC_CHROME_DEVTOOLS_PORT = '15166';
|
|
1770
|
+
}
|
|
1641
1771
|
if (editSectionType === 'cursor' && vars.CHATCCC_CURSOR_AVATAR_BATTERY_MODE === 'onDemandUse' && !vars.CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET) {
|
|
1642
1772
|
vars.CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET = '1000';
|
|
1643
1773
|
}
|