openxgen 2.0.0 → 2.1.1

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
@@ -891,75 +891,6 @@ var init_provider = __esm({
891
891
  }
892
892
  });
893
893
 
894
- // src/agent/llm.ts
895
- var llm_exports = {};
896
- __export(llm_exports, {
897
- createLLMClient: () => createLLMClient,
898
- streamChat: () => streamChat
899
- });
900
- import OpenAI2 from "openai";
901
- function createLLMClient(provider) {
902
- const opts = {
903
- apiKey: provider.apiKey || "ollama"
904
- };
905
- if (provider.baseUrl) {
906
- opts.baseURL = provider.baseUrl;
907
- }
908
- return new OpenAI2(opts);
909
- }
910
- async function streamChat(client2, model, messages, tools2, onDelta) {
911
- const params = {
912
- model,
913
- messages,
914
- stream: true,
915
- stream_options: { include_usage: true }
916
- };
917
- if (tools2 && tools2.length > 0) {
918
- params.tools = tools2;
919
- }
920
- const stream = await client2.chat.completions.create(params);
921
- let content = "";
922
- let usage = null;
923
- const toolCallMap = /* @__PURE__ */ new Map();
924
- for await (const chunk of stream) {
925
- if (chunk.usage) {
926
- usage = {
927
- promptTokens: chunk.usage.prompt_tokens ?? 0,
928
- completionTokens: chunk.usage.completion_tokens ?? 0,
929
- totalTokens: chunk.usage.total_tokens ?? 0
930
- };
931
- }
932
- const delta = chunk.choices[0]?.delta;
933
- if (!delta) continue;
934
- if (delta.content) {
935
- content += delta.content;
936
- onDelta?.(delta.content);
937
- }
938
- if (delta.tool_calls) {
939
- for (const tc of delta.tool_calls) {
940
- const idx = tc.index;
941
- if (!toolCallMap.has(idx)) {
942
- toolCallMap.set(idx, { id: tc.id ?? "", name: tc.function?.name ?? "", arguments: "" });
943
- }
944
- const entry = toolCallMap.get(idx);
945
- if (tc.id) entry.id = tc.id;
946
- if (tc.function?.name) entry.name = tc.function.name;
947
- if (tc.function?.arguments) entry.arguments += tc.function.arguments;
948
- }
949
- }
950
- }
951
- return {
952
- content,
953
- toolCalls: [...toolCallMap.values()],
954
- usage
955
- };
956
- }
957
- var init_llm = __esm({
958
- "src/agent/llm.ts"() {
959
- "use strict";
960
- }
961
- });
962
-
963
894
  // src/api/xgen-extra.ts
964
895
  var xgen_extra_exports = {};
