aiexecode 1.0.54 → 1.0.56
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.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/index.js +7 -10
- package/package.json +1 -1
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +1 -1
- package/src/ai_based/pip_package_lookup.js +3 -3
- package/src/system/ai_request.js +3 -3
- package/src/system/code_executer.js +13 -13
- package/src/system/command_loader.js +2 -2
- package/src/system/file_integrity.js +9 -9
- package/src/system/log.js +4 -4
- package/src/system/session.js +7 -3
- package/src/system/session_memory.js +52 -26
- package/src/tools/code_editor.js +21 -21
- package/src/tools/file_reader.js +3 -3
- package/src/tools/glob.js +1 -1
- package/src/tools/web_downloader.js +2 -2
- package/src/ui/components/HistoryItemDisplay.js +11 -5
- package/src/util/config.js +9 -9
- package/src/util/debug_log.js +11 -6
- package/src/util/mcp_config_manager.js +5 -5
- package/src/util/output_formatter.js +1 -1
- package/src/util/prompt_loader.js +3 -3
- package/src/util/safe_fs.js +522 -0
- /package/payload_viewer/out/_next/static/{CkG9PSc7KAlXzs3w8IZSf → h5BNZL0hrewfv11rqfUcZ}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{CkG9PSc7KAlXzs3w8IZSf → h5BNZL0hrewfv11rqfUcZ}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{CkG9PSc7KAlXzs3w8IZSf → h5BNZL0hrewfv11rqfUcZ}/_ssgManifest.js +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// 세션별 실행 기록을 파일로 저장하고 로드하는 모듈
|
|
2
|
-
import
|
|
3
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { safeReadFile, safeWriteFile, safeExists, safeMkdir, safeUnlink } from '../util/safe_fs.js';
|
|
4
3
|
import { join, resolve } from 'path';
|
|
5
4
|
import chalk from 'chalk';
|
|
6
5
|
import { formatToolCall, formatToolResult, getToolDisplayName, getToolDisplayConfig } from './tool_registry.js';
|
|
7
6
|
import { createDebugLogger } from '../util/debug_log.js';
|
|
8
7
|
|
|
9
|
-
const MAX_HISTORY_SESSIONS =
|
|
8
|
+
const MAX_HISTORY_SESSIONS = 1; // 최대 보관 세션 수
|
|
10
9
|
|
|
11
10
|
const debugLog = createDebugLogger('session_memory.log', 'session_memory');
|
|
12
11
|
|
|
@@ -21,11 +20,10 @@ function getHistoryFile(sessionID) {
|
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
// 디렉토리 생성 확인
|
|
24
|
-
function ensureHistoryDir(sessionID) {
|
|
23
|
+
async function ensureHistoryDir(sessionID) {
|
|
25
24
|
const sessionDir = getSessionDir(sessionID);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
25
|
+
// recursive: true 이므로 이미 존재해도 에러 없음
|
|
26
|
+
await safeMkdir(sessionDir, { recursive: true });
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
function consolelog() { }
|
|
@@ -38,9 +36,8 @@ function consolelog() { }
|
|
|
38
36
|
export async function deleteHistoryFile(sessionID) {
|
|
39
37
|
try {
|
|
40
38
|
const historyFile = getHistoryFile(sessionID);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
39
|
+
// safeUnlink 내부에서 파일 존재 여부를 체크하므로 중복 체크 불필요
|
|
40
|
+
await safeUnlink(historyFile);
|
|
44
41
|
} catch (err) {
|
|
45
42
|
}
|
|
46
43
|
}
|
|
@@ -52,10 +49,10 @@ export async function deleteHistoryFile(sessionID) {
|
|
|
52
49
|
*/
|
|
53
50
|
export async function loadPreviousSessions(sessionID) {
|
|
54
51
|
try {
|
|
55
|
-
ensureHistoryDir(sessionID);
|
|
52
|
+
await ensureHistoryDir(sessionID);
|
|
56
53
|
const historyFile = getHistoryFile(sessionID);
|
|
57
|
-
if (
|
|
58
|
-
const content = await
|
|
54
|
+
if (await safeExists(historyFile)) {
|
|
55
|
+
const content = await safeReadFile(historyFile, 'utf8');
|
|
59
56
|
const sessions = JSON.parse(content);
|
|
60
57
|
// consolelog(chalk.blue(`📚 Loaded ${sessions.length} previous sessions from history`));
|
|
61
58
|
return sessions;
|
|
@@ -74,20 +71,45 @@ export async function loadPreviousSessions(sessionID) {
|
|
|
74
71
|
export async function saveSessionToHistory(sessionData) {
|
|
75
72
|
try {
|
|
76
73
|
const sessionID = sessionData.sessionID || process.app_custom.sessionID;
|
|
77
|
-
ensureHistoryDir(sessionID);
|
|
74
|
+
await ensureHistoryDir(sessionID);
|
|
78
75
|
const historyFile = getHistoryFile(sessionID);
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
debugLog('========== saveSessionToHistory START ==========');
|
|
78
|
+
debugLog(`sessionID: ${sessionID}`);
|
|
79
|
+
debugLog(`historyFile: ${historyFile}`);
|
|
80
|
+
|
|
81
|
+
// 파일에서 이전 세션들을 로드 (메모리 누적 방지)
|
|
82
|
+
const previousSessions = (await safeExists(historyFile))
|
|
83
|
+
? JSON.parse(await safeReadFile(historyFile, 'utf8'))
|
|
84
|
+
: [];
|
|
85
|
+
|
|
86
|
+
debugLog(`previousSessions loaded from file: ${previousSessions.length} sessions`);
|
|
82
87
|
|
|
83
|
-
//
|
|
88
|
+
// sessionData에서 previousSessions 제거 (중복 저장 방지)
|
|
89
|
+
const { previousSessions: _, ...sessionToSave } = sessionData;
|
|
90
|
+
|
|
91
|
+
debugLog(`sessionToSave size: ${JSON.stringify(sessionToSave).length} bytes`);
|
|
92
|
+
|
|
93
|
+
const allSessions = [...previousSessions, sessionToSave];
|
|
94
|
+
debugLog(`allSessions total: ${allSessions.length} sessions`);
|
|
95
|
+
|
|
96
|
+
// 최대 개수 제한 (fileSnapshot은 UI 표시를 위해 모두 유지)
|
|
84
97
|
const sessionsToKeep = allSessions.length > MAX_HISTORY_SESSIONS
|
|
85
98
|
? allSessions.slice(-MAX_HISTORY_SESSIONS)
|
|
86
99
|
: allSessions;
|
|
87
100
|
|
|
88
|
-
|
|
101
|
+
debugLog(`sessionsToKeep: ${sessionsToKeep.length} sessions`);
|
|
102
|
+
const totalSize = JSON.stringify(sessionsToKeep).length;
|
|
103
|
+
debugLog(`Total file size to write: ${totalSize} bytes (${(totalSize / 1024).toFixed(2)} KB)`);
|
|
104
|
+
|
|
105
|
+
await safeWriteFile(historyFile, JSON.stringify(sessionsToKeep, null, 2), 'utf8');
|
|
106
|
+
debugLog('File written successfully');
|
|
107
|
+
debugLog('========== saveSessionToHistory END ==========');
|
|
108
|
+
|
|
89
109
|
return sessionsToKeep;
|
|
90
110
|
} catch (err) {
|
|
111
|
+
debugLog(`ERROR in saveSessionToHistory: ${err.message}`);
|
|
112
|
+
debugLog(`Stack: ${err.stack}`);
|
|
91
113
|
}
|
|
92
114
|
return null;
|
|
93
115
|
}
|
|
@@ -262,14 +284,6 @@ export function reconstructUIHistory(sessions) {
|
|
|
262
284
|
debugLog(`toolUsageHistory items: ${session.toolUsageHistory.length}`);
|
|
263
285
|
}
|
|
264
286
|
|
|
265
|
-
// 미션 시작
|
|
266
|
-
uiHistory.push({
|
|
267
|
-
type: 'user',
|
|
268
|
-
text: session.mission,
|
|
269
|
-
timestamp: new Date(session.started_at).getTime()
|
|
270
|
-
});
|
|
271
|
-
debugLog(`Added user mission to uiHistory (index ${uiHistory.length - 1})`);
|
|
272
|
-
|
|
273
287
|
// orchestratorConversation에서 도구 사용 이벤트 추출
|
|
274
288
|
// 이전 세션까지의 대화 길이를 계산하여 중복 제거
|
|
275
289
|
if (session.orchestratorConversation && session.orchestratorConversation.length > 0) {
|
|
@@ -462,6 +476,18 @@ export function reconstructUIHistory(sessions) {
|
|
|
462
476
|
});
|
|
463
477
|
}
|
|
464
478
|
}
|
|
479
|
+
|
|
480
|
+
// user 메시지 타입 처리
|
|
481
|
+
if (item.role === 'user' && item.content) {
|
|
482
|
+
const textContent = item.content.find(c => c.type === 'input_text');
|
|
483
|
+
if (textContent && textContent.text) {
|
|
484
|
+
uiHistory.push({
|
|
485
|
+
type: 'user',
|
|
486
|
+
text: textContent.text,
|
|
487
|
+
timestamp: Date.now()
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
465
491
|
}
|
|
466
492
|
}
|
|
467
493
|
|
package/src/tools/code_editor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { safeReadFile, safeWriteFile, safeAccess, safeMkdir, safeAppendFile } from '../util/safe_fs.js';
|
|
2
2
|
import { dirname, resolve, join } from 'path';
|
|
3
3
|
import * as diff from 'diff';
|
|
4
4
|
import { assertFileIntegrity, trackFileRead, saveFileSnapshot } from '../system/file_integrity.js';
|
|
@@ -62,12 +62,12 @@ export async function write_file({ file_path, content }) {
|
|
|
62
62
|
|
|
63
63
|
// 디렉토리가 존재하지 않으면 생성
|
|
64
64
|
try {
|
|
65
|
-
await
|
|
65
|
+
await safeAccess(dir);
|
|
66
66
|
internalDebugLog.push(`[${timestamp}] Directory exists`);
|
|
67
67
|
debugLog(`Directory exists`);
|
|
68
68
|
} catch (error) {
|
|
69
|
-
if (error.
|
|
70
|
-
await
|
|
69
|
+
if (error.message.includes('ENOENT') || error.message.includes('does not exist')) {
|
|
70
|
+
await safeMkdir(dir, { recursive: true });
|
|
71
71
|
internalDebugLog.push(`[${timestamp}] Directory created`);
|
|
72
72
|
debugLog(`Directory created`);
|
|
73
73
|
} else {
|
|
@@ -80,13 +80,13 @@ export async function write_file({ file_path, content }) {
|
|
|
80
80
|
let fileExists = false;
|
|
81
81
|
debugLog(`Checking if file exists...`);
|
|
82
82
|
try {
|
|
83
|
-
originalContent = await
|
|
83
|
+
originalContent = await safeReadFile(absolutePath, 'utf8');
|
|
84
84
|
fileExists = true;
|
|
85
85
|
internalDebugLog.push(`[${timestamp}] File EXISTS - size: ${originalContent.length} bytes`);
|
|
86
86
|
debugLog(`File EXISTS - size: ${originalContent.length} bytes`);
|
|
87
87
|
debugLog(`Original content first 100 chars: ${JSON.stringify(originalContent.substring(0, 100))}`);
|
|
88
88
|
} catch (error) {
|
|
89
|
-
if (error.
|
|
89
|
+
if (!error.message.includes('ENOENT') && !error.message.includes('no such file')) {
|
|
90
90
|
throw error;
|
|
91
91
|
}
|
|
92
92
|
internalDebugLog.push(`[${timestamp}] File DOES NOT EXIST`);
|
|
@@ -105,8 +105,8 @@ export async function write_file({ file_path, content }) {
|
|
|
105
105
|
internalDebugLog.push(`[${timestamp}] assertFileIntegrity FAILED: ${integrityError.message}`);
|
|
106
106
|
debugLog(`ERROR: assertFileIntegrity FAILED: ${integrityError.message}`);
|
|
107
107
|
const logFile = getLogFile();
|
|
108
|
-
await
|
|
109
|
-
await
|
|
108
|
+
await safeMkdir(dirname(logFile), { recursive: true }).catch(() => {});
|
|
109
|
+
await safeAppendFile(logFile, internalDebugLog.join('\n') + '\n');
|
|
110
110
|
throw integrityError;
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -114,7 +114,7 @@ export async function write_file({ file_path, content }) {
|
|
|
114
114
|
// 파일 쓰기 (절대경로 사용)
|
|
115
115
|
internalDebugLog.push(`[${timestamp}] Writing file - content size: ${content.length} bytes`);
|
|
116
116
|
debugLog(`Writing file - content size: ${content.length} bytes`);
|
|
117
|
-
await
|
|
117
|
+
await safeWriteFile(absolutePath, content, 'utf8');
|
|
118
118
|
internalDebugLog.push(`[${timestamp}] File written successfully`);
|
|
119
119
|
debugLog(`File written successfully`);
|
|
120
120
|
|
|
@@ -130,8 +130,8 @@ export async function write_file({ file_path, content }) {
|
|
|
130
130
|
debugLog(`Snapshot saved`);
|
|
131
131
|
|
|
132
132
|
const logFile = getLogFile();
|
|
133
|
-
await
|
|
134
|
-
await
|
|
133
|
+
await safeMkdir(dirname(logFile), { recursive: true }).catch(() => {});
|
|
134
|
+
await safeAppendFile(logFile, internalDebugLog.join('\n') + '\n');
|
|
135
135
|
|
|
136
136
|
// diff 생성 (기존 파일이 있었을 경우에만, 절대경로 사용)
|
|
137
137
|
let diffInfo = null;
|
|
@@ -176,8 +176,8 @@ export async function write_file({ file_path, content }) {
|
|
|
176
176
|
debugLog(`Stack trace: ${error.stack}`);
|
|
177
177
|
debugLog('========== write_file EXCEPTION END ==========');
|
|
178
178
|
const logFile = getLogFile();
|
|
179
|
-
await
|
|
180
|
-
await
|
|
179
|
+
await safeMkdir(dirname(logFile), { recursive: true }).catch(() => {});
|
|
180
|
+
await safeAppendFile(logFile, internalDebugLog.join('\n') + '\n').catch(() => {});
|
|
181
181
|
|
|
182
182
|
// 에러 시에도 절대경로로 반환
|
|
183
183
|
const absolutePath = resolve(file_path);
|
|
@@ -272,10 +272,10 @@ export async function edit_file_replace({ file_path, old_string, new_string }) {
|
|
|
272
272
|
|
|
273
273
|
// 파일 존재 확인
|
|
274
274
|
try {
|
|
275
|
-
await
|
|
275
|
+
await safeAccess(absolutePath);
|
|
276
276
|
debugLog(`File access OK`);
|
|
277
277
|
} catch (error) {
|
|
278
|
-
if (error.
|
|
278
|
+
if (error.message.includes('ENOENT') || error.message.includes('no such file')) {
|
|
279
279
|
debugLog(`ERROR: File not found`);
|
|
280
280
|
return {
|
|
281
281
|
operation_successful: false,
|
|
@@ -293,7 +293,7 @@ export async function edit_file_replace({ file_path, old_string, new_string }) {
|
|
|
293
293
|
|
|
294
294
|
// 원본 파일 읽기 (절대경로 사용)
|
|
295
295
|
debugLog(`Reading file content...`);
|
|
296
|
-
const originalContent = await
|
|
296
|
+
const originalContent = await safeReadFile(absolutePath, 'utf8');
|
|
297
297
|
debugLog(`File read successful:`);
|
|
298
298
|
debugLog(` Content length: ${originalContent.length} bytes`);
|
|
299
299
|
debugLog(` Line count: ${originalContent.split('\n').length}`);
|
|
@@ -415,7 +415,7 @@ export async function edit_file_replace({ file_path, old_string, new_string }) {
|
|
|
415
415
|
|
|
416
416
|
// 파일 저장 (절대경로 사용)
|
|
417
417
|
debugLog(`Writing file...`);
|
|
418
|
-
await
|
|
418
|
+
await safeWriteFile(absolutePath, newContent, 'utf8');
|
|
419
419
|
debugLog(`File written successfully`);
|
|
420
420
|
|
|
421
421
|
// 파일 수정 후 해시 업데이트 (절대경로 사용)
|
|
@@ -509,10 +509,10 @@ export async function edit_file_range({ file_path, start_line, end_line, new_con
|
|
|
509
509
|
// 파일 존재 확인
|
|
510
510
|
debugLog(`Checking if file exists...`);
|
|
511
511
|
try {
|
|
512
|
-
await
|
|
512
|
+
await safeAccess(absolutePath);
|
|
513
513
|
debugLog(`File access OK`);
|
|
514
514
|
} catch (error) {
|
|
515
|
-
if (error.
|
|
515
|
+
if (error.message.includes('ENOENT') || error.message.includes('no such file')) {
|
|
516
516
|
debugLog(`ERROR: File not found`);
|
|
517
517
|
debugLog('========== edit_file_range ERROR END ==========');
|
|
518
518
|
return {
|
|
@@ -531,7 +531,7 @@ export async function edit_file_range({ file_path, start_line, end_line, new_con
|
|
|
531
531
|
|
|
532
532
|
// 원본 파일 읽기 (절대경로 사용)
|
|
533
533
|
debugLog(`Reading file content...`);
|
|
534
|
-
const originalContent = await
|
|
534
|
+
const originalContent = await safeReadFile(absolutePath, 'utf8');
|
|
535
535
|
debugLog(`File read successful:`);
|
|
536
536
|
debugLog(` Content length: ${originalContent.length} bytes`);
|
|
537
537
|
debugLog(` Line count: ${originalContent.split('\n').length}`);
|
|
@@ -632,7 +632,7 @@ export async function edit_file_range({ file_path, start_line, end_line, new_con
|
|
|
632
632
|
|
|
633
633
|
// 파일 저장 (절대경로 사용)
|
|
634
634
|
debugLog(`Writing file...`);
|
|
635
|
-
await
|
|
635
|
+
await safeWriteFile(absolutePath, newFileContent, 'utf8');
|
|
636
636
|
debugLog(`File written successfully`);
|
|
637
637
|
|
|
638
638
|
// 파일 수정 후 해시 업데이트 (절대경로 사용)
|
package/src/tools/file_reader.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { safeReadFile } from '../util/safe_fs.js';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
3
|
import { trackFileRead, saveFileSnapshot } from '../system/file_integrity.js';
|
|
4
4
|
import { createDebugLogger } from '../util/debug_log.js';
|
|
@@ -41,7 +41,7 @@ export async function read_file({ filePath }) {
|
|
|
41
41
|
debugLog(` - Absolute path length: ${absolutePath.length}`);
|
|
42
42
|
|
|
43
43
|
debugLog(`Reading file...`);
|
|
44
|
-
const content = await
|
|
44
|
+
const content = await safeReadFile(absolutePath, 'utf8');
|
|
45
45
|
debugLog(`File read successful: ${content.length} bytes`);
|
|
46
46
|
|
|
47
47
|
const lines = content.split('\n');
|
|
@@ -168,7 +168,7 @@ export async function read_file_range({ filePath, startLine, endLine }) {
|
|
|
168
168
|
|
|
169
169
|
// 파일 전체를 읽고 범위만 추출
|
|
170
170
|
debugLog(`Reading file...`);
|
|
171
|
-
const content = await
|
|
171
|
+
const content = await safeReadFile(absolutePath, 'utf8');
|
|
172
172
|
debugLog(`File read successful: ${content.length} bytes`);
|
|
173
173
|
|
|
174
174
|
const lines = content.split('\n');
|
package/src/tools/glob.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { glob } from 'glob';
|
|
2
2
|
import { resolve, relative } from 'path';
|
|
3
|
-
import {
|
|
3
|
+
import { safeStat } from '../util/safe_fs.js';
|
|
4
4
|
import { createDebugLogger } from '../util/debug_log.js';
|
|
5
5
|
|
|
6
6
|
const debugLog = createDebugLogger('glob.log', 'glob');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execPythonCode } from "../system/code_executer.js";
|
|
2
|
-
import
|
|
2
|
+
import { safeReadFile } from '../util/safe_fs.js';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
|
|
5
5
|
// 이 파일은 웹 페이지를 가져와서 텍스트 형식으로 변환하는 도구를 제공합니다.
|
|
@@ -21,7 +21,7 @@ export async function fetch_web_page({ url }) {
|
|
|
21
21
|
const timeout = 30;
|
|
22
22
|
const user_agent = 'Mozilla/5.0 (compatible; WebFetcher/1.0)';
|
|
23
23
|
const encoding = 'utf-8';
|
|
24
|
-
const pythonCode = await
|
|
24
|
+
const pythonCode = await safeReadFile(join(process.app_custom?.__dirname || process.cwd(), 'src', 'tools', 'web_download.py'), 'utf8');
|
|
25
25
|
const result = await execPythonCode(pythonCode, [url, timeout, user_agent, encoding]);
|
|
26
26
|
|
|
27
27
|
return {
|
|
@@ -11,7 +11,7 @@ import { renderMarkdown } from '../utils/markdownRenderer.js';
|
|
|
11
11
|
import { FileDiffViewer } from './FileDiffViewer.js';
|
|
12
12
|
import { getToolDisplayName, formatToolCall } from '../../system/tool_registry.js';
|
|
13
13
|
import { getFileSnapshot } from '../../system/file_integrity.js';
|
|
14
|
-
import
|
|
14
|
+
import { safeMkdir, safeAppendFile } from '../../util/safe_fs.js';
|
|
15
15
|
import { resolve, join, dirname } from 'path';
|
|
16
16
|
import { DEBUG_LOG_DIR } from '../../util/config.js';
|
|
17
17
|
import { createHash } from 'crypto';
|
|
@@ -25,9 +25,6 @@ const OLDSTRING_LOG_FILE = join(DEBUG_LOG_DIR, 'oldstring.txt');
|
|
|
25
25
|
function logOldStringEvidence(evidence) {
|
|
26
26
|
try {
|
|
27
27
|
const dir = dirname(OLDSTRING_LOG_FILE);
|
|
28
|
-
if (!fs.existsSync(dir)) {
|
|
29
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
30
|
-
}
|
|
31
28
|
const timestamp = new Date().toISOString();
|
|
32
29
|
const separator = '\n' + '='.repeat(80) + '\n';
|
|
33
30
|
|
|
@@ -62,7 +59,16 @@ function logOldStringEvidence(evidence) {
|
|
|
62
59
|
logEntry += `\n--- Result Args ---\n`;
|
|
63
60
|
logEntry += JSON.stringify(evidence.resultArgs, null, 2) + '\n';
|
|
64
61
|
|
|
65
|
-
|
|
62
|
+
// Convert sync operations to async (non-blocking)
|
|
63
|
+
setImmediate(async () => {
|
|
64
|
+
try {
|
|
65
|
+
// 디렉토리가 없으면 생성 (recursive: true 이므로 이미 존재해도 에러 없음)
|
|
66
|
+
await safeMkdir(dir, { recursive: true });
|
|
67
|
+
await safeAppendFile(OLDSTRING_LOG_FILE, logEntry);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
// Ignore logging errors
|
|
70
|
+
}
|
|
71
|
+
});
|
|
66
72
|
} catch (err) {
|
|
67
73
|
// Ignore logging errors
|
|
68
74
|
}
|
package/src/util/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 설정 파일 및 환경 관리 유틸리티
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { join, dirname } from 'path';
|
|
4
|
-
import {
|
|
4
|
+
import { safeReadFile, safeWriteFile, safeMkdir, safeReaddir, safeStat, safeCopyFile } from './safe_fs.js';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { DEFAULT_CLAUDE_MODEL } from '../config/claude_models.js';
|
|
7
7
|
import { DEFAULT_OPENAI_MODEL } from '../config/openai_models.js';
|
|
@@ -71,20 +71,20 @@ const DEFAULT_SETTINGS = {
|
|
|
71
71
|
*/
|
|
72
72
|
export async function ensureConfigDirectory() {
|
|
73
73
|
try {
|
|
74
|
-
const created = await
|
|
74
|
+
const created = await safeMkdir(CONFIG_DIR, { recursive: true });
|
|
75
75
|
if (!created) return;
|
|
76
76
|
|
|
77
77
|
const templateDir = process.app_custom?.__dirname ? join(process.app_custom.__dirname, 'config_template') : null;
|
|
78
78
|
if (!templateDir) return;
|
|
79
79
|
|
|
80
|
-
const templateFiles = await
|
|
80
|
+
const templateFiles = await safeReaddir(templateDir);
|
|
81
81
|
await Promise.all(
|
|
82
82
|
templateFiles.map(async (fileName) => {
|
|
83
83
|
const sourcePath = join(templateDir, fileName);
|
|
84
|
-
const stat = await
|
|
84
|
+
const stat = await safeStat(sourcePath);
|
|
85
85
|
if (stat.isFile()) {
|
|
86
86
|
const destPath = join(CONFIG_DIR, fileName);
|
|
87
|
-
await
|
|
87
|
+
await safeCopyFile(sourcePath, destPath);
|
|
88
88
|
}
|
|
89
89
|
})
|
|
90
90
|
);
|
|
@@ -101,15 +101,15 @@ export async function ensureConfigDirectory() {
|
|
|
101
101
|
export async function loadSettings() {
|
|
102
102
|
await ensureConfigDirectory();
|
|
103
103
|
try {
|
|
104
|
-
const data = await
|
|
104
|
+
const data = await safeReadFile(SETTINGS_FILE, 'utf8');
|
|
105
105
|
const parsed = JSON.parse(data);
|
|
106
106
|
return {
|
|
107
107
|
...DEFAULT_SETTINGS,
|
|
108
108
|
...(parsed && typeof parsed === 'object' ? parsed : {})
|
|
109
109
|
};
|
|
110
110
|
} catch (error) {
|
|
111
|
-
if (error.
|
|
112
|
-
await
|
|
111
|
+
if (error.message && error.message.includes('Failed to read file')) {
|
|
112
|
+
await safeWriteFile(SETTINGS_FILE, JSON.stringify(DEFAULT_SETTINGS, null, 2), 'utf8');
|
|
113
113
|
return { ...DEFAULT_SETTINGS };
|
|
114
114
|
}
|
|
115
115
|
consolelog(`Failed to load settings from ${SETTINGS_FILE}:`, error);
|
|
@@ -125,7 +125,7 @@ export async function loadSettings() {
|
|
|
125
125
|
export async function saveSettings(settings) {
|
|
126
126
|
await ensureConfigDirectory();
|
|
127
127
|
try {
|
|
128
|
-
await
|
|
128
|
+
await safeWriteFile(SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf8');
|
|
129
129
|
} catch (error) {
|
|
130
130
|
consolelog(`Failed to save settings to ${SETTINGS_FILE}:`, error);
|
|
131
131
|
throw error;
|
package/src/util/debug_log.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { DEBUG_LOG_DIR } from './config.js';
|
|
7
7
|
import { join, dirname } from 'path';
|
|
8
|
-
import {
|
|
8
|
+
import { safeMkdir, safeAppendFile } from './safe_fs.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* 디버그 로그를 파일에 기록합니다.
|
|
@@ -19,16 +19,21 @@ export function debugLog(logFileName, context, message) {
|
|
|
19
19
|
const LOG_FILE = join(DEBUG_LOG_DIR, logFileName);
|
|
20
20
|
const logDir = dirname(LOG_FILE);
|
|
21
21
|
|
|
22
|
-
if (!existsSync(logDir)) {
|
|
23
|
-
mkdirSync(logDir, { recursive: true });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
22
|
const timestamp = new Date().toISOString();
|
|
27
23
|
const logMessage = context
|
|
28
24
|
? `[${timestamp}] [${context}] ${message}\n`
|
|
29
25
|
: `[${timestamp}] ${message}\n`;
|
|
30
26
|
|
|
31
|
-
|
|
27
|
+
// 디렉토리 생성 및 로그 쓰기를 비동기로 처리
|
|
28
|
+
setImmediate(async () => {
|
|
29
|
+
try {
|
|
30
|
+
// 디렉토리가 없으면 생성 (recursive: true 이므로 이미 존재해도 에러 없음)
|
|
31
|
+
await safeMkdir(logDir, { recursive: true });
|
|
32
|
+
await safeAppendFile(LOG_FILE, logMessage);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
// Ignore logging errors
|
|
35
|
+
}
|
|
36
|
+
});
|
|
32
37
|
} catch (err) {
|
|
33
38
|
// Ignore logging errors
|
|
34
39
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// MCP 설정 관리 유틸리티 - Claude Code 스타일
|
|
2
|
-
import {
|
|
2
|
+
import { safeReadFile, safeWriteFile, safeMkdir } from './safe_fs.js';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { createHash } from 'crypto';
|
|
5
5
|
import { CONFIG_DIR } from './config.js';
|
|
@@ -22,14 +22,14 @@ export function getMcpConfigPath() {
|
|
|
22
22
|
export async function loadMergedMcpConfig() {
|
|
23
23
|
try {
|
|
24
24
|
const path = getMcpConfigPath();
|
|
25
|
-
const data = await
|
|
25
|
+
const data = await safeReadFile(path, 'utf8');
|
|
26
26
|
const config = JSON.parse(data);
|
|
27
27
|
|
|
28
28
|
consolelog(`Loaded MCP config from ${path}`);
|
|
29
29
|
return config.mcpServers || {};
|
|
30
30
|
} catch (error) {
|
|
31
31
|
// 파일이 없으면 빈 객체 반환
|
|
32
|
-
if (error.
|
|
32
|
+
if (error.message && error.message.includes('Failed to read file')) {
|
|
33
33
|
return {};
|
|
34
34
|
}
|
|
35
35
|
consolelog(`Warning: Failed to load MCP config: ${error.message}`);
|
|
@@ -76,13 +76,13 @@ export async function saveMcpConfig(servers) {
|
|
|
76
76
|
const path = getMcpConfigPath();
|
|
77
77
|
const dir = join(path, '..');
|
|
78
78
|
|
|
79
|
-
await
|
|
79
|
+
await safeMkdir(dir, { recursive: true });
|
|
80
80
|
|
|
81
81
|
const config = {
|
|
82
82
|
mcpServers: servers
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
await
|
|
85
|
+
await safeWriteFile(path, JSON.stringify(config, null, 2), 'utf8');
|
|
86
86
|
consolelog(`Saved MCP config to ${path}`);
|
|
87
87
|
return path;
|
|
88
88
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { safeReadFile, safeAccess } from "./safe_fs.js";
|
|
2
2
|
import { join, dirname } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
|
|
@@ -21,7 +21,7 @@ export function getProjectRoot() {
|
|
|
21
21
|
*/
|
|
22
22
|
async function loadSystemPrompt(promptPath) {
|
|
23
23
|
try {
|
|
24
|
-
const content = await
|
|
24
|
+
const content = await safeReadFile(promptPath, "utf8");
|
|
25
25
|
return content.trim();
|
|
26
26
|
} catch (error) {
|
|
27
27
|
consolelog(`Failed to load system prompt from ${promptPath}:`, error);
|
|
@@ -36,7 +36,7 @@ async function loadSystemPrompt(promptPath) {
|
|
|
36
36
|
*/
|
|
37
37
|
async function fileExists(filePath) {
|
|
38
38
|
try {
|
|
39
|
-
await
|
|
39
|
+
await safeAccess(filePath);
|
|
40
40
|
return true;
|
|
41
41
|
} catch {
|
|
42
42
|
return false;
|