fix(scaffold): fix javascript syntax errors in dropdown and folder picker preventing category loading
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 17s

This commit is contained in:
jade
2026-08-20 10:33:31 +09:00
parent b1e779e93a
commit 469e1f9425

View File

@@ -801,17 +801,17 @@
<div class="col-md-12">
<label class="form-label">Workspace Path</label>
<div class="input-group">
<input type="text" class="form-control" name="workspacePath" id="podWorkspacePathInput" placeholder="직접 더 경로를 입력(붙여기) 하거나 선택하세요" value="c:\eGovFrameDev-4.3.1-64bit\workspace-egov\dat-was-datmt">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" style="border-color: #3f3f46; color: #a1a1aa;">폴더 선</button>
<input type="text" class="form-control" name="workspacePath" id="podWorkspacePathInput" placeholder="직접 ?<3F>더 경로<EFBFBD>??<3F>력(붙여?<3F>기) ?<3F>거???<3F>택?<3F>세?? value="c:\eGovFrameDev-4.3.1-64bit\workspace-egov\dat-was-datmt">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" style="border-color: #3f3f46; color: #a1a1aa;">?<3F>더 ?<3F></button>
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-dark" style="background-color: #18181b; border-color: #3f3f46;">
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('podWorkspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dat-was-datmt'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">dat-was-datmt (기본)</a></li>
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('podWorkspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\axhub-backend-main'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">axhub-backend-main</a></li>
<li><hr class="dropdown-divider" style="border-color: #3f3f46;"></li>
<li><a class="dropdown-item" href="#" onclick="openServerFolderPicker('podWorkspacePathInput'); return false;" style="color: #f4f4f5;">폴더 선택(+)</a></li>
<li><a class="dropdown-item" href="#" onclick="openServerFolderPicker('podWorkspacePathInput'); return false;" style="color: #f4f4f5;">?<3F>더 ?<3F>택(+)</a></li>
</ul>
</div>
<input type="file" id="podFolderPicker" webkitdirectory directory style="display: none;" onchange="
if(this.files && this.files.length > 0) { alert("Path: " + this.files[0].path + ", webkitRel: " + this.files[0].webkitRelativePath + ", name: " + this.files[0].name); if(this.files[0].path) {
if(this.files && this.files.length > 0) { if(this.files[0].path) {
let firstFilePath = this.files[0].path;
let relPath = this.files[0].webkitRelativePath;
let folderPath = firstFilePath.substring(0, firstFilePath.length - relPath.length);
@@ -823,7 +823,7 @@
input.dispatchEvent(new Event('change'));
}
">
<div class="input-hint">직접 경로를 붙여넣거나 우<b>[폴더 선택]</b> 버튼을 눌러 선택하세요.</div>
<div class="input-hint">직접 경로<EFBFBD>?붙여?<3F>거???<3F><b>[?<3F>더 ?<3F>택]</b> 버튼???<3F>러 ?<3F>택?<3F>세??</div>
</div>
</div>
<div class="mb-4">
@@ -858,17 +858,17 @@
<div class="col-md-12">
<label class="form-label">Workspace Path</label>
<div class="input-group">
<input type="text" class="form-control" name="workspacePath" id="workspacePathInput" placeholder="직접 더 경로를 입력(붙여기) 하거나 선택하세요" value="c:\eGovFrameDev-4.3.1-64bit\workspace-egov\dat-was-datmt">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" style="border-color: #3f3f46; color: #a1a1aa;">폴더 선</button>
<input type="text" class="form-control" name="workspacePath" id="workspacePathInput" placeholder="직접 ?<3F>더 경로<EFBFBD>??<3F>력(붙여?<3F>기) ?<3F>거???<3F>택?<3F>세?? value="c:\eGovFrameDev-4.3.1-64bit\workspace-egov\dat-was-datmt">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" style="border-color: #3f3f46; color: #a1a1aa;">?<3F>더 ?<3F></button>
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-dark" style="background-color: #18181b; border-color: #3f3f46;">
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('workspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dat-was-datmt'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">dat-was-datmt (기본)</a></li>
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('workspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\axhub-backend-main'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">axhub-backend-main</a></li>
<li><hr class="dropdown-divider" style="border-color: #3f3f46;"></li>
<li><a class="dropdown-item" href="#" onclick="openServerFolderPicker('workspacePathInput'); return false;" style="color: #f4f4f5;">폴더 선택(+)</a></li>
<li><a class="dropdown-item" href="#" onclick="openServerFolderPicker('workspacePathInput'); return false;" style="color: #f4f4f5;">?<3F>더 ?<3F>택(+)</a></li>
</ul>
</div>
<input type="file" id="toolFolderPicker" webkitdirectory directory style="display: none;" onchange="
if(this.files && this.files.length > 0) { alert("Path: " + this.files[0].path + ", webkitRel: " + this.files[0].webkitRelativePath + ", name: " + this.files[0].name); if(this.files[0].path) {
if(this.files && this.files.length > 0) { if(this.files[0].path) {
let firstFilePath = this.files[0].path;
let relPath = this.files[0].webkitRelativePath;
let folderPath = firstFilePath.substring(0, firstFilePath.length - relPath.length);
@@ -880,7 +880,7 @@
input.dispatchEvent(new Event('change'));
}
">
<div class="input-hint">직접 경로를 붙여넣거나 우<b>[폴더 선택]</b> 버튼을 눌러 선택하세요.</div>
<div class="input-hint">직접 경로<EFBFBD>?붙여?<3F>거???<3F><b>[?<3F>더 ?<3F>택]</b> 버튼???<3F>러 ?<3F>택?<3F>세??</div>
</div>
</div>
<div class="row mb-4">
@@ -911,11 +911,11 @@
<div class="row g-2">
<div class="col-md-5">
<select id="domainCategorySelect" class="form-select">
<option value="">새 도메인 생</option>
<option value="">???<3F>메???<3F></option>
</select>
</div>
<div class="col-md-7">
<input type="text" id="domainCategoryName" class="form-control" name="categoryKey" pattern="^[a-z0-9]{3}$" maxlength="3" minlength="3" placeholder="e.g. cmm (3글자 영소문자)" oninput="this.value = this.value.toLowerCase()" required>
<input type="text" id="domainCategoryName" class="form-control" name="categoryKey" pattern="^[a-z0-9]{3}$" maxlength="3" minlength="3" placeholder="e.g. cmm (3글???<3F>소문자)" oninput="this.value = this.value.toLowerCase()" required>
</div>
</div>
</div>
@@ -931,24 +931,24 @@
<div class="d-flex justify-content-between align-items-center gap-3">
<div>
<label class="form-label mb-1">Multi Tool UseCase (HTTP / MCI)</label>
<div class="input-hint mt-0">재 Tool을 같은 UseCase에 추가하면, ToolClient를 호출하는 여러 MCP Tool 메서드가 생성됩니다.</div>
<div class="input-hint mt-0">?<3F>재 Tool??같<>? UseCase??추<>??<3F>면, Tool<EFBFBD>?Client<EFBFBD>??<3F>출?<3F>는 ?<3F>러 MCP Tool 메서?<3F><>? ?<3F>성?<3F>니??</div>
</div>
<button type="button" class="btn-secondary-action" onclick="addCurrentToolToGroup()">재 Tool 묶음에 추가</button>
<button type="button" class="btn-secondary-action" onclick="addCurrentToolToGroup()">?<3F>재 Tool 묶음??추<>?</button>
</div>
<div class="row g-2 mt-1">
<div class="col-md-4"><select id="toolGroupUseCaseSelect" class="form-select"><option value="">UseCase </option></select></div>
<div class="col-md-5"><input id="toolGroupUseCaseName" type="text" class="form-control" placeholder="UseCase 름 e.g. Customer"></div>
<div class="col-md-7"><div id="toolGroupSummary" class="input-hint pt-2">묶음에 추가된 Tool 없음 — 일반 Generate Tool 기존 일 Tool 생성입니다.</div></div>
<div class="col-md-4"><select id="toolGroupUseCaseSelect" class="form-select"><option value="">??UseCase ?<3F></option></select></div>
<div class="col-md-5"><input id="toolGroupUseCaseName" type="text" class="form-control" placeholder="UseCase ?<3F>름 e.g. Customer"></div>
<div class="col-md-7"><div id="toolGroupSummary" class="input-hint pt-2">묶음??추<>???Tool ?<3F>음 ???<3F>반 Generate Tool?<3F> 기존 ?<3F>일 Tool ?<3F>성?<3F>니??</div></div>
</div>
</div>
<div class="mb-4 p-3 rounded" style="background:#18181b; border:1px solid #3f3f46;">
<label class="form-label">AI Tool 초안 만들</label>
<label class="form-label">AI Tool 초안 만들<EFBFBD>?/label>
<div class="row g-2">
<div class="col-md-6">
<input id="toolNaturalLanguage" type="text" class="form-control" placeholder="예: 고객번호계약 상태를 조회하는 Tool">
<input id="toolNaturalLanguage" type="text" class="form-control" placeholder="?? 고객번호<EFBFBD>?계약 ?<3F><EFBFBD>?조회?<3F>는 Tool">
</div>
<div class="col-md-3">
<select id="aiModelSelect" class="form-select" aria-label="AI 모델 택">
<select id="aiModelSelect" class="form-select" aria-label="AI 모델 ?<3F>택">
<option value="cohere/north-mini-code:free" selected>Cohere North Mini Code</option>
<option value="inclusionai/ling-3.0-flash:free">InclusionAI Ling 3 Flash</option>
<option value="openai/gpt-oss-20b:free">OpenAI GPT-OSS 20B</option>
@@ -957,10 +957,10 @@
</select>
</div>
<div class="col-md-3 d-grid">
<button id="toolDraftButton" type="button" class="btn-action" onclick="createAiToolDraft()">AITool 채우</button>
<button id="toolDraftButton" type="button" class="btn-action" onclick="createAiToolDraft()">AI<EFBFBD>?Tool 채우<EFBFBD>?/button>
</div>
</div>
<div class="input-hint">선택한 AI가 Tool 기본 정보와 Input/Output Fields를 채웁니다. Legacy Interface ID Client System Code는 실제 연계 명세를 확인해 직접 입력하세요.</div>
<div class="input-hint">?<3F>택??AI가 Tool 기본 ?<3F>보?<3F> Input/Output Fields<EFBFBD>?채웁?<3F>다. Legacy Interface ID?<3F> Client System Code???<3F>제 ?<3F>계 명세<EBAA85>??<3F>인??직접 ?<3F>력?<3F>세??</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
@@ -988,31 +988,31 @@
<div class="mb-3 p-3 rounded" style="background:#18181b; border:1px solid #3f3f46;">
<div class="form-label mb-2">Tool Schema V17 Metadata</div>
<div class="input-hint mb-3">LLMTool을 올바르게 선택하도록 기능·용 조건·외 조건·입출력 제한을 분리해 입력합니다.</div>
<div class="input-hint mb-3">LLM??Tool???<3F>바르게 ?<3F>택?<3F><EFBFBD>?기능·?<3F>용 조건·?<3F>외 조건·?<3F>출???<3F>한??분리???<3F>력?<3F>니??</div>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Function Description</label>
<textarea class="form-control" name="functionDescription" rows="2" placeholder="Tool이 수행하는 핵심 기능"></textarea>
<textarea class="form-control" name="functionDescription" rows="2" placeholder="??Tool???<3F>행?<3F>는 ?<3F>심 기능"></textarea>
</div>
<div class="col-md-6">
<label class="form-label">Display Description</label>
<textarea class="form-control" name="displayDescription" rows="2" placeholder="Portal에 보여줄 짧은 설명"></textarea>
<textarea class="form-control" name="displayDescription" rows="2" placeholder="Portal??보여<EBB3B4>?짧<>? ?<3F>명"></textarea>
</div>
<div class="col-md-6">
<label class="form-label">When To Use</label>
<textarea class="form-control" name="whenToUse" rows="2" placeholder="어떤 사용자 요청에서 이 Tool을 사용하는지"></textarea>
<textarea class="form-control" name="whenToUse" rows="2" placeholder="?<3F>떤 ?<3F>용???<3F>청?<3F>서 ??Tool???<3F>용?<3F>는지"></textarea>
</div>
<div class="col-md-6">
<label class="form-label">When Not To Use</label>
<textarea class="form-control" name="whenNotToUse" rows="2" placeholder="Tool을 사용하면 안 되는 조건"></textarea>
<textarea class="form-control" name="whenNotToUse" rows="2" placeholder="??Tool???<3F>용?<3F>면 ???<3F>는 조건"></textarea>
</div>
<div class="col-md-12">
<label class="form-label">I/O Limits</label>
<textarea class="form-control" name="ioLimits" rows="2" placeholder="허용되는 입력, 반환 범위, 건수 제한 등"></textarea>
<textarea class="form-control" name="ioLimits" rows="2" placeholder="?<3F>용?<3F>는 ?<3F>력, 반환 범위, 건수 ?<3F>한 ??></textarea>
</div>
<div class="col-md-8">
<label class="form-label">Example Queries (3~10)</label>
<textarea class="form-control" name="exampleQueries" rows="3" placeholder="줄에 하나씩 입력\n예: 사번 10001 직원 정보를 조회해줘"></textarea>
<textarea class="form-control" name="exampleQueries" rows="3" placeholder="??줄에 ?<3F>나???<3F>력\n?? ?<3F>번 10001 직원 ?<3F><EFBFBD>?조회?<3F>줘"></textarea>
</div>
<div class="col-md-4">
<label class="form-label">Tags</label>
@@ -1040,8 +1040,8 @@
<div class="d-flex justify-content-between align-items-center mb-1">
<label class="form-label mb-0">Input Fields (JSON)</label>
<div class="d-flex gap-1">
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="openFieldEditor('inputFields')">필드 편</button>
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="loadFieldExample('inputFields')">예제 넣</button>
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="openFieldEditor('inputFields')">?<3F>드 ?<3F></button>
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="loadFieldExample('inputFields')">?<3F>제 ?<3F></button>
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="copyFieldJson('inputFields')">복사</button>
</div>
</div>
@@ -1051,8 +1051,8 @@
<div class="d-flex justify-content-between align-items-center mb-1">
<label class="form-label mb-0">Output Fields (JSON)</label>
<div class="d-flex gap-1">
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="openFieldEditor('outputFields')">필드 편</button>
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="loadFieldExample('outputFields')">예제 넣</button>
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="openFieldEditor('outputFields')">?<3F>드 ?<3F></button>
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="loadFieldExample('outputFields')">?<3F>제 ?<3F></button>
<button type="button" class="btn-secondary-action" style="padding: 0.2rem 0.45rem; font-size: 0.72rem;" onclick="copyFieldJson('outputFields')">복사</button>
</div>
</div>
@@ -1064,7 +1064,7 @@
<div class="row mb-4">
<div class="col-md-6">
<label class="form-label">HTTP API Name (Glow api-list)</label>
<input type="text" class="form-control" name="httpApiName" pattern="^[a-zA-Z0-9_-]+$" placeholder="비우Tool 이름으로 자동 생성">
<input type="text" class="form-control" name="httpApiName" pattern="^[a-zA-Z0-9_-]+$" placeholder="비우<EFBFBD>?Tool ?<3F>름?<3F>로 ?<3F>동 ?<3F>성">
<div class="input-hint">HTTP only. Must match glow.communication.http.api-list[].name; URL and method are read from Glow YAML.</div>
</div>
<div class="col-md-6 mt-3 mt-md-0">
@@ -1096,12 +1096,12 @@
<div class="docgen-workspace">
<section class="docgen-config">
<h2 class="docgen-section-title">Document Generator</h2>
<p class="docgen-section-copy">Tool Metadata를 기준으로 개발 산출물 Excel을 생성합니다. 선택된 문서만 파일과 시트에 포함됩니다.</p>
<p class="docgen-section-copy">Tool Metadata<EFBFBD>?기<>??<3F>로 개발 ?<3F><EFBFBD>?Excel???<3F>성?<3F>니?? ?<3F>택??문서<EBACB8>??<3F><EFBFBD>??<3F>트???<3F>함?<3F>니??</p>
<div class="docgen-field">
<label class="docgen-field-label" for="docgenToolSelect">Tool</label>
<select id="docgenToolSelect" disabled><option>Tool Metadata를 불러오는 중...</option></select>
<div id="docgenGrowToolHint" class="docgen-hint">GatewayToolMetadata 목록을 조회하고 있습니다.</div>
<select id="docgenToolSelect" disabled><option>Tool Metadata<EFBFBD>?불러?<3F><20>?..</option></select>
<div id="docgenGrowToolHint" class="docgen-hint">Gateway??ToolMetadata 목록??조회?<3F>고 ?<3F>습?<3F>다.</div>
</div>
<div class="docgen-two-col docgen-field">
@@ -1118,10 +1118,10 @@
<div class="docgen-field">
<label class="docgen-field-label">Included Sheets</label>
<div class="docgen-check-grid">
<label class="docgen-check-card"><input id="docgenProgramCheck" type="checkbox" checked><span>로그램정의서</span></label>
<label class="docgen-check-card"><input id="docgenProcessCheck" type="checkbox" checked><span>처리</span></label>
<label class="docgen-check-card"><input id="docgenInterfaceCheck" type="checkbox" checked><span>인터페이스</span><span class="docgen-mini-badge">별도 </span></label>
<label class="docgen-check-card"><input id="docgenRevisionCheck" type="checkbox" checked><span>개정</span></label>
<label class="docgen-check-card"><input id="docgenProgramCheck" type="checkbox" checked><span>?<3F>로그램?<3F>의??/span></label>
<label class="docgen-check-card"><input id="docgenProcessCheck" type="checkbox" checked><span>처리?<3F></span></label>
<label class="docgen-check-card"><input id="docgenInterfaceCheck" type="checkbox" checked><span>?<3F>터?<3F>이??/span><span class="docgen-mini-badge">별도 ?<3F></span></label>
<label class="docgen-check-card"><input id="docgenRevisionCheck" type="checkbox" checked><span>개정?<3F></span></label>
</div>
</div>
@@ -1136,7 +1136,7 @@
<div class="docgen-meta-title">
<div>
<h2 id="docgenToolTitle" class="docgen-loading">Tool Metadata loading...</h2>
<p id="docgenToolDescription">등록된 Tool 목록을 가져오는 중입니다.</p>
<p id="docgenToolDescription">?<3F>록??Tool 목록??가?<3F>오??중입?<3F>다.</p>
</div>
<span id="docgenMetadataStatus" class="docgen-status">SYNC</span>
</div>
@@ -1148,16 +1148,16 @@
</div>
<div class="docgen-list">
<div id="docgenProgramRow" class="docgen-row docgen-enabled"><span class="docgen-number">01</span><div><div class="docgen-name">로그램정의서</div><div class="docgen-description">Basic information · Runtime · Process summary</div></div><span class="docgen-mode docgen-auto">AUTO</span></div>
<div id="docgenProcessRow" class="docgen-row docgen-enabled"><span class="docgen-number">02</span><div><div class="docgen-name">처리</div><div class="docgen-description">Tool metadata + common design</div></div><span class="docgen-mode docgen-mixed">MIXED</span></div>
<div id="docgenInterfaceRow" class="docgen-row docgen-enabled"><span class="docgen-number">03</span><div><div class="docgen-name">인터페이스</div><div class="docgen-description">Request In · Response Out · 2 sheets</div></div><span class="docgen-mode docgen-separate">2 SHEETS · SEPARATE</span></div>
<div id="docgenRevisionRow" class="docgen-row docgen-enabled"><span class="docgen-number">04</span><div><div class="docgen-name">개정</div><div class="docgen-description">Version · Date · Change information</div></div><span class="docgen-mode">MANUAL</span></div>
<div id="docgenProgramRow" class="docgen-row docgen-enabled"><span class="docgen-number">01</span><div><div class="docgen-name">?<3F>로그램?<3F>의??/div><div class="docgen-description">Basic information · Runtime · Process summary</div></div><span class="docgen-mode docgen-auto">AUTO</span></div>
<div id="docgenProcessRow" class="docgen-row docgen-enabled"><span class="docgen-number">02</span><div><div class="docgen-name">처리?<3F></div><div class="docgen-description">Tool metadata + common design</div></div><span class="docgen-mode docgen-mixed">MIXED</span></div>
<div id="docgenInterfaceRow" class="docgen-row docgen-enabled"><span class="docgen-number">03</span><div><div class="docgen-name">?<3F>터?<3F>이??/div><div class="docgen-description">Request In · Response Out · 2 sheets</div></div><span class="docgen-mode docgen-separate">2 SHEETS · SEPARATE</span></div>
<div id="docgenRevisionRow" class="docgen-row docgen-enabled"><span class="docgen-number">04</span><div><div class="docgen-name">개정?<3F></div><div class="docgen-description">Version · Date · Change information</div></div><span class="docgen-mode">MANUAL</span></div>
</div>
<p class="docgen-legend">AUTO: Tool metadata · MIXED: Metadata + common design · MANUAL: Review required<br>Output Files · 인터페이스 정의서는 별도 Excel 파일로 생성됩니다.</p>
<p class="docgen-legend">AUTO: Tool metadata · MIXED: Metadata + common design · MANUAL: Review required<br>Output Files · ?<3F>터?<3F>이???<3F>의?<3F>는 별도 Excel ?<3F><EFBFBD>??<3F>성?<3F>니??</p>
<div class="docgen-files">
<div id="docgenProgramFile" class="docgen-file"><div class="docgen-file-kind">PROGRAM SPEC</div><div id="docgenProgramFileName" class="docgen-file-name">tool_로그램정의서_v1.0_YYYYMMDD.xlsx</div></div>
<div id="docgenInterfaceFile" class="docgen-file"><div class="docgen-file-kind">INTERFACE SPEC · REQUEST IN / RESPONSE OUT</div><div id="docgenInterfaceFileName" class="docgen-file-name">tool_인터페이스정의서_v1.0_YYYYMMDD.xlsx</div></div>
<div id="docgenProgramFile" class="docgen-file"><div class="docgen-file-kind">PROGRAM SPEC</div><div id="docgenProgramFileName" class="docgen-file-name">tool_?<3F>로그램?<3F>의??v1.0_YYYYMMDD.xlsx</div></div>
<div id="docgenInterfaceFile" class="docgen-file"><div class="docgen-file-kind">INTERFACE SPEC · REQUEST IN / RESPONSE OUT</div><div id="docgenInterfaceFileName" class="docgen-file-name">tool_?<3F>터?<3F>이?<3F>정?<3F>서_v1.0_YYYYMMDD.xlsx</div></div>
</div>
</section>
</div>
@@ -1210,26 +1210,26 @@
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="fieldEditorModalLabel">필드 편</h5>
<h5 class="modal-title" id="fieldEditorModalLabel">?<3F>드 ?<3F></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="input-hint mt-0 mb-3">을 추가해 필드를 입력하면 저장할 때 JSON으로 자동 변환됩니다.</p>
<p class="input-hint mt-0 mb-3">?<3F>을 추<EFBFBD>????<3F><EFBFBD>??<3F>력?<3F>면 ?<3F>?<3F>할 ??JSON?<3F>로 ?<3F>동 변?<3F>됩?<3F>다.</p>
<div class="d-flex flex-wrap gap-2 align-items-center mb-3">
<span class="input-hint mt-0 mb-0">빠른 력:</span>
<span class="input-hint mt-0 mb-0">빠른 ?<3F>력:</span>
<button type="button" class="btn-secondary-action" onclick="applyFieldTemplate('customer')">고객조회</button>
<button type="button" class="btn-secondary-action" onclick="applyFieldTemplate('list')">목록조회</button>
<button type="button" class="btn-secondary-action" onclick="applyFieldTemplate('detail')">건조회</button>
<button type="button" class="btn-secondary-action" onclick="applyFieldTemplate('write')">록/변</button>
<button type="button" class="btn-secondary-action" onclick="applyFieldTemplate('clear')">초기</button>
<button type="button" class="btn-secondary-action" onclick="applyFieldTemplate('detail')">?<3F>건조회</button>
<button type="button" class="btn-secondary-action" onclick="applyFieldTemplate('write')">?<3F>록/변<EFBFBD>?/button>
<button type="button" class="btn-secondary-action" onclick="applyFieldTemplate('clear')">초기??/button>
</div>
<div class="mb-4">
<label class="form-label">자연어 초안 만들기</label>
<label class="form-label">?<3F>연??초안 만들<EBA78C>?/label>
<div class="input-group">
<input id="fieldNaturalLanguage" type="text" class="form-control" placeholder="예: 고객번호계약 상태를 조회">
<button id="fieldDraftButton" type="button" class="btn-action" onclick="createNaturalFieldDraft()">AI 초안 만들</button>
<input id="fieldNaturalLanguage" type="text" class="form-control" placeholder="?? 고객번호<EFBFBD>?계약 ?<3F><EFBFBD>?조회">
<button id="fieldDraftButton" type="button" class="btn-action" onclick="createNaturalFieldDraft()">AI 초안 만들<EFBFBD>?/button>
</div>
<div class="input-hint">Chat 화면과 같은 AI 모델이 문장을 분석해 현재 Input 는 Output 드 초안을 만듭니다.</div>
<div class="input-hint">Chat ?<3F><EFBFBD>?같<>? AI 모델??문장??분석???<3F>재 Input ?<3F>는 Output ?<3F>드 초안??만듭?<3F>다.</div>
</div>
<div class="table-responsive">
<table class="table align-middle" id="fieldEditorTable">
@@ -1241,15 +1241,15 @@
<th style="min-width: 260px;">Object list item fields (JSON)</th>
<th style="min-width: 210px;">Description</th>
<th style="min-width: 150px;">Pattern (Regex)</th>
<th style="min-width: 180px;">Examples (Enter 위 구분)</th>
<th style="min-width: 180px;">Examples (Enter ?<3F>위 구분)</th>
<th style="min-width: 125px;">Required</th>
<th style="width: 1%;">삭제</th>
<th style="width: 1%;">??<3F><></th>
</tr>
</thead>
<tbody id="fieldEditorBody"></tbody>
</table>
</div>
<button type="button" class="btn-secondary-action" onclick="addFieldEditorRow()">+ 드 추</button>
<button type="button" class="btn-secondary-action" onclick="addFieldEditorRow()">+ ?<3F>드 추<EFBFBD>?</button>
<div class="mt-4">
<label class="form-label">JSON 미리보기</label>
<pre id="fieldEditorPreview" class="mb-0 p-3 rounded" style="background:#09090b; border:1px solid #27272a; color:#e4e4e7; font-size:0.78rem; max-height:190px; overflow:auto;"></pre>
@@ -1257,7 +1257,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn-secondary-action" data-bs-dismiss="modal" style="padding: 0.5rem 1rem; font-size: 0.875rem;">취소</button>
<button type="button" class="btn-action" onclick="applyFieldEditor()">JSON에 적</button>
<button type="button" class="btn-action" onclick="applyFieldEditor()">JSON???<3F></button>
</div>
</div>
</div>
@@ -1268,7 +1268,7 @@
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">하위 필드(itemFields) </h5>
<h5 class="modal-title">?<3F>위 ?<3F>드(itemFields) ?<3F></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -1289,7 +1289,7 @@
<tbody id="subFieldEditorBody"></tbody>
</table>
</div>
<button type="button" class="btn-action mt-2" onclick="addSubFieldEditorRow()">+ 하위 필드 추</button>
<button type="button" class="btn-action mt-2" onclick="addSubFieldEditorRow()">+ ?<3F>위 ?<3F>드 추<EFBFBD>?</button>
<div class="mt-4">
<label class="form-label">JSON 미리보기</label>
<pre id="subFieldEditorPreview" class="mb-0 p-3 rounded" style="background:#09090b; border:1px solid #27272a; color:#e4e4e7; font-size:0.78rem; max-height:190px; overflow:auto;"></pre>
@@ -1297,7 +1297,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn-secondary-action" data-bs-dismiss="modal">취소</button>
<button type="button" class="btn-action" onclick="applySubFieldEditor()"></button>
<button type="button" class="btn-action" onclick="applySubFieldEditor()">?<3F></button>
</div>
</div>
</div>
@@ -1382,13 +1382,13 @@
const button = dg('docgenGenerateBtn');
select.disabled = true;
button.disabled = true;
dg('docgenGrowToolHint').textContent = 'GatewayToolMetadata 목록을 조회하고 있습니다.';
dg('docgenGrowToolHint').textContent = 'Gateway??ToolMetadata 목록??조회?<3F>고 ?<3F>습?<3F>다.';
try {
const response = await fetch('/mcp/api/v1/tools/list', { cache: 'no-store' });
if (!response.ok) throw new Error(`Gateway HTTP ${response.status}`);
const payload = await response.json();
const tools = payload?.result?.tools;
if (!Array.isArray(tools)) throw new Error('ToolMetadata 응답 형식이 올바르지 않습니다.');
if (!Array.isArray(tools)) throw new Error('ToolMetadata ?<3F>답 ?<3F>식???<3F>바르<EBB094>? ?<3F>습?<3F>다.');
state.tools = tools.slice().sort((a, b) =>
(a.categoryKey || '').localeCompare(b.categoryKey || '') ||
(a.name || '').localeCompare(b.name || '')
@@ -1397,19 +1397,19 @@
`<option value="${index}">[${escapeHtml(tool.categoryKey || 'etc')}] ${escapeHtml(tool.name || tool.displayName || tool.uid)}</option>`
).join('');
select.disabled = state.tools.length === 0;
dg('docgenGrowToolHint').textContent = `ToolMetadata에 등록된 ${state.tools.length}Tool모두 표시합니다.`;
dg('docgenGrowToolHint').textContent = `ToolMetadata???<3F>록??${state.tools.length}<EFBFBD>?Tool??모두 ?<3F>시?<3F>니??`;
if (state.tools.length) {
select.value = '0';
selectTool();
} else {
showMetadataError('등록된 Tool이 없습니다.');
showMetadataError('?<3F>록??Tool???<3F>습?<3F>다.');
}
} catch (error) {
state.tools = [];
state.selected = null;
showMetadataError(error.message);
select.innerHTML = '<option>Tool Metadata 조회 패</option>';
dg('docgenGrowToolHint').innerHTML = `<span class="docgen-error">${escapeHtml(error.message)}</span> · Gateway 실행 상태를 확인하세요.`;
select.innerHTML = '<option>Tool Metadata 조회 ?<3F>패</option>';
dg('docgenGrowToolHint').innerHTML = `<span class="docgen-error">${escapeHtml(error.message)}</span> · Gateway ?<3F>행 ?<3F><EFBFBD>??<3F>인?<3F>세??`;
}
}
@@ -1420,7 +1420,7 @@
dg('docgenVersion').value = normalizeVersion(tool.semver || dg('docgenVersion').value || '1.0');
dg('docgenToolTitle').classList.remove('docgen-loading', 'docgen-error');
dg('docgenToolTitle').textContent = tool.displayName || tool.name;
dg('docgenToolDescription').textContent = tool.description || '설명이 등록되지 않은 Tool입니다.';
dg('docgenToolDescription').textContent = tool.description || '?<3F>명???<3F>록?<3F><>? ?<3F><>? Tool?<3F>니??';
dg('docgenCategoryTag').textContent = `Category · ${tool.categoryKey || '-'}`;
dg('docgenInterfaceTag').textContent = `Interface · ${tool.integrationType || '-'}`;
dg('docgenMetadataStatus').textContent = tool.enabled === false ? 'DISABLED' : 'READY';
@@ -1442,14 +1442,14 @@
const hasProgram = checks.program.checked || checks.process.checked || checks.revision.checked;
dg('docgenProgramFile').classList.toggle('docgen-disabled', !hasProgram);
dg('docgenInterfaceFile').classList.toggle('docgen-disabled', !checks.interface.checked);
dg('docgenProgramFileName').textContent = `${toolName}_로그램정의서_v${version}_${date}.xlsx`;
dg('docgenInterfaceFileName').textContent = `${toolName}_인터페이스정의서_v${version}_${date}.xlsx`;
dg('docgenProgramFileName').textContent = `${toolName}_?<3F>로그램?<3F>의??v${version}_${date}.xlsx`;
dg('docgenInterfaceFileName').textContent = `${toolName}_?<3F>터?<3F>이?<3F>정?<3F>서_v${version}_${date}.xlsx`;
dg('docgenGenerateBtn').disabled = !state.selected || (!hasProgram && !checks.interface.checked);
}
async function generateFiles() {
const tool = state.selected;
if (!tool) return notify('생성할 Tool을 선택하세요.');
if (!tool) return notify('?<3F>성??Tool???<3F>택?<3F>세??');
const button = dg('docgenGenerateBtn');
const version = safeVersion(dg('docgenVersion').value);
const date = yyyymmdd();
@@ -1468,13 +1468,13 @@
try {
if (hasProgram) {
await requestDownload('/mcp/api/v1/admin/documents/program', request,
`${toolName}_로그램정의서_v${version}_${date}.xlsx`);
`${toolName}_?<3F>로그램?<3F>의??v${version}_${date}.xlsx`);
}
if (checks.interface.checked) {
await requestDownload('/mcp/api/v1/admin/documents/interface', request,
`${toolName}_인터페이스정의서_v${version}_${date}.xlsx`);
`${toolName}_?<3F>터?<3F>이?<3F>정?<3F>서_v${version}_${date}.xlsx`);
}
notify('선택한 Excel 문서 운로드를 완료했습니다.');
notify('?<3F>택??Excel 문서 ?<3F>운로드<EFBFBD>??<3F>료?<3F>습?<3F>다.');
} catch (error) {
notify(error.message);
} finally {
@@ -1490,12 +1490,12 @@
body: JSON.stringify(payload)
});
if (!response.ok) {
let message = `문서 생성에 실패했습니다. (HTTP ${response.status})`;
let message = `문서 ?<3F>성???<3F>패?<3F>습?<3F>다. (HTTP ${response.status})`;
try {
const error = await response.json();
if (error?.error) message = error.error;
} catch (_) {
// JSON 오류 응답이 아닌 경우 기본 메시지를 사용합니다.
// JSON ?<3F>류 ?<3F>답???<3F>닌 경우 기본 메시지<EFBFBD>??<3F>용?<3F>니??
}
throw new Error(message);
}
@@ -1556,7 +1556,7 @@
const resultBox = document.getElementById('resultBox');
resultBox.style.display = 'block';
if (result.includes('[류]') || result.includes('류 발생:')) {
if (result.includes('[?<3F>류]') || result.includes('?<3F>류 발생:')) {
resultBox.className = 'error';
resultBox.textContent = result;
} else {
@@ -1622,16 +1622,16 @@
const form = document.getElementById('toolForm');
const data = Object.fromEntries(new FormData(form).entries());
if (!data.baseName) {
throw new Error('Base Name을 입력해주세요.');
throw new Error('Base Name???<3F>력?<3F>주?<3F>요.');
}
if (data.routingType === 'MCI' && (!data.interfaceId || !data.clientSystemCode)) {
throw new Error('MCI Tool Legacy Interface ID Target System Code를 입력해야 합니다.');
throw new Error('MCI Tool?<3F> Legacy Interface ID?<3F> Target System Code<EFBFBD>??<3F>력?<3F>야 ?<3F>니??');
}
if (data.routingType === 'HTTP' && !data.httpApiName) {
throw new Error('HTTP Tool HTTP API Name을 입력해야 합니다.');
throw new Error('HTTP Tool?<3F> HTTP API Name???<3F>력?<3F>야 ?<3F>니??');
}
if (!['MCI', 'HTTP'].includes(data.routingType)) {
throw new Error('러 Tool UseCaseMCI 는 HTTP 프로토콜만 지원합니다.');
throw new Error('?<3F>러 Tool UseCase??MCI ?<3F>는 HTTP ?<3F>로?<3F><EFBFBD>?지?<3F>합?<3F>다.');
}
return {
baseName: data.baseName,
@@ -1659,7 +1659,7 @@
try {
const tool = currentToolDefinition();
if (groupedTools.some(item => item.methodName === tool.methodName || item.baseName === tool.baseName)) {
throw new Error('같 Base Name 는 메서드명이 이미 묶음에 있습니다.');
throw new Error('같<EFBFBD>? Base Name ?<3F>는 메서?<3F>명???<3F><>? 묶음???<3F>습?<3F>다.');
}
groupedTools.push(tool);
renderToolGroupSummary();
@@ -1673,13 +1673,14 @@
const workspacePath = document.querySelector('#toolForm input[name="workspacePath"]')?.value || document.querySelector('input[name="workspacePath"]')?.value || '';
const categoryKey = document.querySelector('#toolForm [name="categoryKey"]').value.trim();
const select = document.getElementById('toolGroupUseCaseSelect');
select.innerHTML = '<option value="">UseCase 성</option>';
select.innerHTML = '<option value="">??UseCase ?<3F>성</option>';
select.innerHTML = '<option value="">??UseCase ?성</option>';
if (!/^[a-z0-9]{3}$/.test(categoryKey)) return;
try {
const response = await fetch(`/api/v1/scaffold/usecases?moduleName=${encodeURIComponent(moduleName)}&categoryKey=${encodeURIComponent(categoryKey)}&workspacePath=${encodeURIComponent(workspacePath)}`);
if (!response.ok) throw new Error('UseCase 목록 조회 패');
if (!response.ok) throw new Error('UseCase 목록 조회 ?패');
const useCases = await response.json();
useCases.forEach(useCaseName => select.add(new Option(`기존 ${useCaseName}에 함수 추가`, useCaseName)));
useCases.forEach(useCaseName => select.add(new Option(`기존 ${useCaseName} UseCase 추가`, useCaseName)));
} catch (error) {
console.warn(error);
}
@@ -1690,10 +1691,10 @@
const workspacePath = document.querySelector('#toolForm input[name="workspacePath"]')?.value || document.querySelector('input[name="workspacePath"]')?.value || '';
const select = document.getElementById('domainCategorySelect');
const nameInput = document.getElementById('domainCategoryName');
select.innerHTML = '<option value="">새 도메인 생성</option>';
select.innerHTML = '<option value="">???메???성</option>';
try {
const response = await fetch(`/api/v1/scaffold/categories?moduleName=${encodeURIComponent(moduleName)}&workspacePath=${encodeURIComponent(workspacePath)}`);
if (!response.ok) throw new Error('Category 목록 조회 패');
if (!response.ok) throw new Error('Category 목록 조회 ?패');
const categories = await response.json();
categories.forEach(cat => select.add(new Option(`기존 ${cat} 도메인`, cat)));
} catch (error) {
@@ -1731,8 +1732,8 @@
function renderToolGroupSummary() {
const summary = document.getElementById('toolGroupSummary');
summary.textContent = groupedTools.length
? `${groupedTools.length}Tool: ${groupedTools.map(tool => `${tool.baseName} ${tool.methodName}()`).join(', ')}`
: '묶음에 추가된 Tool 없음 — 일반 Generate Tool 기존 일 Tool 생성입니다.';
? `${groupedTools.length}<EFBFBD>?Tool: ${groupedTools.map(tool => `${tool.baseName} ??${tool.methodName}()`).join(', ')}`
: '묶음??추<>???Tool ?<3F>음 ???<3F>반 Generate Tool?<3F> 기존 ?<3F>일 Tool ?<3F>성?<3F>니??';
}
document.getElementById('toolForm').addEventListener('submit', function(e) {
@@ -1741,7 +1742,7 @@
e.preventDefault();
e.stopImmediatePropagation();
if (!/^[A-Z][A-Za-z0-9]*$/.test(useCaseName)) {
alert('UseCase 이름은 PascalCase로 입력해주세요. 예: Customer');
alert('UseCase ?<3F>름?<3F> PascalCase<EFBFBD>??<3F>력?<3F>주?<3F>요. ?? Customer');
return;
}
try {
@@ -1751,7 +1752,7 @@
const btn = this.querySelector('button[type="submit"]');
const originalText = btn.innerHTML;
btn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 처리중...';
btn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 처리<EFBFBD>?..';
btn.disabled = true;
fetch('/api/v1/scaffold/tool-group', {
@@ -1956,15 +1957,15 @@
fields = JSON.parse(textarea.value);
if (!Array.isArray(fields)) throw new Error('not an array');
} catch (error) {
alert('재 JSON 형식이 올바르지 않습니다. JSON을 수정한 후 다시 열어주세요.');
alert('?<3F>재 JSON ?<3F>식???<3F>바르<EBB094>? ?<3F>습?<3F>다. JSON???<3F>정?????<3F>시 ?<3F>어주세??');
return;
}
}
fieldEditorTargetId = targetId;
document.getElementById('fieldEditorModalLabel').textContent = targetId === 'inputFields'
? 'Input Fields 집'
: 'Output Fields 집';
? 'Input Fields ?<3F>집'
: 'Output Fields ?<3F>집';
const body = document.getElementById('fieldEditorBody');
body.replaceChildren();
(fields.length ? fields : [{}]).forEach(addFieldEditorRow);
@@ -2035,14 +2036,14 @@
itemFieldsContainer.className = 'd-flex gap-1';
const itemFieldsInput = makeInput(field.type === 'List' && field.itemType === 'Object'
? JSON.stringify(field.itemFields || []) : '', 'JSON 직접입력 또는 우측 편집 클릭');
? JSON.stringify(field.itemFields || []) : '', 'JSON 직접?<3F>력 ?<3F>는 ?<3F>측 ?<3F>집 ?<3F>릭');
itemFieldsInput.dataset.field = 'itemFields';
itemFieldsInput.style.flex = '1';
const editItemBtn = document.createElement('button');
editItemBtn.type = 'button';
editItemBtn.className = 'btn-secondary-action';
editItemBtn.textContent = '집';
editItemBtn.textContent = '?<3F>집';
editItemBtn.style.padding = '0.2rem 0.5rem';
editItemBtn.onclick = () => {
if (typeof openSubFieldEditor === 'function') openSubFieldEditor(itemFieldsInput);
@@ -2056,7 +2057,7 @@
itemFieldsInput.disabled = !objectList;
editItemBtn.disabled = !objectList;
itemFieldsInput.placeholder = objectList
? 'JSON 직접입력 또는 우측 편집 클릭'
? 'JSON 직접?<3F>력 ?<3F>는 ?<3F>측 ?<3F>집 ?<3F>릭'
: 'Select List > Object to enter item fields';
if (!objectList) itemFieldsInput.value = '';
};
@@ -2092,7 +2093,7 @@
const deleteButton = document.createElement('button');
deleteButton.type = 'button';
deleteButton.className = 'btn-secondary-action';
deleteButton.textContent = '삭제';
deleteButton.textContent = '??<3F><>';
deleteButton.addEventListener('click', () => {
row.remove();
updateFieldEditorPreview();
@@ -2150,12 +2151,12 @@
async function createNaturalFieldDraft() {
const source = document.getElementById('fieldNaturalLanguage').value.trim();
if (!source) {
alert('드 초안만들 문장을 입력해주세요.');
alert('?<3F>드 초안??만들 문장???<3F>력?<3F>주?<3F>요.');
return;
}
const button = document.getElementById('fieldDraftButton');
const originalLabel = button.textContent;
button.textContent = 'AI 생성 중...';
button.textContent = 'AI ?<3F><20>?..';
button.disabled = true;
try {
const response = await fetch('/api/v1/scaffold/field-draft', {
@@ -2164,16 +2165,16 @@
body: JSON.stringify({ description: source, target: fieldEditorTargetId, model: selectedAiModel() })
});
const result = await response.json();
if (!response.ok) throw new Error(result.error || 'AI 초안 생성에 실패했습니다.');
if (!response.ok) throw new Error(result.error || 'AI 초안 ?<3F>성???<3F>패?<3F>습?<3F>다.');
if (!Array.isArray(result.fields) || result.fields.length === 0) {
throw new Error('AI가 사용할 수 있는 필드를 만들지 못했습니다.');
throw new Error('AI가 ?<3F>용?????<3F>는 ?<3F><EFBFBD>?만들지 못했?<3F>니??');
}
const body = document.getElementById('fieldEditorBody');
body.replaceChildren();
result.fields.forEach(addFieldEditorRow);
updateFieldEditorPreview();
} catch (error) {
alert(`AI 초안 생성 실패: ${error.message}`);
alert(`AI 초안 ?<3F>성 ?<3F>패: ${error.message}`);
} finally {
button.textContent = originalLabel;
button.disabled = false;
@@ -2187,12 +2188,12 @@
async function createAiToolDraft() {
const source = document.getElementById('toolNaturalLanguage').value.trim();
if (!source) {
alert('AI가 만들 Tool 설명을 입력해주세요.');
alert('AI가 만들 Tool ?<3F>명???<3F>력?<3F>주?<3F>요.');
return;
}
const button = document.getElementById('toolDraftButton');
const originalLabel = button.textContent;
button.textContent = 'AI 생성 중...';
button.textContent = 'AI ?<3F><20>?..';
button.disabled = true;
try {
const response = await fetch('/api/v1/scaffold/tool-draft', {
@@ -2201,7 +2202,7 @@
body: JSON.stringify({ description: source, model: selectedAiModel() })
});
const result = await response.json();
if (!response.ok) throw new Error(result.error || 'AI Tool 초안 생성에 실패했습니다.');
if (!response.ok) throw new Error(result.error || 'AI Tool 초안 ?<3F>성???<3F>패?<3F>습?<3F>다.');
const form = document.getElementById('toolForm');
form.elements.baseName.value = result.baseName || '';
@@ -2225,9 +2226,9 @@
form.elements.ownerOrg.value = result.ownerOrg || 'MCP_TOOL';
document.getElementById('inputFields').value = JSON.stringify(result.inputFields || [], null, 2);
document.getElementById('outputFields').value = JSON.stringify(result.outputFields || [], null, 2);
alert('Tool 초안을 채웠습니다. Legacy Interface ID Client System Code는 실제 연계 명세를 확인해 입력해주세요.');
alert('Tool 초안??채웠?<3F>니?? Legacy Interface ID?<3F> Client System Code???<3F>제 ?<3F>계 명세<EBAA85>??<3F>인???<3F>력?<3F>주?<3F>요.');
} catch (error) {
alert(`AI Tool 초안 생성 실패: ${error.message}`);
alert(`AI Tool 초안 ?<3F>성 ?<3F>패: ${error.message}`);
} finally {
button.textContent = originalLabel;
button.disabled = false;
@@ -2240,11 +2241,11 @@
for (const field of fields) {
const name = field.name;
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) {
alert(`필드명 "${name}"은(는) Java 필드명 형식으로 입력해주세요.`);
alert(`?<3F><EFBFBD>?"${name}"?<3F>(?? Java ?<3F><EFBFBD>??<3F>식?<3F>로 ?<3F>력?<3F>주?<3F>요.`);
return;
}
if (names.has(name)) {
alert(`필드명 "${name}"이(가) 중복되었습니다.`);
alert(`?<3F><EFBFBD>?"${name}"??가) 중복?<3F>었?<3F>니??`);
return;
}
names.add(name);
@@ -2373,7 +2374,7 @@
})
.then(response => response.text())
.then(result => {
if (result.includes('[류]') || result.includes('류 발생:')) {
if (result.includes('[?<3F>류]') || result.includes('?<3F>류 발생:')) {
alert('Error updating source:\n' + result);
} else {
alert('Source code updated successfully.\nPlease rebuild the Pod container to reflect changes.');