@adversity/coding-tool-x 2.2.0
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/CHANGELOG.md +333 -0
- package/LICENSE +21 -0
- package/README.md +404 -0
- package/bin/ctx.js +8 -0
- package/dist/web/assets/index-D1AYlFLZ.js +3220 -0
- package/dist/web/assets/index-aL3cKxSK.css +41 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/index.html +14 -0
- package/dist/web/logo.png +0 -0
- package/docs/CHANGELOG.md +582 -0
- package/docs/DIRECTORY_MIGRATION.md +112 -0
- package/docs/PROJECT_STRUCTURE.md +396 -0
- package/docs/bannel.png +0 -0
- package/docs/home.png +0 -0
- package/docs/logo.png +0 -0
- package/docs/multi-channel-load-balancing.md +249 -0
- package/package.json +73 -0
- package/src/commands/channels.js +504 -0
- package/src/commands/cli-type.js +99 -0
- package/src/commands/daemon.js +286 -0
- package/src/commands/doctor.js +332 -0
- package/src/commands/list.js +222 -0
- package/src/commands/logs.js +259 -0
- package/src/commands/port-config.js +115 -0
- package/src/commands/proxy-control.js +258 -0
- package/src/commands/proxy.js +152 -0
- package/src/commands/resume.js +137 -0
- package/src/commands/search.js +190 -0
- package/src/commands/stats.js +224 -0
- package/src/commands/switch.js +48 -0
- package/src/commands/toggle-proxy.js +222 -0
- package/src/commands/ui.js +92 -0
- package/src/commands/workspace.js +454 -0
- package/src/config/default.js +40 -0
- package/src/config/loader.js +75 -0
- package/src/config/paths.js +121 -0
- package/src/index.js +373 -0
- package/src/reset-config.js +92 -0
- package/src/server/api/agents.js +248 -0
- package/src/server/api/aliases.js +36 -0
- package/src/server/api/channels.js +258 -0
- package/src/server/api/claude-hooks.js +480 -0
- package/src/server/api/codex-channels.js +312 -0
- package/src/server/api/codex-projects.js +91 -0
- package/src/server/api/codex-proxy.js +182 -0
- package/src/server/api/codex-sessions.js +491 -0
- package/src/server/api/codex-statistics.js +57 -0
- package/src/server/api/commands.js +245 -0
- package/src/server/api/config-templates.js +182 -0
- package/src/server/api/config.js +147 -0
- package/src/server/api/convert.js +127 -0
- package/src/server/api/dashboard.js +125 -0
- package/src/server/api/env.js +144 -0
- package/src/server/api/favorites.js +77 -0
- package/src/server/api/gemini-channels.js +261 -0
- package/src/server/api/gemini-projects.js +91 -0
- package/src/server/api/gemini-proxy.js +160 -0
- package/src/server/api/gemini-sessions.js +397 -0
- package/src/server/api/gemini-statistics.js +57 -0
- package/src/server/api/health-check.js +118 -0
- package/src/server/api/mcp.js +336 -0
- package/src/server/api/pm2-autostart.js +269 -0
- package/src/server/api/projects.js +124 -0
- package/src/server/api/prompts.js +279 -0
- package/src/server/api/proxy.js +235 -0
- package/src/server/api/rules.js +271 -0
- package/src/server/api/sessions.js +595 -0
- package/src/server/api/settings.js +61 -0
- package/src/server/api/skills.js +305 -0
- package/src/server/api/statistics.js +91 -0
- package/src/server/api/terminal.js +202 -0
- package/src/server/api/ui-config.js +64 -0
- package/src/server/api/workspaces.js +407 -0
- package/src/server/codex-proxy-server.js +538 -0
- package/src/server/dev-server.js +26 -0
- package/src/server/gemini-proxy-server.js +518 -0
- package/src/server/index.js +305 -0
- package/src/server/proxy-server.js +469 -0
- package/src/server/services/agents-service.js +354 -0
- package/src/server/services/alias.js +71 -0
- package/src/server/services/channel-health.js +234 -0
- package/src/server/services/channel-scheduler.js +234 -0
- package/src/server/services/channels.js +347 -0
- package/src/server/services/codex-channels.js +625 -0
- package/src/server/services/codex-config.js +90 -0
- package/src/server/services/codex-parser.js +322 -0
- package/src/server/services/codex-sessions.js +665 -0
- package/src/server/services/codex-settings-manager.js +397 -0
- package/src/server/services/codex-speed-test-template.json +24 -0
- package/src/server/services/codex-statistics-service.js +255 -0
- package/src/server/services/commands-service.js +360 -0
- package/src/server/services/config-templates-service.js +732 -0
- package/src/server/services/env-checker.js +307 -0
- package/src/server/services/env-manager.js +300 -0
- package/src/server/services/favorites.js +163 -0
- package/src/server/services/gemini-channels.js +333 -0
- package/src/server/services/gemini-config.js +73 -0
- package/src/server/services/gemini-sessions.js +689 -0
- package/src/server/services/gemini-settings-manager.js +263 -0
- package/src/server/services/gemini-statistics-service.js +253 -0
- package/src/server/services/health-check.js +399 -0
- package/src/server/services/mcp-service.js +1188 -0
- package/src/server/services/prompts-service.js +492 -0
- package/src/server/services/proxy-runtime.js +79 -0
- package/src/server/services/pty-manager.js +435 -0
- package/src/server/services/rules-service.js +401 -0
- package/src/server/services/session-cache.js +127 -0
- package/src/server/services/session-converter.js +577 -0
- package/src/server/services/sessions.js +757 -0
- package/src/server/services/settings-manager.js +163 -0
- package/src/server/services/skill-service.js +965 -0
- package/src/server/services/speed-test.js +545 -0
- package/src/server/services/statistics-service.js +386 -0
- package/src/server/services/terminal-commands.js +155 -0
- package/src/server/services/terminal-config.js +140 -0
- package/src/server/services/terminal-detector.js +306 -0
- package/src/server/services/ui-config.js +130 -0
- package/src/server/services/workspace-service.js +662 -0
- package/src/server/utils/pricing.js +41 -0
- package/src/server/websocket-server.js +557 -0
- package/src/ui/menu.js +129 -0
- package/src/ui/prompts.js +100 -0
- package/src/utils/format.js +43 -0
- package/src/utils/port-helper.js +94 -0
- package/src/utils/session.js +239 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts 管理服务
|
|
3
|
+
*
|
|
4
|
+
* 负责系统提示词预设的管理和多平台同步
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
|
|
11
|
+
// Prompts 配置文件路径
|
|
12
|
+
const CC_TOOL_DIR = path.join(os.homedir(), '.claude', 'cc-tool');
|
|
13
|
+
const PROMPTS_FILE = path.join(CC_TOOL_DIR, 'prompts.json');
|
|
14
|
+
|
|
15
|
+
// 各平台提示词文件路径
|
|
16
|
+
const CLAUDE_PROMPT_PATH = path.join(os.homedir(), '.claude', 'CLAUDE.md');
|
|
17
|
+
const CODEX_PROMPT_PATH = path.join(os.homedir(), '.codex', 'AGENTS.md');
|
|
18
|
+
const GEMINI_PROMPT_PATH = path.join(os.homedir(), '.gemini', 'GEMINI.md');
|
|
19
|
+
|
|
20
|
+
// 内置模板(不是"默认",只是可选模板)
|
|
21
|
+
const BUILTIN_TEMPLATES = [
|
|
22
|
+
{
|
|
23
|
+
id: 'tpl-code-review',
|
|
24
|
+
name: '代码审查',
|
|
25
|
+
description: '专注于代码审查和改进建议',
|
|
26
|
+
content: `# 代码审查专家
|
|
27
|
+
|
|
28
|
+
你是一个专业的代码审查专家,帮助用户审查代码并提供改进建议。
|
|
29
|
+
|
|
30
|
+
## 审查重点
|
|
31
|
+
- **代码质量**: 可读性、可维护性、命名规范
|
|
32
|
+
- **性能**: 算法复杂度、资源使用、潜在瓶颈
|
|
33
|
+
- **安全**: 常见漏洞、输入验证、敏感数据处理
|
|
34
|
+
- **最佳实践**: 设计模式、SOLID 原则、DRY 原则
|
|
35
|
+
|
|
36
|
+
## 输出格式
|
|
37
|
+
1. 总体评价
|
|
38
|
+
2. 具体问题列表(按严重程度排序)
|
|
39
|
+
3. 改进建议和示例代码
|
|
40
|
+
`,
|
|
41
|
+
apps: { claude: true, codex: true, gemini: true },
|
|
42
|
+
isBuiltin: true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'tpl-debugging',
|
|
46
|
+
name: '调试专家',
|
|
47
|
+
description: '帮助定位和解决代码问题',
|
|
48
|
+
content: `# 调试专家
|
|
49
|
+
|
|
50
|
+
你是一个经验丰富的调试专家,帮助用户定位和解决代码中的问题。
|
|
51
|
+
|
|
52
|
+
## 调试方法
|
|
53
|
+
1. **理解问题**: 先完整理解错误信息和预期行为
|
|
54
|
+
2. **复现问题**: 确定问题的触发条件
|
|
55
|
+
3. **缩小范围**: 通过二分法定位问题代码
|
|
56
|
+
4. **根因分析**: 找到问题的根本原因,而非表面症状
|
|
57
|
+
5. **验证修复**: 确保修复不会引入新问题
|
|
58
|
+
|
|
59
|
+
## 输出格式
|
|
60
|
+
- 问题分析
|
|
61
|
+
- 可能的原因(按可能性排序)
|
|
62
|
+
- 建议的解决方案
|
|
63
|
+
- 预防措施
|
|
64
|
+
`,
|
|
65
|
+
apps: { claude: true, codex: true, gemini: true },
|
|
66
|
+
isBuiltin: true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'tpl-refactor',
|
|
70
|
+
name: '重构助手',
|
|
71
|
+
description: '帮助优化和重构代码结构',
|
|
72
|
+
content: `# 重构助手
|
|
73
|
+
|
|
74
|
+
你是一个代码重构专家,帮助用户优化代码结构和质量。
|
|
75
|
+
|
|
76
|
+
## 重构原则
|
|
77
|
+
- **小步前进**: 每次只做一个小改动
|
|
78
|
+
- **保持可用**: 每次重构后代码都应该能正常运行
|
|
79
|
+
- **测试保障**: 确保有测试覆盖,重构后测试通过
|
|
80
|
+
|
|
81
|
+
## 常见重构手法
|
|
82
|
+
- 提取函数/方法
|
|
83
|
+
- 重命名(变量、函数、类)
|
|
84
|
+
- 移动代码到合适位置
|
|
85
|
+
- 简化条件表达式
|
|
86
|
+
- 消除重复代码
|
|
87
|
+
`,
|
|
88
|
+
apps: { claude: true, codex: true, gemini: true },
|
|
89
|
+
isBuiltin: true
|
|
90
|
+
}
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 确保目录存在
|
|
95
|
+
*/
|
|
96
|
+
function ensureDir(dirPath) {
|
|
97
|
+
if (!fs.existsSync(dirPath)) {
|
|
98
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 安全读取 JSON 文件
|
|
104
|
+
*/
|
|
105
|
+
function readJsonFile(filePath, defaultValue = {}) {
|
|
106
|
+
try {
|
|
107
|
+
if (fs.existsSync(filePath)) {
|
|
108
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
109
|
+
return JSON.parse(content);
|
|
110
|
+
}
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error(`[Prompts] Failed to read ${filePath}:`, err.message);
|
|
113
|
+
}
|
|
114
|
+
return defaultValue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 安全写入 JSON 文件(原子写入)
|
|
119
|
+
*/
|
|
120
|
+
function writeJsonFile(filePath, data) {
|
|
121
|
+
ensureDir(path.dirname(filePath));
|
|
122
|
+
const tempPath = filePath + '.tmp';
|
|
123
|
+
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
124
|
+
fs.renameSync(tempPath, filePath);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 安全读取文本文件
|
|
129
|
+
*/
|
|
130
|
+
function readTextFile(filePath, defaultValue = '') {
|
|
131
|
+
try {
|
|
132
|
+
if (fs.existsSync(filePath)) {
|
|
133
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
134
|
+
}
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error(`[Prompts] Failed to read ${filePath}:`, err.message);
|
|
137
|
+
}
|
|
138
|
+
return defaultValue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 安全写入文本文件(原子写入)
|
|
143
|
+
*/
|
|
144
|
+
function writeTextFile(filePath, content) {
|
|
145
|
+
ensureDir(path.dirname(filePath));
|
|
146
|
+
const tempPath = filePath + '.tmp';
|
|
147
|
+
fs.writeFileSync(tempPath, content, 'utf-8');
|
|
148
|
+
fs.renameSync(tempPath, filePath);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 删除文件(如果存在)
|
|
153
|
+
*/
|
|
154
|
+
function deleteFile(filePath) {
|
|
155
|
+
try {
|
|
156
|
+
if (fs.existsSync(filePath)) {
|
|
157
|
+
fs.unlinkSync(filePath);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
} catch (err) {
|
|
161
|
+
console.error(`[Prompts] Failed to delete ${filePath}:`, err.message);
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// Prompts 数据管理
|
|
168
|
+
// ============================================================================
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 初始化 Prompts 数据
|
|
172
|
+
* 如果用户已有提示词文件,自动导入为"当前使用"
|
|
173
|
+
*/
|
|
174
|
+
function initPromptsData() {
|
|
175
|
+
const data = readJsonFile(PROMPTS_FILE, null);
|
|
176
|
+
|
|
177
|
+
if (!data) {
|
|
178
|
+
// 首次使用,创建初始数据
|
|
179
|
+
const initialData = {
|
|
180
|
+
presets: {},
|
|
181
|
+
activePresetId: null // 没有默认激活的预设
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// 添加内置模板
|
|
185
|
+
BUILTIN_TEMPLATES.forEach(tpl => {
|
|
186
|
+
initialData.presets[tpl.id] = {
|
|
187
|
+
...tpl,
|
|
188
|
+
createdAt: Date.now(),
|
|
189
|
+
updatedAt: Date.now()
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// 检查用户是否已有提示词文件,如果有则导入为"当前使用"
|
|
194
|
+
const existingContent = readTextFile(CLAUDE_PROMPT_PATH, '');
|
|
195
|
+
if (existingContent.trim()) {
|
|
196
|
+
const currentPreset = {
|
|
197
|
+
id: 'current',
|
|
198
|
+
name: '当前使用',
|
|
199
|
+
description: '从现有配置导入',
|
|
200
|
+
content: existingContent,
|
|
201
|
+
apps: { claude: true, codex: false, gemini: false },
|
|
202
|
+
isBuiltin: false,
|
|
203
|
+
isImported: true,
|
|
204
|
+
createdAt: Date.now(),
|
|
205
|
+
updatedAt: Date.now()
|
|
206
|
+
};
|
|
207
|
+
initialData.presets['current'] = currentPreset;
|
|
208
|
+
initialData.activePresetId = 'current';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
writeJsonFile(PROMPTS_FILE, initialData);
|
|
212
|
+
return initialData;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return data;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 获取所有预设(内置模板排在后面)
|
|
220
|
+
*/
|
|
221
|
+
function getAllPresets() {
|
|
222
|
+
const data = initPromptsData();
|
|
223
|
+
|
|
224
|
+
// 分离用户预设和内置模板
|
|
225
|
+
const presets = Object.values(data.presets);
|
|
226
|
+
const userPresets = presets.filter(p => !p.isBuiltin);
|
|
227
|
+
const builtinPresets = presets.filter(p => p.isBuiltin);
|
|
228
|
+
|
|
229
|
+
// 用户预设按更新时间排序,内置模板保持固定顺序
|
|
230
|
+
userPresets.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
231
|
+
|
|
232
|
+
// 合并:用户预设在前,内置模板在后
|
|
233
|
+
const sortedPresets = {};
|
|
234
|
+
[...userPresets, ...builtinPresets].forEach(p => {
|
|
235
|
+
sortedPresets[p.id] = p;
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
presets: sortedPresets,
|
|
240
|
+
activePresetId: data.activePresetId
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 获取单个预设
|
|
246
|
+
*/
|
|
247
|
+
function getPreset(id) {
|
|
248
|
+
const data = initPromptsData();
|
|
249
|
+
return data.presets[id] || null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* 获取当前激活的预设
|
|
254
|
+
*/
|
|
255
|
+
function getActivePreset() {
|
|
256
|
+
const data = initPromptsData();
|
|
257
|
+
const activeId = data.activePresetId;
|
|
258
|
+
return {
|
|
259
|
+
preset: activeId ? data.presets[activeId] : null,
|
|
260
|
+
activePresetId: activeId
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 保存预设(添加或更新)
|
|
266
|
+
*/
|
|
267
|
+
function savePreset(preset) {
|
|
268
|
+
if (!preset.id || !preset.id.trim()) {
|
|
269
|
+
throw new Error('预设 ID 不能为空');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (!preset.name || !preset.name.trim()) {
|
|
273
|
+
throw new Error('预设名称不能为空');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const data = initPromptsData();
|
|
277
|
+
const existing = data.presets[preset.id];
|
|
278
|
+
|
|
279
|
+
// 内置模板不允许修改
|
|
280
|
+
if (existing && existing.isBuiltin) {
|
|
281
|
+
throw new Error('不能修改内置模板');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 设置时间戳
|
|
285
|
+
if (!existing) {
|
|
286
|
+
preset.createdAt = Date.now();
|
|
287
|
+
}
|
|
288
|
+
preset.updatedAt = Date.now();
|
|
289
|
+
|
|
290
|
+
// 确保 apps 字段存在
|
|
291
|
+
if (!preset.apps) {
|
|
292
|
+
preset.apps = { claude: true, codex: true, gemini: true };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
data.presets[preset.id] = preset;
|
|
296
|
+
writeJsonFile(PROMPTS_FILE, data);
|
|
297
|
+
|
|
298
|
+
return preset;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 删除预设
|
|
303
|
+
*/
|
|
304
|
+
function deletePreset(id) {
|
|
305
|
+
const data = initPromptsData();
|
|
306
|
+
const preset = data.presets[id];
|
|
307
|
+
|
|
308
|
+
if (!preset) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (preset.isBuiltin) {
|
|
313
|
+
throw new Error('不能删除内置模板');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// 如果删除的是当前激活的预设,清空激活状态
|
|
317
|
+
if (data.activePresetId === id) {
|
|
318
|
+
data.activePresetId = null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
delete data.presets[id];
|
|
322
|
+
writeJsonFile(PROMPTS_FILE, data);
|
|
323
|
+
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* 激活预设(将内容写入各平台文件)
|
|
329
|
+
*/
|
|
330
|
+
async function activatePreset(id) {
|
|
331
|
+
const data = initPromptsData();
|
|
332
|
+
const preset = data.presets[id];
|
|
333
|
+
|
|
334
|
+
if (!preset) {
|
|
335
|
+
throw new Error(`预设 "${id}" 不存在`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 更新激活状态
|
|
339
|
+
data.activePresetId = id;
|
|
340
|
+
writeJsonFile(PROMPTS_FILE, data);
|
|
341
|
+
|
|
342
|
+
// 同步到各平台
|
|
343
|
+
await syncPresetToAllPlatforms(preset);
|
|
344
|
+
|
|
345
|
+
return preset;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* 停用/移除提示词(删除各平台文件)
|
|
350
|
+
*/
|
|
351
|
+
async function deactivatePrompt() {
|
|
352
|
+
const data = initPromptsData();
|
|
353
|
+
|
|
354
|
+
// 清空激活状态
|
|
355
|
+
data.activePresetId = null;
|
|
356
|
+
writeJsonFile(PROMPTS_FILE, data);
|
|
357
|
+
|
|
358
|
+
// 删除各平台的提示词文件
|
|
359
|
+
const results = {
|
|
360
|
+
claude: deleteFile(CLAUDE_PROMPT_PATH),
|
|
361
|
+
codex: deleteFile(CODEX_PROMPT_PATH),
|
|
362
|
+
gemini: deleteFile(GEMINI_PROMPT_PATH)
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
console.log('[Prompts] Deactivated and removed prompt files:', results);
|
|
366
|
+
|
|
367
|
+
return results;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// ============================================================================
|
|
371
|
+
// 平台同步
|
|
372
|
+
// ============================================================================
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 同步预设到所有已启用的平台
|
|
376
|
+
*/
|
|
377
|
+
async function syncPresetToAllPlatforms(preset) {
|
|
378
|
+
const { apps, content } = preset;
|
|
379
|
+
|
|
380
|
+
if (apps.claude) {
|
|
381
|
+
writeTextFile(CLAUDE_PROMPT_PATH, content);
|
|
382
|
+
console.log('[Prompts] Synced to Claude:', CLAUDE_PROMPT_PATH);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (apps.codex) {
|
|
386
|
+
writeTextFile(CODEX_PROMPT_PATH, content);
|
|
387
|
+
console.log('[Prompts] Synced to Codex:', CODEX_PROMPT_PATH);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (apps.gemini) {
|
|
391
|
+
writeTextFile(GEMINI_PROMPT_PATH, content);
|
|
392
|
+
console.log('[Prompts] Synced to Gemini:', GEMINI_PROMPT_PATH);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* 从平台读取当前提示词
|
|
398
|
+
*/
|
|
399
|
+
function readPlatformPrompt(platform) {
|
|
400
|
+
switch (platform) {
|
|
401
|
+
case 'claude':
|
|
402
|
+
return readTextFile(CLAUDE_PROMPT_PATH, '');
|
|
403
|
+
case 'codex':
|
|
404
|
+
return readTextFile(CODEX_PROMPT_PATH, '');
|
|
405
|
+
case 'gemini':
|
|
406
|
+
return readTextFile(GEMINI_PROMPT_PATH, '');
|
|
407
|
+
default:
|
|
408
|
+
throw new Error(`无效的平台: ${platform}`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* 获取各平台当前的提示词状态
|
|
414
|
+
*/
|
|
415
|
+
function getPlatformStatus() {
|
|
416
|
+
return {
|
|
417
|
+
claude: {
|
|
418
|
+
path: CLAUDE_PROMPT_PATH,
|
|
419
|
+
exists: fs.existsSync(CLAUDE_PROMPT_PATH),
|
|
420
|
+
content: readTextFile(CLAUDE_PROMPT_PATH, '')
|
|
421
|
+
},
|
|
422
|
+
codex: {
|
|
423
|
+
path: CODEX_PROMPT_PATH,
|
|
424
|
+
exists: fs.existsSync(CODEX_PROMPT_PATH),
|
|
425
|
+
content: readTextFile(CODEX_PROMPT_PATH, '')
|
|
426
|
+
},
|
|
427
|
+
gemini: {
|
|
428
|
+
path: GEMINI_PROMPT_PATH,
|
|
429
|
+
exists: fs.existsSync(GEMINI_PROMPT_PATH),
|
|
430
|
+
content: readTextFile(GEMINI_PROMPT_PATH, '')
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* 从平台导入提示词作为新预设
|
|
437
|
+
*/
|
|
438
|
+
function importFromPlatform(platform, presetName) {
|
|
439
|
+
const content = readPlatformPrompt(platform);
|
|
440
|
+
|
|
441
|
+
if (!content.trim()) {
|
|
442
|
+
throw new Error(`${platform} 的提示词文件为空`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// 生成唯一 ID
|
|
446
|
+
const id = `imported-${platform}-${Date.now()}`;
|
|
447
|
+
|
|
448
|
+
const preset = {
|
|
449
|
+
id,
|
|
450
|
+
name: presetName || `从 ${platform} 导入`,
|
|
451
|
+
description: `从 ${platform} 导入的提示词`,
|
|
452
|
+
content,
|
|
453
|
+
apps: { claude: false, codex: false, gemini: false },
|
|
454
|
+
isBuiltin: false,
|
|
455
|
+
createdAt: Date.now(),
|
|
456
|
+
updatedAt: Date.now()
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// 标记来源平台
|
|
460
|
+
preset.apps[platform] = true;
|
|
461
|
+
|
|
462
|
+
return savePreset(preset);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* 获取统计信息
|
|
467
|
+
*/
|
|
468
|
+
function getStats() {
|
|
469
|
+
const data = initPromptsData();
|
|
470
|
+
const presets = Object.values(data.presets);
|
|
471
|
+
|
|
472
|
+
return {
|
|
473
|
+
total: presets.length,
|
|
474
|
+
builtin: presets.filter(p => p.isBuiltin).length,
|
|
475
|
+
custom: presets.filter(p => !p.isBuiltin).length,
|
|
476
|
+
activePresetId: data.activePresetId
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
module.exports = {
|
|
481
|
+
getAllPresets,
|
|
482
|
+
getPreset,
|
|
483
|
+
getActivePreset,
|
|
484
|
+
savePreset,
|
|
485
|
+
deletePreset,
|
|
486
|
+
activatePreset,
|
|
487
|
+
deactivatePrompt,
|
|
488
|
+
readPlatformPrompt,
|
|
489
|
+
getPlatformStatus,
|
|
490
|
+
importFromPlatform,
|
|
491
|
+
getStats
|
|
492
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
function getRuntimeFilePath(proxyType) {
|
|
6
|
+
const ccToolDir = path.join(os.homedir(), '.claude', 'cc-tool');
|
|
7
|
+
if (!fs.existsSync(ccToolDir)) {
|
|
8
|
+
fs.mkdirSync(ccToolDir, { recursive: true });
|
|
9
|
+
}
|
|
10
|
+
return path.join(ccToolDir, `${proxyType}-proxy-runtime.json`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function saveProxyStartTime(proxyType, preserveExisting = false) {
|
|
14
|
+
try {
|
|
15
|
+
const filePath = getRuntimeFilePath(proxyType);
|
|
16
|
+
if (preserveExisting && fs.existsSync(filePath)) {
|
|
17
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
18
|
+
}
|
|
19
|
+
const data = { startTime: Date.now(), type: proxyType };
|
|
20
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
|
21
|
+
return data;
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error(`Failed to save ${proxyType} proxy start time:`, err);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getProxyStartTime(proxyType) {
|
|
29
|
+
try {
|
|
30
|
+
const filePath = getRuntimeFilePath(proxyType);
|
|
31
|
+
if (!fs.existsSync(filePath)) return null;
|
|
32
|
+
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
33
|
+
return data.startTime || null;
|
|
34
|
+
} catch (err) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function clearProxyStartTime(proxyType) {
|
|
40
|
+
try {
|
|
41
|
+
const filePath = getRuntimeFilePath(proxyType);
|
|
42
|
+
if (fs.existsSync(filePath)) {
|
|
43
|
+
fs.unlinkSync(filePath);
|
|
44
|
+
}
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error(`Failed to clear ${proxyType} proxy start time:`, err);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getProxyRuntime(proxyType) {
|
|
51
|
+
const startTime = getProxyStartTime(proxyType);
|
|
52
|
+
return startTime ? Date.now() - startTime : null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function formatRuntime(ms) {
|
|
56
|
+
if (!ms || ms < 0) {
|
|
57
|
+
return { hours: 0, minutes: 0, seconds: 0, formatted: '0秒' };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
61
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
62
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
63
|
+
const seconds = totalSeconds % 60;
|
|
64
|
+
|
|
65
|
+
let formatted = '';
|
|
66
|
+
if (hours > 0) formatted += `${hours}小时`;
|
|
67
|
+
if (minutes > 0) formatted += `${minutes}分`;
|
|
68
|
+
if (seconds > 0 || formatted === '') formatted += `${seconds}秒`;
|
|
69
|
+
|
|
70
|
+
return { hours, minutes, seconds, formatted };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
saveProxyStartTime,
|
|
75
|
+
getProxyStartTime,
|
|
76
|
+
clearProxyStartTime,
|
|
77
|
+
getProxyRuntime,
|
|
78
|
+
formatRuntime
|
|
79
|
+
};
|