openxgen 2.4.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
@@ -1222,15 +1222,21 @@ var raw_tui_exports = {};
1222
1222
  __export(raw_tui_exports, {
1223
1223
  startRawTui: () => startRawTui
1224
1224
  });
1225
- function withTimeout(p, ms = 5e3, label = "API") {
1226
- return new Promise((resolve, reject) => {
1227
- const t = setTimeout(() => reject(new Error(`${label} \uD0C0\uC784\uC544\uC6C3 (${ms}ms)`)), ms);
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);
1228
1234
  p.then((v) => {
1229
1235
  clearTimeout(t);
1230
- resolve(v);
1236
+ res(v);
1231
1237
  }, (e) => {
1232
1238
  clearTimeout(t);
1233
- reject(e);
1239
+ rej(e);
1234
1240
  });
1235
1241
  });
1236
1242
  }
@@ -1238,283 +1244,213 @@ async function startRawTui() {
1238
1244
  const provider = getDefaultProvider();
1239
1245
  const server = getServer();
1240
1246
  const auth = getAuth();
1241
- const serverDisplay = auth && server ? `${auth.username}@${server.replace(/https?:\/\//, "")}` : "\uBBF8\uC5F0\uACB0";
1247
+ const sv = auth && server ? `${auth.username}@${server.replace(/https?:\/\//, "")}` : "\uBBF8\uC5F0\uACB0";
1242
1248
  let tab = "workflows";
1243
- let selected = 0;
1244
- let inputMode = false;
1245
- let inputBuffer = "";
1246
- let formCtx = null;
1249
+ let sel = 0;
1250
+ let inputBuf = "";
1251
+ let inputActive = false;
1247
1252
  let workflows = [];
1248
1253
  let collections = [];
1249
1254
  let nodes = [];
1250
1255
  let prompts = [];
1251
1256
  let tools2 = [];
1252
1257
  let mcpSessions = [];
1253
- let detail = ["\u2190 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694"];
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)")];
1254
1260
  let statusMsg = "\uB85C\uB529...";
1255
- let errors = [];
1256
1261
  async function loadData() {
1257
- statusMsg = "\uB370\uC774\uD130 \uB85C\uB529 \uC911...";
1258
- errors = [];
1262
+ statusMsg = "\uB85C\uB529 \uC911...";
1259
1263
  render();
1260
1264
  if (!server || !auth) {
1261
1265
  statusMsg = "\uC11C\uBC84 \uBBF8\uC5F0\uACB0";
1262
- detail = [red("\uC11C\uBC84 \uBBF8\uC5F0\uACB0"), "", cyan(" xgen config set-server <URL>"), cyan(" xgen login"), "", "\uD6C4 r \uC0C8\uB85C\uACE0\uCE68"];
1266
+ sandbox = [red("\uC11C\uBC84 \uBBF8\uC5F0\uACB0"), "", cyan(" xgen config set-server <URL>"), cyan(" xgen login")];
1263
1267
  render();
1264
1268
  return;
1265
1269
  }
1270
+ const errors = [];
1266
1271
  try {
1267
- const [wfMod, docMod, extraMod] = await Promise.all([
1272
+ const [wf, doc, ex] = await Promise.all([
1268
1273
  Promise.resolve().then(() => (init_workflow(), workflow_exports)),
1269
1274
  Promise.resolve().then(() => (init_document(), document_exports)),
1270
1275
  Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports))
1271
1276
  ]);
1272
- const results = await Promise.allSettled([
1273
- withTimeout(wfMod.getWorkflowListDetail(), 3e3, "\uC6CC\uD06C\uD50C\uB85C\uC6B0"),
1274
- withTimeout(docMod.listCollections(), 3e3, "\uCEEC\uB809\uC158"),
1275
- withTimeout(extraMod.listNodes(), 3e3, "\uB178\uB4DC"),
1276
- withTimeout(extraMod.listPrompts(), 3e3, "\uD504\uB86C\uD504\uD2B8"),
1277
- withTimeout(extraMod.listToolStore(), 3e3, "\uB3C4\uAD6C"),
1278
- withTimeout(extraMod.listMcpSessions(), 3e3, "MCP").catch(() => [])
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(() => [])
1279
1284
  ]);
1280
- const [wfR, colR, nodeR, promptR, toolR, mcpR] = results;
1281
- if (wfR.status === "fulfilled") {
1282
- workflows = wfR.value.map((w) => ({
1283
- name: w.workflow_name ?? w.name ?? "?",
1284
- id: w.workflow_id ?? w.id ?? "",
1285
- deployed: w.deploy_status === "deployed" || w.deploy_status === "DEPLOYED" || !!w.is_deployed
1286
- }));
1287
- } else errors.push(`\uC6CC\uD06C\uD50C\uB85C\uC6B0: ${wfR.reason?.message ?? "\uC2E4\uD328"}`);
1288
- if (colR.status === "fulfilled") {
1289
- collections = colR.value.map((c) => ({
1290
- name: c.collection_make_name ?? c.collection_name ?? c.name ?? "?",
1291
- id: c.id ?? c.collection_id ?? "",
1292
- docs: c.total_documents ?? 0,
1293
- chunks: c.total_chunks ?? 0
1294
- }));
1295
- } else errors.push(`\uCEEC\uB809\uC158: ${colR.reason?.message ?? "\uC2E4\uD328"}`);
1296
- if (nodeR.status === "fulfilled") {
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") {
1297
1299
  nodes = [];
1298
- const raw = nodeR.value;
1299
- for (const cat of raw) {
1300
- const catName = cat.categoryName ?? cat.categoryId ?? "";
1301
- const fns = cat.functions ?? cat.nodes ?? [];
1302
- if (Array.isArray(fns)) {
1303
- for (const fn of fns) {
1304
- const fnNodes = fn.nodes ?? [];
1305
- if (Array.isArray(fnNodes)) {
1306
- for (const n of fnNodes) {
1307
- nodes.push({
1308
- name: n.nodeName ?? n.name ?? "?",
1309
- desc: (n.description ?? "").slice(0, 50),
1310
- id: n.id ?? n.nodeId,
1311
- category: `${catName}/${fn.functionName ?? fn.functionId ?? ""}`
1312
- });
1313
- }
1314
- } else {
1315
- nodes.push({
1316
- name: fn.nodeName ?? fn.name ?? "?",
1317
- desc: (fn.description ?? "").slice(0, 50),
1318
- id: fn.id ?? fn.nodeId,
1319
- category: catName
1320
- });
1321
- }
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 ?? ""}` });
1322
1305
  }
1323
1306
  }
1324
1307
  }
1325
- } else errors.push(`\uB178\uB4DC: ${nodeR.reason?.message ?? "\uC2E4\uD328"}`);
1326
- if (promptR.status === "fulfilled") {
1327
- prompts = promptR.value.map((p) => ({
1328
- name: p.prompt_title ?? p.name ?? p.title ?? "?",
1329
- type: p.prompt_type ?? p.type ?? "",
1330
- uid: p.prompt_uid ?? p.uid,
1331
- content: p.prompt_content ?? p.content,
1332
- dbId: p.id
1333
- }));
1334
- } else errors.push(`\uD504\uB86C\uD504\uD2B8: ${promptR.reason?.message ?? "\uC2E4\uD328"}`);
1335
- if (toolR.status === "fulfilled") {
1336
- tools2 = toolR.value.map((t) => {
1337
- const fd = t.function_data ?? {};
1338
- return {
1339
- name: fd.function_name ?? t.function_name ?? t.name ?? t.tool_name ?? "?",
1340
- desc: (fd.description ?? t.description ?? "").slice(0, 50),
1341
- id: t.id,
1342
- functionId: fd.function_id ?? t.function_id ?? t.function_upload_id,
1343
- apiUrl: fd.api_url ?? t.api_url,
1344
- status: fd.status ?? t.status
1345
- };
1346
- });
1347
- } else errors.push(`\uB3C4\uAD6C: ${toolR.reason?.message ?? "\uC2E4\uD328"}`);
1348
- if (mcpR.status === "fulfilled" && Array.isArray(mcpR.value)) {
1349
- mcpSessions = mcpR.value.map((s) => ({
1350
- id: s.session_id ?? s.id ?? "",
1351
- name: s.session_name ?? s.name ?? "\uC774\uB984\uC5C6\uC74C",
1352
- type: s.server_type ?? "?",
1353
- status: s.status ?? "unknown",
1354
- command: s.server_command
1355
- }));
1356
- }
1357
- } catch (err) {
1358
- errors.push(`\uC804\uCCB4: ${err.message}`);
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);
1359
1337
  }
1360
- selected = 0;
1361
- updateDetail();
1338
+ sel = 0;
1339
+ updateSandbox();
1362
1340
  statusMsg = errors.length > 0 ? `\u26A0 ${errors.length}\uAC1C \uC624\uB958 \u2502 r \uC7AC\uC2DC\uB3C4` : getHint();
1363
- if (errors.length > 0) detail = [red("\uB85C\uB4DC \uC624\uB958:"), "", ...errors.map((e) => yellow(` \u2022 ${e}`))];
1341
+ if (errors.length > 0) sandbox = [red("\uC624\uB958:"), ...errors.map((e) => yellow(` \u2022 ${e}`))];
1364
1342
  render();
1365
1343
  }
1366
1344
  function getHint() {
1367
- const hints = {
1368
- workflows: "\u2191\u2193 \u2502 Enter \uB178\uB4DC\uAD6C\uC870/\uC2E4\uD589 \u2502 1-6 \uD0ED \u2502 r \u2502 q",
1369
- collections: "\u2191\u2193 \u2502 Enter \uBB38\uC11C\uBAA9\uB85D \u2502 1-6 \uD0ED \u2502 r \u2502 q",
1370
- nodes: "\u2191\u2193 \u2502 Enter \uC0C1\uC138(\uD30C\uB77C\uBBF8\uD130) \u2502 1-6 \uD0ED \u2502 r \u2502 q",
1371
- prompts: "\u2191\u2193 \u2502 Enter \uB0B4\uC6A9 \u2502 c\uC0DD\uC131 e\uC218\uC815 d\uC0AD\uC81C u\uC2A4\uD1A0\uC5B4 v\uBC84\uC804 \u2502 q",
1372
- tools: "\u2191\u2193 \u2502 Enter/t \uD14C\uC2A4\uD2B8 \u2502 c\uC0DD\uC131 u\uC2A4\uD1A0\uC5B4\uB4F1\uB85D \u2502 q",
1373
- mcp: "\u2191\u2193 \u2502 Enter \uB3C4\uAD6C\uBAA9\uB85D \u2502 c\uC138\uC158\uC0DD\uC131 t\uB3C4\uAD6C\uD638\uCD9C d\uC0AD\uC81C \u2502 q"
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"
1374
1352
  };
1375
- return hints[tab];
1353
+ return h[tab];
1376
1354
  }
1377
1355
  function getItems() {
1378
1356
  switch (tab) {
1379
1357
  case "workflows":
1380
- return workflows.map((w) => ({ label: `${w.deployed ? green("\u25CF") : dim("\u25CB")} ${w.name}`, sub: w.id.slice(0, 16) }));
1358
+ return workflows.map((w) => ({ label: `${w.deployed ? green("\u25CF") : dim("\u25CB")} ${w.name}`, sub: w.id.slice(0, 12) }));
1381
1359
  case "collections":
1382
- 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` }));
1383
1361
  case "nodes":
1384
- return nodes.map((n) => ({ label: n.name, sub: n.category ? `[${n.category}] ${n.desc}` : n.desc }));
1362
+ return nodes.map((n) => ({ label: n.name, sub: n.category }));
1385
1363
  case "prompts":
1386
1364
  return prompts.map((p) => ({ label: p.name, sub: `[${p.type}]` }));
1387
1365
  case "tools":
1388
- return tools2.map((t) => {
1389
- const st = t.status === "active" ? green("\u25CF") : t.status === "inactive" ? red("\u25CF") : dim("\u25CB");
1390
- return { label: `${st} ${t.name}`, sub: t.desc };
1391
- });
1366
+ return tools2.map((t) => ({ label: `${t.status === "active" ? green("\u25CF") : dim("\u25CB")} ${t.name}`, sub: t.desc }));
1392
1367
  case "mcp":
1393
- return mcpSessions.map((s) => {
1394
- const st = s.status === "running" ? green("\u25CF") : s.status === "error" ? red("\u25CF") : dim("\u25CB");
1395
- return { label: `${st} ${s.name}`, sub: `[${s.type}] ${s.status}` };
1396
- });
1368
+ return mcpSessions.map((s) => ({ label: `${s.status === "running" ? green("\u25CF") : dim("\u25CB")} ${s.name}`, sub: s.type }));
1397
1369
  }
1398
1370
  }
