feat: v2.12.0 - UI/UX Refinement (Model Sync & Premium Tooltips)
This commit is contained in:
+120
-64
@@ -1,97 +1,153 @@
|
||||
/**
|
||||
* ============================================================
|
||||
* Centralized Configuration (중앙 집중식 설정 관리)
|
||||
*
|
||||
* 모든 환경 설정(모델 이름, API 엔드포인트, 타임아웃, 보안 정책 등)
|
||||
* 을 한 곳에서 관리합니다. Single Source of Truth 원칙 적용.
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
// ─── VS Code 설정에서 읽어오는 값 ───
|
||||
export function getConfig(): IAgentConfig {
|
||||
const cfg = vscode.workspace.getConfiguration('g1nation');
|
||||
return {
|
||||
ollamaBase: cfg.get<string>('ollamaUrl', 'http://127.0.0.1:11434'),
|
||||
defaultModel: cfg.get<string>('defaultModel', 'gemma4:e2b'),
|
||||
maxTreeFiles: cfg.get<number>('maxTreeFiles', 200),
|
||||
timeout: cfg.get<number>('requestTimeout', 300) * 1000,
|
||||
localBrainPath: cfg.get<string>('localBrainPath', '')
|
||||
};
|
||||
// ─── 브레인 프로필 인터페이스 ───
|
||||
export interface BrainProfile {
|
||||
id: string;
|
||||
name: string;
|
||||
localBrainPath: string;
|
||||
secondBrainRepo?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// ─── 에이전트 설정 인터페이스 ───
|
||||
// ─── 에이전트 설정 인터페이스 (통합 버전) ───
|
||||
export interface IAgentConfig {
|
||||
ollamaBase: string;
|
||||
ollamaUrl: string;
|
||||
defaultModel: string;
|
||||
maxTreeFiles: number;
|
||||
timeout: number;
|
||||
localBrainPath: string;
|
||||
secondBrainRepo: string;
|
||||
brainProfiles: BrainProfile[];
|
||||
activeBrainId: string;
|
||||
maxContextSize: number;
|
||||
maxAutoSteps: number;
|
||||
dryRun: boolean;
|
||||
multiAgentEnabled: boolean;
|
||||
}
|
||||
|
||||
// ─── 두뇌 폴더 경로 유틸리티 ───
|
||||
export function getBrainDir(): string {
|
||||
const { localBrainPath } = getConfig();
|
||||
if (localBrainPath && localBrainPath.trim() !== '') {
|
||||
if (localBrainPath.startsWith('~/')) {
|
||||
return path.join(os.homedir(), localBrainPath.substring(2));
|
||||
}
|
||||
return localBrainPath.trim();
|
||||
// ─── 경로 정규화 유틸리티 ───
|
||||
function normalizePath(p: string): string {
|
||||
if (!p) return p;
|
||||
if (p.startsWith('~/')) {
|
||||
return path.join(os.homedir(), p.substring(2));
|
||||
}
|
||||
return path.join(os.homedir(), '.g1nation-brain');
|
||||
return p.trim();
|
||||
}
|
||||
|
||||
export function isBrainDirExplicitlySet(): boolean {
|
||||
const { localBrainPath } = getConfig();
|
||||
return !!(localBrainPath && localBrainPath.trim() !== '');
|
||||
function toBrainProfile(raw: Partial<BrainProfile> | undefined, fallbackIndex: number): BrainProfile | null {
|
||||
if (!raw) return null;
|
||||
const localBrainPath = normalizePath(raw.localBrainPath || '');
|
||||
if (!localBrainPath) return null;
|
||||
return {
|
||||
id: (raw.id || `brain-${fallbackIndex + 1}`).trim(),
|
||||
name: (raw.name || path.basename(localBrainPath) || `Brain ${fallbackIndex + 1}`).trim(),
|
||||
localBrainPath,
|
||||
secondBrainRepo: (raw.secondBrainRepo || '').trim(),
|
||||
description: (raw.description || '').trim()
|
||||
};
|
||||
}
|
||||
|
||||
// ─── VS Code 설정에서 읽어오는 값 (통합 구현) ───
|
||||
export function getConfig(): IAgentConfig {
|
||||
const cfg = vscode.workspace.getConfiguration('g1nation');
|
||||
|
||||
// 브레인 프로필 로직 (utils.ts에서 이관)
|
||||
const legacyBrainPath = cfg.get<string>('localBrainPath', '');
|
||||
const legacyBrainRepo = cfg.get<string>('secondBrainRepo', '');
|
||||
const configuredProfiles = cfg.get<Partial<BrainProfile>[]>('brainProfiles', []);
|
||||
const profiles = configuredProfiles
|
||||
.map((profile, index) => toBrainProfile(profile, index))
|
||||
.filter((profile): profile is BrainProfile => !!profile);
|
||||
|
||||
if (profiles.length === 0) {
|
||||
const fallbackPath = normalizePath(legacyBrainPath) || path.join(os.homedir(), '.g1nation-brain');
|
||||
profiles.push({
|
||||
id: 'default-brain',
|
||||
name: 'Local Brain',
|
||||
localBrainPath: fallbackPath,
|
||||
secondBrainRepo: legacyBrainRepo.trim(),
|
||||
description: legacyBrainPath
|
||||
? 'Migrated from your existing localBrainPath setting'
|
||||
: 'Auto-created local knowledge folder.'
|
||||
});
|
||||
}
|
||||
|
||||
const activeBrainId = cfg.get<string>('activeBrainId', profiles[0].id) || profiles[0].id;
|
||||
const activeBrain = profiles.find((profile) => profile.id === activeBrainId) || profiles[0];
|
||||
|
||||
const rationaleProtocol = `
|
||||
3. Always explain your thought process using the <rationale> tag BEFORE performing any actions. Use the following structure:
|
||||
<rationale>
|
||||
[PROBLEM] Description of the issue or need found in the context.
|
||||
[GOAL] What you intend to achieve with your proposed changes.
|
||||
[REASONING] Detailed logical basis for choosing specific actions or architecture.
|
||||
</rationale>
|
||||
`;
|
||||
|
||||
return {
|
||||
ollamaUrl: cfg.get<string>('ollamaUrl', 'http://127.0.0.1:11434') || 'http://127.0.0.1:11434',
|
||||
defaultModel: cfg.get<string>('defaultModel', 'gemma4:e2b') || 'gemma4:e2b',
|
||||
maxTreeFiles: 200,
|
||||
timeout: cfg.get<number>('requestTimeout', 300) * 1000,
|
||||
localBrainPath: activeBrain.localBrainPath,
|
||||
secondBrainRepo: activeBrain.secondBrainRepo || '',
|
||||
brainProfiles: profiles,
|
||||
activeBrainId: activeBrain.id,
|
||||
maxContextSize: cfg.get<number>('maxContextSize', 12000),
|
||||
maxAutoSteps: cfg.get<number>('maxAutoSteps', 50),
|
||||
dryRun: cfg.get<boolean>('dryRun', false),
|
||||
multiAgentEnabled: cfg.get<boolean>('multiAgentEnabled', true)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Config Validator: Validates the current configuration.
|
||||
*/
|
||||
export function validateConfig(): { valid: boolean; errors: string[] } {
|
||||
const config = getConfig();
|
||||
const errors: string[] = [];
|
||||
|
||||
// 1. Ollama URL Validation
|
||||
try {
|
||||
new URL(config.ollamaUrl);
|
||||
} catch (e) {
|
||||
errors.push(`Invalid Ollama URL: ${config.ollamaUrl}`);
|
||||
}
|
||||
|
||||
// 2. Brain Path Validation
|
||||
if (config.localBrainPath && !fs.existsSync(config.localBrainPath)) {
|
||||
errors.push(`Brain path does not exist: ${config.localBrainPath}`);
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 보안 정책 (Security Policy) ───
|
||||
export const SECURITY_POLICY = {
|
||||
// 허용된 터미널 명령어 프리픽스 (Whitelist)
|
||||
allowedCommandPrefixes: [
|
||||
'npm', 'yarn', 'pnpm', 'npx',
|
||||
'node', 'ts-node',
|
||||
'git',
|
||||
'python', 'python3', 'pip', 'pip3',
|
||||
'docker', 'docker-compose',
|
||||
'ls', 'dir', 'cat', 'type',
|
||||
'echo', 'print',
|
||||
'cargo', 'go', 'rustc',
|
||||
'java', 'javac', 'mvn', 'gradle',
|
||||
'flutter', 'dart', 'pub',
|
||||
'webpack', 'vite', 'esbuild', 'parcel',
|
||||
'jest', 'mocha', 'vitest', 'cypress',
|
||||
'tsc', 'vue-tsc',
|
||||
'npm', 'yarn', 'pnpm', 'npx', 'node', 'ts-node', 'git', 'python', 'python3', 'pip', 'pip3',
|
||||
'docker', 'docker-compose', 'ls', 'dir', 'cat', 'type', 'echo', 'print', 'cargo', 'go', 'rustc',
|
||||
'java', 'javac', 'mvn', 'gradle', 'flutter', 'dart', 'pub', 'webpack', 'vite', 'esbuild', 'parcel',
|
||||
'jest', 'mocha', 'vitest', 'cypress', 'tsc', 'vue-tsc',
|
||||
],
|
||||
|
||||
// 절대 실행 금지 명령어 (Blacklist)
|
||||
forbiddenCommands: [
|
||||
'rm -rf', 'rm-rf', 'del /f', 'format',
|
||||
'mkfs', 'dd if=', ':(){ :|:& };:',
|
||||
'wget http', 'curl http', 'sudo',
|
||||
'chmod 777', 'chown root',
|
||||
'rm -rf', 'rm-rf', 'del /f', 'format', 'mkfs', 'dd if=', ':(){ :|:& };:',
|
||||
'wget http', 'curl http', 'sudo', 'chmod 777', 'chown root',
|
||||
],
|
||||
|
||||
// 민감한 파일 패턴 (파일 생성/수정 시 경고)
|
||||
sensitiveFilePatterns: [
|
||||
'.env', '.env.*',
|
||||
'id_rsa', 'id_ed25519',
|
||||
'.gitconfig', '.npmrc', '.pypirc',
|
||||
'.env', '.env.*', 'id_rsa', 'id_ed25519', '.gitconfig', '.npmrc', '.pypirc',
|
||||
'credentials.json', 'service-account.json',
|
||||
],
|
||||
|
||||
// 파일 생성 시 최대 크기 (bytes)
|
||||
maxFileSize: 10 * 1024 * 1024, // 10MB
|
||||
|
||||
// 맥락 파일 최대 개수
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxContextFiles: 200,
|
||||
};
|
||||
|
||||
// ─── 시스템 프롬프트 상수 ───
|
||||
export const MAX_CONTEXT_SIZE = 12_000;
|
||||
|
||||
export const EXCLUDED_DIRS = new Set([
|
||||
|
||||
Reference in New Issue
Block a user