openxgen 2.3.0 → 2.5.0

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.
package/dist/index.js CHANGED
@@ -894,23 +894,40 @@ var init_provider = __esm({
894
894
  // src/api/xgen-extra.ts
895
895
  var xgen_extra_exports = {};
896
896
  __export(xgen_extra_exports, {
897
+ apiTest: () => apiTest,
898
+ changePromptVersion: () => changePromptVersion,
899
+ createMcpSession: () => createMcpSession,
897
900
  createPrompt: () => createPrompt,
901
+ deleteMcpSession: () => deleteMcpSession,
902
+ deletePrompt: () => deletePrompt,
898
903
  generateWorkflow: () => generateWorkflow,
904
+ getMcpSession: () => getMcpSession,
905
+ getMcpSessionTools: () => getMcpSessionTools,
899
906
  getNodeCategories: () => getNodeCategories,
900
907
  getNodeDetail: () => getNodeDetail,
908
+ getNodeParameters: () => getNodeParameters,
909
+ getNodeTags: () => getNodeTags,
901
910
  getSchedulerStatus: () => getSchedulerStatus,
902
911
  getTraceDetail: () => getTraceDetail,
903
912
  getWorkflowPerformance: () => getWorkflowPerformance,
904
913
  listInteractions: () => listInteractions,
905
914
  listMcpSessions: () => listMcpSessions,
906
915
  listNodes: () => listNodes,
916
+ listPromptStore: () => listPromptStore,
917
+ listPromptVersions: () => listPromptVersions,
907
918
  listPrompts: () => listPrompts,
908
919
  listSchedules: () => listSchedules,
909
920
  listToolStore: () => listToolStore,
910
921
  listTraces: () => listTraces,
911
922
  listUserTools: () => listUserTools,
912
923
  listWorkflowStore: () => listWorkflowStore,
913
- searchNodes: () => searchNodes
924
+ saveTool: () => saveTool,
925
+ searchNodes: () => searchNodes,
926
+ sendMcpRequest: () => sendMcpRequest,
927
+ toolTest: () => toolTest,
928
+ updatePrompt: () => updatePrompt,
929
+ uploadPromptToStore: () => uploadPromptToStore,
930
+ uploadToolToStore: () => uploadToolToStore
914
931
  });
915
932
  async function listNodes() {
916
933
  const client2 = getClient();
@@ -939,11 +956,6 @@ async function listPrompts(opts) {
939
956
  const res = await client2.get("/api/prompt/list", { params });
940
957
  return res.data.prompts ?? res.data ?? [];
941
958
  }
942
- async function createPrompt(data) {
943
- const client2 = getClient();
944
- const res = await client2.post("/api/prompt/create", data);
945
- return res.data;
946
- }
947
959
  async function listToolStore() {
948
960
  const client2 = getClient();
949
961
  const res = await client2.get("/api/tools/store/list");
@@ -1000,11 +1012,108 @@ async function getWorkflowPerformance(workflowId, workflowName) {
1000
1012
  });
1001
1013
  return res.data;
1002
1014
  }
1015
+ async function saveTool(functionName, content) {
1016
+ const client2 = getClient();
1017
+ const res = await client2.post("/api/tools/storage/save", { function_name: functionName, content });
1018
+ return res.data;
1019
+ }
1020
+ async function apiTest(params) {
1021
+ const client2 = getClient();
1022
+ const res = await client2.post("/api/tools/storage/api-test", params);
1023
+ return res.data;
1024
+ }
1025
+ async function toolTest(toolId, functionId) {
1026
+ const client2 = getClient();
1027
+ const res = await client2.post(`/api/tools/storage/tool-test?tool_id=${toolId}&function_id=${functionId}`);
1028
+ return res.data;
1029
+ }
1030
+ async function uploadToolToStore(functionUploadId, description, tags) {
1031
+ const client2 = getClient();
1032
+ const res = await client2.post("/api/tools/store/upload", {
1033
+ function_upload_id: functionUploadId,
1034
+ description: description ?? "",
1035
+ tags: tags ?? []
1036
+ });
1037
+ return res.data;
1038
+ }
1039
+ async function createPrompt(data) {
1040
+ const client2 = getClient();
1041
+ const res = await client2.post("/api/prompt/create", data);
1042
+ return res.data;
1043
+ }
1044
+ async function updatePrompt(data) {
1045
+ const client2 = getClient();
1046
+ const res = await client2.post("/api/prompt/update", data);
1047
+ return res.data;
1048
+ }
1049
+ async function deletePrompt(promptUid) {
1050
+ const client2 = getClient();
1051
+ const res = await client2.post("/api/prompt/delete", { prompt_uid: promptUid });
1052
+ return res.data;
1053
+ }
1054
+ async function getNodeTags() {
1055
+ const client2 = getClient();
1056
+ const res = await client2.get("/api/node/tags");
1057
+ return res.data ?? [];
1058
+ }
1059
+ async function getNodeParameters(nodeId) {
1060
+ const client2 = getClient();
1061
+ const res = await client2.get(`/api/node/parameters/categorized/${nodeId}`);
1062
+ return res.data;
1063
+ }
1064
+ async function listPromptStore() {
1065
+ const client2 = getClient();
1066
+ const res = await client2.post("/api/prompt/store/list");
1067
+ return res.data.prompts ?? res.data ?? [];
1068
+ }
1069
+ async function uploadPromptToStore(promptId) {
1070
+ const client2 = getClient();
1071
+ const res = await client2.post(`/api/prompt/store/upload`, null, { params: { prompt_id: promptId } });
1072
+ return res.data;
1073
+ }
1074
+ async function listPromptVersions(promptUid) {
1075
+ const client2 = getClient();
1076
+ const res = await client2.get("/api/prompt/version/list", { params: { prompt_uid: promptUid } });
1077
+ return res.data.versions ?? res.data ?? [];
1078
+ }
1079
+ async function changePromptVersion(promptUid, version) {
1080
+ const client2 = getClient();
1081
+ const res = await client2.post("/api/prompt/version/change", { prompt_uid: promptUid, version });
1082
+ return res.data;
1083
+ }
1003
1084
  async function listMcpSessions() {
1004
1085
  const client2 = getClient();
1005
1086
  const res = await client2.get("/api/mcp/sessions");
1006
1087
  return res.data.sessions ?? res.data ?? [];
1007
1088
  }
1089
+ async function createMcpSession(data) {
1090
+ const client2 = getClient();
1091
+ const res = await client2.post("/api/mcp/sessions", data);
1092
+ return res.data;
1093
+ }
1094
+ async function getMcpSession(sessionId) {
1095
+ const client2 = getClient();
1096
+ const res = await client2.get(`/api/mcp/sessions/${sessionId}`);
1097
+ return res.data;
1098
+ }
1099
+ async function deleteMcpSession(sessionId) {
1100
+ const client2 = getClient();
1101
+ await client2.delete(`/api/mcp/sessions/${sessionId}`);
1102
+ }
1103
+ async function getMcpSessionTools(sessionId) {
1104
+ const client2 = getClient();
1105
+ const res = await client2.get(`/api/mcp/sessions/${sessionId}/tools`);
1106
+ return res.data.tools ?? res.data ?? [];
1107
+ }
1108
+ async function sendMcpRequest(sessionId, method, params) {
1109
+ const client2 = getClient();
1110
+ const res = await client2.post("/api/mcp/mcp-request", {
1111
+ session_id: sessionId,
1112
+ method,
1113
+ params
1114
+ });
1115
+ return res.data;
1116
+ }
1008
1117
  var init_xgen_extra = __esm({
1009
1118
  "src/api/xgen-extra.ts"() {
1010
1119
  "use strict";
@@ -1113,186 +1222,395 @@ var raw_tui_exports = {};
1113
1222
  __export(raw_tui_exports, {
1114
1223
  startRawTui: () => startRawTui
1115
1224
  });
1225
+ function visualLen(s) {
1226
+ return s.replace(/\x1b\[[0-9;]*m/g, "").length;
1227
+ }
1228
+ function padV(s, w) {
1229
+ return s + " ".repeat(Math.max(0, w - visualLen(s)));
1230
+ }
1231
+ function withTimeout(p, ms = 3e3, label = "") {
1232
+ return new Promise((res, rej) => {
1233
+ const t = setTimeout(() => rej(new Error(`${label} \uD0C0\uC784\uC544\uC6C3`)), ms);
1234
+ p.then((v) => {
1235
+ clearTimeout(t);
1236
+ res(v);
1237
+ }, (e) => {
1238
+ clearTimeout(t);
1239
+ rej(e);
1240
+ });
1241
+ });
1242
+ }
1116
1243
  async function startRawTui() {
1117
1244
  const provider = getDefaultProvider();
1118
1245
  const server = getServer();
1119
1246
  const auth = getAuth();
1120
- const serverDisplay = auth && server ? `${auth.username}@${server.replace("https://", "").replace("http://", "")}` : "\uBBF8\uC5F0\uACB0";
1247
+ const sv = auth && server ? `${auth.username}@${server.replace(/https?:\/\//, "")}` : "\uBBF8\uC5F0\uACB0";
1121
1248
  let tab = "workflows";
1122
- let selected = 0;
1123
- let inputMode = false;
1124
- let inputBuffer = "";
1125
- let runTarget = null;
1249
+ let sel = 0;
1250
+ let inputBuf = "";
1251
+ let inputActive = false;
1126
1252
  let workflows = [];
1127
1253
  let collections = [];
1128
1254
  let nodes = [];
1129
1255
  let prompts = [];
1130
1256
  let tools2 = [];
1131
- let detail = ["\u2190 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694"];
1257
+ let mcpSessions = [];
1258
+ let sandbox = ["\u2190 \uD56D\uBAA9 \uC120\uD0DD"];
1259
+ let chatLines = [dim("\uCC44\uD305: \uC9C8\uBB38\uC744 \uC785\uB825\uD558\uC138\uC694 (\uC120\uD0DD\uD55C \uC6CC\uD06C\uD50C\uB85C\uC6B0\uB85C \uC2E4\uD589)")];
1132
1260
  let statusMsg = "\uB85C\uB529...";
1133
1261
  async function loadData() {
1134
- statusMsg = "\uB85C\uB529...";
1262
+ statusMsg = "\uB85C\uB529 \uC911...";
1135
1263
  render();
1136
1264
  if (!server || !auth) {
1137
1265
  statusMsg = "\uC11C\uBC84 \uBBF8\uC5F0\uACB0";
1266
+ sandbox = [red("\uC11C\uBC84 \uBBF8\uC5F0\uACB0"), "", cyan(" xgen config set-server <URL>"), cyan(" xgen login")];
1138
1267
  render();
1139
1268
  return;
1140
1269
  }
1270
+ const errors = [];
1141
1271
  try {
1142
- const [wfMod, docMod, extraMod] = await Promise.all([
1272
+ const [wf, doc, ex] = await Promise.all([
1143
1273
  Promise.resolve().then(() => (init_workflow(), workflow_exports)),
1144
1274
  Promise.resolve().then(() => (init_document(), document_exports)),
1145
1275
  Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports))
1146
1276
  ]);
1147
- const [wfR, colR, nodeR, promptR, toolR] = await Promise.allSettled([
1148
- wfMod.getWorkflowListDetail(),
1149
- docMod.listCollections(),
1150
- extraMod.listNodes(),
1151
- extraMod.listPrompts(),
1152
- extraMod.listToolStore()
1277
+ const r = await Promise.allSettled([
1278
+ withTimeout(wf.getWorkflowListDetail(), 3e3, "\uC6CC\uD06C\uD50C\uB85C\uC6B0"),
1279
+ withTimeout(doc.listCollections(), 3e3, "\uCEEC\uB809\uC158"),
1280
+ withTimeout(ex.listNodes(), 3e3, "\uB178\uB4DC"),
1281
+ withTimeout(ex.listPrompts(), 3e3, "\uD504\uB86C\uD504\uD2B8"),
1282
+ withTimeout(ex.listToolStore(), 3e3, "\uB3C4\uAD6C"),
1283
+ withTimeout(ex.listMcpSessions(), 3e3, "MCP").catch(() => [])
1153
1284
  ]);
1154
- if (wfR.status === "fulfilled") workflows = wfR.value.map((w) => ({ name: w.workflow_name, id: w.workflow_id ?? w.id ?? "", deployed: !!w.is_deployed }));
1155
- if (colR.status === "fulfilled") collections = colR.value.map((c) => ({ name: c.collection_make_name, docs: c.total_documents, chunks: c.total_chunks }));
1156
- if (nodeR.status === "fulfilled") nodes = nodeR.value.map((n) => ({ name: n.nodeName ?? n.name ?? "?", desc: (n.description ?? "").slice(0, 40) }));
1157
- if (promptR.status === "fulfilled") prompts = promptR.value.map((p) => ({ name: p.name ?? p.title ?? "?", type: p.prompt_type ?? "" }));
1158
- if (toolR.status === "fulfilled") tools2 = toolR.value.map((t) => ({ name: t.name ?? t.tool_name ?? "?", desc: (t.description ?? "").slice(0, 40) }));
1159
- } catch {
1285
+ if (r[0].status === "fulfilled") workflows = r[0].value.map((w) => ({
1286
+ name: w.workflow_name ?? w.name ?? "?",
1287
+ id: w.workflow_id ?? w.id ?? "",
1288
+ deployed: w.deploy_status === "deployed" || w.deploy_status === "DEPLOYED" || !!w.is_deployed
1289
+ }));
1290
+ else errors.push(`\uC6CC\uD06C\uD50C\uB85C\uC6B0: ${r[0].reason?.message ?? "\uC2E4\uD328"}`);
1291
+ if (r[1].status === "fulfilled") collections = r[1].value.map((c) => ({
1292
+ name: c.collection_make_name ?? c.collection_name ?? "?",
1293
+ id: c.id ?? c.collection_id ?? "",
1294
+ docs: c.total_documents ?? 0,
1295
+ chunks: c.total_chunks ?? 0
1296
+ }));
1297
+ else errors.push(`\uCEEC\uB809\uC158: ${r[1].reason?.message ?? "\uC2E4\uD328"}`);
1298
+ if (r[2].status === "fulfilled") {
1299
+ nodes = [];
1300
+ for (const cat of r[2].value) {
1301
+ const catName = cat.categoryName ?? "";
1302
+ for (const fn of cat.functions ?? []) {
1303
+ for (const n of fn.nodes ?? []) {
1304
+ nodes.push({ name: n.nodeName ?? n.name ?? "?", desc: (n.description ?? "").slice(0, 40), id: n.id, category: `${catName}/${fn.functionName ?? ""}` });
1305
+ }
1306
+ }
1307
+ }
1308
+ } else errors.push(`\uB178\uB4DC: ${r[2].reason?.message ?? "\uC2E4\uD328"}`);
1309
+ if (r[3].status === "fulfilled") prompts = r[3].value.map((p) => ({
1310
+ name: p.prompt_title ?? p.name ?? "?",
1311
+ type: p.prompt_type ?? "",
1312
+ uid: p.prompt_uid,
1313
+ content: p.prompt_content,
1314
+ dbId: p.id
1315
+ }));
1316
+ else errors.push(`\uD504\uB86C\uD504\uD2B8: ${r[3].reason?.message ?? "\uC2E4\uD328"}`);
1317
+ if (r[4].status === "fulfilled") tools2 = r[4].value.map((t) => {
1318
+ const fd = t.function_data ?? {};
1319
+ return {
1320
+ name: fd.function_name ?? t.function_name ?? "?",
1321
+ desc: (fd.description ?? t.description ?? "").slice(0, 40),
1322
+ id: t.id,
1323
+ fid: fd.function_id ?? t.function_upload_id,
1324
+ url: fd.api_url,
1325
+ status: fd.status ?? t.status
1326
+ };
1327
+ });
1328
+ else errors.push(`\uB3C4\uAD6C: ${r[4].reason?.message ?? "\uC2E4\uD328"}`);
1329
+ if (r[5].status === "fulfilled" && Array.isArray(r[5].value)) mcpSessions = r[5].value.map((s) => ({
1330
+ id: s.session_id ?? "",
1331
+ name: s.session_name ?? "?",
1332
+ type: s.server_type ?? "?",
1333
+ status: s.status ?? "?"
1334
+ }));
1335
+ } catch (e) {
1336
+ errors.push(e.message);
1160
1337
  }
1161
- statusMsg = "\u2191\u2193:\uC774\uB3D9 Enter:\uC120\uD0DD/\uC2E4\uD589 1-5:\uD0ED i:\uC785\uB825 r:\uC0C8\uB85C\uACE0\uCE68 q:\uC885\uB8CC";
1338
+ sel = 0;
1339
+ updateSandbox();
1340
+ statusMsg = errors.length > 0 ? `\u26A0 ${errors.length}\uAC1C \uC624\uB958 \u2502 r \uC7AC\uC2DC\uB3C4` : getHint();
1341
+ if (errors.length > 0) sandbox = [red("\uC624\uB958:"), ...errors.map((e) => yellow(` \u2022 ${e}`))];
1162
1342
  render();
1163
1343
  }
1344
+ function getHint() {
1345
+ const h = {
1346
+ workflows: "\u2191\u2193 \uC774\uB3D9 \u2502 Enter \uAD6C\uC870 \u2502 \uC9C8\uBB38 \uC785\uB825\u2192\uC2E4\uD589 \u2502 1-6 \uD0ED \u2502 r \u2502 q",
1347
+ collections: "\u2191\u2193 \u2502 Enter \uBB38\uC11C \u2502 1-6 \uD0ED \u2502 q",
1348
+ nodes: "\u2191\u2193 \u2502 Enter \uC0C1\uC138 \u2502 1-6 \uD0ED \u2502 q",
1349
+ prompts: "\u2191\u2193 \u2502 Enter \uB0B4\uC6A9 \u2502 c\uC0DD\uC131 e\uC218\uC815 d\uC0AD\uC81C \u2502 q",
1350
+ tools: "\u2191\u2193 \u2502 Enter \uD14C\uC2A4\uD2B8 \u2502 c\uC0DD\uC131 u\uC2A4\uD1A0\uC5B4 \u2502 q",
1351
+ mcp: "\u2191\u2193 \u2502 Enter \uB3C4\uAD6C \u2502 c\uC0DD\uC131 t\uD638\uCD9C d\uC0AD\uC81C \u2502 q"
1352
+ };
1353
+ return h[tab];
1354
+ }
1164
1355
  function getItems() {
1165
1356
  switch (tab) {
1166
1357
  case "workflows":
1167
- return workflows.map((w) => ({ label: `${w.deployed ? "\u25CF" : "\u25CB"} ${w.name}`, sub: w.id.slice(0, 20) }));
1358
+ return workflows.map((w) => ({ label: `${w.deployed ? green("\u25CF") : dim("\u25CB")} ${w.name}`, sub: w.id.slice(0, 12) }));
1168
1359
  case "collections":
1169
- return collections.map((c) => ({ label: c.name, sub: `${c.docs}\uBB38\uC11C ${c.chunks}\uCCAD\uD06C` }));
1360
+ return collections.map((c) => ({ label: c.name, sub: `${c.docs}\uBB38\uC11C` }));
1170
1361
  case "nodes":
1171
- return nodes.map((n) => ({ label: n.name, sub: n.desc }));
1362
+ return nodes.map((n) => ({ label: n.name, sub: n.category }));
1172
1363
  case "prompts":
1173
1364
  return prompts.map((p) => ({ label: p.name, sub: `[${p.type}]` }));
1174
1365
  case "tools":
1175
- return tools2.map((t) => ({ label: t.name, sub: t.desc }));
1366
+ return tools2.map((t) => ({ label: `${t.status === "active" ? green("\u25CF") : dim("\u25CB")} ${t.name}`, sub: t.desc }));
1367
+ case "mcp":
1368
+ return mcpSessions.map((s) => ({ label: `${s.status === "running" ? green("\u25CF") : dim("\u25CB")} ${s.name}`, sub: s.type }));
1176
1369
  }
1177
1370
  }
1178
- function updateDetail() {
1371
+ function updateSandbox() {
1179
1372
  const items = getItems();
1180
- if (selected < 0 || selected >= items.length) return;
1181
- if (tab === "workflows" && workflows[selected]) {
1182
- const w = workflows[selected];
1183
- detail = [bold(w.name), "", `ID ${w.id}`, `\uBC30\uD3EC ${w.deployed ? "Yes" : "No"}`, "", "Enter \u2192 \uC2E4\uD589"];
1184
- } else if (tab === "collections" && collections[selected]) {
1185
- const c = collections[selected];
1186
- detail = [bold(c.name), "", `\uBB38\uC11C ${c.docs}\uAC1C`, `\uCCAD\uD06C ${c.chunks}\uAC1C`];
1187
- } else if (tab === "nodes" && nodes[selected]) {
1188
- const n = nodes[selected];
1189
- detail = [bold(n.name), "", n.desc];
1190
- } else if (tab === "prompts" && prompts[selected]) {
1191
- const p = prompts[selected];
1192
- detail = [bold(p.name), `[${p.type}]`];
1193
- } else if (tab === "tools" && tools2[selected]) {
1194
- const t = tools2[selected];
1195
- detail = [bold(t.name), "", t.desc];
1373
+ if (items.length === 0) {
1374
+ sandbox = [dim("\uB370\uC774\uD130 \uC5C6\uC74C")];
1375
+ return;
1376
+ }
1377
+ if (sel < 0) sel = 0;
1378
+ if (sel >= items.length) sel = items.length - 1;
1379
+ if (tab === "workflows" && workflows[sel]) {
1380
+ const w = workflows[sel];
1381
+ sandbox = [
1382
+ bold(w.name),
1383
+ `ID: ${w.id}`,
1384
+ `\uBC30\uD3EC: ${w.deployed ? green("Yes") : red("No")}`,
1385
+ "",
1386
+ cyan("Enter") + " \uB178\uB4DC\uAD6C\uC870",
1387
+ dim("\uC9C8\uBB38 \uC785\uB825 \u2192 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC2E4\uD589")
1388
+ ];
1389
+ } else if (tab === "collections" && collections[sel]) {
1390
+ const c = collections[sel];
1391
+ sandbox = [bold(c.name), `\uBB38\uC11C ${c.docs}\uAC1C \u2502 \uCCAD\uD06C ${c.chunks}\uAC1C`, "", cyan("Enter") + " \uBB38\uC11C \uBAA9\uB85D"];
1392
+ } else if (tab === "nodes" && nodes[sel]) {
1393
+ const n = nodes[sel];
1394
+ sandbox = [bold(n.name), `\uCE74\uD14C\uACE0\uB9AC: ${n.category ?? "?"}`, n.desc, "", cyan("Enter") + " \uD30C\uB77C\uBBF8\uD130 \uC0C1\uC138"];
1395
+ } else if (tab === "prompts" && prompts[sel]) {
1396
+ const p = prompts[sel];
1397
+ sandbox = [bold(p.name), `\uD0C0\uC785: ${p.type}`, "", ...p.content?.split("\n").slice(0, 8) ?? [dim("\uB0B4\uC6A9 \uC5C6\uC74C")]];
1398
+ } else if (tab === "tools" && tools2[sel]) {
1399
+ const t = tools2[sel];
1400
+ sandbox = [bold(t.name), `URL: ${t.url ?? dim("\uC5C6\uC74C")}`, `\uC0C1\uD0DC: ${t.status ?? "?"}`, "", cyan("Enter") + " API \uD14C\uC2A4\uD2B8"];
1401
+ } else if (tab === "mcp" && mcpSessions[sel]) {
1402
+ const s = mcpSessions[sel];
1403
+ sandbox = [bold(s.name), `\uC138\uC158: ${s.id}`, `\uD0C0\uC785: ${s.type}`, `\uC0C1\uD0DC: ${s.status}`, "", cyan("Enter") + " \uB3C4\uAD6C \uBAA9\uB85D"];
1196
1404
  }
1197
1405
  }
1198
1406
  function render() {
1199
1407
  const cols = process.stdout.columns || 120;
1200
1408
  const rows = process.stdout.rows || 30;
1201
- const leftW = Math.floor(cols / 2);
1202
- const rightW = cols - leftW;
1203
- const listH = rows - 5;
1409
+ const leftW = Math.floor(cols * 0.35);
1410
+ const rightW = cols - leftW - 1;
1411
+ const topH = Math.floor((rows - 4) * 0.55);
1412
+ const botH = rows - 4 - topH;
1204
1413
  clear();
1205
1414
  moveTo(1, 1);
1206
- const tabStr = TABS.map((t) => {
1207
- const active = tab === t.key;
1208
- return `[${t.num}]${active ? bold("\u25B8" + t.label) : dim(t.label)}`;
1209
- }).join(" ");
1210
- process.stdout.write(` ${bold("OPEN XGEN")} ${dim(provider?.model ?? "")} ${serverDisplay} \u2502 ${tabStr}`);
1415
+ const tabStr = TABS.map((t) => tab === t.key ? inverse(` ${t.num}:${t.label} `) : dim(` ${t.num}:${t.label} `)).join("");
1416
+ process.stdout.write(` ${bold(cyan("OPEN XGEN"))} ${dim("v3")} ${dim(sv)} \u2502 ${tabStr}`);
1211
1417
  moveTo(2, 1);
1212
- process.stdout.write(dim("\u2500".repeat(cols)));
1418
+ process.stdout.write(dim("\u2500".repeat(leftW) + "\u252C" + "\u2500".repeat(rightW)));
1213
1419
  const items = getItems();
1214
- const visibleCount = listH;
1215
- const scrollStart = Math.max(0, Math.min(selected - Math.floor(visibleCount / 2), items.length - visibleCount));
1216
- for (let i = 0; i < visibleCount; i++) {
1420
+ const listH = topH;
1421
+ const scr = items.length <= listH ? 0 : Math.max(0, Math.min(sel - Math.floor(listH / 2), items.length - listH));
1422
+ for (let i = 0; i < listH; i++) {
1217
1423
  moveTo(3 + i, 1);
1218
- const idx = scrollStart + i;
1424
+ const idx = scr + i;
1425
+ let left;
1219
1426
  if (idx < items.length) {
1220
1427
  const item = items[idx];
1221
- const prefix = idx === selected ? "\u25B8 " : " ";
1222
- const line = `${prefix}${String(idx + 1).padStart(2)}. ${item.label}`;
1223
- const trimmed = line.slice(0, leftW - 2);
1224
- process.stdout.write(idx === selected ? inverse(trimmed.padEnd(leftW - 1)) : trimmed);
1428
+ const sub = item.sub ? dim(` ${item.sub}`) : "";
1429
+ left = padV(`${idx === sel ? "\u25B8" : " "} ${String(idx + 1).padStart(2)}. ${item.label}${sub}`, leftW);
1430
+ if (idx === sel) left = inverse(left);
1225
1431
  } else {
1226
- process.stdout.write(" ".repeat(leftW - 1));
1432
+ left = " ".repeat(leftW);
1227
1433
  }
1434
+ process.stdout.write(left);
1228
1435
  process.stdout.write(dim("\u2502"));
1229
- if (i < detail.length) {
1230
- const dline = detail[i].slice(0, rightW - 2);
1231
- process.stdout.write(` ${dline}`);
1436
+ if (i < sandbox.length) process.stdout.write(` ${sandbox[i]}`);
1437
+ }
1438
+ moveTo(3 + topH, 1);
1439
+ process.stdout.write(dim("\u2500".repeat(leftW) + "\u2534" + "\u2500".repeat(rightW)));
1440
+ const chatStart = 4 + topH;
1441
+ const chatDisplayH = botH - 2;
1442
+ const chatOffset = Math.max(0, chatLines.length - chatDisplayH);
1443
+ for (let i = 0; i < chatDisplayH; i++) {
1444
+ moveTo(chatStart + i, 1);
1445
+ const lineIdx = chatOffset + i;
1446
+ if (lineIdx < chatLines.length) {
1447
+ process.stdout.write(` ${chatLines[lineIdx]}`.slice(0, cols));
1232
1448
  }
1233
1449
  }
1234
- moveTo(rows - 2, 1);
1235
- process.stdout.write(dim("\u2500".repeat(cols)));
1236
1450
  moveTo(rows - 1, 1);
1237
- if (inputMode) {
1238
- const prompt2 = runTarget ? `${runTarget.name} \u276F ` : "\u276F ";
1239
- process.stdout.write(` ${prompt2}${inputBuffer}`);
1451
+ if (inputActive) {
1452
+ const ctx = tab === "workflows" && workflows[sel] ? `${workflows[sel].name} ` : "";
1453
+ process.stdout.write(` ${cyan(ctx + "\u276F")} ${inputBuf}`);
1240
1454
  showCursor();
1241
1455
  } else {
1242
- process.stdout.write(dim(" \u276F i\uB97C \uB20C\uB7EC \uC785\uB825 \xB7 Enter\uB85C \uC2E4\uD589"));
1456
+ process.stdout.write(dim(` ${getHint()}`));
1243
1457
  hideCursor();
1244
1458
  }
1245
1459
  moveTo(rows, 1);
1246
1460
  process.stdout.write(dim(` ${statusMsg}`.slice(0, cols)));
1247
1461
  }
1248
- async function executeWorkflowAction(wf, input) {
1249
- detail = [`\uC2E4\uD589 \uC911: ${wf.name}`, `\uC785\uB825: ${input}`, "", "..."];
1462
+ function addChat(line) {
1463
+ chatLines.push(line);
1464
+ if (chatLines.length > 200) chatLines = chatLines.slice(-100);
1465
+ }
1466
+ async function runWorkflow(wf, input) {
1467
+ addChat(`${cyan("\u276F")} ${input}`);
1468
+ addChat(yellow(` ${wf.name} \uC2E4\uD589 \uC911...`));
1469
+ sandbox = [bold(wf.name), "", yellow("\uC2E4\uD589 \uC911...")];
1250
1470
  render();
1251
1471
  try {
1252
1472
  const { executeWorkflow: executeWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1253
1473
  const { randomUUID: randomUUID4 } = await import("crypto");
1254
- const r = await executeWorkflow2({
1474
+ const r = await withTimeout(executeWorkflow2({
1255
1475
  workflow_id: wf.id,
1256
1476
  workflow_name: wf.name,
1257
1477
  input_data: input,
1258
1478
  interaction_id: `tui_${randomUUID4().slice(0, 8)}`,
1259
1479
  user_id: auth?.userId ? parseInt(auth.userId) : 1
1260
- });
1261
- const content = r.content ?? r.message ?? JSON.stringify(r).slice(0, 500);
1262
- detail = [bold(wf.name), "", `\uC785\uB825: ${input}`, "", "\uACB0\uACFC:", ...String(content).split("\n").slice(0, 15)];
1480
+ }), 3e4, "\uC2E4\uD589");
1481
+ const content = String(r?.content ?? r?.message ?? r?.result ?? JSON.stringify(r).slice(0, 500));
1482
+ const lines = content.split("\n");
1483
+ addChat(green(" \uACB0\uACFC:"));
1484
+ for (const l of lines.slice(0, 20)) addChat(` ${l}`);
1485
+ addChat("");
1486
+ sandbox = [bold(wf.name), "", green("\uC644\uB8CC"), "", ...lines.slice(0, 10)];
1487
+ } catch (err) {
1488
+ addChat(red(` \uC2E4\uD328: ${err.message}`));
1489
+ sandbox = [bold(wf.name), "", red(`\uC2E4\uD328: ${err.message}`)];
1490
+ }
1491
+ statusMsg = getHint();
1492
+ render();
1493
+ }
1494
+ async function loadWorkflowStructure(wf) {
1495
+ sandbox = [bold(wf.name), "", yellow("\uB178\uB4DC/\uC5E3\uC9C0 \uB85C\uB529...")];
1496
+ render();
1497
+ try {
1498
+ const { getWorkflowDetail: getWorkflowDetail2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1499
+ const data = await withTimeout(getWorkflowDetail2(wf.id), 8e3, "\uB85C\uB4DC");
1500
+ const wd = data?.workflow_data ?? data;
1501
+ const ns = wd?.nodes ?? data?.nodes ?? [];
1502
+ const es = wd?.edges ?? data?.edges ?? [];
1503
+ sandbox = [
1504
+ bold(wf.name),
1505
+ `\uB178\uB4DC ${ns.length}\uAC1C \uC5E3\uC9C0 ${es.length}\uAC1C`,
1506
+ "",
1507
+ ...ns.slice(0, 12).map((n, i) => ` ${i + 1}. ${n.data?.label ?? n.data?.nodeName ?? n.type ?? "?"} ${dim(n.data?.nodeId ?? "")}`),
1508
+ ...ns.length > 12 ? [dim(` +${ns.length - 12}\uAC1C`)] : [],
1509
+ "",
1510
+ bold("\uC5F0\uACB0:"),
1511
+ ...es.slice(0, 6).map((e) => {
1512
+ const s = ns.find((n) => n.id === e.source);
1513
+ const t = ns.find((n) => n.id === e.target);
1514
+ return ` ${s?.data?.label ?? "?"} \u2192 ${t?.data?.label ?? "?"}`;
1515
+ })
1516
+ ];
1517
+ } catch (err) {
1518
+ sandbox = [bold(wf.name), "", red(err.message)];
1519
+ }
1520
+ render();
1521
+ }
1522
+ async function loadNodeDetail(n) {
1523
+ if (!n.id) {
1524
+ sandbox = [red("\uB178\uB4DC ID \uC5C6\uC74C")];
1525
+ render();
1526
+ return;
1527
+ }
1528
+ sandbox = [bold(n.name), yellow("\uB85C\uB529...")];
1529
+ render();
1530
+ try {
1531
+ const { getNodeDetail: getNodeDetail2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1532
+ const d = await withTimeout(getNodeDetail2(n.id), 5e3, "\uB178\uB4DC");
1533
+ const ins = d?.inputs ?? d?.inputPorts ?? [];
1534
+ const outs = d?.outputs ?? d?.outputPorts ?? [];
1535
+ const params = d?.parameters ?? d?.defaultParams ?? {};
1536
+ const pkeys = typeof params === "object" ? Object.keys(params) : [];
1537
+ sandbox = [
1538
+ bold(d?.nodeName ?? n.name),
1539
+ `ID: ${d?.nodeId ?? n.id}`,
1540
+ `\uCE74\uD14C\uACE0\uB9AC: ${d?.category ?? "?"}`,
1541
+ "",
1542
+ bold(`\uC785\uB825(${ins.length}):`),
1543
+ ...ins.slice(0, 4).map((p) => ` ${p.name ?? "?"} ${dim(p.type ?? "")}`),
1544
+ bold(`\uCD9C\uB825(${outs.length}):`),
1545
+ ...outs.slice(0, 4).map((p) => ` ${p.name ?? "?"} ${dim(p.type ?? "")}`),
1546
+ bold(`\uD30C\uB77C\uBBF8\uD130(${pkeys.length}):`),
1547
+ ...pkeys.slice(0, 6).map((k) => ` ${k}: ${dim(String(params[k]).slice(0, 25))}`)
1548
+ ];
1549
+ } catch (err) {
1550
+ sandbox = [red(err.message)];
1551
+ }
1552
+ render();
1553
+ }
1554
+ async function testTool(t) {
1555
+ if (!t.url) {
1556
+ sandbox = [red("URL \uC5C6\uC74C")];
1557
+ render();
1558
+ return;
1559
+ }
1560
+ sandbox = [bold(t.name), yellow(`\uD14C\uC2A4\uD2B8: ${t.url}`)];
1561
+ addChat(yellow(` \uB3C4\uAD6C \uD14C\uC2A4\uD2B8: ${t.name}`));
1562
+ render();
1563
+ try {
1564
+ const { apiTest: apiTest2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1565
+ const r = await withTimeout(apiTest2({ api_url: t.url, api_method: "GET", api_timeout: 10 }), 15e3, "\uD14C\uC2A4\uD2B8");
1566
+ const ok = r?.success ?? (r?.data?.status && r.data.status < 400);
1567
+ const st = r?.data?.status ?? "?";
1568
+ const resp = r?.data?.response;
1569
+ const lines = (typeof resp === "string" ? resp : JSON.stringify(resp ?? {}, null, 2)).split("\n").slice(0, 10);
1570
+ sandbox = [bold(t.name), ok ? green(`\u2713 ${st} OK`) : red(`\u2717 ${st} FAIL`), "", ...lines];
1571
+ addChat(ok ? green(` \u2713 ${t.name} ${st} OK`) : red(` \u2717 ${t.name} ${st} FAIL`));
1263
1572
  } catch (err) {
1264
- detail = [`\uC2E4\uD589 \uC2E4\uD328: ${err.message}`];
1573
+ sandbox = [red(err.message)];
1574
+ addChat(red(` \uC2E4\uD328: ${err.message}`));
1265
1575
  }
1266
1576
  render();
1267
1577
  }
1268
- function handleKey(data) {
1269
- const s = data.toString();
1270
- if (inputMode) {
1271
- if (s === "\x1B" || s === "\x1B\x1B") {
1272
- inputMode = false;
1273
- inputBuffer = "";
1274
- runTarget = null;
1578
+ function handleKey(s) {
1579
+ if (inputActive) {
1580
+ if (s === "\x1B") {
1581
+ inputActive = false;
1582
+ inputBuf = "";
1275
1583
  render();
1276
1584
  return;
1277
1585
  }
1278
1586
  if (s === "\r" || s === "\n") {
1279
- const val = inputBuffer.trim();
1280
- inputMode = false;
1281
- inputBuffer = "";
1282
- if (val && runTarget) {
1283
- executeWorkflowAction(runTarget, val);
1284
- runTarget = null;
1587
+ const val = inputBuf.trim();
1588
+ inputActive = false;
1589
+ inputBuf = "";
1590
+ if (val && tab === "workflows" && workflows[sel]) {
1591
+ runWorkflow(workflows[sel], val);
1592
+ } else if (val) {
1593
+ addChat(`${cyan("\u276F")} ${val}`);
1594
+ addChat(dim(" \uC6CC\uD06C\uD50C\uB85C\uC6B0\uB97C \uC120\uD0DD\uD55C \uD6C4 \uC9C8\uBB38\uC744 \uC785\uB825\uD558\uC138\uC694."));
1595
+ render();
1596
+ } else {
1597
+ render();
1285
1598
  }
1286
- render();
1287
1599
  return;
1288
1600
  }
1289
1601
  if (s === "\x7F" || s === "\b") {
1290
- inputBuffer = inputBuffer.slice(0, -1);
1602
+ inputBuf = inputBuf.slice(0, -1);
1603
+ render();
1604
+ return;
1605
+ }
1606
+ if (s === "") {
1607
+ inputActive = false;
1608
+ inputBuf = "";
1291
1609
  render();
1292
1610
  return;
1293
1611
  }
1294
- if (s.length > 0 && s.charCodeAt(0) >= 32) {
1295
- inputBuffer += s;
1612
+ if (s.charCodeAt(0) >= 32) {
1613
+ inputBuf += s;
1296
1614
  render();
1297
1615
  return;
1298
1616
  }
@@ -1302,67 +1620,173 @@ async function startRawTui() {
1302
1620
  cleanup();
1303
1621
  process.exit(0);
1304
1622
  }
1305
- if (s === "r") {
1623
+ if (s === "r" || s === "R") {
1306
1624
  loadData();
1307
1625
  return;
1308
1626
  }
1309
- if (s === "i") {
1310
- inputMode = true;
1627
+ if (s.length === 1 && s.charCodeAt(0) >= 32 && !/^[0-9cutedvq]$/i.test(s)) {
1628
+ inputActive = true;
1629
+ inputBuf = s;
1311
1630
  render();
1312
1631
  return;
1313
1632
  }
1314
- const tabNum = parseInt(s);
1315
- if (tabNum >= 1 && tabNum <= 5) {
1316
- tab = TABS[tabNum - 1].key;
1317
- selected = 0;
1318
- updateDetail();
1633
+ if (/^[cC]$/.test(s)) {
1634
+ if (tab === "tools") {
1635
+ addChat(cyan(" \uB3C4\uAD6C \uC0DD\uC131\uC740 xgen agent\uC5D0\uC11C: '\uC0C8 \uB3C4\uAD6C \uB9CC\uB4E4\uC5B4\uC918'"));
1636
+ render();
1637
+ return;
1638
+ }
1639
+ if (tab === "prompts") {
1640
+ addChat(cyan(" \uD504\uB86C\uD504\uD2B8 \uC0DD\uC131\uC740 xgen agent\uC5D0\uC11C: '\uD504\uB86C\uD504\uD2B8 \uB9CC\uB4E4\uC5B4\uC918'"));
1641
+ render();
1642
+ return;
1643
+ }
1644
+ if (tab === "mcp") {
1645
+ addChat(cyan(" MCP \uC138\uC158 \uC0DD\uC131\uC740 xgen agent\uC5D0\uC11C: 'MCP \uC138\uC158 \uB9CC\uB4E4\uC5B4\uC918'"));
1646
+ render();
1647
+ return;
1648
+ }
1649
+ }
1650
+ if (/^[tT]$/.test(s) && tab === "tools" && tools2[sel]) {
1651
+ testTool(tools2[sel]);
1652
+ return;
1653
+ }
1654
+ if (/^[dD]$/.test(s)) {
1655
+ if (tab === "prompts" && prompts[sel]?.uid) {
1656
+ sandbox = [yellow("\uC0AD\uC81C \uC911...")];
1657
+ render();
1658
+ Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports)).then(async (m) => {
1659
+ try {
1660
+ await m.deletePrompt(prompts[sel].uid);
1661
+ sandbox = [green("\uC0AD\uC81C \uC644\uB8CC")];
1662
+ addChat(green(" \uD504\uB86C\uD504\uD2B8 \uC0AD\uC81C\uB428"));
1663
+ } catch (e) {
1664
+ sandbox = [red(e.message)];
1665
+ }
1666
+ render();
1667
+ });
1668
+ return;
1669
+ }
1670
+ if (tab === "mcp" && mcpSessions[sel]) {
1671
+ const sid = mcpSessions[sel].id;
1672
+ sandbox = [yellow("\uC0AD\uC81C \uC911...")];
1673
+ render();
1674
+ Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports)).then(async (m) => {
1675
+ try {
1676
+ await m.deleteMcpSession(sid);
1677
+ sandbox = [green("\uC0AD\uC81C \uC644\uB8CC")];
1678
+ } catch (e) {
1679
+ sandbox = [red(e.message)];
1680
+ }
1681
+ render();
1682
+ });
1683
+ return;
1684
+ }
1685
+ }
1686
+ if (/^[uU]$/.test(s) && tab === "tools" && tools2[sel]?.fid) {
1687
+ sandbox = [yellow("\uC2A4\uD1A0\uC5B4 \uB4F1\uB85D \uC911...")];
1688
+ render();
1689
+ Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports)).then(async (m) => {
1690
+ try {
1691
+ await m.uploadToolToStore(tools2[sel].fid, tools2[sel].desc);
1692
+ sandbox = [green("\uC2A4\uD1A0\uC5B4 \uB4F1\uB85D \uC644\uB8CC")];
1693
+ addChat(green(" \uC2A4\uD1A0\uC5B4 \uB4F1\uB85D\uB428"));
1694
+ } catch (e) {
1695
+ sandbox = [red(e.message)];
1696
+ }
1697
+ render();
1698
+ });
1699
+ return;
1700
+ }
1701
+ const n = parseInt(s);
1702
+ if (n >= 1 && n <= 6) {
1703
+ tab = TABS[n - 1].key;
1704
+ sel = 0;
1705
+ updateSandbox();
1706
+ statusMsg = getHint();
1319
1707
  render();
1320
1708
  return;
1321
1709
  }
1322
1710
  if (s === " ") {
1323
- const idx = TABS.findIndex((t) => t.key === tab);
1324
- tab = TABS[(idx + 1) % TABS.length].key;
1325
- selected = 0;
1326
- updateDetail();
1711
+ const i = TABS.findIndex((t) => t.key === tab);
1712
+ tab = TABS[(i + 1) % TABS.length].key;
1713
+ sel = 0;
1714
+ updateSandbox();
1715
+ statusMsg = getHint();
1716
+ render();
1717
+ return;
1718
+ }
1719
+ if (s === "\x1B[Z") {
1720
+ const i = TABS.findIndex((t) => t.key === tab);
1721
+ tab = TABS[(i - 1 + TABS.length) % TABS.length].key;
1722
+ sel = 0;
1723
+ updateSandbox();
1724
+ statusMsg = getHint();
1327
1725
  render();
1328
1726
  return;
1329
1727
  }
1330
1728
  if (s === "\x1B[A") {
1331
1729
  const items = getItems();
1332
- selected = Math.max(0, selected - 1);
1333
- updateDetail();
1334
- render();
1730
+ if (items.length > 0) {
1731
+ sel = Math.max(0, sel - 1);
1732
+ updateSandbox();
1733
+ render();
1734
+ }
1335
1735
  return;
1336
1736
  }
1337
1737
  if (s === "\x1B[B") {
1338
1738
  const items = getItems();
1339
- selected = Math.min(items.length - 1, selected + 1);
1340
- updateDetail();
1739
+ if (items.length > 0) {
1740
+ sel = Math.min(items.length - 1, sel + 1);
1741
+ updateSandbox();
1742
+ render();
1743
+ }
1744
+ return;
1745
+ }
1746
+ if (s === "\x1B[5~") {
1747
+ sel = Math.max(0, sel - 10);
1748
+ updateSandbox();
1749
+ render();
1750
+ return;
1751
+ }
1752
+ if (s === "\x1B[6~") {
1753
+ const items = getItems();
1754
+ sel = Math.min(items.length - 1, sel + 10);
1755
+ updateSandbox();
1341
1756
  render();
1342
1757
  return;
1343
1758
  }
1344
1759
  if (s === "\r" || s === "\n") {
1345
- if (tab === "workflows" && workflows[selected]) {
1346
- runTarget = workflows[selected];
1347
- inputMode = true;
1348
- statusMsg = `${runTarget.name} \u2014 \uC785\uB825 \uD6C4 Enter \uC2E4\uD589, Esc \uCDE8\uC18C`;
1349
- render();
1350
- } else if (tab === "collections" && collections[selected]) {
1351
- const c = collections[selected];
1352
- detail = [`${c.name} \uBB38\uC11C \uB85C\uB529...`];
1760
+ const items = getItems();
1761
+ if (items.length === 0) return;
1762
+ if (tab === "workflows" && workflows[sel]) loadWorkflowStructure(workflows[sel]);
1763
+ else if (tab === "collections" && collections[sel]) {
1764
+ sandbox = [bold(collections[sel].name), yellow("\uBB38\uC11C \uB85C\uB529...")];
1353
1765
  render();
1354
1766
  Promise.resolve().then(() => (init_document(), document_exports)).then(async (m) => {
1355
1767
  try {
1356
- const docs = await m.listDocuments(String(collections[selected].id ?? ""));
1357
- if (!docs.length) {
1358
- detail = [bold(c.name), "", "\uBB38\uC11C \uC5C6\uC74C"];
1359
- } else {
1360
- detail = [bold(c.name) + ` \u2014 ${docs.length}\uAC1C \uBB38\uC11C`, "", ...docs.map(
1361
- (d, i) => ` ${i + 1}. ${d.name || d.file_name || "\uC774\uB984\uC5C6\uC74C"}`
1362
- ).slice(0, 15)];
1363
- }
1364
- } catch (err) {
1365
- detail = [`\uBB38\uC11C \uB85C\uB4DC \uC2E4\uD328: ${err.message}`];
1768
+ const docs = await withTimeout(m.listDocuments(String(collections[sel].id)), 5e3, "\uBB38\uC11C");
1769
+ sandbox = docs?.length ? [bold(collections[sel].name) + ` ${docs.length}\uAC1C`, "", ...docs.map((d, i) => ` ${i + 1}. ${d.name || d.file_name || "?"}`).slice(0, 15)] : [bold(collections[sel].name), dim("\uBB38\uC11C \uC5C6\uC74C")];
1770
+ } catch (e) {
1771
+ sandbox = [red(e.message)];
1772
+ }
1773
+ render();
1774
+ });
1775
+ } else if (tab === "nodes" && nodes[sel]) loadNodeDetail(nodes[sel]);
1776
+ else if (tab === "prompts" && prompts[sel]) {
1777
+ const p = prompts[sel];
1778
+ sandbox = [bold(p.name), `\uD0C0\uC785: ${p.type}`, `UID: ${p.uid ?? "?"}`, "", ...p.content?.split("\n").slice(0, 15) ?? [dim("\uC5C6\uC74C")]];
1779
+ render();
1780
+ } else if (tab === "tools" && tools2[sel]) testTool(tools2[sel]);
1781
+ else if (tab === "mcp" && mcpSessions[sel]) {
1782
+ sandbox = [bold(mcpSessions[sel].name), yellow("\uB3C4\uAD6C \uB85C\uB529...")];
1783
+ render();
1784
+ Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports)).then(async (m) => {
1785
+ try {
1786
+ const ts = await withTimeout(m.getMcpSessionTools(mcpSessions[sel].id), 8e3, "MCP");
1787
+ sandbox = ts?.length ? [bold(mcpSessions[sel].name) + ` ${ts.length}\uAC1C \uB3C4\uAD6C`, "", ...ts.map((t, i) => ` ${i + 1}. ${t.name ?? "?"} ${dim(t.description?.slice(0, 30) ?? "")}`).slice(0, 15)] : [bold(mcpSessions[sel].name), dim("\uB3C4\uAD6C \uC5C6\uC74C")];
1788
+ } catch (e) {
1789
+ sandbox = [red(e.message)];
1366
1790
  }
1367
1791
  render();
1368
1792
  });
@@ -1373,26 +1797,27 @@ async function startRawTui() {
1373
1797
  function cleanup() {
1374
1798
  showCursor();
1375
1799
  clear();
1376
- if (process.stdin.isTTY && process.stdin.isRaw) {
1377
- process.stdin.setRawMode(false);
1378
- }
1800
+ if (process.stdin.isTTY && process.stdin.isRaw) process.stdin.setRawMode(false);
1379
1801
  process.stdin.removeAllListeners("data");
1380
1802
  process.stdin.pause();
1381
1803
  }
1382
1804
  if (!process.stdin.isTTY) {
1383
- console.error("\uB300\uC2DC\uBCF4\uB4DC\uB294 \uD130\uBBF8\uB110(TTY)\uC5D0\uC11C\uB9CC \uC2E4\uD589 \uAC00\uB2A5\uD569\uB2C8\uB2E4.");
1805
+ console.error("\uD130\uBBF8\uB110(TTY)\uC5D0\uC11C\uB9CC \uC2E4\uD589 \uAC00\uB2A5\uD569\uB2C8\uB2E4.");
1384
1806
  return;
1385
1807
  }
1386
1808
  process.stdin.setRawMode(true);
1387
1809
  process.stdin.resume();
1388
1810
  process.stdin.setEncoding("utf8");
1389
1811
  hideCursor();
1390
- process.stdin.on("data", (data) => {
1812
+ process.stdin.on("data", (d) => {
1391
1813
  try {
1392
- handleKey(data);
1393
- } catch {
1814
+ handleKey(String(d));
1815
+ } catch (e) {
1816
+ statusMsg = red(e.message);
1817
+ render();
1394
1818
  }
1395
1819
  });
1820
+ process.stdout.on("resize", () => render());
1396
1821
  process.on("SIGINT", () => {
1397
1822
  cleanup();
1398
1823
  process.exit(0);
@@ -1400,11 +1825,11 @@ async function startRawTui() {
1400
1825
  process.on("exit", () => {
1401
1826
  showCursor();
1402
1827
  });
1403
- await loadData();
1828
+ loadData();
1404
1829
  await new Promise(() => {
1405
1830
  });
1406
1831
  }
1407
- var TABS, CSI, clear, moveTo, dim, bold, inverse, hideCursor, showCursor;
1832
+ var TABS, CSI, clear, moveTo, dim, bold, inverse, red, green, yellow, cyan, hideCursor, showCursor;
1408
1833
  var init_raw_tui = __esm({
1409
1834
  "src/dashboard/raw-tui.ts"() {
1410
1835
  "use strict";
@@ -1414,7 +1839,8 @@ var init_raw_tui = __esm({
1414
1839
  { key: "collections", label: "\uCEEC\uB809\uC158", num: "2" },
1415
1840
  { key: "nodes", label: "\uB178\uB4DC", num: "3" },
1416
1841
  { key: "prompts", label: "\uD504\uB86C\uD504\uD2B8", num: "4" },
1417
- { key: "tools", label: "\uB3C4\uAD6C", num: "5" }
1842
+ { key: "tools", label: "\uB3C4\uAD6C", num: "5" },
1843
+ { key: "mcp", label: "MCP", num: "6" }
1418
1844
  ];
1419
1845
  CSI = "\x1B[";
1420
1846
  clear = () => process.stdout.write(`${CSI}2J${CSI}H`);
@@ -1422,6 +1848,10 @@ var init_raw_tui = __esm({
1422
1848
  dim = (s) => `${CSI}2m${s}${CSI}0m`;
1423
1849
  bold = (s) => `${CSI}1m${s}${CSI}0m`;
1424
1850
  inverse = (s) => `${CSI}7m${s}${CSI}0m`;
1851
+ red = (s) => `${CSI}31m${s}${CSI}0m`;
1852
+ green = (s) => `${CSI}32m${s}${CSI}0m`;
1853
+ yellow = (s) => `${CSI}33m${s}${CSI}0m`;
1854
+ cyan = (s) => `${CSI}36m${s}${CSI}0m`;
1425
1855
  hideCursor = () => process.stdout.write(`${CSI}?25l`);
1426
1856
  showCursor = () => process.stdout.write(`${CSI}?25h`);
1427
1857
  }
@@ -2832,6 +3262,141 @@ ID: ${d.id}
2832
3262
  }
2833
3263
  };
2834
3264
 
3265
+ // src/agent/tools/tool-search.ts
3266
+ var TOOL_INDEX = {
3267
+ workflow: {
3268
+ description: "\uC6CC\uD06C\uD50C\uB85C\uC6B0 \uAD00\uB9AC \u2014 \uBAA9\uB85D, \uC2E4\uD589, \uC0C1\uC138, \uC774\uB825, \uC131\uB2A5, \uC2A4\uD1A0\uC5B4, \uC790\uB3D9\uC0DD\uC131",
3269
+ tools: ["xgen_workflow_list", "xgen_workflow_run", "xgen_workflow_info", "xgen_execution_history", "xgen_workflow_performance", "xgen_workflow_store", "xgen_workflow_generate"]
3270
+ },
3271
+ document: {
3272
+ description: "\uBB38\uC11C/\uCEEC\uB809\uC158 \uAD00\uB9AC \u2014 \uCEEC\uB809\uC158 \uBAA9\uB85D, \uBB38\uC11C \uBAA9\uB85D, \uC5C5\uB85C\uB4DC",
3273
+ tools: ["xgen_collection_list", "xgen_document_list", "xgen_document_upload"]
3274
+ },
3275
+ node: {
3276
+ description: "\uB178\uB4DC \uD0D0\uC0C9 \u2014 \uC804\uCCB4 \uBAA9\uB85D, \uAC80\uC0C9, \uCE74\uD14C\uACE0\uB9AC",
3277
+ tools: ["xgen_node_list", "xgen_node_search", "xgen_node_categories"]
3278
+ },
3279
+ prompt: {
3280
+ description: "\uD504\uB86C\uD504\uD2B8 \uB77C\uC774\uBE0C\uB7EC\uB9AC \u2014 \uBAA9\uB85D \uC870\uD68C",
3281
+ tools: ["xgen_prompt_list"]
3282
+ },
3283
+ tool: {
3284
+ description: "\uB3C4\uAD6C \uC2A4\uD1A0\uC5B4 \u2014 \uACF5\uAC1C \uB3C4\uAD6C, \uB0B4 \uB3C4\uAD6C",
3285
+ tools: ["xgen_tool_store", "xgen_user_tools"]
3286
+ },
3287
+ schedule: {
3288
+ description: "\uC2A4\uCF00\uC904 \uAD00\uB9AC \u2014 cron \uC791\uC5C5 \uBAA9\uB85D",
3289
+ tools: ["xgen_schedule_list"]
3290
+ },
3291
+ trace: {
3292
+ description: "\uD2B8\uB808\uC774\uC2A4/\uC778\uD130\uB799\uC158 \u2014 \uC2E4\uD589 \uCD94\uC801, \uBA54\uD0C0\uB370\uC774\uD130",
3293
+ tools: ["xgen_trace_list", "xgen_interaction_list"]
3294
+ },
3295
+ graph: {
3296
+ description: "\uC628\uD1A8\uB85C\uC9C0 GraphRAG \u2014 \uC9C8\uC758, \uADF8\uB798\uD504 \uD1B5\uACC4",
3297
+ tools: ["xgen_graph_rag_query", "xgen_graph_stats"]
3298
+ },
3299
+ mcp: {
3300
+ description: "MCP \uC138\uC158 \uAD00\uB9AC \u2014 \uC138\uC158 \uBAA9\uB85D",
3301
+ tools: ["xgen_mcp_sessions"]
3302
+ },
3303
+ server: {
3304
+ description: "\uC11C\uBC84 \uC0C1\uD0DC \uD655\uC778",
3305
+ tools: ["xgen_server_status"]
3306
+ }
3307
+ };
3308
+ var loadedTools = /* @__PURE__ */ new Set();
3309
+ function getToolIndexSummary() {
3310
+ const lines = Object.entries(TOOL_INDEX).map(
3311
+ ([cat, info]) => ` - ${cat}: ${info.description}`
3312
+ );
3313
+ return `Available XGEN tool categories (use tool_search to load):
3314
+ ${lines.join("\n")}`;
3315
+ }
3316
+ function searchTools(query) {
3317
+ const q = query.toLowerCase().trim();
3318
+ const matched = [];
3319
+ const matchedNames = [];
3320
+ if (TOOL_INDEX[q]) {
3321
+ for (const name of TOOL_INDEX[q].tools) {
3322
+ const def = definitions.find((d) => d.function.name === name);
3323
+ if (def && !loadedTools.has(name)) {
3324
+ matched.push(def);
3325
+ loadedTools.add(name);
3326
+ matchedNames.push(name);
3327
+ }
3328
+ }
3329
+ }
3330
+ if (matched.length === 0) {
3331
+ for (const def of definitions) {
3332
+ const name = def.function.name;
3333
+ const desc = def.function.description ?? "";
3334
+ if ((name.includes(q) || desc.toLowerCase().includes(q)) && !loadedTools.has(name)) {
3335
+ matched.push(def);
3336
+ loadedTools.add(name);
3337
+ matchedNames.push(name);
3338
+ }
3339
+ }
3340
+ }
3341
+ if (matched.length === 0) {
3342
+ for (const [cat, info] of Object.entries(TOOL_INDEX)) {
3343
+ if (cat.includes(q) || info.description.includes(q)) {
3344
+ for (const name of info.tools) {
3345
+ const def = definitions.find((d) => d.function.name === name);
3346
+ if (def && !loadedTools.has(name)) {
3347
+ matched.push(def);
3348
+ loadedTools.add(name);
3349
+ matchedNames.push(name);
3350
+ }
3351
+ }
3352
+ }
3353
+ }
3354
+ }
3355
+ if (matched.length === 0) {
3356
+ return {
3357
+ tools: [],
3358
+ summary: `"${query}"\uC5D0 \uD574\uB2F9\uD558\uB294 \uB3C4\uAD6C \uC5C6\uC74C. \uCE74\uD14C\uACE0\uB9AC: ${Object.keys(TOOL_INDEX).join(", ")}`
3359
+ };
3360
+ }
3361
+ return {
3362
+ tools: matched,
3363
+ summary: `${matched.length}\uAC1C \uB3C4\uAD6C \uB85C\uB4DC\uB428: ${matchedNames.join(", ")}`
3364
+ };
3365
+ }
3366
+ function getLoadedToolDefs() {
3367
+ return definitions.filter((d) => loadedTools.has(d.function.name));
3368
+ }
3369
+ function resetLoadedTools() {
3370
+ loadedTools.clear();
3371
+ }
3372
+ var definition8 = {
3373
+ type: "function",
3374
+ function: {
3375
+ name: "tool_search",
3376
+ description: "XGEN \uD50C\uB7AB\uD3FC \uB3C4\uAD6C\uB97C \uCE74\uD14C\uACE0\uB9AC/\uD0A4\uC6CC\uB4DC\uB85C \uAC80\uC0C9\uD558\uC5EC \uB85C\uB4DC\uD569\uB2C8\uB2E4. \uC6CC\uD06C\uD50C\uB85C\uC6B0, \uBB38\uC11C, \uB178\uB4DC, \uD504\uB86C\uD504\uD2B8, \uADF8\uB798\uD504 \uB4F1\uC758 \uB3C4\uAD6C\uB97C \uD544\uC694\uD560 \uB54C \uC774 \uB3C4\uAD6C\uB85C \uB85C\uB4DC\uD558\uC138\uC694. \uCE74\uD14C\uACE0\uB9AC: workflow, document, node, prompt, tool, graph, mcp, trace, schedule, server",
3377
+ parameters: {
3378
+ type: "object",
3379
+ properties: {
3380
+ query: {
3381
+ type: "string",
3382
+ description: "\uAC80\uC0C9 \uCE74\uD14C\uACE0\uB9AC \uB610\uB294 \uD0A4\uC6CC\uB4DC (\uC608: 'workflow', '\uB178\uB4DC', 'GraphRAG')"
3383
+ }
3384
+ },
3385
+ required: ["query"]
3386
+ }
3387
+ }
3388
+ };
3389
+ async function execute9(args) {
3390
+ const query = args.query;
3391
+ if (!query) return "query \uD30C\uB77C\uBBF8\uD130 \uD544\uC694. \uCE74\uD14C\uACE0\uB9AC: " + Object.keys(TOOL_INDEX).join(", ");
3392
+ const result = searchTools(query);
3393
+ return result.summary;
3394
+ }
3395
+ function getNewlyLoadedTools(query) {
3396
+ const result = searchTools(query);
3397
+ return result.tools;
3398
+ }
3399
+
2835
3400
  // src/mcp/client.ts
2836
3401
  import { spawn } from "child_process";
2837
3402
  import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
@@ -2990,6 +3555,116 @@ var McpManager = class {
2990
3555
  }
2991
3556
  };
2992
3557
 
3558
+ // src/agent/hooks.ts
3559
+ init_store();
3560
+ var preHooks = [
3561
+ // XGEN 미연결 시 XGEN 도구 차단
3562
+ {
3563
+ match: (name) => name.startsWith("xgen_"),
3564
+ handler: () => {
3565
+ if (!getServer() || !getAuth()) {
3566
+ return { proceed: false, message: "XGEN \uC11C\uBC84 \uBBF8\uC5F0\uACB0. /connect\uB85C \uC5F0\uACB0\uD558\uC138\uC694." };
3567
+ }
3568
+ return { proceed: true };
3569
+ }
3570
+ },
3571
+ // 위험한 bash 명령 경고
3572
+ {
3573
+ match: (name) => name === "bash",
3574
+ handler: (_name, args) => {
3575
+ const cmd = String(args.command ?? "");
3576
+ if (/rm\s+-rf\s+[\/~]|dd\s+if=|mkfs|>\s*\/dev\/sd/.test(cmd)) {
3577
+ return { proceed: false, message: `\uC704\uD5D8\uD55C \uBA85\uB839\uC5B4 \uCC28\uB2E8: ${cmd.slice(0, 50)}` };
3578
+ }
3579
+ return { proceed: true };
3580
+ }
3581
+ }
3582
+ ];
3583
+ var postHooks = [
3584
+ // 결과가 너무 길면 자동 truncate + 안내
3585
+ {
3586
+ match: () => true,
3587
+ handler: (_name, result) => {
3588
+ if (result.length > 6e3) {
3589
+ return {
3590
+ proceed: true,
3591
+ modifiedResult: result.slice(0, 5e3) + `
3592
+
3593
+ [... ${result.length - 5e3}\uC790 \uC0DD\uB7B5. \uC804\uCCB4 \uACB0\uACFC\uAC00 \uD544\uC694\uD558\uBA74 \uB354 \uAD6C\uCCB4\uC801\uC778 \uC870\uAC74\uC73C\uB85C \uC7AC\uC870\uD68C\uD558\uC138\uC694]`
3594
+ };
3595
+ }
3596
+ return { proceed: true };
3597
+ }
3598
+ }
3599
+ ];
3600
+ function runPreHooks(name, args) {
3601
+ for (const hook of preHooks) {
3602
+ if (hook.match(name)) {
3603
+ const result = hook.handler(name, args);
3604
+ if (!result.proceed) return result;
3605
+ if (result.modifiedArgs) Object.assign(args, result.modifiedArgs);
3606
+ }
3607
+ }
3608
+ return { proceed: true };
3609
+ }
3610
+ function runPostHooks(name, result) {
3611
+ let current = result;
3612
+ for (const hook of postHooks) {
3613
+ if (hook.match(name)) {
3614
+ const hr = hook.handler(name, current);
3615
+ if (hr.modifiedResult) current = hr.modifiedResult;
3616
+ }
3617
+ }
3618
+ return current;
3619
+ }
3620
+
3621
+ // src/agent/context.ts
3622
+ var COMPACT_THRESHOLD = 30;
3623
+ var KEEP_RECENT = 10;
3624
+ var REMINDER_INTERVAL = 10;
3625
+ function compactMessages(messages) {
3626
+ if (messages.length <= COMPACT_THRESHOLD) return messages;
3627
+ const system = messages[0];
3628
+ const toCompress = messages.slice(1, -KEEP_RECENT);
3629
+ const recent = messages.slice(-KEEP_RECENT);
3630
+ const toolCalls = [];
3631
+ const userRequests = [];
3632
+ for (const msg of toCompress) {
3633
+ if (msg.role === "user" && typeof msg.content === "string") {
3634
+ userRequests.push(msg.content.slice(0, 80));
3635
+ }
3636
+ if (msg.role === "assistant" && typeof msg.content === "string" && msg.content) {
3637
+ const tc = msg.tool_calls;
3638
+ if (tc && Array.isArray(tc)) {
3639
+ for (const t of tc) {
3640
+ toolCalls.push(t.function?.name ?? "unknown");
3641
+ }
3642
+ }
3643
+ }
3644
+ }
3645
+ const summary = [
3646
+ `[\uC774\uC804 \uB300\uD654 \uC694\uC57D \u2014 ${toCompress.length}\uAC1C \uBA54\uC2DC\uC9C0 \uC555\uCD95]`,
3647
+ userRequests.length > 0 ? `\uC0AC\uC6A9\uC790 \uC694\uCCAD: ${userRequests.join(" \u2192 ")}` : "",
3648
+ toolCalls.length > 0 ? `\uC2E4\uD589\uB41C \uB3C4\uAD6C: ${[...new Set(toolCalls)].join(", ")}` : ""
3649
+ ].filter(Boolean).join("\n");
3650
+ return [
3651
+ system,
3652
+ { role: "system", content: summary },
3653
+ ...recent
3654
+ ];
3655
+ }
3656
+ function shouldInjectReminder(turnCount) {
3657
+ return turnCount > 0 && turnCount % REMINDER_INTERVAL === 0;
3658
+ }
3659
+ function createReminder(serverUrl, loadedTools2) {
3660
+ const lines = [
3661
+ `[System Reminder \u2014 \uD134 ${Date.now()}]`,
3662
+ serverUrl ? `\uC11C\uBC84: ${serverUrl} (\uC5F0\uACB0\uB428)` : "\uC11C\uBC84: \uBBF8\uC5F0\uACB0",
3663
+ loadedTools2 && loadedTools2.length > 0 ? `\uD65C\uC131 XGEN \uB3C4\uAD6C: ${loadedTools2.join(", ")}` : "XGEN \uB3C4\uAD6C: tool_search\uB85C \uB85C\uB4DC \uD544\uC694"
3664
+ ];
3665
+ return { role: "system", content: lines.join("\n") };
3666
+ }
3667
+
2993
3668
  // src/commands/agent.ts
2994
3669
  init_provider();
2995
3670
  init_ui();
@@ -3027,20 +3702,13 @@ EXAMPLES OF GOOD RESPONSES:
3027
3702
 
3028
3703
  XGEN CONNECTED: ${server} as ${auth.username}
3029
3704
 
3030
- You have full access to XGEN platform. Available tools:
3705
+ XGEN \uD50C\uB7AB\uD3FC \uB3C4\uAD6C\uB294 tool_search\uB85C \uD544\uC694\uD560 \uB54C \uB85C\uB4DC\uD558\uC138\uC694.
3706
+ tool_search("workflow") \u2192 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uB3C4\uAD6C \uB85C\uB4DC \u2192 \uB2E4\uC74C \uD134\uC5D0\uC11C \uC0AC\uC6A9 \uAC00\uB2A5
3031
3707
 
3032
- WORKFLOW: xgen_workflow_list, xgen_workflow_run (\uBAA8\uB4E0 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC2E4\uD589 \uAC00\uB2A5, \uBC30\uD3EC \uBB34\uAD00), xgen_workflow_info, xgen_execution_history, xgen_workflow_performance, xgen_workflow_store, xgen_workflow_generate (\uC790\uC5F0\uC5B4\uB85C \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC790\uB3D9 \uC0DD\uC131)
3033
- DOCUMENTS: xgen_collection_list, xgen_document_list, xgen_document_upload
3034
- NODES: xgen_node_list, xgen_node_search, xgen_node_categories
3035
- PROMPTS: xgen_prompt_list
3036
- TOOLS: xgen_tool_store, xgen_user_tools
3037
- SCHEDULE: xgen_schedule_list
3038
- TRACE: xgen_trace_list, xgen_interaction_list
3039
- MCP: xgen_mcp_sessions
3040
- ONTOLOGY: xgen_graph_rag_query, xgen_graph_stats
3041
- SERVER: xgen_server_status
3708
+ ${getToolIndexSummary()}
3042
3709
 
3043
- When user says a number \u2192 find it from previous list. "\uC2E4\uD589" \u2192 execute immediately.`;
3710
+ When user says a number \u2192 find it from previous list. "\uC2E4\uD589" \u2192 execute immediately.
3711
+ \uC6CC\uD06C\uD50C\uB85C\uC6B0/\uCEEC\uB809\uC158/\uB178\uB4DC \uB4F1 XGEN \uAD00\uB828 \uC694\uCCAD \u2192 \uBA3C\uC800 tool_search\uB85C \uD574\uB2F9 \uCE74\uD14C\uACE0\uB9AC \uB3C4\uAD6C \uB85C\uB4DC \uD6C4 \uC2E4\uD589.`;
3044
3712
  } else {
3045
3713
  prompt2 += `
3046
3714
  XGEN: Not connected. User can run /connect to connect.`;
@@ -3146,7 +3814,8 @@ async function agentRepl() {
3146
3814
  }
3147
3815
  }
3148
3816
  const client2 = createLLMClient(provider);
3149
- const allTools = [...getAllToolDefs(), ...definitions];
3817
+ resetLoadedTools();
3818
+ const allTools = [...getAllToolDefs(), definition8];
3150
3819
  const builtinNames = getToolNames();
3151
3820
  const mcpConfig = loadMcpConfig();
3152
3821
  if (mcpConfig && Object.keys(mcpConfig.mcpServers).length > 0) {
@@ -3317,7 +3986,7 @@ async function agentRepl() {
3317
3986
  if (input === "/tools") {
3318
3987
  console.log(`
3319
3988
  ${chalk12.bold("\uCF54\uB529")} ${builtinNames.join(", ")}`);
3320
- console.log(` ${chalk12.bold("XGEN")} ${definitions.map((t) => t.function.name).join(", ")}`);
3989
+ console.log(` ${chalk12.bold("XGEN")} ${xgenToolDefs.map((t) => t.function.name).join(", ")}`);
3321
3990
  if (mcpManager?.serverCount) {
3322
3991
  console.log(` ${chalk12.bold("MCP")} ${mcpManager.getAllTools().map((t) => t.function.name).join(", ")}`);
3323
3992
  }
@@ -3381,6 +4050,15 @@ async function agentRepl() {
3381
4050
  }
3382
4051
  async function runLoop(client2, model, messages, tools2) {
3383
4052
  for (let i = 0; i < 20; i++) {
4053
+ const compacted = compactMessages(messages);
4054
+ if (compacted.length < messages.length) {
4055
+ messages.length = 0;
4056
+ messages.push(...compacted);
4057
+ }
4058
+ if (shouldInjectReminder(i)) {
4059
+ const loadedNames = getLoadedToolDefs().map((t) => t.function.name);
4060
+ messages.push(createReminder(getServer() ?? void 0, loadedNames));
4061
+ }
3384
4062
  let first = true;
3385
4063
  const result = await streamChat(client2, model, messages, tools2, (delta) => {
3386
4064
  if (first) {
@@ -3411,23 +4089,37 @@ async function runLoop(client2, model, messages, tools2) {
3411
4089
  } catch {
3412
4090
  args = {};
3413
4091
  }
3414
- const shortArgs = Object.entries(args).map(([k, v]) => {
4092
+ const preResult = runPreHooks(tc.name, args);
4093
+ if (!preResult.proceed) {
4094
+ console.log(chalk12.red(` \u2717 ${tc.name} \uCC28\uB2E8: ${preResult.message}`));
4095
+ messages.push({ role: "tool", tool_call_id: tc.id, content: preResult.message ?? "\uCC28\uB2E8\uB428" });
4096
+ continue;
4097
+ }
4098
+ const shortArgs = Object.entries(args).map(([, v]) => {
3415
4099
  const s = String(v);
3416
4100
  return s.length > 40 ? s.slice(0, 40) + "\u2026" : s;
3417
4101
  }).join(", ");
3418
4102
  console.log(chalk12.dim(` \u250C ${tc.name}(${shortArgs})`));
3419
4103
  let toolResult2;
3420
- if (isXgenTool(tc.name)) {
4104
+ if (tc.name === "tool_search") {
4105
+ toolResult2 = await execute9(args);
4106
+ const newTools = getNewlyLoadedTools(args.query);
4107
+ for (const nt of newTools) {
4108
+ if (!tools2.some((t) => t.function.name === nt.function.name)) {
4109
+ tools2.push(nt);
4110
+ }
4111
+ }
4112
+ } else if (isXgenTool(tc.name)) {
3421
4113
  toolResult2 = await execute8(tc.name, args);
3422
4114
  } else if (mcpManager?.isMcpTool(tc.name)) {
3423
4115
  toolResult2 = await mcpManager.callTool(tc.name, args);
3424
4116
  } else {
3425
4117
  toolResult2 = await executeTool(tc.name, args);
3426
4118
  }
3427
- const truncated = toolResult2.length > 8e3 ? toolResult2.slice(0, 8e3) + "\n\u2026(truncated)" : toolResult2;
4119
+ toolResult2 = runPostHooks(tc.name, toolResult2);
3428
4120
  const preview = toolResult2.split("\n")[0].slice(0, 60);
3429
4121
  console.log(chalk12.dim(` \u2514 ${preview}${toolResult2.length > 60 ? "\u2026" : ""}`));
3430
- messages.push({ role: "tool", tool_call_id: tc.id, content: truncated });
4122
+ messages.push({ role: "tool", tool_call_id: tc.id, content: toolResult2 });
3431
4123
  }
3432
4124
  }
3433
4125
  console.log(chalk12.yellow("\n \uCD5C\uB300 \uBC18\uBCF5 \uD69F\uC218 \uB3C4\uB2EC.\n"));
@@ -3631,7 +4323,7 @@ ${result.answer}
3631
4323
  }
3632
4324
 
3633
4325
  // src/index.ts
3634
- var VERSION = "2.3.0";
4326
+ var VERSION = "2.5.0";
3635
4327
  var LOGO = chalk15.cyan(`
3636
4328
  \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588
3637
4329
  \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588
@@ -3663,11 +4355,17 @@ ${chalk15.bold("AI \uC5D0\uC774\uC804\uD2B8:")}
3663
4355
  ${chalk15.cyan("xgen provider add")} \uD504\uB85C\uBC14\uC774\uB354 \uCD94\uAC00
3664
4356
 
3665
4357
  ${chalk15.bold("XGEN \uD50C\uB7AB\uD3FC:")}
4358
+ ${chalk15.cyan("xgen dash")} TUI \uB300\uC2DC\uBCF4\uB4DC (\uC870\uD68C/\uD14C\uC2A4\uD2B8/\uC0DD\uC131)
3666
4359
  ${chalk15.cyan("xgen chat")} \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uB300\uD654
3667
4360
  ${chalk15.cyan("xgen wf ls")} \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uBAA9\uB85D
3668
4361
  ${chalk15.cyan("xgen wf run")} <id> "\uC9C8\uBB38" \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC2E4\uD589
3669
4362
  ${chalk15.cyan("xgen doc ls")} \uBB38\uC11C \uBAA9\uB85D
3670
4363
  ${chalk15.cyan("xgen ont query")} "\uC9C8\uBB38" \uC628\uD1A8\uB85C\uC9C0 \uC9C8\uC758
4364
+
4365
+ ${chalk15.bold("\uB300\uC2DC\uBCF4\uB4DC (xgen dash):")}
4366
+ ${chalk15.gray("\u2191\u2193")} \uC774\uB3D9 ${chalk15.gray("Enter")} \uC0C1\uC138/\uC2E4\uD589 ${chalk15.gray("1-6")} \uD0ED\uC804\uD658 ${chalk15.gray("r")} \uC0C8\uB85C\uACE0\uCE68 ${chalk15.gray("q")} \uC885\uB8CC
4367
+ ${chalk15.gray("c")} \uC0DD\uC131 ${chalk15.gray("e")} \uC218\uC815 ${chalk15.gray("d")} \uC0AD\uC81C ${chalk15.gray("t")} \uD14C\uC2A4\uD2B8 ${chalk15.gray("u")} \uC2A4\uD1A0\uC5B4\uB4F1\uB85D ${chalk15.gray("v")} \uBC84\uC804
4368
+ \uD0ED: \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uCEEC\uB809\uC158 \uB178\uB4DC \uD504\uB86C\uD504\uD2B8 \uB3C4\uAD6C MCP
3671
4369
  `
3672
4370
  );
3673
4371
  registerConfigCommand(program);
@@ -3679,6 +4377,9 @@ registerAgentCommand(program);
3679
4377
  registerDocCommand(program);
3680
4378
  registerOntologyCommand(program);
3681
4379
  program.command("dash").alias("dashboard").description("XGEN TUI \uB300\uC2DC\uBCF4\uB4DC").action(async () => {
4380
+ process.stdin.removeAllListeners();
4381
+ process.stdin.pause();
4382
+ await new Promise((r) => setTimeout(r, 50));
3682
4383
  const { startRawTui: startRawTui2 } = await Promise.resolve().then(() => (init_raw_tui(), raw_tui_exports));
3683
4384
  await startRawTui2();
3684
4385
  });