1399
- function updateDetail() {
1371
+ function updateSandbox() {
1400
1372
  const items = getItems();
1401
1373
  if (items.length === 0) {
1402
- const createHint = tab === "tools" ? "c \uB3C4\uAD6C \uC0DD\uC131" : tab === "prompts" ? "c \uD504\uB86C\uD504\uD2B8 \uC0DD\uC131" : tab === "mcp" ? "c MCP \uC138\uC158 \uC0DD\uC131" : "";
1403
- detail = [dim("\uB370\uC774\uD130 \uC5C6\uC74C"), "", dim(createHint)];
1374
+ sandbox = [dim("\uB370\uC774\uD130 \uC5C6\uC74C")];
1404
1375
  return;
1405
1376
  }
1406
- if (selected < 0) selected = 0;
1407
- if (selected >= items.length) selected = items.length - 1;
1408
- if (tab === "workflows" && workflows[selected]) {
1409
- const w = workflows[selected];
1410
- detail = [
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 = [
1411
1382
  bold(w.name),
1383
+ `ID: ${w.id}`,
1384
+ `\uBC30\uD3EC: ${w.deployed ? green("Yes") : red("No")}`,
1412
1385
  "",
1413
- `ID ${w.id || dim("\uC5C6\uC74C")}`,
1414
- `\uBC30\uD3EC ${w.deployed ? green("\u25CF Yes") : red("\u25CB No")}`,
1415
- "",
1416
- cyan("Enter") + " \uB178\uB4DC/\uC5E3\uC9C0 \uAD6C\uC870 \uBCF4\uAE30",
1417
- cyan("i") + " \uC9C8\uBB38 \uC785\uB825 \uD6C4 \uC2E4\uD589"
1418
- ];
1419
- } else if (tab === "collections" && collections[selected]) {
1420
- const c = collections[selected];
1421
- detail = [bold(c.name), "", `\uBB38\uC11C ${c.docs}\uAC1C`, `\uCCAD\uD06C ${c.chunks}\uAC1C`, "", cyan("Enter") + " \uBB38\uC11C \uBAA9\uB85D"];
1422
- } else if (tab === "nodes" && nodes[selected]) {
1423
- const n = nodes[selected];
1424
- detail = [
1425
- bold(n.name),
1426
- "",
1427
- `\uCE74\uD14C\uACE0\uB9AC: ${n.category || dim("\uC5C6\uC74C")}`,
1428
- n.desc || dim("\uC124\uBA85 \uC5C6\uC74C"),
1429
- "",
1430
- cyan("Enter") + " \uD30C\uB77C\uBBF8\uD130/\uD3EC\uD2B8 \uC0C1\uC138"
1431
- ];
1432
- } else if (tab === "prompts" && prompts[selected]) {
1433
- const p = prompts[selected];
1434
- const preview = p.content ? p.content.split("\n").slice(0, 8) : [dim("\uB0B4\uC6A9 \uC5C6\uC74C")];
1435
- detail = [
1436
- bold(p.name),
1437
- `\uD0C0\uC785: ${p.type || "\uC5C6\uC74C"}`,
1438
- `UID: ${p.uid || dim("\uC5C6\uC74C")}`,
1439
- "",
1440
- ...preview,
1441
- "",
1442
- `${cyan("c")}\uC0DD\uC131 ${cyan("e")}\uC218\uC815 ${cyan("d")}\uC0AD\uC81C ${cyan("u")}\uC2A4\uD1A0\uC5B4 ${cyan("v")}\uBC84\uC804`
1443
- ];
1444
- } else if (tab === "tools" && tools2[selected]) {
1445
- const t = tools2[selected];
1446
- detail = [
1447
- bold(t.name),
1448
- "",
1449
- `ID ${t.functionId || dim("\uC5C6\uC74C")}`,
1450
- `URL ${t.apiUrl || dim("\uC5C6\uC74C")}`,
1451
- `\uC0C1\uD0DC ${t.status === "active" ? green("active") : t.status === "inactive" ? red("inactive") : dim(t.status || "\uC5C6\uC74C")}`,
1452
- "",
1453
- t.desc || dim("\uC124\uBA85 \uC5C6\uC74C"),
1454
- "",
1455
- `${cyan("t")}\uD14C\uC2A4\uD2B8 ${cyan("u")}\uC2A4\uD1A0\uC5B4\uB4F1\uB85D ${cyan("c")}\uC0DD\uC131`
1456
- ];
1457
- } else if (tab === "mcp" && mcpSessions[selected]) {
1458
- const s = mcpSessions[selected];
1459
- detail = [
1460
- bold(s.name),
1461
- "",
1462
- `\uC138\uC158ID ${s.id}`,
1463
- `\uD0C0\uC785 ${s.type}`,
1464
- `\uC0C1\uD0DC ${s.status === "running" ? green(s.status) : red(s.status)}`,
1465
- `\uCEE4\uB9E8\uB4DC ${s.command || dim("\uC5C6\uC74C")}`,
1466
- "",
1467
- `${cyan("Enter")}\uB3C4\uAD6C\uBAA9\uB85D ${cyan("t")}\uB3C4\uAD6C\uD638\uCD9C ${cyan("d")}\uC0AD\uC81C`
1386
+ cyan("Enter") + " \uB178\uB4DC\uAD6C\uC870",
1387
+ dim("\uC9C8\uBB38 \uC785\uB825 \u2192 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC2E4\uD589")
1468
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"];
1469
1404
  }
1470
1405
  }
1471
- function visualLen(s) {
1472
- return s.replace(/\x1b\[[0-9;]*m/g, "").length;
1473
- }
1474
- function padVisual(s, w) {
1475
- return s + " ".repeat(Math.max(0, w - visualLen(s)));
1476
- }
1477
1406
  function render() {
1478
1407
  const cols = process.stdout.columns || 120;
1479
1408
  const rows = process.stdout.rows || 30;
1480
- const leftW = Math.floor(cols * 0.5);
1409
+ const leftW = Math.floor(cols * 0.35);
1481
1410
  const rightW = cols - leftW - 1;
1482
- const listH = rows - 5;
1411
+ const topH = Math.floor((rows - 4) * 0.55);
1412
+ const botH = rows - 4 - topH;
1483
1413
  clear();
1484
1414
  moveTo(1, 1);
1485
1415
  const tabStr = TABS.map((t) => tab === t.key ? inverse(` ${t.num}:${t.label} `) : dim(` ${t.num}:${t.label} `)).join("");
1486
- process.stdout.write(` ${bold(cyan("OPEN XGEN"))} ${dim("v2.4")} ${dim(serverDisplay)} \u2502 ${tabStr}`);
1416
+ process.stdout.write(` ${bold(cyan("OPEN XGEN"))} ${dim("v3")} ${dim(sv)} \u2502 ${tabStr}`);
1487
1417
  moveTo(2, 1);
1488
1418
  process.stdout.write(dim("\u2500".repeat(leftW) + "\u252C" + "\u2500".repeat(rightW)));
1489
1419
  const items = getItems();
1490
- const vis = Math.max(1, listH);
1491
- const scr = items.length <= vis ? 0 : Math.max(0, Math.min(selected - Math.floor(vis / 2), items.length - vis));
1492
- for (let i = 0; i < vis; 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++) {
1493
1423
  moveTo(3 + i, 1);
1494
1424
  const idx = scr + i;
1495
1425
  let left;
1496
1426
  if (idx < items.length) {
1497
1427
  const item = items[idx];
1498
1428
  const sub = item.sub ? dim(` ${item.sub}`) : "";
1499
- left = padVisual(`${idx === selected ? "\u25B8" : " "} ${String(idx + 1).padStart(2)}. ${item.label}${sub}`, leftW);
1500
- if (idx === selected) left = inverse(left);
1429
+ left = padV(`${idx === sel ? "\u25B8" : " "} ${String(idx + 1).padStart(2)}. ${item.label}${sub}`, leftW);
1430
+ if (idx === sel) left = inverse(left);
1501
1431
  } else {
1502
1432
  left = " ".repeat(leftW);
1503
1433
  }
1504
1434
  process.stdout.write(left);
1505
1435
  process.stdout.write(dim("\u2502"));
1506
- if (i < detail.length) process.stdout.write(` ${detail[i]}`);
1507
- }
1508
- if (items.length === 0) {
1509
- moveTo(4, 3);
1510
- process.stdout.write(dim("(\uD56D\uBAA9 \uC5C6\uC74C)"));
1436
+ if (i < sandbox.length) process.stdout.write(` ${sandbox[i]}`);
1511
1437
  }
1512
- moveTo(rows - 2, 1);
1438
+ moveTo(3 + topH, 1);
1513
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));
1448
+ }
1449
+ }
1514
1450
  moveTo(rows - 1, 1);
1515
- if (inputMode || formCtx) {
1516
- const pr = formCtx ? `${formCtx.fields[formCtx.step].label}${formCtx.fields[formCtx.step].required ? "*" : ""} \u276F ` : "\u276F ";
1517
- process.stdout.write(` ${cyan(pr)}${inputBuffer}`);
1451
+ if (inputActive) {
1452
+ const ctx = tab === "workflows" && workflows[sel] ? `${workflows[sel].name} ` : "";
1453
+ process.stdout.write(` ${cyan(ctx + "\u276F")} ${inputBuf}`);
1518
1454
  showCursor();
1519
1455
  } else {
1520
1456
  process.stdout.write(dim(` ${getHint()}`));
@@ -1523,8 +1459,14 @@ async function startRawTui() {
1523
1459
  moveTo(rows, 1);
1524
1460
  process.stdout.write(dim(` ${statusMsg}`.slice(0, cols)));
1525
1461
  }
1462
+ function addChat(line) {
1463
+ chatLines.push(line);
1464
+ if (chatLines.length > 200) chatLines = chatLines.slice(-100);
1465
+ }
1526
1466
  async function runWorkflow(wf, input) {
1527
- detail = [bold(wf.name), "", `\uC785\uB825: ${input}`, "", yellow("\uC2E4\uD589 \uC911...")];
1467
+ addChat(`${cyan("\u276F")} ${input}`);
1468
+ addChat(yellow(` ${wf.name} \uC2E4\uD589 \uC911...`));
1469
+ sandbox = [bold(wf.name), "", yellow("\uC2E4\uD589 \uC911...")];
1528
1470
  render();
1529
1471
  try {
1530
1472
  const { executeWorkflow: executeWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
@@ -1536,497 +1478,139 @@ async function startRawTui() {
1536
1478
  interaction_id: `tui_${randomUUID4().slice(0, 8)}`,
1537
1479
  user_id: auth?.userId ? parseInt(auth.userId) : 1
1538
1480
  }), 3e4, "\uC2E4\uD589");
1539
- const content = r?.content ?? r?.message ?? r?.result ?? JSON.stringify(r).slice(0, 500);
1540
- detail = [bold(wf.name), "", `\uC785\uB825: ${input}`, "", green("\uACB0\uACFC:"), ...String(content).split("\n").slice(0, 15)];
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)];
1541
1487
  } catch (err) {
1542
- detail = [bold(wf.name), "", red(`\uC2E4\uD328: ${err.message}`)];
1488
+ addChat(red(` \uC2E4\uD328: ${err.message}`));
1489
+ sandbox = [bold(wf.name), "", red(`\uC2E4\uD328: ${err.message}`)];
1543
1490
  }
1544
1491
  statusMsg = getHint();
1545
1492
  render();
1546
1493
  }
