aiexecode 1.0.66 → 1.0.69
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/config_template/settings.json +1 -3
- package/index.js +46 -71
- package/package.json +1 -12
- 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/payload_viewer/web_server.js +0 -163
- package/prompts/completion_judge.txt +11 -7
- package/src/ai_based/completion_judge.js +97 -6
- package/src/ai_based/orchestrator.js +71 -3
- package/src/ai_based/pip_package_installer.js +14 -12
- package/src/ai_based/pip_package_lookup.js +13 -10
- package/src/commands/apikey.js +8 -34
- package/src/commands/help.js +3 -4
- package/src/commands/model.js +17 -74
- package/src/commands/reasoning_effort.js +1 -1
- package/src/config/feature_flags.js +0 -12
- package/src/{ui → frontend}/App.js +23 -25
- package/src/frontend/README.md +81 -0
- package/src/{ui/components/SuggestionsDisplay.js → frontend/components/AutocompleteMenu.js} +3 -3
- package/src/{ui/components/HistoryItemDisplay.js → frontend/components/ConversationItem.js} +37 -89
- package/src/{ui → frontend}/components/CurrentModelView.js +3 -5
- package/src/{ui → frontend}/components/Footer.js +4 -6
- package/src/{ui → frontend}/components/Header.js +2 -5
- package/src/{ui/components/InputPrompt.js → frontend/components/Input.js} +16 -54
- package/src/frontend/components/ModelListView.js +106 -0
- package/src/{ui → frontend}/components/ModelUpdatedView.js +3 -5
- package/src/{ui → frontend}/components/SessionSpinner.js +3 -3
- package/src/{ui → frontend}/components/SetupWizard.js +8 -101
- package/src/{ui → frontend}/components/ToolApprovalPrompt.js +16 -14
- package/src/frontend/design/themeColors.js +42 -0
- package/src/{ui → frontend}/index.js +7 -7
- package/src/frontend/utils/inputBuffer.js +441 -0
- package/src/{ui/utils/markdownRenderer.js → frontend/utils/markdownParser.js} +3 -3
- package/src/{ui/utils/ConsolePatcher.js → frontend/utils/outputRedirector.js} +9 -9
- package/src/{ui/utils/codeColorizer.js → frontend/utils/syntaxHighlighter.js} +2 -3
- package/src/system/ai_request.js +145 -595
- package/src/system/code_executer.js +111 -16
- package/src/system/file_integrity.js +5 -7
- package/src/system/log.js +3 -3
- package/src/system/mcp_integration.js +15 -13
- package/src/system/output_helper.js +0 -20
- package/src/system/session.js +97 -23
- package/src/system/session_memory.js +2 -82
- package/src/system/system_info.js +1 -1
- package/src/system/ui_events.js +0 -43
- package/src/tools/code_editor.js +17 -2
- package/src/tools/file_reader.js +17 -2
- package/src/tools/glob.js +9 -1
- package/src/tools/response_message.js +0 -2
- package/src/tools/ripgrep.js +9 -1
- package/src/tools/web_downloader.js +9 -1
- package/src/util/config.js +3 -8
- package/src/util/debug_log.js +4 -11
- package/src/util/mcp_config_manager.js +3 -5
- package/src/util/output_formatter.js +0 -47
- package/src/util/prompt_loader.js +3 -4
- package/src/util/safe_fs.js +60 -0
- package/src/util/setup_wizard.js +1 -3
- package/src/util/text_formatter.js +0 -86
- package/src/config/claude_models.js +0 -195
- package/src/ui/README.md +0 -208
- package/src/ui/api.js +0 -167
- package/src/ui/components/AgenticProgressDisplay.js +0 -126
- package/src/ui/components/Composer.js +0 -55
- package/src/ui/components/LoadingIndicator.js +0 -54
- package/src/ui/components/ModelListView.js +0 -214
- package/src/ui/components/Notifications.js +0 -55
- package/src/ui/components/StreamingIndicator.js +0 -36
- package/src/ui/contexts/AppContext.js +0 -25
- package/src/ui/contexts/StreamingContext.js +0 -20
- package/src/ui/contexts/UIStateContext.js +0 -117
- package/src/ui/example-usage.js +0 -180
- package/src/ui/hooks/useTerminalResize.js +0 -39
- package/src/ui/themes/semantic-tokens.js +0 -73
- package/src/ui/utils/text-buffer.js +0 -975
- /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → 7FgHZugvVp3pn1XStUYUJ}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → 7FgHZugvVp3pn1XStUYUJ}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → 7FgHZugvVp3pn1XStUYUJ}/_ssgManifest.js +0 -0
- /package/src/{ui → frontend}/components/BlankLine.js +0 -0
- /package/src/{ui → frontend}/components/FileDiffViewer.js +0 -0
- /package/src/{ui → frontend}/components/HelpView.js +0 -0
- /package/src/{ui → frontend}/hooks/useCompletion.js +0 -0
- /package/src/{ui → frontend}/hooks/useKeypress.js +0 -0
- /package/src/{ui → frontend}/utils/diffUtils.js +0 -0
- /package/src/{ui → frontend}/utils/renderInkComponent.js +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { checkValidPackageName } from "./pip_package_lookup.js";
|
|
2
2
|
import { installPythonPackage, isPackageInstalled, analyzeImports } from "../system/code_executer.js";
|
|
3
|
+
import { createDebugLogger } from "../util/debug_log.js";
|
|
4
|
+
|
|
5
|
+
const debugLog = createDebugLogger('pip_package_installer.log', 'pip_package_installer');
|
|
3
6
|
|
|
4
7
|
// 이 파일은 코드에서 필요로 하는 파이썬 패키지를 확인하고 자동으로 설치합니다.
|
|
5
8
|
// Code Executer가 스크립트를 실행할 때 패키지 누락 오류가 나지 않도록 Orchestrator 실행 이전에 환경을 준비합니다.
|
|
6
|
-
function consolelog() { }
|
|
7
9
|
|
|
8
10
|
// 하나의 파일을 분석해 필요한 외부 패키지를 찾아 설치합니다.
|
|
9
11
|
export async function installRequiredPackages(filePath) {
|
|
@@ -62,34 +64,34 @@ export async function installRequiredPackages(filePath) {
|
|
|
62
64
|
|
|
63
65
|
export async function installPackageList(packageList) {
|
|
64
66
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
debugLog(`\n=== Installing package list ===`);
|
|
68
|
+
debugLog("Packages to install:", packageList);
|
|
67
69
|
|
|
68
70
|
const validPackageNames = await checkValidPackageName(packageList);
|
|
69
|
-
|
|
71
|
+
debugLog("Valid package names:", validPackageNames);
|
|
70
72
|
|
|
71
73
|
const installResults = [];
|
|
72
74
|
const alreadyInstalled = [];
|
|
73
75
|
const failedInstalls = [];
|
|
74
76
|
|
|
75
77
|
for (const packageName of validPackageNames) {
|
|
76
|
-
|
|
78
|
+
debugLog(`\n--- Processing package: ${packageName} ---`);
|
|
77
79
|
|
|
78
80
|
const isInstalled = await isPackageInstalled(packageName);
|
|
79
81
|
if (isInstalled) {
|
|
80
|
-
|
|
82
|
+
debugLog(`[SKIP] ${packageName} is already installed`);
|
|
81
83
|
alreadyInstalled.push(packageName);
|
|
82
84
|
continue;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
|
|
87
|
+
debugLog(`Installing ${packageName}...`);
|
|
86
88
|
const installSuccess = await installPythonPackage(packageName);
|
|
87
89
|
|
|
88
90
|
if (installSuccess) {
|
|
89
|
-
|
|
91
|
+
debugLog(`[OK] Successfully installed ${packageName}`);
|
|
90
92
|
installResults.push(packageName);
|
|
91
93
|
} else {
|
|
92
|
-
|
|
94
|
+
debugLog(`[FAIL] Failed to install ${packageName}`);
|
|
93
95
|
failedInstalls.push(packageName);
|
|
94
96
|
}
|
|
95
97
|
}
|
|
@@ -120,8 +122,8 @@ export async function installPackageList(packageList) {
|
|
|
120
122
|
|
|
121
123
|
export async function installRequiredPackagesForFiles(filePaths) {
|
|
122
124
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
debugLog(`\n=== Analyzing multiple files ===`);
|
|
126
|
+
debugLog("Files to analyze:", filePaths);
|
|
125
127
|
|
|
126
128
|
const allExternalPackages = new Set();
|
|
127
129
|
const analysisResults = [];
|
|
@@ -142,7 +144,7 @@ export async function installRequiredPackagesForFiles(filePaths) {
|
|
|
142
144
|
}
|
|
143
145
|
|
|
144
146
|
const uniquePackages = Array.from(allExternalPackages);
|
|
145
|
-
|
|
147
|
+
debugLog("All unique external packages:", uniquePackages);
|
|
146
148
|
|
|
147
149
|
if (uniquePackages.length === 0) {
|
|
148
150
|
return {
|
|
@@ -3,9 +3,12 @@ import { request, getModelForProvider } from "../system/ai_request.js";
|
|
|
3
3
|
import { safeReadFile, safeWriteFile } from '../util/safe_fs.js';
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { CONFIG_DIR, ensureConfigDirectory } from "../util/config.js";
|
|
6
|
+
import { createDebugLogger } from "../util/debug_log.js";
|
|
7
|
+
|
|
8
|
+
const debugLog = createDebugLogger('pip_package_lookup.log', 'pip_package_lookup');
|
|
9
|
+
|
|
6
10
|
dotenv.config({ quiet: true });
|
|
7
11
|
|
|
8
|
-
function consolelog() { }
|
|
9
12
|
// 이 파일은 import에 쓰인 이름을 실제 pip 설치 이름과 연결해 줍니다.
|
|
10
13
|
// pip_package_installer가 정확한 패키지를 설치할 수 있도록 모든 모듈이 같은 이름을 공유하게 만듭니다.
|
|
11
14
|
// Package name cache file path
|
|
@@ -28,9 +31,9 @@ async function savePackageStore(store) {
|
|
|
28
31
|
try {
|
|
29
32
|
await ensureConfigDirectory();
|
|
30
33
|
await safeWriteFile(CACHE_FILE, JSON.stringify(store, null, 2), 'utf8');
|
|
31
|
-
|
|
34
|
+
debugLog(`✓ Package cache saved to ${CACHE_FILE}`);
|
|
32
35
|
} catch (error) {
|
|
33
|
-
|
|
36
|
+
debugLog('Failed to save package cache:', error);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
@@ -107,7 +110,7 @@ export async function checkValidPackageName(pkgList) {
|
|
|
107
110
|
|
|
108
111
|
for (const pkg of pkgList) {
|
|
109
112
|
if (packageStore.hasOwnProperty(pkg)) {
|
|
110
|
-
|
|
113
|
+
debugLog(`✓ Found in cache: ${pkg} -> ${packageStore[pkg]}`);
|
|
111
114
|
// 캐시된 값이 null이 아닌 경우만 결과에 추가 (표준 라이브러리는 null로 저장됨)
|
|
112
115
|
if (packageStore[pkg] !== null) {
|
|
113
116
|
cachedResults.push(packageStore[pkg]);
|
|
@@ -119,12 +122,12 @@ export async function checkValidPackageName(pkgList) {
|
|
|
119
122
|
|
|
120
123
|
// 3. 캐시되지 않은 패키지가 없으면 캐시 결과만 반환
|
|
121
124
|
if (uncachedPackages.length === 0) {
|
|
122
|
-
|
|
125
|
+
debugLog("All packages found in cache, no AI request needed");
|
|
123
126
|
return cachedResults;
|
|
124
127
|
}
|
|
125
128
|
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
debugLog(`Cache miss for packages: ${uncachedPackages.join(', ')}`);
|
|
130
|
+
debugLog("Making AI request for uncached packages...");
|
|
128
131
|
|
|
129
132
|
// 4. AI에게 캐시되지 않은 패키지들만 요청
|
|
130
133
|
const codeGenerationTemplate = [
|
|
@@ -169,14 +172,14 @@ export async function checkValidPackageName(pkgList) {
|
|
|
169
172
|
packageStore[import_name] = pip_name;
|
|
170
173
|
aiResults.push(pip_name);
|
|
171
174
|
receivedMappings.add(import_name);
|
|
172
|
-
|
|
175
|
+
debugLog(`✓ Cached: ${import_name} -> ${pip_name}`);
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
// 6. AI 응답에 포함되지 않은 패키지들은 표준 라이브러리로 간주하여 null로 캐시
|
|
176
179
|
for (const pkg of uncachedPackages) {
|
|
177
180
|
if (!receivedMappings.has(pkg)) {
|
|
178
181
|
packageStore[pkg] = null;
|
|
179
|
-
|
|
182
|
+
debugLog(`✓ Cached as standard library: ${pkg} -> null`);
|
|
180
183
|
}
|
|
181
184
|
}
|
|
182
185
|
|
|
@@ -187,7 +190,7 @@ export async function checkValidPackageName(pkgList) {
|
|
|
187
190
|
return [...cachedResults, ...aiResults];
|
|
188
191
|
|
|
189
192
|
} catch (error) {
|
|
190
|
-
|
|
193
|
+
debugLog("Error validating package names:", error);
|
|
191
194
|
// 에러 발생 시 캐시 결과와 원본 패키지명 반환
|
|
192
195
|
return [...cachedResults, ...uncachedPackages];
|
|
193
196
|
}
|
package/src/commands/apikey.js
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
1
|
import { uiEvents } from '../system/ui_events.js';
|
|
2
2
|
import { loadSettings, saveSettings, SETTINGS_FILE } from '../util/config.js';
|
|
3
3
|
import { resetAIClients } from '../system/ai_request.js';
|
|
4
|
-
import { ENABLE_ANTHROPIC_PROVIDER } from '../config/feature_flags.js';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* /apikey 커맨드 - AI Provider 및 API 키 설정
|
|
8
7
|
*/
|
|
9
8
|
export default {
|
|
10
9
|
name: 'apikey',
|
|
11
|
-
description:
|
|
12
|
-
|
|
13
|
-
: 'Set AI provider and API key (openai)',
|
|
14
|
-
usage: ENABLE_ANTHROPIC_PROVIDER
|
|
15
|
-
? '/apikey <provider> <api-key>'
|
|
16
|
-
: '/apikey openai <api-key>',
|
|
10
|
+
description: 'Set AI provider and API key (openai)',
|
|
11
|
+
usage: '/apikey openai <api-key>',
|
|
17
12
|
handler: async (args, context) => {
|
|
18
13
|
// 인자 확인
|
|
19
14
|
if (!args || args.length < 2) {
|
|
20
|
-
const usageExamples = ENABLE_ANTHROPIC_PROVIDER
|
|
21
|
-
? ` /apikey openai sk-proj-...\n /apikey anthropic sk-ant-...`
|
|
22
|
-
: ` /apikey openai sk-proj-...`;
|
|
23
|
-
|
|
24
15
|
uiEvents.addSystemMessage(
|
|
25
16
|
`Please enter provider and API key.\n\n` +
|
|
26
17
|
`Usage:\n` +
|
|
27
|
-
|
|
18
|
+
` /apikey openai sk-proj-...`
|
|
28
19
|
);
|
|
29
20
|
return;
|
|
30
21
|
}
|
|
@@ -36,36 +27,19 @@ export default {
|
|
|
36
27
|
const providerArg = args[0].toLowerCase();
|
|
37
28
|
|
|
38
29
|
// Provider 검증
|
|
39
|
-
|
|
40
|
-
? ['openai', 'anthropic']
|
|
41
|
-
: ['openai'];
|
|
42
|
-
|
|
43
|
-
if (!validProviders.includes(providerArg)) {
|
|
44
|
-
const validProvidersText = ENABLE_ANTHROPIC_PROVIDER
|
|
45
|
-
? 'openai, anthropic'
|
|
46
|
-
: 'openai';
|
|
47
|
-
|
|
30
|
+
if (providerArg !== 'openai') {
|
|
48
31
|
uiEvents.addSystemMessage(
|
|
49
32
|
`Invalid provider: ${providerArg}\n\n` +
|
|
50
|
-
`Valid providers:
|
|
33
|
+
`Valid providers: openai`
|
|
51
34
|
);
|
|
52
35
|
return;
|
|
53
36
|
}
|
|
54
37
|
|
|
55
|
-
const provider = providerArg;
|
|
56
38
|
const apiKey = args[1];
|
|
57
39
|
|
|
58
|
-
// Provider 설정
|
|
59
|
-
settings.AI_PROVIDER = provider;
|
|
60
|
-
|
|
61
40
|
// API 키 업데이트
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
process.env.OPENAI_API_KEY = apiKey;
|
|
65
|
-
} else if (provider === 'anthropic') {
|
|
66
|
-
settings.ANTHROPIC_API_KEY = apiKey;
|
|
67
|
-
process.env.ANTHROPIC_API_KEY = apiKey;
|
|
68
|
-
}
|
|
41
|
+
settings.OPENAI_API_KEY = apiKey;
|
|
42
|
+
process.env.OPENAI_API_KEY = apiKey;
|
|
69
43
|
|
|
70
44
|
// 설정 저장
|
|
71
45
|
await saveSettings(settings);
|
|
@@ -80,7 +54,7 @@ export default {
|
|
|
80
54
|
|
|
81
55
|
uiEvents.addSystemMessage(
|
|
82
56
|
`API configuration complete\n\n` +
|
|
83
|
-
`Provider:
|
|
57
|
+
`Provider: OPENAI\n` +
|
|
84
58
|
`API Key: ${maskedKey}\n` +
|
|
85
59
|
`Saved to: ${SETTINGS_FILE}`
|
|
86
60
|
);
|
package/src/commands/help.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import { uiEvents } from '../system/ui_events.js';
|
|
2
|
-
import { renderInkComponent } from '../
|
|
3
|
+
import { renderInkComponent } from '../frontend/utils/renderInkComponent.js';
|
|
4
|
+
import { HelpView } from '../frontend/components/HelpView.js';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* /help 커맨드 - 사용 가능한 커맨드 목록 표시
|
|
@@ -11,9 +13,6 @@ export default {
|
|
|
11
13
|
handler: async (args, context) => {
|
|
12
14
|
const { commandRegistry } = context;
|
|
13
15
|
|
|
14
|
-
const React = await import('react');
|
|
15
|
-
const { HelpView } = await import('../ui/components/HelpView.js');
|
|
16
|
-
|
|
17
16
|
const commands = commandRegistry.getCommands();
|
|
18
17
|
|
|
19
18
|
const component = React.createElement(HelpView, {
|
package/src/commands/model.js
CHANGED
|
@@ -1,65 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import { uiEvents } from '../system/ui_events.js';
|
|
2
3
|
import { loadSettings, saveSettings, SETTINGS_FILE } from '../util/config.js';
|
|
3
4
|
import { resetAIClients } from '../system/ai_request.js';
|
|
4
|
-
import { CLAUDE_MODELS, getClaude4Models, getClaude3Models, DEFAULT_CLAUDE_MODEL } from '../config/claude_models.js';
|
|
5
5
|
import { OPENAI_MODELS, getGPT5Models, DEFAULT_OPENAI_MODEL } from '../config/openai_models.js';
|
|
6
|
-
import { renderInkComponent } from '../
|
|
7
|
-
import {
|
|
6
|
+
import { renderInkComponent } from '../frontend/utils/renderInkComponent.js';
|
|
7
|
+
import { ModelListView } from '../frontend/components/ModelListView.js';
|
|
8
|
+
import { CurrentModelView } from '../frontend/components/CurrentModelView.js';
|
|
9
|
+
import { ModelUpdatedView } from '../frontend/components/ModelUpdatedView.js';
|
|
8
10
|
|
|
9
11
|
// 지원하는 모델 목록
|
|
10
12
|
// OpenAI: https://platform.openai.com/docs/pricing
|
|
11
|
-
// Anthropic: https://docs.claude.com/en/docs/about-claude/models/overview
|
|
12
13
|
const MODELS = {
|
|
13
|
-
openai: OPENAI_MODELS
|
|
14
|
-
anthropic: CLAUDE_MODELS
|
|
14
|
+
openai: OPENAI_MODELS
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// 모델 ID로 provider 찾기
|
|
18
18
|
function getProviderForModel(modelId) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (!ENABLE_ANTHROPIC_PROVIDER && provider === 'anthropic') {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
if (models[modelId]) {
|
|
25
|
-
return provider;
|
|
26
|
-
}
|
|
19
|
+
if (MODELS.openai[modelId]) {
|
|
20
|
+
return 'openai';
|
|
27
21
|
}
|
|
28
22
|
return null;
|
|
29
23
|
}
|
|
30
24
|
|
|
31
25
|
// 모든 모델 목록 표시
|
|
32
26
|
async function listAllModels() {
|
|
33
|
-
const React = await import('react');
|
|
34
|
-
const { ModelListView } = await import('../ui/components/ModelListView.js');
|
|
35
|
-
|
|
36
27
|
const gpt5Models = getGPT5Models();
|
|
37
28
|
const openaiModels = gpt5Models.map(id => ({
|
|
38
29
|
id,
|
|
39
30
|
...MODELS.openai[id]
|
|
40
31
|
}));
|
|
41
32
|
|
|
42
|
-
// Feature flag에 따라 Claude 모델 표시 여부 결정
|
|
43
|
-
let claudeModels = null;
|
|
44
|
-
if (ENABLE_ANTHROPIC_PROVIDER) {
|
|
45
|
-
const claude4Models = getClaude4Models();
|
|
46
|
-
const claude3Models = getClaude3Models();
|
|
47
|
-
|
|
48
|
-
claudeModels = {
|
|
49
|
-
claude4: claude4Models.map(id => ({
|
|
50
|
-
id,
|
|
51
|
-
...CLAUDE_MODELS[id]
|
|
52
|
-
})),
|
|
53
|
-
claude3: claude3Models.map(id => ({
|
|
54
|
-
id,
|
|
55
|
-
...CLAUDE_MODELS[id]
|
|
56
|
-
}))
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
33
|
const component = React.createElement(ModelListView, {
|
|
61
|
-
openaiModels
|
|
62
|
-
claudeModels
|
|
34
|
+
openaiModels
|
|
63
35
|
});
|
|
64
36
|
|
|
65
37
|
const output = await renderInkComponent(component);
|
|
@@ -69,23 +41,12 @@ async function listAllModels() {
|
|
|
69
41
|
// 현재 설정된 모델 표시
|
|
70
42
|
async function showCurrentModel() {
|
|
71
43
|
try {
|
|
72
|
-
const React = await import('react');
|
|
73
|
-
const { CurrentModelView } = await import('../ui/components/CurrentModelView.js');
|
|
74
|
-
|
|
75
44
|
const settings = await loadSettings();
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
let currentModel;
|
|
79
|
-
if (provider === 'anthropic') {
|
|
80
|
-
currentModel = settings?.ANTHROPIC_MODEL || DEFAULT_CLAUDE_MODEL;
|
|
81
|
-
} else {
|
|
82
|
-
currentModel = settings?.OPENAI_MODEL || DEFAULT_OPENAI_MODEL;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const modelInfo = MODELS[provider]?.[currentModel];
|
|
45
|
+
const currentModel = settings?.OPENAI_MODEL || DEFAULT_OPENAI_MODEL;
|
|
46
|
+
const modelInfo = MODELS.openai?.[currentModel];
|
|
86
47
|
|
|
87
48
|
const component = React.createElement(CurrentModelView, {
|
|
88
|
-
provider,
|
|
49
|
+
provider: 'openai',
|
|
89
50
|
modelId: currentModel,
|
|
90
51
|
modelInfo
|
|
91
52
|
});
|
|
@@ -102,9 +63,7 @@ async function showCurrentModel() {
|
|
|
102
63
|
*/
|
|
103
64
|
export default {
|
|
104
65
|
name: 'model',
|
|
105
|
-
description:
|
|
106
|
-
? 'Select AI model (OpenAI or Anthropic)'
|
|
107
|
-
: 'Select AI model (OpenAI)',
|
|
66
|
+
description: 'Select AI model (OpenAI)',
|
|
108
67
|
usage: '/model [model-id] or /model list',
|
|
109
68
|
handler: async (args, context) => {
|
|
110
69
|
// 인자가 없으면 현재 모델 표시
|
|
@@ -139,17 +98,9 @@ export default {
|
|
|
139
98
|
// 현재 설정 로드
|
|
140
99
|
const settings = await loadSettings();
|
|
141
100
|
|
|
142
|
-
//
|
|
143
|
-
settings.
|
|
144
|
-
process.env.
|
|
145
|
-
|
|
146
|
-
if (provider === 'openai') {
|
|
147
|
-
settings.OPENAI_MODEL = modelId;
|
|
148
|
-
process.env.OPENAI_MODEL = modelId;
|
|
149
|
-
} else if (provider === 'anthropic') {
|
|
150
|
-
settings.ANTHROPIC_MODEL = modelId;
|
|
151
|
-
process.env.ANTHROPIC_MODEL = modelId;
|
|
152
|
-
}
|
|
101
|
+
// 모델 업데이트
|
|
102
|
+
settings.OPENAI_MODEL = modelId;
|
|
103
|
+
process.env.OPENAI_MODEL = modelId;
|
|
153
104
|
|
|
154
105
|
// 설정 저장
|
|
155
106
|
await saveSettings(settings);
|
|
@@ -161,22 +112,14 @@ export default {
|
|
|
161
112
|
uiEvents.emit('model:changed', { model: modelId });
|
|
162
113
|
|
|
163
114
|
// 성공 메시지
|
|
164
|
-
const React = await import('react');
|
|
165
|
-
const { ModelUpdatedView } = await import('../ui/components/ModelUpdatedView.js');
|
|
166
|
-
|
|
167
115
|
const modelInfo = MODELS[provider][modelId];
|
|
168
116
|
|
|
169
117
|
let warning = null;
|
|
170
|
-
if (
|
|
118
|
+
if (!settings.OPENAI_API_KEY) {
|
|
171
119
|
warning = {
|
|
172
120
|
message: 'OpenAI API key is not configured.',
|
|
173
121
|
hint: 'Set your API key with: `/apikey openai sk-proj-...`'
|
|
174
122
|
};
|
|
175
|
-
} else if (provider === 'anthropic' && !settings.ANTHROPIC_API_KEY) {
|
|
176
|
-
warning = {
|
|
177
|
-
message: 'Anthropic API key is not configured.',
|
|
178
|
-
hint: 'Set your API key with: `/apikey anthropic sk-ant-...`'
|
|
179
|
-
};
|
|
180
123
|
}
|
|
181
124
|
|
|
182
125
|
const component = React.createElement(ModelUpdatedView, {
|
|
@@ -38,7 +38,7 @@ async function checkModelSupport() {
|
|
|
38
38
|
const provider = settings?.AI_PROVIDER || 'openai';
|
|
39
39
|
|
|
40
40
|
if (provider !== 'openai') {
|
|
41
|
-
return { supported: false, reason: '
|
|
41
|
+
return { supported: false, reason: 'Only OpenAI models support reasoning_effort.' };
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const modelInfo = OPENAI_MODELS[currentModel];
|
|
@@ -3,17 +3,6 @@
|
|
|
3
3
|
* 기능 활성화/비활성화를 제어하는 플래그들을 관리합니다.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Anthropic 제공자 활성화 여부
|
|
8
|
-
*
|
|
9
|
-
* true: Anthropic(Claude) 모델을 사용자 인터페이스에 표시하고 사용 가능
|
|
10
|
-
* false: Anthropic 관련 기능을 사용자 인터페이스에서 숨김 (구현은 보존됨)
|
|
11
|
-
*
|
|
12
|
-
* 이 플래그는 사용자가 접하는 UI, 명령어, 설정 화면 등에만 영향을 미치며,
|
|
13
|
-
* 실제 API 호출 및 내부 구현 코드는 모두 보존됩니다.
|
|
14
|
-
*/
|
|
15
|
-
export const ENABLE_ANTHROPIC_PROVIDER = false;
|
|
16
|
-
|
|
17
6
|
/**
|
|
18
7
|
* 에러 메시지 상세도 설정
|
|
19
8
|
*
|
|
@@ -23,6 +12,5 @@ export const ENABLE_ANTHROPIC_PROVIDER = false;
|
|
|
23
12
|
export const ERROR_VERBOSITY = 'concise';
|
|
24
13
|
|
|
25
14
|
export default {
|
|
26
|
-
ENABLE_ANTHROPIC_PROVIDER,
|
|
27
15
|
ERROR_VERBOSITY
|
|
28
16
|
};
|
|
@@ -7,11 +7,11 @@ import React, { useState, useEffect, useMemo, useCallback, memo, useRef } from '
|
|
|
7
7
|
import { Box, Text, Newline, useStdout, Static } from 'ink';
|
|
8
8
|
import { Header } from './components/Header.js';
|
|
9
9
|
import { Footer } from './components/Footer.js';
|
|
10
|
-
import {
|
|
10
|
+
import { Input } from './components/Input.js';
|
|
11
11
|
import { SessionSpinner } from './components/SessionSpinner.js';
|
|
12
|
-
import {
|
|
12
|
+
import { ConversationItem } from './components/ConversationItem.js';
|
|
13
13
|
import { BlankLine } from './components/BlankLine.js';
|
|
14
|
-
import { useTextBuffer } from './utils/
|
|
14
|
+
import { useTextBuffer } from './utils/inputBuffer.js';
|
|
15
15
|
import { uiEvents } from '../system/ui_events.js';
|
|
16
16
|
import { getToolDisplayConfig, extractMessageFromArgs, formatToolCall, formatToolResult, getToolDisplayName } from '../system/tool_registry.js';
|
|
17
17
|
import { ToolApprovalPrompt } from './components/ToolApprovalPrompt.js';
|
|
@@ -20,9 +20,6 @@ import { createDebugLogger } from '../util/debug_log.js';
|
|
|
20
20
|
|
|
21
21
|
const debugLog = createDebugLogger('ui_app.log', 'App');
|
|
22
22
|
|
|
23
|
-
// pendingHistory 렌더링 활성화 여부
|
|
24
|
-
const RENDER_PENDING_HISTORY = false;
|
|
25
|
-
|
|
26
23
|
/**
|
|
27
24
|
* 빈 줄 추가 여부를 결정하는 함수
|
|
28
25
|
*
|
|
@@ -179,13 +176,13 @@ function shouldAddBlankLineAfter(item, nextItem) {
|
|
|
179
176
|
*
|
|
180
177
|
* ## 기능
|
|
181
178
|
*
|
|
182
|
-
* 히스토리 아이템 배열을 받아서
|
|
179
|
+
* 히스토리 아이템 배열을 받아서 ConversationItem과 BlankLine 컴포넌트를
|
|
183
180
|
* 적절히 조합한 React 엘리먼트 배열을 생성합니다.
|
|
184
181
|
*
|
|
185
182
|
* ## 처리 과정
|
|
186
183
|
*
|
|
187
184
|
* 각 아이템마다:
|
|
188
|
-
* 1.
|
|
185
|
+
* 1. ConversationItem 컴포넌트 생성
|
|
189
186
|
* 2. shouldAddBlankLineAfter()로 빈 줄 필요 여부 확인
|
|
190
187
|
* 3. 필요하면 BlankLine 컴포넌트 추가
|
|
191
188
|
*
|
|
@@ -193,16 +190,16 @@ function shouldAddBlankLineAfter(item, nextItem) {
|
|
|
193
190
|
*
|
|
194
191
|
* 입력: [user, tool_start, tool_result]
|
|
195
192
|
* 출력:
|
|
196
|
-
* -
|
|
193
|
+
* - ConversationItem(user)
|
|
197
194
|
* - BlankLine (user 뒤)
|
|
198
|
-
* -
|
|
199
|
-
* -
|
|
195
|
+
* - ConversationItem(tool_start)
|
|
196
|
+
* - ConversationItem(tool_result) <- tool_start와 붙음, 빈 줄 없음
|
|
200
197
|
* - BlankLine (tool_result 뒤)
|
|
201
198
|
*
|
|
202
199
|
* ## 키 관리
|
|
203
200
|
*
|
|
204
201
|
* - startKey부터 시작하여 순차적으로 증가
|
|
205
|
-
* -
|
|
202
|
+
* - ConversationItem: `${keyPrefix}-${n}`
|
|
206
203
|
* - BlankLine: `${keyPrefix}-blank-${n}`
|
|
207
204
|
* - 반환값의 nextKey: 다음에 사용할 키 번호
|
|
208
205
|
*
|
|
@@ -234,7 +231,7 @@ function createItemsWithBlankLines(items, terminalWidth, startKey, keyPrefix = '
|
|
|
234
231
|
|
|
235
232
|
// 1. 아이템 추가
|
|
236
233
|
elements.push(
|
|
237
|
-
React.createElement(
|
|
234
|
+
React.createElement(ConversationItem, {
|
|
238
235
|
key: `${keyPrefix}-${currentKey}`,
|
|
239
236
|
item,
|
|
240
237
|
terminalWidth,
|
|
@@ -242,7 +239,7 @@ function createItemsWithBlankLines(items, terminalWidth, startKey, keyPrefix = '
|
|
|
242
239
|
isLastInBatch
|
|
243
240
|
})
|
|
244
241
|
);
|
|
245
|
-
debugLog(`[createItemsWithBlankLines] Added
|
|
242
|
+
debugLog(`[createItemsWithBlankLines] Added ConversationItem with key: ${keyPrefix}-${currentKey}`);
|
|
246
243
|
currentKey++;
|
|
247
244
|
|
|
248
245
|
// 2. 빈 줄 추가 여부 확인 (shouldAddBlankLineAfter 함수 참조)
|
|
@@ -591,7 +588,8 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
591
588
|
|
|
592
589
|
debugLog(`[Transform] Transformed type: ${transformed.type}`);
|
|
593
590
|
debugLog(`[Transform] Transformed toolName: ${transformed.toolName || 'N/A'}`);
|
|
594
|
-
|
|
591
|
+
const textPreview1 = transformed.text ? (typeof transformed.text === 'string' ? transformed.text.substring(0, 100) : JSON.stringify(transformed.text).substring(0, 100)) : 'N/A';
|
|
592
|
+
debugLog(`[Transform] Transformed text (first 100 chars): ${textPreview1}...`);
|
|
595
593
|
debugLog(`[Session] isSessionRunning: ${isSessionRunning}`);
|
|
596
594
|
|
|
597
595
|
if (isSessionRunning) {
|
|
@@ -643,7 +641,8 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
643
641
|
if (prev.length > 0) {
|
|
644
642
|
debugLog(`[Pair Matching] Pending items:`);
|
|
645
643
|
prev.forEach((p, idx) => {
|
|
646
|
-
|
|
644
|
+
const textPreview2 = p.text ? (typeof p.text === 'string' ? p.text.substring(0, 50) : JSON.stringify(p.text).substring(0, 50)) : 'N/A';
|
|
645
|
+
debugLog(` [${idx}] type=${p.type}, toolName=${p.toolName || 'N/A'}, text=${textPreview2}...`);
|
|
647
646
|
});
|
|
648
647
|
} else {
|
|
649
648
|
debugLog(`[Pair Matching] WARNING: Pending array is EMPTY - no tool_start to match!`);
|
|
@@ -746,7 +745,8 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
746
745
|
|
|
747
746
|
// 쌍의 시작 부분 (tool_start, code_execution)은 pending에 추가
|
|
748
747
|
debugLog(`[Pending Add] Adding to pending (waiting for pair): ${newType}, toolName=${transformed.toolName || 'N/A'}`);
|
|
749
|
-
|
|
748
|
+
const textPreview3 = transformed.text ? (typeof transformed.text === 'string' ? transformed.text.substring(0, 80) : JSON.stringify(transformed.text).substring(0, 80)) : 'N/A';
|
|
749
|
+
debugLog(`[Pending Add] Text: ${textPreview3}...`);
|
|
750
750
|
debugLog(`[Pending Add] Current pending items BEFORE add:`);
|
|
751
751
|
if (prev.length > 0) {
|
|
752
752
|
prev.forEach((p, idx) => {
|
|
@@ -779,9 +779,9 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
779
779
|
}, [isSessionRunning, transformEvent, terminalWidth]);
|
|
780
780
|
|
|
781
781
|
// Render a single history item (Memoized)
|
|
782
|
-
const
|
|
783
|
-
React.memo(function
|
|
784
|
-
return React.createElement(
|
|
782
|
+
const MemoizedConversationItem = React.useMemo(() =>
|
|
783
|
+
React.memo(function MemoizedConversationItem({ item, terminalWidth, nextItem, itemKey }) {
|
|
784
|
+
return React.createElement(ConversationItem, {
|
|
785
785
|
item,
|
|
786
786
|
terminalWidth,
|
|
787
787
|
nextItem
|
|
@@ -791,14 +791,14 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
791
791
|
|
|
792
792
|
const renderHistoryItem = useCallback((item, index, prefix = 'history', allItems = []) => {
|
|
793
793
|
const nextItem = allItems[index + 1];
|
|
794
|
-
return React.createElement(
|
|
794
|
+
return React.createElement(MemoizedConversationItem, {
|
|
795
795
|
key: `${prefix}-${index}`,
|
|
796
796
|
itemKey: `${prefix}-${index}`,
|
|
797
797
|
item,
|
|
798
798
|
terminalWidth,
|
|
799
799
|
nextItem
|
|
800
800
|
});
|
|
801
|
-
}, [terminalWidth,
|
|
801
|
+
}, [terminalWidth, MemoizedConversationItem]);
|
|
802
802
|
|
|
803
803
|
// Handle session state transitions
|
|
804
804
|
const handleSessionTransition = useCallback((wasRunning, isRunning) => {
|
|
@@ -914,8 +914,6 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
914
914
|
process.env.OPENAI_API_KEY = settings.OPENAI_API_KEY;
|
|
915
915
|
process.env.OPENAI_MODEL = settings.OPENAI_MODEL;
|
|
916
916
|
process.env.OPENAI_REASONING_EFFORT = settings.OPENAI_REASONING_EFFORT;
|
|
917
|
-
process.env.ANTHROPIC_API_KEY = settings.ANTHROPIC_API_KEY;
|
|
918
|
-
process.env.ANTHROPIC_MODEL = settings.ANTHROPIC_MODEL;
|
|
919
917
|
|
|
920
918
|
// 클라이언트 리셋
|
|
921
919
|
resetAIClients();
|
|
@@ -981,7 +979,7 @@ export function App({ onSubmit, onClearScreen, onExit, commands = [], model, ver
|
|
|
981
979
|
isRunning: isSessionRunning,
|
|
982
980
|
message: sessionMessage
|
|
983
981
|
}),
|
|
984
|
-
!approvalRequest && React.createElement(
|
|
982
|
+
!approvalRequest && React.createElement(Input, {
|
|
985
983
|
buffer,
|
|
986
984
|
onSubmit: handleSubmit,
|
|
987
985
|
onClearScreen: handleClearScreen,
|