aicodeswitch 5.1.1 → 5.1.3
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/dist/server/coding-plan-headers.js +121 -0
- package/dist/server/config-managed-fields.js +1 -0
- package/dist/server/conversions/body-sanitizer.js +138 -0
- package/dist/server/conversions/index.js +46 -21
- package/dist/server/conversions/server-tool/mapper.js +49 -0
- package/dist/server/conversions/server-tool/providers.js +40 -0
- package/dist/server/conversions/thinking/mapper.js +21 -0
- package/dist/server/conversions/utils/tool-result.js +35 -0
- package/dist/server/fs-database.js +58 -0
- package/dist/server/main.js +308 -8
- package/dist/server/proxy-server.js +91 -14
- package/dist/server/rules-status-service.js +16 -0
- package/dist/server/session-launcher.js +282 -0
- package/dist/server/session-migration.js +419 -0
- package/dist/server/transformers/chunk-collector.js +28 -1
- package/dist/ui/assets/claude-XtpLmGtF.webp +0 -0
- package/dist/ui/assets/index-CMoQtBmK.css +1 -0
- package/dist/ui/assets/index-CXdNTFiX.js +532 -0
- package/dist/ui/assets/openai-CPEiZpaN.webp +0 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-BHR12ImE.css +0 -1
- package/dist/ui/assets/index-CumAhpXg.js +0 -517
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.resolveProjectDir = resolveProjectDir;
|
|
13
|
+
exports.writePromptToTempFile = writePromptToTempFile;
|
|
14
|
+
exports.cleanupTempFile = cleanupTempFile;
|
|
15
|
+
exports.cleanupOldTempFiles = cleanupOldTempFiles;
|
|
16
|
+
exports.launchTargetTool = launchTargetTool;
|
|
17
|
+
exports.launchTargetWithFallback = launchTargetWithFallback;
|
|
18
|
+
const child_process_1 = require("child_process");
|
|
19
|
+
const fs_1 = require("fs");
|
|
20
|
+
const path_1 = require("path");
|
|
21
|
+
const os_1 = require("os");
|
|
22
|
+
function which(cmd) {
|
|
23
|
+
try {
|
|
24
|
+
const command = process.platform === 'win32' ? `where ${cmd}` : `which ${cmd}`;
|
|
25
|
+
(0, child_process_1.execSync)(command, { stdio: 'ignore' });
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch (_a) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function isToolInstalled(tool) {
|
|
33
|
+
if (tool === 'claude-code')
|
|
34
|
+
return which('claude');
|
|
35
|
+
if (tool === 'codex')
|
|
36
|
+
return which('codex');
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function getToolCli(tool) {
|
|
40
|
+
return tool === 'claude-code' ? 'claude' : 'codex';
|
|
41
|
+
}
|
|
42
|
+
// ─── 项目目录自动推断 ───
|
|
43
|
+
/**
|
|
44
|
+
* 从 Claude Code session 文件中查找 cwd
|
|
45
|
+
* Claude Code 在 ~/.claude/sessions/<PID>.json 中记录 sessionId 和 cwd
|
|
46
|
+
*/
|
|
47
|
+
function resolveProjectDirFromClaudeSessions(sessionId) {
|
|
48
|
+
try {
|
|
49
|
+
const sessionsDir = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'sessions');
|
|
50
|
+
if (!(0, fs_1.existsSync)(sessionsDir))
|
|
51
|
+
return null;
|
|
52
|
+
const files = (0, fs_1.readdirSync)(sessionsDir).filter(f => f.endsWith('.json'));
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
try {
|
|
55
|
+
const content = (0, fs_1.readFileSync)((0, path_1.join)(sessionsDir, file), 'utf-8');
|
|
56
|
+
const meta = JSON.parse(content);
|
|
57
|
+
if (meta.sessionId === sessionId && meta.cwd) {
|
|
58
|
+
return meta.cwd;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch ( /* skip unreadable files */_a) { /* skip unreadable files */ }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 从 Codex session 文件中查找 cwd
|
|
69
|
+
* Codex 在 ~/.codex/sessions/ 目录下存储 JSONL 格式的会话文件
|
|
70
|
+
*/
|
|
71
|
+
function resolveProjectDirFromCodexSessions(sessionId) {
|
|
72
|
+
try {
|
|
73
|
+
const sessionsDir = (0, path_1.join)((0, os_1.homedir)(), '.codex', 'sessions');
|
|
74
|
+
if (!(0, fs_1.existsSync)(sessionsDir))
|
|
75
|
+
return null;
|
|
76
|
+
const files = (0, fs_1.readdirSync)(sessionsDir);
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
// Codex session 文件名格式: <uuid>.jsonl 或在子目录中
|
|
79
|
+
const filePath = (0, path_1.join)(sessionsDir, file);
|
|
80
|
+
try {
|
|
81
|
+
if ((0, fs_1.existsSync)(filePath) && !filePath.endsWith('.jsonl'))
|
|
82
|
+
continue;
|
|
83
|
+
const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
84
|
+
const lines = content.trim().split('\n');
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
try {
|
|
87
|
+
const entry = JSON.parse(line);
|
|
88
|
+
// Codex session 文件中可能包含 cwd 字段
|
|
89
|
+
if (entry.sessionId === sessionId && entry.cwd)
|
|
90
|
+
return entry.cwd;
|
|
91
|
+
if (entry.cwd && line.includes(sessionId))
|
|
92
|
+
return entry.cwd;
|
|
93
|
+
}
|
|
94
|
+
catch ( /* skip unparseable lines */_a) { /* skip unparseable lines */ }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch ( /* skip unreadable files */_b) { /* skip unreadable files */ }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch ( /* ignore */_c) { /* ignore */ }
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 自动推断会话对应的项目目录
|
|
105
|
+
*/
|
|
106
|
+
function resolveProjectDir(sessionId, _sourceTool) {
|
|
107
|
+
// 优先从 Claude Code sessions 查找
|
|
108
|
+
let cwd = resolveProjectDirFromClaudeSessions(sessionId);
|
|
109
|
+
if (cwd)
|
|
110
|
+
return cwd;
|
|
111
|
+
// 然后从 Codex sessions 查找
|
|
112
|
+
cwd = resolveProjectDirFromCodexSessions(sessionId);
|
|
113
|
+
if (cwd)
|
|
114
|
+
return cwd;
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
// ─── 临时文件管理 ───
|
|
118
|
+
function writePromptToTempFile(prompt, sessionId) {
|
|
119
|
+
const fileName = `aicodeswitch-migration-${sessionId}.txt`;
|
|
120
|
+
const filePath = (0, path_1.join)((0, os_1.tmpdir)(), fileName);
|
|
121
|
+
(0, fs_1.writeFileSync)(filePath, prompt, 'utf-8');
|
|
122
|
+
return filePath;
|
|
123
|
+
}
|
|
124
|
+
function cleanupTempFile(filePath) {
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
try {
|
|
127
|
+
(0, fs_1.unlinkSync)(filePath);
|
|
128
|
+
}
|
|
129
|
+
catch ( /* already cleaned up */_a) { /* already cleaned up */ }
|
|
130
|
+
}, 30000);
|
|
131
|
+
}
|
|
132
|
+
function cleanupOldTempFiles() {
|
|
133
|
+
try {
|
|
134
|
+
const dir = (0, os_1.tmpdir)();
|
|
135
|
+
const files = (0, fs_1.readdirSync)(dir);
|
|
136
|
+
const now = Date.now();
|
|
137
|
+
for (const file of files) {
|
|
138
|
+
if (file.startsWith('aicodeswitch-migration-') && file.endsWith('.txt')) {
|
|
139
|
+
const fullPath = (0, path_1.join)(dir, file);
|
|
140
|
+
try {
|
|
141
|
+
const { statSync } = require('fs');
|
|
142
|
+
const stat = statSync(fullPath);
|
|
143
|
+
if (now - stat.mtimeMs > 3600000) {
|
|
144
|
+
(0, fs_1.unlinkSync)(fullPath);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch ( /* ignore per-file errors */_a) { /* ignore per-file errors */ }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
152
|
+
}
|
|
153
|
+
// ─── 命令构建 ───
|
|
154
|
+
function buildCommand(tool, promptFilePath, projectDir) {
|
|
155
|
+
const cli = getToolCli(tool);
|
|
156
|
+
const cdPrefix = projectDir ? `cd "${projectDir}" && ` : '';
|
|
157
|
+
const readCmd = process.platform === 'win32' ? `type "${promptFilePath}"` : `cat "${promptFilePath}"`;
|
|
158
|
+
return `${cdPrefix}${readCmd} | ${cli}`;
|
|
159
|
+
}
|
|
160
|
+
// ─── 终端启动 ───
|
|
161
|
+
function launchViaOSAScript(command) {
|
|
162
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
163
|
+
return new Promise((resolve) => {
|
|
164
|
+
const escaped = command.replace(/"/g, '\\"');
|
|
165
|
+
const script = `tell app "Terminal" to do script "${escaped}"`;
|
|
166
|
+
const child = (0, child_process_1.spawn)('osascript', ['-e', script], {
|
|
167
|
+
stdio: 'ignore',
|
|
168
|
+
detached: true,
|
|
169
|
+
});
|
|
170
|
+
child.on('error', () => {
|
|
171
|
+
// Try iTerm2 as fallback
|
|
172
|
+
const itermScript = `tell app "iTerm" to tell current window to set newTab to (create tab with default profile) then write session 1 of newTab text "${escaped}"`;
|
|
173
|
+
const child2 = (0, child_process_1.spawn)('osascript', ['-e', itermScript], {
|
|
174
|
+
stdio: 'ignore',
|
|
175
|
+
detached: true,
|
|
176
|
+
});
|
|
177
|
+
child2.on('error', () => resolve(null));
|
|
178
|
+
child2.on('spawn', () => resolve(child2.pid || 0));
|
|
179
|
+
});
|
|
180
|
+
child.on('spawn', () => resolve(child.pid || 0));
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function launchViaTerminal(command) {
|
|
185
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
return new Promise((resolve) => {
|
|
187
|
+
const child = (0, child_process_1.spawn)('gnome-terminal', ['--', 'bash', '-c', `${command}; exec bash`], {
|
|
188
|
+
stdio: 'ignore',
|
|
189
|
+
detached: true,
|
|
190
|
+
});
|
|
191
|
+
child.on('error', () => {
|
|
192
|
+
const child2 = (0, child_process_1.spawn)('xterm', ['-e', `${command}; exec bash`], {
|
|
193
|
+
stdio: 'ignore',
|
|
194
|
+
detached: true,
|
|
195
|
+
});
|
|
196
|
+
child2.on('error', () => resolve(null));
|
|
197
|
+
child2.on('spawn', () => resolve(child2.pid || 0));
|
|
198
|
+
});
|
|
199
|
+
child.on('spawn', () => resolve(child.pid || 0));
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
// ─── 回退结果 ───
|
|
204
|
+
function createFallbackResult(tool, command, promptFilePath) {
|
|
205
|
+
const toolName = tool === 'claude-code' ? 'Claude Code' : 'Codex';
|
|
206
|
+
return {
|
|
207
|
+
success: false,
|
|
208
|
+
method: 'fallback',
|
|
209
|
+
reason: `无法自动启动 ${toolName}`,
|
|
210
|
+
command,
|
|
211
|
+
promptFilePath,
|
|
212
|
+
fallbackSuggestions: [
|
|
213
|
+
`请在终端中执行: ${command}`,
|
|
214
|
+
'或复制下方 Prompt 内容,在新会话中粘贴',
|
|
215
|
+
],
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// ─── 主启动逻辑 ───
|
|
219
|
+
function launchTargetTool(tool, promptFilePath, projectDir) {
|
|
220
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
221
|
+
const command = buildCommand(tool, promptFilePath, projectDir);
|
|
222
|
+
try {
|
|
223
|
+
let pid = null;
|
|
224
|
+
if (process.platform === 'darwin') {
|
|
225
|
+
pid = yield launchViaOSAScript(command);
|
|
226
|
+
}
|
|
227
|
+
else if (process.platform === 'linux') {
|
|
228
|
+
pid = yield launchViaTerminal(command);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
// Windows: open new cmd window
|
|
232
|
+
const cdPrefix = projectDir ? `cd /d "${projectDir}" && ` : '';
|
|
233
|
+
const cli = getToolCli(tool);
|
|
234
|
+
const winCommand = `start cmd /k "${cdPrefix}type "${promptFilePath}" | ${cli}"`;
|
|
235
|
+
yield new Promise((resolve, reject) => {
|
|
236
|
+
const child = (0, child_process_1.spawn)('cmd', ['/c', winCommand], {
|
|
237
|
+
stdio: 'ignore',
|
|
238
|
+
detached: true,
|
|
239
|
+
});
|
|
240
|
+
child.on('error', reject);
|
|
241
|
+
child.on('spawn', () => resolve());
|
|
242
|
+
});
|
|
243
|
+
pid = 0;
|
|
244
|
+
}
|
|
245
|
+
if (pid !== null) {
|
|
246
|
+
return {
|
|
247
|
+
success: true,
|
|
248
|
+
method: 'cli-launch',
|
|
249
|
+
pid: pid || undefined,
|
|
250
|
+
command,
|
|
251
|
+
promptFilePath,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (_a) {
|
|
256
|
+
// Fall through to fallback
|
|
257
|
+
}
|
|
258
|
+
return createFallbackResult(tool, command, promptFilePath);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
function launchTargetWithFallback(tool, promptFilePath, prompt, projectDir) {
|
|
262
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
263
|
+
const installed = isToolInstalled(tool);
|
|
264
|
+
if (!installed) {
|
|
265
|
+
const toolName = tool === 'claude-code' ? 'Claude Code' : 'Codex';
|
|
266
|
+
const command = buildCommand(tool, promptFilePath, projectDir);
|
|
267
|
+
return {
|
|
268
|
+
success: false,
|
|
269
|
+
method: 'fallback',
|
|
270
|
+
reason: `${toolName} CLI (${getToolCli(tool)}) not found in PATH`,
|
|
271
|
+
command,
|
|
272
|
+
prompt,
|
|
273
|
+
promptFilePath,
|
|
274
|
+
fallbackSuggestions: [
|
|
275
|
+
`请在终端中执行: ${command}`,
|
|
276
|
+
'或复制下方 Prompt 内容,在新会话中粘贴',
|
|
277
|
+
],
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
return launchTargetTool(tool, promptFilePath, projectDir);
|
|
281
|
+
});
|
|
282
|
+
}
|