docs: Add detailed backend, database, and frontend architecture analysis documents

- Created a comprehensive analysis document for the backend architecture, detailing the directory structure, API routes, authentication workflows, and more.
- Added a database architecture analysis report outlining the database structure, multi-tenancy architecture, and key system tables.
- Introduced a frontend architecture analysis document that covers the directory structure, component systems, and Next.js App Router structure.

These documents aim to enhance the understanding of the WACE ERP system's architecture and facilitate better workflow documentation.
This commit is contained in:
DDD1542 2026-02-06 16:00:43 +09:00
parent 0ac2d78ad3
commit 08dde416b1
4 changed files with 5612 additions and 47 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -16,15 +16,12 @@ import {
CallToolRequestSchema, CallToolRequestSchema,
ListToolsRequestSchema, ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js"; } from "@modelcontextprotocol/sdk/types.js";
import { exec } from "child_process"; import { spawn } from "child_process";
import { promisify } from "util";
import { platform } from "os"; import { platform } from "os";
import { AGENT_CONFIGS } from "./agents/prompts.js"; import { AGENT_CONFIGS } from "./agents/prompts.js";
import { AgentType, ParallelResult } from "./agents/types.js"; import { AgentType, ParallelResult } from "./agents/types.js";
import { logger } from "./utils/logger.js"; import { logger } from "./utils/logger.js";
const execAsync = promisify(exec);
// OS 감지 // OS 감지
const isWindows = platform() === "win32"; const isWindows = platform() === "win32";
logger.info(`Platform detected: ${platform()} (isWindows: ${isWindows})`); logger.info(`Platform detected: ${platform()} (isWindows: ${isWindows})`);
@ -46,9 +43,11 @@ const server = new Server(
* Cursor Agent CLI를 * Cursor Agent CLI를
* Cursor Team Plan - API ! * Cursor Team Plan - API !
* *
* spawn + stdin
*
* : * :
* - Windows: cmd /c "echo. | agent ..." (stdin ) * - Windows: agent (PATH에서 )
* - Mac/Linux: ~/.local/bin/agent * - Mac/Linux: ~/.local/bin/agent
*/ */
async function callAgentCLI( async function callAgentCLI(
agentType: AgentType, agentType: AgentType,
@ -60,56 +59,90 @@ async function callAgentCLI(
// 모델 선택: PM은 opus, 나머지는 sonnet // 모델 선택: PM은 opus, 나머지는 sonnet
const model = agentType === 'pm' ? 'opus-4.5' : 'sonnet-4.5'; const model = agentType === 'pm' ? 'opus-4.5' : 'sonnet-4.5';
logger.info(`Calling ${agentType} agent via CLI`, { model, task: task.substring(0, 100) }); logger.info(`Calling ${agentType} agent via CLI (spawn)`, { model, task: task.substring(0, 100) });
try { const userMessage = context
const userMessage = context ? `${task}\n\n배경 정보:\n${context}`
? `${task}\n\n배경 정보:\n${context}` : task;
: task;
const fullPrompt = `${config.systemPrompt}\n\n---\n\n${userMessage}`;
// 프롬프트를 임시 파일에 저장하여 쉘 이스케이프 문제 회피 const agentPath = isWindows ? 'agent' : `${process.env.HOME}/.local/bin/agent`;
const fullPrompt = `${config.systemPrompt}\n\n---\n\n${userMessage}`;
return new Promise<string>((resolve, reject) => {
// Base64 인코딩으로 특수문자 문제 해결 let stdout = '';
const encodedPrompt = Buffer.from(fullPrompt).toString('base64'); let stderr = '';
let settled = false;
let cmd: string;
let shell: string; const child = spawn(agentPath, ['--model', model, '--print'], {
const agentPath = isWindows ? 'agent' : `${process.env.HOME}/.local/bin/agent`;
if (isWindows) {
// Windows: PowerShell을 통해 Base64 디코딩 후 실행
cmd = `powershell -Command "$prompt = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${encodedPrompt}')); echo $prompt | ${agentPath} --model ${model} --print"`;
shell = 'powershell.exe';
} else {
// Mac/Linux: echo로 base64 디코딩 후 파이프
cmd = `echo "${encodedPrompt}" | base64 -d | ${agentPath} --model ${model} --print`;
shell = '/bin/bash';
}
logger.debug(`Executing: ${agentPath} --model ${model} --print`);
const { stdout, stderr } = await execAsync(cmd, {
cwd: process.cwd(), cwd: process.cwd(),
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
timeout: 300000, // 5분 타임아웃
shell,
env: { env: {
...process.env, ...process.env,
PATH: `${process.env.HOME}/.local/bin:${process.env.PATH}`, PATH: `${process.env.HOME}/.local/bin:${process.env.PATH}`,
}, },
stdio: ['pipe', 'pipe', 'pipe'],
}); });
if (stderr && !stderr.includes('warning') && !stderr.includes('info')) { child.stdout.on('data', (data: Buffer) => {
logger.warn(`${agentType} agent stderr`, { stderr: stderr.substring(0, 500) }); stdout += data.toString();
} });
logger.info(`${agentType} agent completed via CLI`); child.stderr.on('data', (data: Buffer) => {
return stdout.trim(); stderr += data.toString();
} catch (error) { });
logger.error(`${agentType} agent CLI error`, error);
throw error; child.on('error', (err: Error) => {
} if (!settled) {
settled = true;
logger.error(`${agentType} agent spawn error`, err);
reject(err);
}
});
child.on('close', (code: number | null) => {
if (settled) return;
settled = true;
if (stderr) {
// 경고/정보 레벨 stderr는 무시
const significantStderr = stderr
.split('\n')
.filter((line: string) => line && !line.includes('warning') && !line.includes('info') && !line.includes('debug'))
.join('\n');
if (significantStderr) {
logger.warn(`${agentType} agent stderr`, { stderr: significantStderr.substring(0, 500) });
}
}
if (code === 0 || stdout.trim().length > 0) {
// 정상 종료이거나, 에러 코드여도 stdout에 결과가 있으면 성공 처리
logger.info(`${agentType} agent completed via CLI (exit code: ${code})`);
resolve(stdout.trim());
} else {
const errorMsg = `Agent exited with code ${code}. stderr: ${stderr.substring(0, 1000)}`;
logger.error(`${agentType} agent CLI error`, { code, stderr: stderr.substring(0, 1000) });
reject(new Error(errorMsg));
}
});
// 타임아웃 (5분)
const timeout = setTimeout(() => {
if (!settled) {
settled = true;
child.kill('SIGTERM');
logger.error(`${agentType} agent timed out after 5 minutes`);
reject(new Error(`${agentType} agent timed out after 5 minutes`));
}
}, 300000);
// 프로세스 종료 시 타이머 클리어
child.on('close', () => clearTimeout(timeout));
// stdin으로 프롬프트 직접 전달 (쉘 이스케이프 문제 없음!)
child.stdin.write(fullPrompt);
child.stdin.end();
logger.debug(`Prompt sent to ${agentType} agent via stdin (${fullPrompt.length} chars)`);
});
} }
/** /**