1547
1494
  async function loadWorkflowStructure(wf) {
1548
- detail = [bold(wf.name), "", yellow("\uB178\uB4DC/\uC5E3\uC9C0 \uB85C\uB529...")];
1495
+ sandbox = [bold(wf.name), "", yellow("\uB178\uB4DC/\uC5E3\uC9C0 \uB85C\uB529...")];
1549
1496
  render();
1550
1497
  try {
1551
1498
  const { getWorkflowDetail: getWorkflowDetail2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1552
- const data = await withTimeout(getWorkflowDetail2(wf.id), 1e4, "\uC6CC\uD06C\uD50C\uB85C\uC6B0 \uB85C\uB4DC");
1553
- const wfData = data?.workflow_data ?? data;
1554
- const ns = wfData?.nodes ?? data?.nodes ?? [];
1555
- const es = wfData?.edges ?? data?.edges ?? [];
1556
- detail = [
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 = [
1557
1504
  bold(wf.name),
1558
- "",
1559
1505
  `\uB178\uB4DC ${ns.length}\uAC1C \uC5E3\uC9C0 ${es.length}\uAC1C`,
1560
1506
  "",
1561
- bold("\uB178\uB4DC:"),
1562
- ...ns.slice(0, 15).map((n, i) => {
1563
- const label = n.data?.label ?? n.data?.nodeName ?? n.type ?? "?";
1564
- const nodeType = n.data?.nodeId ?? n.type ?? "";
1565
- return ` ${i + 1}. ${label} ${dim(nodeType)}`;
1566
- }),
1567
- ...ns.length > 15 ? [dim(` ... +${ns.length - 15}\uAC1C`)] : [],
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`)] : [],
1568
1509
  "",
1569
1510
  bold("\uC5F0\uACB0:"),
1570
- ...es.slice(0, 8).map((e) => {
1571
- const src = ns.find((n) => n.id === e.source);
1572
- const tgt = ns.find((n) => n.id === e.target);
1573
- const srcName = src?.data?.label ?? src?.data?.nodeName ?? e.source;
1574
- const tgtName = tgt?.data?.label ?? tgt?.data?.nodeName ?? e.target;
1575
- return ` ${srcName} \u2192 ${tgtName}`;
1576
- }),
1577
- ...es.length > 8 ? [dim(` ... +${es.length - 8}\uAC1C`)] : [],
1578
- "",
1579
- cyan("i") + " \uC9C8\uBB38 \uC785\uB825 \uD6C4 \uC2E4\uD589"
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
+ })
1580
1516
  ];
1581
1517
  } catch (err) {
1582
- detail = [bold(wf.name), "", red(`\uB85C\uB4DC \uC2E4\uD328: ${err.message}`)];
1518
+ sandbox = [bold(wf.name), "", red(err.message)];
1583
1519
  }
1584
1520
  render();
1585
1521
  }
1586
1522
  async function loadNodeDetail(n) {
1587
1523
  if (!n.id) {
1588
- detail = [bold(n.name), "", red("\uB178\uB4DC ID \uC5C6\uC74C")];
1524
+ sandbox = [red("\uB178\uB4DC ID \uC5C6\uC74C")];
1589
1525
  render();
1590
1526
  return;
1591
1527
  }
1592
- detail = [bold(n.name), "", yellow("\uC0C1\uC138 \uB85C\uB529...")];
1528
+ sandbox = [bold(n.name), yellow("\uB85C\uB529...")];
1593
1529
  render();
1594
1530
  try {
1595
1531
  const { getNodeDetail: getNodeDetail2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1596
- const d = await withTimeout(getNodeDetail2(n.id), 8e3, "\uB178\uB4DC \uC0C1\uC138");
1597
- const inputs = d?.inputs ?? d?.inputPorts ?? [];
1598
- const outputs = d?.outputs ?? d?.outputPorts ?? [];
1599
- const params = d?.parameters ?? d?.params ?? d?.defaultParams ?? {};
1600
- const paramKeys = typeof params === "object" ? Object.keys(params) : [];
1601
- detail = [
1602
- bold(d?.nodeName ?? d?.name ?? n.name),
1603
- "",
1604
- `ID ${d?.nodeId ?? d?.id ?? n.id}`,
1605
- `\uCE74\uD14C\uACE0\uB9AC ${d?.category ?? d?.nodeCategory ?? dim("\uC5C6\uC74C")}`,
1606
- `\uC124\uBA85 ${d?.description ?? dim("\uC5C6\uC74C")}`,
1607
- "",
1608
- bold(`\uC785\uB825 \uD3EC\uD2B8 (${inputs.length}):`),
1609
- ...inputs.slice(0, 5).map((p) => ` \u2022 ${p.name ?? p.id ?? "?"} ${dim(p.type ?? "")}`),
1610
- "",
1611
- bold(`\uCD9C\uB825 \uD3EC\uD2B8 (${outputs.length}):`),
1612
- ...outputs.slice(0, 5).map((p) => ` \u2022 ${p.name ?? p.id ?? "?"} ${dim(p.type ?? "")}`),
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 ?? "?"}`,
1613
1541
  "",
1614
- bold(`\uD30C\uB77C\uBBF8\uD130 (${paramKeys.length}):`),
1615
- ...paramKeys.slice(0, 8).map((k) => ` \u2022 ${k}: ${dim(String(params[k]).slice(0, 30))}`)
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))}`)
1616
1548
  ];
1617
1549
  } catch (err) {
1618
- detail = [bold(n.name), "", red(`\uC2E4\uD328: ${err.message}`)];
1550
+ sandbox = [red(err.message)];
1619
1551
  }
1620
1552
  render();
1621
1553
  }
1622
- function startCreatePrompt() {
1623
- startForm("create-prompt", "\uC0C8 \uD504\uB86C\uD504\uD2B8 \uC0DD\uC131", [
1624
- { key: "prompt_title", label: "\uC81C\uBAA9", required: true },
1625
- { key: "prompt_content", label: "\uB0B4\uC6A9", required: true },
1626
- { key: "prompt_type", label: "\uD0C0\uC785 (user/system)", default: "user" },
1627
- { key: "language", label: "\uC5B8\uC5B4 (ko/en)", default: "ko" }
1628
- ], async (v) => {
1629
- detail = [bold("\uD504\uB86C\uD504\uD2B8 \uC0DD\uC131"), "", yellow("\uC800\uC7A5 \uC911...")];
1630
- render();
1631
- try {
1632
- const { createPrompt: createPrompt2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1633
- await withTimeout(createPrompt2({ prompt_title: v.prompt_title, prompt_content: v.prompt_content, prompt_type: v.prompt_type || "user", language: v.language || "ko" }), 1e4, "\uC800\uC7A5");
1634
- detail = [bold("\uD504\uB86C\uD504\uD2B8 \uC0DD\uC131"), "", green("\u2713 \uC800\uC7A5 \uC644\uB8CC!"), "", "r \uC0C8\uB85C\uACE0\uCE68"];
1635
- statusMsg = green("\uC800\uC7A5 \uC644\uB8CC");
1636
- } catch (err) {
1637
- detail = [red(`\u2717 \uC2E4\uD328: ${err.message}`)];
1638
- statusMsg = red("\uC2E4\uD328");
1639
- }
1640
- render();
1641
- });
1642
- }
1643
- function startEditPrompt() {
1644
- if (!prompts[selected]) return;
1645
- const p = prompts[selected];
1646
- if (!p.uid) {
1647
- detail = [red("UID \uC5C6\uC74C \u2014 \uC218\uC815 \uBD88\uAC00")];
1648
- render();
1649
- return;
1650
- }
1651
- startForm("edit-prompt", `\uD504\uB86C\uD504\uD2B8 \uC218\uC815: ${p.name}`, [
1652
- { key: "prompt_title", label: "\uC81C\uBAA9", default: p.name },
1653
- { key: "prompt_content", label: "\uB0B4\uC6A9", default: p.content || "" },
1654
- { key: "prompt_type", label: "\uD0C0\uC785", default: p.type || "user" }
1655
- ], async (v) => {
1656
- detail = [yellow("\uC218\uC815 \uC911...")];
1657
- render();
1658
- try {
1659
- const { updatePrompt: updatePrompt2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1660
- await withTimeout(updatePrompt2({ prompt_uid: p.uid, prompt_title: v.prompt_title, prompt_content: v.prompt_content, prompt_type: v.prompt_type }), 1e4, "\uC218\uC815");
1661
- detail = [green("\u2713 \uC218\uC815 \uC644\uB8CC!"), "", "r \uC0C8\uB85C\uACE0\uCE68"];
1662
- statusMsg = green("\uC218\uC815 \uC644\uB8CC");
1663
- } catch (err) {
1664
- detail = [red(`\u2717 \uC2E4\uD328: ${err.message}`)];
1665
- statusMsg = red("\uC2E4\uD328");
1666
- }
1667
- render();
1668
- });
1669
- }
1670
- async function deleteSelectedPrompt() {
1671
- if (!prompts[selected]?.uid) {
1672
- detail = [red("UID \uC5C6\uC74C")];
1673
- render();
1674
- return;
1675
- }
1676
- const p = prompts[selected];
1677
- detail = [bold(p.name), "", yellow("\uC0AD\uC81C \uC911...")];
1678
- render();
1679
- try {
1680
- const { deletePrompt: deletePrompt2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1681
- await withTimeout(deletePrompt2(p.uid), 1e4, "\uC0AD\uC81C");
1682
- detail = [green("\u2713 \uC0AD\uC81C \uC644\uB8CC!"), "", "r \uC0C8\uB85C\uACE0\uCE68"];
1683
- statusMsg = green("\uC0AD\uC81C \uC644\uB8CC");
1684
- } catch (err) {
1685
- detail = [red(`\uC0AD\uC81C \uC2E4\uD328: ${err.message}`)];
1686
- statusMsg = red("\uC2E4\uD328");
1687
- }
1688
- render();
1689
- }
1690
- async function uploadSelectedPromptToStore() {
1691
- if (!prompts[selected]?.dbId) {
1692
- detail = [red("DB ID \uC5C6\uC74C")];
1693
- render();
1694
- return;
1695
- }
1696
- const p = prompts[selected];
1697
- detail = [bold(p.name), "", yellow("\uC2A4\uD1A0\uC5B4 \uB4F1\uB85D \uC911...")];
1698
- render();
1699
- try {
1700
- const { uploadPromptToStore: uploadPromptToStore2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1701
- await withTimeout(uploadPromptToStore2(p.dbId), 1e4, "\uC2A4\uD1A0\uC5B4");
1702
- detail = [green("\u2713 \uC2A4\uD1A0\uC5B4 \uB4F1\uB85D \uC644\uB8CC!")];
1703
- statusMsg = green("\uB4F1\uB85D \uC644\uB8CC");
1704
- } catch (err) {
1705
- detail = [red(`\uC2E4\uD328: ${err.message}`)];
1706
- statusMsg = red("\uC2E4\uD328");
1707
- }
1708
- render();
1709
- }
1710
- async function showPromptVersions() {
1711
- if (!prompts[selected]?.uid) {
1712
- detail = [red("UID \uC5C6\uC74C")];
1554
+ async function testTool(t) {
1555
+ if (!t.url) {
1556
+ sandbox = [red("URL \uC5C6\uC74C")];
1713
1557
  render();
1714
1558
  return;
1715
1559
  }
1716
- const p = prompts[selected];
1717
- detail = [bold(p.name), "", yellow("\uBC84\uC804 \uB85C\uB529...")];
1718
- render();
1719
- try {
1720
- const { listPromptVersions: listPromptVersions2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1721
- const vers = await withTimeout(listPromptVersions2(p.uid), 8e3, "\uBC84\uC804");
1722
- if (!vers.length) {
1723
- detail = [bold(p.name), "", dim("\uBC84\uC804 \uC5C6\uC74C")];
1724
- } else {
1725
- detail = [
1726
- bold(p.name),
1727
- "",
1728
- bold(`\uBC84\uC804 ${vers.length}\uAC1C:`),
1729
- "",
1730
- ...vers.slice(0, 15).map((v, i) => ` ${i + 1}. v${v.version ?? v.id ?? "?"} ${dim(v.label_name ?? v.created_at ?? "")}`)
1731
- ];
1732
- }
1733
- } catch (err) {
1734
- detail = [red(`\uC2E4\uD328: ${err.message}`)];
1735
- }
1736
- render();
1737
- }
1738
- function startCreateTool() {
1739
- startForm("create-tool", "\uC0C8 \uB3C4\uAD6C \uC0DD\uC131", [
1740
- { key: "function_name", label: "\uB3C4\uAD6C \uC774\uB984", required: true },
1741
- { key: "function_id", label: "\uB3C4\uAD6C ID (\uC601\uBB38)", required: true },
1742
- { key: "description", label: "\uC124\uBA85" },
1743
- { key: "api_url", label: "API URL", required: true },
1744
- { key: "api_method", label: "HTTP \uBA54\uC11C\uB4DC", default: "GET" },
1745
- { key: "api_timeout", label: "\uD0C0\uC784\uC544\uC6C3(\uCD08)", default: "30" }
1746
- ], async (v) => {
1747
- detail = [bold("\uB3C4\uAD6C \uC0DD\uC131"), "", yellow("\uC800\uC7A5 \uC911...")];
1748
- render();
1749
- try {
1750
- const { saveTool: saveTool2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1751
- await withTimeout(saveTool2(v.function_name, {
1752
- function_name: v.function_name,
1753
- function_id: v.function_id,
1754
- description: v.description || "",
1755
- api_url: v.api_url,
1756
- api_method: v.api_method || "GET",
1757
- api_timeout: parseInt(v.api_timeout || "30")
1758
- }), 1e4, "\uC800\uC7A5");
1759
- detail = [green("\u2713 \uB3C4\uAD6C \uC800\uC7A5 \uC644\uB8CC!"), "", "r \uC0C8\uB85C\uACE0\uCE68"];
1760
- statusMsg = green("\uC800\uC7A5 \uC644\uB8CC");
1761
- } catch (err) {
1762
- detail = [red(`\u2717 \uC2E4\uD328: ${err.message}`)];
1763
- statusMsg = red("\uC2E4\uD328");
1764
- }
1765
- render();
1766
- });
1767
- }
1768
- async function testSelectedTool() {
1769
- if (!tools2[selected]) return;
1770
- const t = tools2[selected];
1771
- if (!t.apiUrl) {
1772
- detail = [bold(t.name), "", red("API URL \uC5C6\uC74C")];
1773
- render();
1774
- return;
1775
- }
1776
- detail = [bold(t.name), "", yellow("API \uD14C\uC2A4\uD2B8 \uC911..."), `URL: ${t.apiUrl}`];
1560
+ sandbox = [bold(t.name), yellow(`\uD14C\uC2A4\uD2B8: ${t.url}`)];
1561
+ addChat(yellow(` \uB3C4\uAD6C \uD14C\uC2A4\uD2B8: ${t.name}`));
1777
1562
  render();
1778
1563
  try {
1779
1564
  const { apiTest: apiTest2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1780
- const r = await withTimeout(apiTest2({ api_url: t.apiUrl, api_method: "GET", api_timeout: 15 }), 2e4, "\uD14C\uC2A4\uD2B8");
1565
+ const r = await withTimeout(apiTest2({ api_url: t.url, api_method: "GET", api_timeout: 10 }), 15e3, "\uD14C\uC2A4\uD2B8");
1781
1566
  const ok = r?.success ?? (r?.data?.status && r.data.status < 400);
1782
1567
  const st = r?.data?.status ?? "?";
1783
1568
  const resp = r?.data?.response;
1784
- const lines = (typeof resp === "string" ? resp : JSON.stringify(resp ?? {}, null, 2)).split("\n").slice(0, 12);
1785
- detail = [bold(t.name), "", ok ? green(`\u2713 ${st} OK`) : red(`\u2717 ${st} FAIL`), "", bold("\uC751\uB2F5:"), ...lines];
1786
- statusMsg = ok ? green("\uD14C\uC2A4\uD2B8 \uC131\uACF5") : red("\uD14C\uC2A4\uD2B8 \uC2E4\uD328");
1787
- } catch (err) {
1788
- detail = [bold(t.name), "", red(`\uC2E4\uD328: ${err.message}`)];
1789
- statusMsg = red("\uC2E4\uD328");
1790
- }
1791
- render();
1792
- }
1793
- async function uploadSelectedTool() {
1794
- if (!tools2[selected]?.functionId) {
1795
- detail = [red("function_id \uC5C6\uC74C")];
1796
- render();
1797
- return;
1798
- }
1799
- const t = tools2[selected];
1800
- detail = [bold(t.name), "", yellow("\uC2A4\uD1A0\uC5B4 \uB4F1\uB85D \uC911...")];
1801
- render();
1802
- try {
1803
- const { uploadToolToStore: uploadToolToStore2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1804
- await withTimeout(uploadToolToStore2(t.functionId, t.desc), 1e4, "\uB4F1\uB85D");
1805
- detail = [green("\u2713 \uC2A4\uD1A0\uC5B4 \uB4F1\uB85D \uC644\uB8CC!")];
1806
- statusMsg = green("\uB4F1\uB85D \uC644\uB8CC");
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`));
1807
1572
  } catch (err) {
1808
- detail = [red(`\uC2E4\uD328: ${err.message}`)];
1809
- statusMsg = red("\uC2E4\uD328");
1573
+ sandbox = [red(err.message)];
1574
+ addChat(red(` \uC2E4\uD328: ${err.message}`));
1810
1575
  }