965
896
  __export(xgen_extra_exports, {
@@ -1177,334 +1108,288 @@ var init_ontology = __esm({
1177
1108
  }
1178
1109
  });
1179
1110
 
1180
- // src/dashboard/tui.ts
1181
- var tui_exports = {};
1182
- __export(tui_exports, {
1183
- startTui: () => startTui
1111
+ // src/dashboard/InkDashboard.tsx
1112
+ var InkDashboard_exports = {};
1113
+ __export(InkDashboard_exports, {
1114
+ startInkDashboard: () => startInkDashboard
1184
1115
  });
1185
- import blessed from "blessed";
1186
- async function startTui() {
1187
- const screen = blessed.screen({ smartCSR: true, title: "OPEN XGEN", fullUnicode: true });
1116
+ import { useState, useEffect } from "react";
1117
+ import { render, Box, Text, useInput, useApp, useStdout } from "ink";
1118
+ import TextInput from "ink-text-input";
1119
+ import { jsx, jsxs } from "react/jsx-runtime";
1120
+ function Header({ tab, serverDisplay, model }) {
1121
+ const tabStr = TABS.map((t) => {
1122
+ const active = tab === t.key;
1123
+ return `[${t.shortcut}]${active ? "\u25B8" : " "}${t.label}`;
1124
+ }).join(" ");
1125
+ return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
1126
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "OPEN XGEN" }),
1127
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1128
+ " ",
1129
+ model,
1130
+ " ",
1131
+ serverDisplay,
1132
+ " \u2502 ",
1133
+ tabStr
1134
+ ] })
1135
+ ] });
1136
+ }
1137
+ function ListPanel({ items, selected, onSelect }) {
1138
+ const { stdout } = useStdout();
1139
+ const height = (stdout?.rows ?? 24) - 8;
1140
+ const visibleCount = Math.max(1, height);
1141
+ const start = Math.max(0, Math.min(selected - Math.floor(visibleCount / 2), items.length - visibleCount));
1142
+ const visible = items.slice(start, start + visibleCount);
1143
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "50%", borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
1144
+ visible.map((item, i) => {
1145
+ const realIndex = start + i;
1146
+ const isSelected = realIndex === selected;
1147
+ return /* @__PURE__ */ jsxs(Text, { inverse: isSelected, children: [
1148
+ isSelected ? "\u25B8 " : " ",
1149
+ item.label,
1150
+ item.dimLabel ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1151
+ " ",
1152
+ item.dimLabel
1153
+ ] }) : null
1154
+ ] }, `item-${realIndex}`);
1155
+ }),
1156
+ items.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (\uC5C6\uC74C)" })
1157
+ ] });
1158
+ }
1159
+ function DetailPanel({ lines }) {
1160
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: "50%", borderStyle: "single", borderColor: "gray", paddingX: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx(Text, { children: line }, `line-${i}`)) });
1161
+ }
1162
+ function StatusBar({ message }) {
1163
+ return /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: message }) });
1164
+ }
1165
+ function InputBar({ value, onChange, onSubmit, placeholder }) {
1166
+ return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
1167
+ /* @__PURE__ */ jsx(Text, { children: "\u276F " }),
1168
+ /* @__PURE__ */ jsx(
1169
+ TextInput,
1170
+ {
1171
+ value,
1172
+ onChange,
1173
+ onSubmit,
1174
+ placeholder: placeholder ?? "\uC785\uB825..."
1175
+ }
1176
+ )
1177
+ ] });
1178
+ }
1179
+ function Dashboard() {
1180
+ const { exit } = useApp();
1188
1181
  const provider = getDefaultProvider();
1189
1182
  const server = getServer();
1190
1183
  const auth = getAuth();
1191
1184
  const serverDisplay = auth && server ? `${auth.username}@${server.replace("https://", "").replace("http://", "")}` : "\uBBF8\uC5F0\uACB0";
1192
- let activeTab = "workflows";
1193
- const header = blessed.box({
1194
- top: 0,
1195
- left: 0,
1196
- width: "100%",
1197
- height: 3,
1198
- tags: true,
1199
- style: { fg: "white", bg: "black" }
1200
- });
1201
- function renderHeader() {
1202
- const wf = activeTab === "workflows" ? "{bold}{underline}\uC6CC\uD06C\uD50C\uB85C\uC6B0{/underline}{/bold}" : "{gray-fg}\uC6CC\uD06C\uD50C\uB85C\uC6B0{/gray-fg}";
1203
- const col = activeTab === "collections" ? "{bold}{underline}\uCEEC\uB809\uC158{/underline}{/bold}" : "{gray-fg}\uCEEC\uB809\uC158{/gray-fg}";
1204
- header.setContent(` OPEN XGEN {gray-fg}${provider?.model ?? ""}{/gray-fg} ${serverDisplay} \u2502 [1]${wf} [2]${col}`);
1205
- screen.render();
1206
- }
1207
- const listPanel = blessed.list({
1208
- top: 3,
1209
- left: 0,
1210
- width: "50%",
1211
- height: "100%-9",
1212
- border: { type: "line" },
1213
- style: {
1214
- border: { fg: "gray" },
1215
- selected: { fg: "black", bg: "white" },
1216
- item: { fg: "white" },
1217
- label: { fg: "white", bold: true }
1218
- },
1219
- keys: true,
1220
- vi: true,
1221
- mouse: true,
1222
- scrollbar: { ch: "\u2502", style: { fg: "gray" } },
1223
- tags: true
1224
- });
1225
- const chatInput = blessed.textbox({
1226
- bottom: 3,
1227
- left: 0,
1228
- width: "50%",
1229
- height: 3,
1230
- label: " \u276F ",
1231
- border: { type: "line" },
1232
- inputOnFocus: true,
1233
- style: {
1234
- border: { fg: "gray" },
1235
- label: { fg: "white", bold: true }
1236
- }
1237
- });
1238
- const detailPanel = blessed.log({
1239
- top: 3,
1240
- left: "50%",
1241
- width: "50%",
1242
- height: "100%-6",
1243
- label: " \uC0C1\uC138 ",
1244
- border: { type: "line" },
1245
- scrollable: true,
1246
- alwaysScroll: true,
1247
- mouse: true,
1248
- tags: true,
1249
- style: {
1250
- border: { fg: "gray" },
1251
- label: { fg: "white", bold: true }
1252
- }
1253
- });
1254
- const statusBar = blessed.box({
1255
- bottom: 0,
1256
- left: 0,
1257
- width: "100%",
1258
- height: 3,
1259
- tags: true,
1260
- style: { fg: "gray", bg: "black" }
1261
- });
1262
- function renderStatusBar(msg) {
1263
- statusBar.setContent(msg ?? " {white-fg}{bold}1/2{/bold}{/white-fg}:\uD0ED {white-fg}{bold}Enter{/bold}{/white-fg}:\uC2E4\uD589 {white-fg}{bold}i{/bold}{/white-fg}:\uC785\uB825 {white-fg}{bold}r{/bold}{/white-fg}:\uC0C8\uB85C\uACE0\uCE68 {white-fg}{bold}Esc{/bold}{/white-fg}:\uBAA9\uB85D {white-fg}{bold}q{/bold}{/white-fg}:\uC885\uB8CC");
1264
- screen.render();
1265
- }
1266
- screen.append(header);
1267
- screen.append(listPanel);
1268
- screen.append(chatInput);
1269
- screen.append(detailPanel);
1270
- screen.append(statusBar);
1271
- let workflows = [];
1272
- let collections = [];
1273
- async function loadWorkflows() {
1274
- if (!server || !auth) return;
1185
+ const [tab, setTab] = useState("workflows");
1186
+ const [selected, setSelected] = useState(0);
1187
+ const [workflows, setWorkflows] = useState([]);
1188
+ const [collections, setCollections] = useState([]);
1189
+ const [nodes, setNodes] = useState([]);
1190
+ const [prompts, setPrompts] = useState([]);
1191
+ const [tools2, setTools] = useState([]);
1192
+ const [detail, setDetail] = useState(["\u2190 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694"]);
1193
+ const [inputValue, setInputValue] = useState("");
1194
+ const [inputMode, setInputMode] = useState(false);
1195
+ const [runTarget, setRunTarget] = useState(null);
1196
+ const [loading, setLoading] = useState(true);
1197
+ const [statusMsg, setStatusMsg] = useState("\uB85C\uB529...");
1198
+ useEffect(() => {
1199
+ loadAll();
1200
+ }, []);
1201
+ async function loadAll() {
1202
+ setLoading(true);
1203
+ setStatusMsg("\uB85C\uB529...");
1275
1204
  try {
1276
- const { getWorkflowListDetail: getWorkflowListDetail2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1277
- const wfs = await getWorkflowListDetail2();
1278
- workflows = wfs.map((w) => ({
1279
- name: w.workflow_name,
1280
- id: (w.workflow_id ?? w.id ?? "").toString(),
1281
- deployed: !!w.is_deployed
1282
- }));
1205
+ if (server && auth) {
1206
+ const [wfMod, docMod, extraMod] = await Promise.all([
1207
+ Promise.resolve().then(() => (init_workflow(), workflow_exports)),
1208
+ Promise.resolve().then(() => (init_document(), document_exports)),
1209
+ Promise.resolve().then(() => (init_xgen_extra(), xgen_extra_exports))
1210
+ ]);
1211
+ const [wfs, cols, nodeList, promptList, toolList] = await Promise.allSettled([
1212
+ wfMod.getWorkflowListDetail(),
1213
+ docMod.listCollections(),
1214
+ extraMod.listNodes(),
1215
+ extraMod.listPrompts(),
1216
+ extraMod.listToolStore()
1217
+ ]);
1218
+ if (wfs.status === "fulfilled") {
1219
+ setWorkflows(wfs.value.map((w) => ({
1220
+ name: w.workflow_name,
1221
+ id: w.workflow_id ?? w.id ?? "",
1222
+ deployed: !!w.is_deployed
1223
+ })));
1224
+ }
1225
+ if (cols.status === "fulfilled") {
1226
+ setCollections(cols.value.map((c) => ({
1227
+ name: c.collection_make_name,
1228
+ docs: c.total_documents,
1229
+ chunks: c.total_chunks
1230
+ })));
1231
+ }
1232
+ if (nodeList.status === "fulfilled") setNodes(nodeList.value);
1233
+ if (promptList.status === "fulfilled") setPrompts(promptList.value);
1234
+ if (toolList.status === "fulfilled") setTools(toolList.value);
1235
+ }
1283
1236
  } catch {
1284
- workflows = [];
1285
1237
  }
1286
- }
1287
- async function loadCollections() {
1288
- if (!server || !auth) return;
1289
- try {
1290
- const { listCollections: listCollections2 } = await Promise.resolve().then(() => (init_document(), document_exports));
1291
- const cols = await listCollections2();
1292
- collections = cols.map((c) => ({
1293
- name: c.collection_make_name,
1294
- id: c.id,
1295
- docs: c.total_documents,
1296
- chunks: c.total_chunks,
1297
- shared: c.is_shared,
1298
- group: c.share_group ?? void 0,
1299
- model: c.init_embedding_model ?? void 0
1300
- }));
1301
- } catch {
1302
- collections = [];
1238
+ setLoading(false);
1239
+ setStatusMsg("\u2191\u2193:\uC774\uB3D9 Enter:\uC120\uD0DD 1-5:\uD0ED i:\uC785\uB825 r:\uC0C8\uB85C\uACE0\uCE68 q:\uC885\uB8CC");
1240
+ }
1241
+ function getListItems() {
1242
+ switch (tab) {
1243
+ case "workflows":
1244
+ return workflows.map((w) => ({ label: `${w.deployed ? "\u25CF" : "\u25CB"} ${w.name}`, dimLabel: "" }));
1245
+ case "collections":
1246
+ return collections.map((c) => ({ label: c.name, dimLabel: `${c.docs}\uBB38\uC11C ${c.chunks}\uCCAD\uD06C` }));
1247
+ case "nodes":
1248
+ return nodes.map((n) => ({ label: n.nodeName ?? n.name ?? "?", dimLabel: (n.description ?? "").slice(0, 30) }));
1249
+ case "prompts":
1250
+ return prompts.map((p) => ({ label: p.name ?? p.title ?? "?", dimLabel: `[${p.prompt_type ?? ""}]` }));
1251
+ case "tools":
1252
+ return tools2.map((t) => ({ label: t.name ?? t.tool_name ?? "?", dimLabel: (t.description ?? "").slice(0, 30) }));
1253
+ default:
1254
+ return [];
1255
+ }
1256
+ }
1257
+ function showDetail() {
1258
+ const items = getListItems();
1259
+ if (selected < 0 || selected >= items.length) return;
1260
+ if (tab === "workflows") {
1261
+ const w = workflows[selected];
1262
+ if (w) setDetail([w.name, "", `ID ${w.id}`, `\uBC30\uD3EC ${w.deployed ? "Yes" : "No"}`, "", "Enter \u2192 \uC2E4\uD589"]);
1263
+ } else if (tab === "collections") {
1264
+ const c = collections[selected];
1265
+ if (c) setDetail([c.name, "", `\uBB38\uC11C ${c.docs}\uAC1C`, `\uCCAD\uD06C ${c.chunks}\uAC1C`]);
1266
+ } else if (tab === "nodes") {
1267
+ const n = nodes[selected];
1268
+ if (n) setDetail([n.nodeName ?? n.name ?? "?", "", n.description ?? "", "", `ID: ${n.node_id ?? n.id ?? "?"}`]);
1269
+ } else if (tab === "prompts") {
1270
+ const p = prompts[selected];
1271
+ if (p) setDetail([p.name ?? "?", `[${p.prompt_type ?? ""}]`, "", (p.content ?? "").slice(0, 200)]);
1272
+ } else if (tab === "tools") {
1273
+ const t = tools2[selected];
1274
+ if (t) setDetail([t.name ?? t.tool_name ?? "?", "", t.description ?? ""]);
1275
+ }
1276
+ }
1277
+ useEffect(() => {
1278
+ showDetail();
1279
+ }, [selected, tab]);
1280
+ function switchTab(t) {
1281
+ setTab(t);
1282
+ setSelected(0);
1283
+ setInputMode(false);
1284
+ setRunTarget(null);
1285
+ }
1286
+ async function handleSubmit(value) {
1287
+ if (!value.trim()) {
1288
+ setInputMode(false);
1289
+ setRunTarget(null);
1290
+ return;
1303
1291
  }
1304
- }
1305
- function renderList() {
1306
- if (activeTab === "workflows") {
1307
- listPanel.label = ` \uC6CC\uD06C\uD50C\uB85C\uC6B0 (${workflows.length}) `;
1308
- listPanel.setItems(workflows.map((w, i) => {
1309
- const dot = w.deployed ? "\u25CF" : "\u25CB";
1310
- return ` ${dot} ${String(i + 1).padStart(2)}. ${w.name}`;
1311
- }));
1292
+ if (runTarget) {
1293
+ setDetail([`\uC2E4\uD589 \uC911: ${runTarget.name}`, `\uC785\uB825: ${value}`, "", "..."]);
1294
+ setInputValue("");
1295
+ setInputMode(false);
1296
+ try {
1297
+ const { executeWorkflow: executeWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1298
+ const { randomUUID: randomUUID4 } = await import("crypto");
1299
+ const result = await executeWorkflow2({
1300
+ workflow_id: runTarget.id,
1301
+ workflow_name: runTarget.name,
1302
+ input_data: value,
1303
+ interaction_id: `tui_${randomUUID4().slice(0, 8)}`,
1304
+ user_id: auth?.userId ? parseInt(auth.userId) : 1
1305
+ });
1306
+ const content = result.content ?? result.message ?? JSON.stringify(result).slice(0, 500);
1307
+ setDetail([`${runTarget.name}`, "", `\uC785\uB825: ${value}`, "", `\uACB0\uACFC:`, String(content)]);
1308
+ } catch (err) {
1309
+ setDetail([`\uC2E4\uD589 \uC2E4\uD328: ${err.message}`]);
1310
+ }
1311
+ setRunTarget(null);
1312
1312
  } else {
1313
- listPanel.label = ` \uCEEC\uB809\uC158 (${collections.length}) `;
1314
- listPanel.setItems(collections.map(
1315
- (c, i) => ` ${String(i + 1).padStart(2)}. ${c.name} {gray-fg}${c.docs}\uBB38\uC11C{/gray-fg}`
1316
- ));
1313
+ setInputValue("");
1314
+ setInputMode(false);
1317
1315
  }
1318
- screen.render();
1319
1316
  }
1320
- async function loadAll() {
1321
- renderStatusBar(" \uB85C\uB529...");
1322
- await Promise.all([loadWorkflows(), loadCollections()]);
1323
- renderList();
1324
- renderHeader();
1325
- renderStatusBar();
1326
- }
1327
- listPanel.on("select item", (_item, index) => {
1328
- if (activeTab === "workflows") {
1329
- const w = workflows[index];
1330
- if (!w) return;
1331
- detailPanel.setContent([
1332
- `{bold}${w.name}{/bold}`,
1333
- ``,
1334
- `ID ${w.id}`,
1335
- `\uBC30\uD3EC ${w.deployed ? "Yes" : "No"}`,
1336
- ``,
1337
- `Enter \u2192 \uC2E4\uD589 \uC785\uB825`
1338
- ].join("\n"));
1339
- } else {
1340
- const c = collections[index];
1341
- if (!c) return;
1342
- detailPanel.setContent([
1343
- `{bold}${c.name}{/bold}`,
1344
- ``,
1345
- `\uBB38\uC11C ${c.docs}\uAC1C`,
1346
- `\uCCAD\uD06C ${c.chunks}\uAC1C`,
1347
- `\uACF5\uC720 ${c.shared ? `Yes (${c.group})` : "No"}`,
1348
- `\uBAA8\uB378 ${c.model ?? "-"}`,
1349
- ``,
1350
- `Enter \u2192 \uBB38\uC11C \uBAA9\uB85D`
1351
- ].join("\n"));
1352
- }
1353
- screen.render();
1354
- });
1355
- listPanel.on("select", async (_item, index) => {
1356
- if (activeTab === "workflows") {
1357
- const w = workflows[index];
1358
- if (!w) return;
1359
- chatInput.label = ` ${w.name} \u276F `;
1360
- chatInput.focus();
1361
- screen.render();
1362
- chatInput.once("submit", async (value) => {
1363
- if (!value.trim()) {
1364
- chatInput.label = " \u276F ";
1365
- chatInput.clearValue();
1366
- listPanel.focus();
1367
- screen.render();
1368
- return;
1369
- }
1370
- detailPanel.log(`{gray-fg}\u2500\u2500 ${w.name} \u2500\u2500{/gray-fg}`);
1371
- detailPanel.log(`\uC785\uB825: ${value}`);
1372
- screen.render();
1373
- try {
1374
- const { executeWorkflow: executeWorkflow2 } = await Promise.resolve().then(() => (init_workflow(), workflow_exports));
1375
- const { randomUUID: randomUUID4 } = await import("crypto");
1376
- const result = await executeWorkflow2({
1377
- workflow_id: w.id,
1378
- workflow_name: w.name,
1379
- input_data: value,
1380
- interaction_id: `tui_${randomUUID4().slice(0, 8)}`,
1381
- user_id: auth?.userId ? parseInt(auth.userId) : 1
1382
- });
1383
- if (result.content) {
1384
- detailPanel.log(`${String(result.content)}
1385
- `);
1386
- } else if (result.error || result.message) {
1387
- detailPanel.log(`\uC624\uB958: ${result.error ?? result.message}
1388
- `);
1389
- } else {
1390
- detailPanel.log(JSON.stringify(result, null, 2).slice(0, 800) + "\n");
1391
- }
1392
- } catch (err) {
1393
- detailPanel.log(`\uC2E4\uD328: ${err.message}
1394
- `);
1395
- }
1396
- chatInput.label = " \u276F ";
1397
- chatInput.clearValue();
1398
- listPanel.focus();
1399
- screen.render();
1400
- });
1401
- } else {
1402
- const c = collections[index];
1403
- if (!c) return;
1404
- detailPanel.setContent(`${c.name} \uBB38\uC11C \uB85C\uB529...`);
1405
- screen.render();
1406
- try {
1407
- const { listDocuments: listDocuments2 } = await Promise.resolve().then(() => (init_document(), document_exports));
1408
- const docs = await listDocuments2(c.id?.toString());
1409
- if (!docs.length) {
1410
- detailPanel.setContent(`{bold}${c.name}{/bold}
1411
-
1412
- \uBB38\uC11C \uC5C6\uC74C`);
1413
- } else {
1414
- const docList = docs.map((d, i) => {
1415
- const name = d.name || d.file_name || "\uC774\uB984 \uC5C6\uC74C";
1416
- return ` ${i + 1}. ${name}`;
1417
- }).join("\n");
1418
- detailPanel.setContent(`{bold}${c.name}{/bold} \u2014 ${docs.length}\uAC1C \uBB38\uC11C
1419
-
1420
- ${docList}`);
1421
- }
1422
- } catch (err) {
1423
- detailPanel.setContent(`\uBB38\uC11C \uB85C\uB4DC \uC2E4\uD328: ${err.message}`);
1317
+ useInput((input, key) => {
1318
+ if (inputMode) {
1319
+ if (key.escape) {
1320
+ setInputMode(false);
1321
+ setRunTarget(null);
1424
1322
  }
1425
- screen.render();
1323
+ return;
1426
1324
  }
1427
- });
1428
- chatInput.on("submit", async (value) => {
1429
- if (!value.trim()) {
1430
- chatInput.clearValue();
1431
- listPanel.focus();
1432
- screen.render();
1325
+ if (input === "q" || key.ctrl && input === "c") {
1326
+ exit();
1433
1327
  return;
1434
1328
  }
1435
- detailPanel.log(`{white-fg}\u276F ${value}{/white-fg}`);
1436
- chatInput.clearValue();
1437
- screen.render();
1438
- try {
1439
- const p = getDefaultProvider();
1440
- if (!p) {
1441
- detailPanel.log("\uD504\uB85C\uBC14\uC774\uB354 \uBBF8\uC124\uC815");
1442
- chatInput.focus();
1443
- screen.render();
1444
- return;
1329
+ if (input === "r") {
1330
+ loadAll();
1331
+ return;
1332
+ }
1333
+ if (input === "i") {
1334
+ setInputMode(true);
1335
+ return;
1336
+ }
1337
+ if (input === "1") switchTab("workflows");
1338
+ else if (input === "2") switchTab("collections");
1339
+ else if (input === "3") switchTab("nodes");
1340
+ else if (input === "4") switchTab("prompts");
1341
+ else if (input === "5") switchTab("tools");
1342
+ const items = getListItems();
1343
+ if (key.upArrow) setSelected(Math.max(0, selected - 1));
1344
+ else if (key.downArrow) setSelected(Math.min(items.length - 1, selected + 1));
1345
+ else if (key.tab) switchTab(TABS[(TABS.findIndex((t) => t.key === tab) + 1) % TABS.length].key);
1346
+ if (key.return && items.length > 0) {
1347
+ if (tab === "workflows" && workflows[selected]) {
1348
+ setRunTarget(workflows[selected]);
1349
+ setInputMode(true);
1350
+ setStatusMsg(`${workflows[selected].name} \u2014 \uC785\uB825 \uD6C4 Enter\uB85C \uC2E4\uD589, Esc \uCDE8\uC18C`);
1445
1351
  }
1446
- const { createLLMClient: createLLMClient2, streamChat: streamChat2 } = await Promise.resolve().then(() => (init_llm(), llm_exports));
1447
- const client2 = createLLMClient2(p);
1448
- const result = await streamChat2(client2, p.model, [
1449
- { role: "system", content: "You are OPEN XGEN. Concise. Korean." },
1450
- { role: "user", content: value }
1451
- ]);
1452
- detailPanel.log(`${result.content || "(\uC751\uB2F5 \uC5C6\uC74C)"}
1453
- `);
1454
- } catch (err) {
1455
- detailPanel.log(`\uC624\uB958: ${err.message}
1456
- `);
1457
1352
  }
1458
- chatInput.focus();
1459
- screen.render();
1460
- });
1461
- screen.key(["1"], () => {
1462
- activeTab = "workflows";
1463
- renderList();
1464
- renderHeader();
1465
- listPanel.focus();
1466
- });
1467
- screen.key(["2"], () => {
1468
- activeTab = "collections";
1469
- renderList();
1470
- renderHeader();
1471
- listPanel.focus();
1472
- });
1473
- screen.key(["tab"], () => {
1474
- if (screen.focused === listPanel) chatInput.focus();
1475
- else listPanel.focus();
1476
- screen.render();
1477
- });
1478
- screen.key(["i"], () => {
1479
- chatInput.focus();
1480
- screen.render();
1481
1353
  });
1482
- screen.key(["r"], async () => {
1483
- await loadAll();
1484
- });
1485
- screen.key(["escape"], () => {
1486
- chatInput.label = " \u276F ";
1487
- listPanel.focus();
1488
- screen.render();
1489
- });
1490
- screen.key(["q", "C-c"], () => {
1491
- screen.destroy();
1492
- process.exit(0);
1354
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1355
+ /* @__PURE__ */ jsx(Header, { tab, serverDisplay, model: provider?.model ?? "" }),
1356
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [
1357
+ /* @__PURE__ */ jsx(ListPanel, { items: getListItems(), selected }),
1358
+ /* @__PURE__ */ jsx(DetailPanel, { lines: detail })
1359
+ ] }),
1360
+ inputMode ? /* @__PURE__ */ jsx(
1361
+ InputBar,
1362
+ {
1363
+ value: inputValue,
1364
+ onChange: setInputValue,
1365
+ onSubmit: handleSubmit,
1366
+ placeholder: runTarget ? `${runTarget.name}\uC5D0 \uC785\uB825...` : "\uC785\uB825..."
1367
+ }
1368
+ ) : /* @__PURE__ */ jsx(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u276F i\uB97C \uB20C\uB7EC \uC785\uB825 \xB7 Enter\uB85C \uC2E4\uD589" }) }),
1369
+ /* @__PURE__ */ jsx(StatusBar, { message: loading ? "\uB85C\uB529..." : statusMsg })
1370
+ ] });
1371
+ }
1372
+ async function startInkDashboard() {
1373
+ if (process.stdin.isPaused?.()) {
1374
+ process.stdin.resume();
1375
+ }
1376
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx(Dashboard, {}), {
1377
+ exitOnCtrlC: true
1493
1378
  });
1494
- setInterval(async () => {
1495
- try {
1496
- await loadAll();
1497
- } catch {
1498
- }
1499
- }, 6e4);
1500
- await loadAll();
1501
- listPanel.focus();
1502
- screen.render();
1379
+ await waitUntilExit();
1503
1380
  }
1504
- var init_tui = __esm({
1505
- "src/dashboard/tui.ts"() {
1381
+ var TABS;
1382
+ var init_InkDashboard = __esm({
1383
+ "src/dashboard/InkDashboard.tsx"() {
1506
1384
  "use strict";
1507
1385
  init_store();
1386
+ TABS = [
1387
+ { key: "workflows", label: "\uC6CC\uD06C\uD50C\uB85C\uC6B0", shortcut: "1" },
1388
+ { key: "collections", label: "\uCEEC\uB809\uC158", shortcut: "2" },
1389
+ { key: "nodes", label: "\uB178\uB4DC", shortcut: "3" },
1390
+ { key: "prompts", label: "\uD504\uB86C\uD504\uD2B8", shortcut: "4" },
1391
+ { key: "tools", label: "\uB3C4\uAD6C", shortcut: "5" }
1392
+ ];
1508
1393
  }
1509
1394
  });
1510
1395
 
@@ -2265,13 +2150,71 @@ init_provider();
2265
2150
 
2266
2151
  // src/commands/agent.ts
2267
2152
  init_store();
2268
- init_llm();
2269
2153
  import chalk12 from "chalk";
2270
2154
  import { createInterface as createInterface5 } from "readline";
2271
2155
  import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5, readdirSync } from "fs";
2272
2156
  import { join as join4 } from "path";
2273
2157
  import { homedir as homedir2 } from "os";
2274
2158
 
2159
+ // src/agent/llm.ts
2160
+ import OpenAI2 from "openai";
2161
+ function createLLMClient(provider) {
2162
+ const opts = {
2163
+ apiKey: provider.apiKey || "ollama"
2164
+ };
2165
+ if (provider.baseUrl) {
2166
+ opts.baseURL = provider.baseUrl;
2167
+ }
2168
+ return new OpenAI2(opts);
2169
+ }
2170
+ async function streamChat(client2, model, messages, tools2, onDelta) {
2171
+ const params = {
2172
+ model,
2173
+ messages,
2174
+ stream: true,
2175
+ stream_options: { include_usage: true }
2176
+ };
2177
+ if (tools2 && tools2.length > 0) {
2178
+ params.tools = tools2;
2179
+ }
2180
+ const stream = await client2.chat.completions.create(params);
2181
+ let content = "";
2182
+ let usage = null;
2183
+ const toolCallMap = /* @__PURE__ */ new Map();
2184
+ for await (const chunk of stream) {
2185
+ if (chunk.usage) {
2186
+ usage = {
2187
+ promptTokens: chunk.usage.prompt_tokens ?? 0,
2188
+ completionTokens: chunk.usage.completion_tokens ?? 0,
2189
+ totalTokens: chunk.usage.total_tokens ?? 0
2190
+ };
2191
+ }
2192
+ const delta = chunk.choices[0]?.delta;
2193
+ if (!delta) continue;
2194
+ if (delta.content) {
2195
+ content += delta.content;
2196
+ onDelta?.(delta.content);
2197
+ }
2198
+ if (delta.tool_calls) {
2199
+ for (const tc of delta.tool_calls) {
2200
+ const idx = tc.index;
2201
+ if (!toolCallMap.has(idx)) {
2202
+ toolCallMap.set(idx, { id: tc.id ?? "", name: tc.function?.name ?? "", arguments: "" });
2203
+ }
2204
+ const entry = toolCallMap.get(idx);
2205
+ if (tc.id) entry.id = tc.id;
2206
+ if (tc.function?.name) entry.name = tc.function.name;
2207
+ if (tc.function?.arguments) entry.arguments += tc.function.arguments;
2208
+ }
2209
+ }
2210
+ }
2211
+ return {
2212
+ content,
2213
+ toolCalls: [...toolCallMap.values()],
2214
+ usage
2215
+ };
2216
+ }
2217
+
2275
2218
  // src/agent/tools/file-read.ts
2276
2219
  var file_read_exports = {};
2277
2220
  __export(file_read_exports, {
@@ -3371,11 +3314,16 @@ async function agentRepl() {
3371
3314
  }
3372
3315
  if (input === "/dashboard" || input === "/dash") {
3373
3316
  console.log(chalk12.gray(" \uB300\uC2DC\uBCF4\uB4DC \uC5F4\uAE30...\n"));
3317
+ rl.pause();
3374
3318
  mcpManager?.stopAll();
3319
+ try {
3320
+ const { startInkDashboard: startInkDashboard2 } = await Promise.resolve().then(() => (init_InkDashboard(), InkDashboard_exports));
3321
+ await startInkDashboard2();
3322
+ } catch (err) {
3323
+ console.log(chalk12.red(` \uB300\uC2DC\uBCF4\uB4DC \uC624\uB958: ${err.message}
3324
+ `));
3325
+ }
3375
3326
  rl.close();
3376
- if (process.stdin.isTTY) process.stdin.setRawMode?.(false);
3377
- const { startTui: startTui2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
3378
- await startTui2();
3379
3327
  return;
3380
3328
  }
3381
3329
  messages.push({ role: "user", content: input });
@@ -3462,11 +3410,11 @@ async function connectServer() {
3462
3410
  const { addEnvironment: addEnvironment2 } = await Promise.resolve().then(() => (init_store(), store_exports));
3463
3411
  console.log(chalk12.bold("\n XGEN \uC11C\uBC84 \uC5F0\uACB0\n"));
3464
3412
  const presets = [
3465
- { id: "hq", name: "\uBCF8\uC0AC", url: "https://xgen.x2bee.com", email: "admin@plateer.com" },
3466
- { id: "jeju", name: "\uC81C\uC8FC", url: "https://jeju-xgen.x2bee.com", email: "admin@plateer.com" },
3467
- { id: "lotte", name: "\uB86F\uB370\uBAB0", url: "https://lotteimall-xgen.x2bee.com" }
3413
+ { id: "hq", name: "xgen.x2bee.com", url: "https://xgen.x2bee.com", email: "admin@plateer.com" },
3414
+ { id: "jeju", name: "jeju-xgen.x2bee.com", url: "https://jeju-xgen.x2bee.com", email: "admin@plateer.com" },
3415
+ { id: "lotte", name: "lotteimall-xgen.x2bee.com", url: "https://lotteimall-xgen.x2bee.com" }
3468
3416
  ];
3469
- presets.forEach((p, i) => console.log(` ${chalk12.cyan(`${i + 1}.`)} ${p.name} ${chalk12.gray(p.url)}`));
3417
+ presets.forEach((p, i) => console.log(` ${chalk12.cyan(`${i + 1}.`)} ${p.url}`));
3470
3418
  console.log(` ${chalk12.cyan("4.")} \uC9C1\uC811 \uC785\uB825`);
3471
3419
  console.log();
3472
3420
  const choice = await ask(chalk12.cyan(" \u276F "));