1811
1576
  render();
1812
1577
  }
1813
- function startCreateMcpSession() {
1814
- startForm("create-mcp", "MCP \uC138\uC158 \uC0DD\uC131", [
1815
- { key: "session_name", label: "\uC138\uC158 \uC774\uB984", required: true },
1816
- { key: "server_type", label: "\uD0C0\uC785 (python/node)", required: true, default: "python" },
1817
- { key: "server_command", label: "\uC11C\uBC84 \uCEE4\uB9E8\uB4DC", required: true },
1818
- { key: "server_args", label: "\uCD94\uAC00 \uC778\uC790 (\uC27C\uD45C \uAD6C\uBD84)" },
1819
- { key: "working_dir", label: "\uC791\uC5C5 \uB514\uB809\uD1A0\uB9AC" }
1820
- ], async (v) => {
1821
- detail = [yellow("MCP \uC138\uC158 \uC0DD\uC131 \uC911...")];
1822
- render();
1823
- try {
1824
- const { createMcpSession: createMcpSession2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1825
- const args = v.server_args ? v.server_args.split(",").map((a) => a.trim()) : void 0;
1826
- const r = await withTimeout(createMcpSession2({
1827
- server_type: v.server_type,
1828
- server_command: v.server_command,
1829
- session_name: v.session_name,
1830
- server_args: args,
1831
- working_dir: v.working_dir || void 0
1832
- }), 15e3, "MCP \uC138\uC158");
1833
- detail = [green("\u2713 \uC138\uC158 \uC0DD\uC131 \uC644\uB8CC!"), "", `\uC138\uC158ID: ${r?.session_id ?? "?"}`, "", "r \uC0C8\uB85C\uACE0\uCE68"];
1834
- statusMsg = green("MCP \uC138\uC158 \uC0DD\uC131 \uC644\uB8CC");
1835
- } catch (err) {
1836
- detail = [red(`\u2717 \uC2E4\uD328: ${err.message}`)];
1837
- statusMsg = red("\uC2E4\uD328");
1838
- }
1839
- render();
1840
- });
1841
- }
1842
- async function loadMcpTools() {
1843
- if (!mcpSessions[selected]) return;
1844
- const s = mcpSessions[selected];
1845
- detail = [bold(s.name), "", yellow("\uB3C4\uAD6C \uBAA9\uB85D \uB85C\uB529...")];
1846
- render();
1847
- try {
1848
- const { getMcpSessionTools: getMcpSessionTools2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1849
- const tools3 = await withTimeout(getMcpSessionTools2(s.id), 1e4, "MCP \uB3C4\uAD6C");
1850
- if (!tools3.length) {
1851
- detail = [bold(s.name), "", dim("\uB3C4\uAD6C \uC5C6\uC74C")];
1852
- } else {
1853
- detail = [
1854
- bold(s.name),
1855
- "",
1856
- bold(`\uB3C4\uAD6C ${tools3.length}\uAC1C:`),
1857
- "",
1858
- ...tools3.slice(0, 15).map((t, i) => {
1859
- const name = t.name ?? t.function?.name ?? "?";
1860
- const desc = (t.description ?? t.function?.description ?? "").slice(0, 40);
1861
- return ` ${i + 1}. ${name} ${dim(desc)}`;
1862
- }),
1863
- ...tools3.length > 15 ? [dim(` ... +${tools3.length - 15}\uAC1C`)] : [],
1864
- "",
1865
- cyan("t") + " \uB3C4\uAD6C \uD638\uCD9C \uD14C\uC2A4\uD2B8"
1866
- ];
1867
- }
1868
- } catch (err) {
1869
- detail = [bold(s.name), "", red(`\uC2E4\uD328: ${err.message}`)];
1870
- }
1871
- render();
1872
- }
1873
- function startMcpToolCall() {
1874
- if (!mcpSessions[selected]) return;
1875
- const s = mcpSessions[selected];
1876
- startForm("mcp-call", `MCP \uB3C4\uAD6C \uD638\uCD9C: ${s.name}`, [
1877
- { key: "tool_name", label: "\uB3C4\uAD6C \uC774\uB984", required: true },
1878
- { key: "params_json", label: "\uD30C\uB77C\uBBF8\uD130 (JSON)", default: "{}" }
1879
- ], async (v) => {
1880
- detail = [yellow("MCP \uB3C4\uAD6C \uD638\uCD9C \uC911...")];
1881
- render();
1882
- try {
1883
- const { sendMcpRequest: sendMcpRequest2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1884
- let params = {};
1885
- try {
1886
- params = JSON.parse(v.params_json || "{}");
1887
- } catch {
1888
- }
1889
- const r = await withTimeout(sendMcpRequest2(s.id, "tools/call", { name: v.tool_name, arguments: params }), 2e4, "MCP \uD638\uCD9C");
1890
- const ok = r?.success;
1891
- const data = r?.data;
1892
- const lines = (typeof data === "string" ? data : JSON.stringify(data ?? {}, null, 2)).split("\n").slice(0, 15);
1893
- detail = [bold(`${s.name} \u2192 ${v.tool_name}`), "", ok ? green("\u2713 \uC131\uACF5") : red("\u2717 \uC2E4\uD328"), "", bold("\uC751\uB2F5:"), ...lines];
1894
- statusMsg = ok ? green("\uD638\uCD9C \uC131\uACF5") : red("\uD638\uCD9C \uC2E4\uD328");
1895
- } catch (err) {
1896
- detail = [red(`\uC2E4\uD328: ${err.message}`)];
1897
- statusMsg = red("\uC2E4\uD328");
1898
- }
1899
- render();
1900
- });
1901
- }
1902
- async function deleteSelectedMcpSession() {
1903
- if (!mcpSessions[selected]) return;
1904
- const s = mcpSessions[selected];
1905
- detail = [bold(s.name), "", yellow("\uC0AD\uC81C \uC911...")];
1906
- render();
1907
- try {
1908
- const { deleteMcpSession: deleteMcpSession2 } = await Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports));
1909
- await withTimeout(deleteMcpSession2(s.id), 1e4, "\uC0AD\uC81C");
1910
- detail = [green("\u2713 \uC138\uC158 \uC0AD\uC81C \uC644\uB8CC!"), "", "r \uC0C8\uB85C\uACE0\uCE68"];
1911
- statusMsg = green("\uC0AD\uC81C \uC644\uB8CC");
1912
- } catch (err) {
1913
- detail = [red(`\uC2E4\uD328: ${err.message}`)];
1914
- statusMsg = red("\uC2E4\uD328");
1915
- }
1916
- render();
1917
- }
1918
- function startForm(type, title, fields, onSubmit) {
1919
- formCtx = { type, fields, step: 0, values: {}, onSubmit };
1920
- inputBuffer = "";
1921
- detail = [
1922
- bold(title),
1923
- "",
1924
- ...fields.map((f, i) => ` ${i === 0 ? cyan("\u25B8") : " "} ${f.label}${f.required ? red("*") : ""} ${f.default ? dim(`(\uAE30\uBCF8: ${f.default})`) : ""}`),
1925
- "",
1926
- dim("Enter: \uB2E4\uC74C \u2502 Esc: \uCDE8\uC18C")
1927
- ];
1928
- statusMsg = `${fields[0].label} \uC785\uB825`;
1929
- render();
1930
- }
1931
1578
  function handleKey(s) {
1932
- if (formCtx) {
1933
- if (s === "\x1B" || s === "\x1B\x1B") {
1934
- formCtx = null;
1935
- inputBuffer = "";
1936
- inputMode = false;
1937
- updateDetail();
1938
- statusMsg = getHint();
1579
+ if (inputActive) {
1580
+ if (s === "\x1B") {
1581
+ inputActive = false;
1582
+ inputBuf = "";
1939
1583
  render();
1940
1584
  return;
1941
1585
  }
1942
1586
  if (s === "\r" || s === "\n") {
1943
- const field = formCtx.fields[formCtx.step];
1944
- const value = inputBuffer.trim() || field.default || "";
1945
- if (field.required && !value) {
1946
- statusMsg = red(`${field.label} \uD544\uC218`);
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."));
1947
1595
  render();
1948
- return;
1949
- }
1950
- formCtx.values[field.key] = value;
1951
- inputBuffer = "";
1952
- formCtx.step++;
1953
- if (formCtx.step >= formCtx.fields.length) {
1954
- const ctx = formCtx;
1955
- formCtx = null;
1956
- inputMode = false;
1957
- ctx.onSubmit(ctx.values);
1958
1596
  } else {
1959
- const nf = formCtx.fields[formCtx.step];
1960
- detail = [
1961
- bold(formCtx.type.includes("tool") ? "\uC0C8 \uB3C4\uAD6C" : formCtx.type.includes("prompt") ? "\uC0C8 \uD504\uB86C\uD504\uD2B8" : formCtx.type.includes("mcp") ? "MCP" : "\uC785\uB825"),
1962
- "",
1963
- ...formCtx.fields.map((f, i) => {
1964
- const val = formCtx.values[f.key];
1965
- if (val !== void 0) return ` ${green("\u2713")} ${f.label}: ${val}`;
1966
- if (i === formCtx.step) return ` ${cyan("\u25B8")} ${f.label}${f.required ? red("*") : ""} ${f.default ? dim(`(\uAE30\uBCF8: ${f.default})`) : ""}`;
1967
- return ` ${f.label}`;
1968
- }),
1969
- "",
1970
- dim(`${formCtx.step + 1}/${formCtx.fields.length} \u2502 Enter \u2502 Esc`)
1971
- ];
1972
- statusMsg = `${nf.label} \uC785\uB825`;
1973
1597
  render();
1974
1598
  }
1975
1599
  return;
1976
1600
  }
1977
1601
  if (s === "\x7F" || s === "\b") {
1978
- inputBuffer = inputBuffer.slice(0, -1);
1602
+ inputBuf = inputBuf.slice(0, -1);
1979
1603
  render();
1980
1604
  return;
1981
1605
  }
1982
1606
  if (s === "") {
1983
- formCtx = null;
1984
- inputBuffer = "";
1985
- inputMode = false;
1986
- updateDetail();
1607
+ inputActive = false;
1608
+ inputBuf = "";
1987
1609
  render();
1988
1610
  return;
1989
1611
  }
1990
- if (s.length > 0 && s.charCodeAt(0) >= 32) {
1991
- inputBuffer += s;
1992
- render();
1993
- return;
1994
- }
1995
- return;
1996
- }
1997
- if (inputMode) {
1998
- if (s === "\x1B" || s === "\x1B\x1B") {
1999
- inputMode = false;
2000
- inputBuffer = "";
2001
- updateDetail();
2002
- statusMsg = getHint();
2003
- render();
2004
- return;
2005
- }
2006
- if (s === "\r" || s === "\n") {
2007
- const val = inputBuffer.trim();
2008
- inputMode = false;
2009
- inputBuffer = "";
2010
- if (val && tab === "workflows" && workflows[selected]) {
2011
- runWorkflow(workflows[selected], val);
2012
- } else {
2013
- render();
2014
- }
2015
- return;
2016
- }
2017
- if (s === "\x7F" || s === "\b") {
2018
- inputBuffer = inputBuffer.slice(0, -1);
2019
- render();
2020
- return;
2021
- }
2022
- if (s === "") {
2023
- inputMode = false;
2024
- inputBuffer = "";
2025
- render();
2026
- return;
2027
- }
2028
- if (s.length > 0 && s.charCodeAt(0) >= 32) {
2029
- inputBuffer += s;
1612
+ if (s.charCodeAt(0) >= 32) {
1613
+ inputBuf += s;
2030
1614
  render();
2031
1615
  return;
2032
1616
  }
@@ -2040,92 +1624,103 @@ async function startRawTui() {
2040
1624
  loadData();
2041
1625
  return;
2042
1626
  }
2043
- if (s === "i" && tab === "workflows" && workflows[selected]) {
2044
- inputMode = true;
2045
- inputBuffer = "";
2046
- statusMsg = `${workflows[selected].name} \u2014 \uC9C8\uBB38 \uC785\uB825 \uD6C4 Enter, Esc \uCDE8\uC18C`;
1627
+ if (s.length === 1 && s.charCodeAt(0) >= 32 && !/^[0-9cutedvq]$/i.test(s)) {
1628
+ inputActive = true;
1629
+ inputBuf = s;
2047
1630
  render();
2048
1631
  return;
2049
1632
  }
2050
- if (s === "c" || s === "C") {
1633
+ if (/^[cC]$/.test(s)) {
2051
1634
  if (tab === "tools") {
2052
- startCreateTool();
1635
+ addChat(cyan(" \uB3C4\uAD6C \uC0DD\uC131\uC740 xgen agent\uC5D0\uC11C: '\uC0C8 \uB3C4\uAD6C \uB9CC\uB4E4\uC5B4\uC918'"));
1636
+ render();
2053
1637
  return;
2054
1638
  }
2055
1639
  if (tab === "prompts") {
2056
- startCreatePrompt();
2057
- return;
2058
- }
2059
- if (tab === "mcp") {
2060
- startCreateMcpSession();
2061
- return;
2062
- }
2063
- }
2064
- if (s === "t" || s === "T") {
2065
- if (tab === "tools") {
2066
- testSelectedTool();
1640
+ addChat(cyan(" \uD504\uB86C\uD504\uD2B8 \uC0DD\uC131\uC740 xgen agent\uC5D0\uC11C: '\uD504\uB86C\uD504\uD2B8 \uB9CC\uB4E4\uC5B4\uC918'"));
1641
+ render();
2067
1642
  return;
2068
1643
  }
2069
1644
  if (tab === "mcp") {
2070
- startMcpToolCall();
2071
- return;
2072
- }
2073
- }
2074
- if (s === "u" || s === "U") {
2075
- if (tab === "tools") {
2076
- uploadSelectedTool();
2077
- return;
2078
- }
2079
- if (tab === "prompts") {
2080
- uploadSelectedPromptToStore();
1645
+ addChat(cyan(" MCP \uC138\uC158 \uC0DD\uC131\uC740 xgen agent\uC5D0\uC11C: 'MCP \uC138\uC158 \uB9CC\uB4E4\uC5B4\uC918'"));
1646
+ render();
2081
1647
  return;
2082
1648
  }
2083
1649
  }
2084
- if (s === "e" || s === "E") {
2085
- if (tab === "prompts") {
2086
- startEditPrompt();
2087
- return;
2088
- }
1650
+ if (/^[tT]$/.test(s) && tab === "tools" && tools2[sel]) {
1651
+ testTool(tools2[sel]);
1652
+ return;
2089
1653
  }
2090
- if (s === "d" || s === "D") {
2091
- if (tab === "prompts") {
2092
- deleteSelectedPrompt();
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
+ });
2093
1668
  return;
2094
1669
  }
2095
- if (tab === "mcp") {
2096
- deleteSelectedMcpSession();
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
+ });
2097
1683
  return;
2098
1684
  }
2099
1685
  }
2100
- if (s === "v" || s === "V") {
2101
- if (tab === "prompts") {
2102
- showPromptVersions();
2103
- return;
2104
- }
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;
2105
1700
  }
2106
- const tabNum = parseInt(s);
2107
- if (tabNum >= 1 && tabNum <= 6) {
2108
- tab = TABS[tabNum - 1].key;
2109
- selected = 0;
2110
- updateDetail();
1701
+ const n = parseInt(s);
1702
+ if (n >= 1 && n <= 6) {
1703
+ tab = TABS[n - 1].key;
1704
+ sel = 0;
1705
+ updateSandbox();
2111
1706
  statusMsg = getHint();
2112
1707
  render();
2113
1708
  return;
2114
1709
  }
2115
1710
  if (s === " ") {
2116
- const idx = TABS.findIndex((t) => t.key === tab);
2117
- tab = TABS[(idx + 1) % TABS.length].key;
2118
- selected = 0;
2119
- updateDetail();
1711
+ const i = TABS.findIndex((t) => t.key === tab);
1712
+ tab = TABS[(i + 1) % TABS.length].key;
1713
+ sel = 0;
1714
+ updateSandbox();
2120
1715
  statusMsg = getHint();
2121
1716
  render();
2122
1717
  return;
2123
1718
  }
2124
1719
  if (s === "\x1B[Z") {
2125
- const idx = TABS.findIndex((t) => t.key === tab);
2126
- tab = TABS[(idx - 1 + TABS.length) % TABS.length].key;
2127
- selected = 0;
2128
- updateDetail();
1720
+ const i = TABS.findIndex((t) => t.key === tab);
1721
+ tab = TABS[(i - 1 + TABS.length) % TABS.length].key;
1722
+ sel = 0;
1723
+ updateSandbox();
2129
1724
  statusMsg = getHint();
2130
1725
  render();
2131
1726
  return;
@@ -2133,8 +1728,8 @@ async function startRawTui() {
2133
1728
  if (s === "\x1B[A") {
2134
1729
  const items = getItems();
2135
1730
  if (items.length > 0) {
2136
- selected = Math.max(0, selected - 1);
2137
- updateDetail();
1731
+ sel = Math.max(0, sel - 1);
1732
+ updateSandbox();
2138
1733
  render();
2139
1734
  }
2140
1735
  return;
@@ -2142,57 +1737,59 @@ async function startRawTui() {
2142
1737
  if (s === "\x1B[B") {
2143
1738
  const items = getItems();
2144
1739
  if (items.length > 0) {
2145
- selected = Math.min(items.length - 1, selected + 1);
2146
- updateDetail();
1740
+ sel = Math.min(items.length - 1, sel + 1);
1741
+ updateSandbox();
2147
1742
  render();
2148
1743
  }
2149
1744
  return;
2150
1745
  }
2151
1746
  if (s === "\x1B[5~") {
2152
- selected = Math.max(0, selected - 10);
2153
- updateDetail();
1747
+ sel = Math.max(0, sel - 10);
1748
+ updateSandbox();
2154
1749
  render();
2155
1750
  return;
2156
1751
  }
2157
1752
  if (s === "\x1B[6~") {
2158
1753
  const items = getItems();
2159
- selected = Math.min(items.length - 1, selected + 10);
2160
- updateDetail();
1754
+ sel = Math.min(items.length - 1, sel + 10);
1755
+ updateSandbox();
2161
1756
  render();
2162
1757
  return;
2163
1758
  }
2164
1759
  if (s === "\r" || s === "\n") {
2165
1760
  const items = getItems();
2166
1761
  if (items.length === 0) return;
2167
- if (tab === "workflows" && workflows[selected]) {
2168
- loadWorkflowStructure(workflows[selected]);
2169
- } else if (tab === "collections" && collections[selected]) {
2170
- const c = collections[selected];
2171
- detail = [bold(c.name), "", yellow("\uBB38\uC11C \uB85C\uB529...")];
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...")];
2172
1765
  render();
2173
1766
  Promise.resolve().then(() => (init_document(), document_exports)).then(async (m) => {
2174
1767
  try {
2175
- const docs = await withTimeout(m.listDocuments(String(c.id)), 8e3, "\uBB38\uC11C");
2176
- if (!docs?.length) {
2177
- detail = [bold(c.name), "", dim("\uBB38\uC11C \uC5C6\uC74C")];
2178
- } else {
2179
- detail = [bold(c.name) + ` \u2014 ${docs.length}\uAC1C`, "", ...docs.map((d, i) => ` ${i + 1}. ${d.name || d.file_name || "?"}`).slice(0, 20)];
2180
- }
2181
- } catch (err) {
2182
- detail = [red(`\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)];
2183
1772
  }
2184
1773
  render();
2185
1774
  });
2186
- } else if (tab === "nodes" && nodes[selected]) {
2187
- loadNodeDetail(nodes[selected]);
2188
- } else if (tab === "prompts" && prompts[selected]) {
2189
- const p = prompts[selected];
2190
- detail = [bold(p.name), `\uD0C0\uC785: ${p.type}`, `UID: ${p.uid || "?"}`, "", bold("\uB0B4\uC6A9:"), "", ...p.content ? p.content.split("\n").slice(0, 18) : [dim("\uC5C6\uC74C")]];
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...")];
2191
1783
  render();
2192
- } else if (tab === "tools" && tools2[selected]) {
2193
- testSelectedTool();
2194
- } else if (tab === "mcp" && mcpSessions[selected]) {
2195
- loadMcpTools();
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)];
1790
+ }
1791
+ render();
1792
+ });
2196
1793
  }
2197
1794
  return;
2198
1795
  }
@@ -2205,18 +1802,18 @@ async function startRawTui() {
2205
1802
  process.stdin.pause();
2206
1803
  }
2207
1804
  if (!process.stdin.isTTY) {
2208
- 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.");
2209
1806
  return;
2210
1807
  }
2211
1808
  process.stdin.setRawMode(true);
2212
1809
  process.stdin.resume();
2213
1810
  process.stdin.setEncoding("utf8");
2214
1811
  hideCursor();
2215
- process.stdin.on("data", (data) => {
1812
+ process.stdin.on("data", (d) => {
2216
1813
  try {
2217
- handleKey(String(data));
2218
- } catch (err) {
2219
- statusMsg = red(`\uC624\uB958: ${err.message}`);
1814
+ handleKey(String(d));
1815
+ } catch (e) {
1816
+ statusMsg = red(e.message);
2220
1817
  render();
2221
1818
  }
2222
1819
  });
@@ -3665,6 +3262,141 @@ ID: ${d.id}
3665
3262
  }
3666
3263
  };
3667
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
+
3668
3400
  // src/mcp/client.ts
3669
3401
  import { spawn } from "child_process";
3670
3402
  import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
@@ -3823,6 +3555,116 @@ var McpManager = class {
3823
3555
  }
3824
3556
  };
3825
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
+
3826
3668
  // src/commands/agent.ts
3827
3669
  init_provider();
3828
3670
  init_ui();
@@ -3860,20 +3702,13 @@ EXAMPLES OF GOOD RESPONSES:
3860
3702
 
3861
3703
  XGEN CONNECTED: ${server} as ${auth.username}
3862
3704
 
3863
- 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
3864
3707
 
3865
- 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)
3866
- DOCUMENTS: xgen_collection_list, xgen_document_list, xgen_document_upload
3867
- NODES: xgen_node_list, xgen_node_search, xgen_node_categories
3868
- PROMPTS: xgen_prompt_list
3869
- TOOLS: xgen_tool_store, xgen_user_tools
3870
- SCHEDULE: xgen_schedule_list
3871
- TRACE: xgen_trace_list, xgen_interaction_list
3872
- MCP: xgen_mcp_sessions
3873
- ONTOLOGY: xgen_graph_rag_query, xgen_graph_stats
3874
- SERVER: xgen_server_status
3708
+ ${getToolIndexSummary()}
3875
3709
 
3876
- 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.`;
3877
3712
  } else {
3878
3713
  prompt2 += `
3879
3714
  XGEN: Not connected. User can run /connect to connect.`;
@@ -3979,7 +3814,8 @@ async function agentRepl() {
3979
3814
  }
3980
3815
  }
3981
3816
  const client2 = createLLMClient(provider);
3982
- const allTools = [...getAllToolDefs(), ...definitions];
3817
+ resetLoadedTools();
3818
+ const allTools = [...getAllToolDefs(), definition8];
3983
3819
  const builtinNames = getToolNames();
3984
3820
  const mcpConfig = loadMcpConfig();
3985
3821
  if (mcpConfig && Object.keys(mcpConfig.mcpServers).length > 0) {
@@ -4150,7 +3986,7 @@ async function agentRepl() {
4150
3986
  if (input === "/tools") {
4151
3987
  console.log(`
4152
3988
  ${chalk12.bold("\uCF54\uB529")} ${builtinNames.join(", ")}`);
4153
- 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(", ")}`);
4154
3990
  if (mcpManager?.serverCount) {
4155
3991
  console.log(` ${chalk12.bold("MCP")} ${mcpManager.getAllTools().map((t) => t.function.name).join(", ")}`);
4156
3992
  }
@@ -4214,6 +4050,15 @@ async function agentRepl() {
4214
4050
  }
4215
4051
  async function runLoop(client2, model, messages, tools2) {
4216
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
+ }
4217
4062
  let first = true;
4218
4063
  const result = await streamChat(client2, model, messages, tools2, (delta) => {
4219
4064
  if (first) {
@@ -4244,23 +4089,37 @@ async function runLoop(client2, model, messages, tools2) {
4244
4089
  } catch {
4245
4090
  args = {};
4246
4091
  }
4247
- 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]) => {
4248
4099
  const s = String(v);
4249
4100
  return s.length > 40 ? s.slice(0, 40) + "\u2026" : s;
4250
4101
  }).join(", ");
4251
4102
  console.log(chalk12.dim(` \u250C ${tc.name}(${shortArgs})`));
4252
4103
  let toolResult2;
4253
- 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)) {
4254
4113
  toolResult2 = await execute8(tc.name, args);
4255
4114
  } else if (mcpManager?.isMcpTool(tc.name)) {
4256
4115
  toolResult2 = await mcpManager.callTool(tc.name, args);
4257
4116
  } else {
4258
4117
  toolResult2 = await executeTool(tc.name, args);
4259
4118
  }
4260
- const truncated = toolResult2.length > 8e3 ? toolResult2.slice(0, 8e3) + "\n\u2026(truncated)" : toolResult2;
4119
+ toolResult2 = runPostHooks(tc.name, toolResult2);
4261
4120
  const preview = toolResult2.split("\n")[0].slice(0, 60);
4262
4121
  console.log(chalk12.dim(` \u2514 ${preview}${toolResult2.length > 60 ? "\u2026" : ""}`));
4263
- messages.push({ role: "tool", tool_call_id: tc.id, content: truncated });
4122
+ messages.push({ role: "tool", tool_call_id: tc.id, content: toolResult2 });
4264
4123
  }
4265
4124
  }
4266
4125
  console.log(chalk12.yellow("\n \uCD5C\uB300 \uBC18\uBCF5 \uD69F\uC218 \uB3C4\uB2EC.\n"));
@@ -4464,7 +4323,7 @@ ${result.answer}
4464
4323
  }
4465
4324
 
4466
4325
  // src/index.ts
4467
- var VERSION = "2.4.0";
4326
+ var VERSION = "2.5.0";
4468
4327
  var LOGO = chalk15.cyan(`
4469
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
4470
4329
  \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588