@nomad-e/bluma-cli 0.0.10 → 0.0.12
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/README.md +54 -1
- package/dist/config/bluma-mcp.json +0 -13
- package/dist/config/native_tools.json +0 -3
- package/dist/main.js +581 -257
- package/package.json +57 -55
package/dist/main.js
CHANGED
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
// src/main.ts
|
|
3
3
|
import React6 from "react";
|
|
4
4
|
import { render } from "ink";
|
|
5
|
-
import { EventEmitter } from "events";
|
|
5
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
6
6
|
import { v4 as uuidv42 } from "uuid";
|
|
7
7
|
|
|
8
8
|
// src/app/ui/App.tsx
|
|
9
9
|
import { useState as useState4, useEffect as useEffect3, useRef, useCallback, memo as memo4 } from "react";
|
|
10
|
-
import { Box as
|
|
11
|
-
import Spinner from "ink-spinner";
|
|
10
|
+
import { Box as Box13, Text as Text12, Static } from "ink";
|
|
12
11
|
|
|
13
12
|
// src/app/ui/layout.tsx
|
|
14
13
|
import { Box, Text } from "ink";
|
|
@@ -21,14 +20,34 @@ var BRAND_COLORS = {
|
|
|
21
20
|
greydark: "#444"
|
|
22
21
|
};
|
|
23
22
|
var Header = () => {
|
|
24
|
-
return /* @__PURE__ */
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
24
|
+
/* @__PURE__ */ jsx(
|
|
25
|
+
Box,
|
|
26
|
+
{
|
|
27
|
+
flexDirection: "column",
|
|
28
|
+
height: 8,
|
|
29
|
+
marginBottom: 1,
|
|
30
|
+
children: /* @__PURE__ */ jsx(
|
|
31
|
+
BigText,
|
|
32
|
+
{
|
|
33
|
+
text: "BluMa CLI",
|
|
34
|
+
font: "block",
|
|
35
|
+
colors: [BRAND_COLORS.main, BRAND_COLORS.accent, BRAND_COLORS.shadow]
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
),
|
|
40
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
|
|
41
|
+
/* @__PURE__ */ jsx(Text, { children: "How to get started with BluMa:" }),
|
|
42
|
+
/* @__PURE__ */ jsx(Text, { children: "1. You can ask questions, modify files, or execute commands directly." }),
|
|
43
|
+
/* @__PURE__ */ jsx(Text, { children: "2. Be as clear and specific as possible to get accurate responses." }),
|
|
44
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
45
|
+
"3. Type ",
|
|
46
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "/help" }),
|
|
47
|
+
" to explore available commands and features."
|
|
48
|
+
] })
|
|
49
|
+
] })
|
|
50
|
+
] });
|
|
32
51
|
};
|
|
33
52
|
var SessionInfo = ({
|
|
34
53
|
sessionId: sessionId2,
|
|
@@ -44,7 +63,6 @@ var SessionInfo = ({
|
|
|
44
63
|
{
|
|
45
64
|
borderStyle: "round",
|
|
46
65
|
borderColor: "gray",
|
|
47
|
-
paddingX: 1,
|
|
48
66
|
flexDirection: "column",
|
|
49
67
|
marginBottom: 1,
|
|
50
68
|
children: [
|
|
@@ -64,26 +82,18 @@ var SessionInfo = ({
|
|
|
64
82
|
] }),
|
|
65
83
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
66
84
|
/* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u21B3" }),
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
70
|
-
/* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u21B3" }),
|
|
71
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "MCP: " }),
|
|
85
|
+
" ",
|
|
86
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "mcp: " }),
|
|
72
87
|
/* @__PURE__ */ jsx(Text, { color: mcpStatus === "connected" ? "green" : "yellow", children: mcpStatus })
|
|
73
|
-
] }),
|
|
74
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
75
|
-
/* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u21B3" }),
|
|
76
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Tools: " }),
|
|
77
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: toolsCount !== null ? toolsCount : "loading..." })
|
|
78
88
|
] })
|
|
79
89
|
]
|
|
80
90
|
}
|
|
81
91
|
);
|
|
82
92
|
|
|
83
|
-
// src/app/ui/
|
|
84
|
-
import { Box as Box2, Text as Text2, useStdout } from "ink";
|
|
93
|
+
// src/app/ui/components/InputPrompt.tsx
|
|
94
|
+
import { Box as Box2, Text as Text2, useStdout, useInput as useInput2 } from "ink";
|
|
85
95
|
|
|
86
|
-
// src/app/ui/
|
|
96
|
+
// src/app/ui/utils/useSimpleInputBuffer.ts
|
|
87
97
|
import { useReducer } from "react";
|
|
88
98
|
import { useInput } from "ink";
|
|
89
99
|
function inputReducer(state, action, viewWidth) {
|
|
@@ -139,7 +149,18 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
|
|
|
139
149
|
return;
|
|
140
150
|
}
|
|
141
151
|
if (isReadOnly) {
|
|
142
|
-
return
|
|
152
|
+
if (key.return) {
|
|
153
|
+
if (state.text.trim().length > 0) {
|
|
154
|
+
onSubmit(state.text);
|
|
155
|
+
dispatch({ type: "SUBMIT" });
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (key.backspace || key.delete) return dispatch({ type: "BACKSPACE" });
|
|
160
|
+
if (key.leftArrow) return dispatch({ type: "MOVE_CURSOR", direction: "left" });
|
|
161
|
+
if (key.rightArrow) return dispatch({ type: "MOVE_CURSOR", direction: "right" });
|
|
162
|
+
if (key.ctrl || key.meta || key.tab) return;
|
|
163
|
+
return dispatch({ type: "INPUT", payload: input });
|
|
143
164
|
}
|
|
144
165
|
if (key.return) {
|
|
145
166
|
if (state.text.trim().length > 0) {
|
|
@@ -154,7 +175,7 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
|
|
|
154
175
|
if (key.ctrl || key.meta || key.tab) return;
|
|
155
176
|
dispatch({ type: "INPUT", payload: input });
|
|
156
177
|
},
|
|
157
|
-
//
|
|
178
|
+
// useInput está SEMPRE ativo para capturar todas as teclas
|
|
158
179
|
{ isActive: true }
|
|
159
180
|
);
|
|
160
181
|
return {
|
|
@@ -164,9 +185,27 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
|
|
|
164
185
|
};
|
|
165
186
|
};
|
|
166
187
|
|
|
167
|
-
// src/app/ui/
|
|
168
|
-
import { useEffect, useState } from "react";
|
|
188
|
+
// src/app/ui/components/InputPrompt.tsx
|
|
189
|
+
import { useEffect, useMemo, useState } from "react";
|
|
190
|
+
import { EventEmitter } from "events";
|
|
191
|
+
|
|
192
|
+
// src/app/ui/utils/slashRegistry.ts
|
|
193
|
+
var getSlashCommands = () => [
|
|
194
|
+
{ name: "/help", description: "list commands" },
|
|
195
|
+
{ name: "/mcp", description: "list tools connected via MCP" },
|
|
196
|
+
{ name: "/tools", description: "list native tools" },
|
|
197
|
+
{ name: "/clear", description: "clear history" }
|
|
198
|
+
];
|
|
199
|
+
var filterSlashCommands = (query) => {
|
|
200
|
+
const list = getSlashCommands();
|
|
201
|
+
const q = query.toLowerCase();
|
|
202
|
+
return list.filter((c) => c.name.toLowerCase().startsWith(q));
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// src/app/ui/components/InputPrompt.tsx
|
|
169
206
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
207
|
+
var uiEventBus = global.__bluma_ui_eventbus__ || new EventEmitter();
|
|
208
|
+
global.__bluma_ui_eventbus__ = uiEventBus;
|
|
170
209
|
var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt }) => {
|
|
171
210
|
const { stdout } = useStdout();
|
|
172
211
|
const [viewWidth, setViewWidth] = useState(() => stdout.columns - 6);
|
|
@@ -177,12 +216,26 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt }) => {
|
|
|
177
216
|
stdout.off("resize", onResize);
|
|
178
217
|
};
|
|
179
218
|
}, [stdout]);
|
|
219
|
+
const permissiveOnSubmit = (value) => {
|
|
220
|
+
const trimmed = (value || "").trim();
|
|
221
|
+
if (isReadOnly) {
|
|
222
|
+
if (trimmed.length > 0) {
|
|
223
|
+
const payload = trimmed;
|
|
224
|
+
uiEventBus.emit("dev_overlay", { kind: "message", payload, ts: Date.now() });
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
onSubmit(value);
|
|
230
|
+
};
|
|
180
231
|
const { text, cursorPosition, viewStart } = useCustomInput({
|
|
181
|
-
onSubmit,
|
|
232
|
+
onSubmit: permissiveOnSubmit,
|
|
182
233
|
viewWidth,
|
|
183
234
|
isReadOnly,
|
|
184
235
|
onInterrupt
|
|
185
236
|
});
|
|
237
|
+
const [slashOpen, setSlashOpen] = useState(false);
|
|
238
|
+
const [slashIndex, setSlashIndex] = useState(0);
|
|
186
239
|
const visibleText = text.slice(viewStart, viewStart + viewWidth);
|
|
187
240
|
const visibleCursorPosition = cursorPosition - viewStart;
|
|
188
241
|
const textBeforeCursor = visibleText.slice(0, visibleCursorPosition);
|
|
@@ -191,22 +244,71 @@ var InputPrompt = ({ onSubmit, isReadOnly, onInterrupt }) => {
|
|
|
191
244
|
visibleCursorPosition + 1
|
|
192
245
|
);
|
|
193
246
|
const textAfterCursor = visibleText.slice(visibleCursorPosition + 1);
|
|
247
|
+
const cursorGlyph = charAtCursor && charAtCursor.length > 0 ? charAtCursor : " ";
|
|
194
248
|
const borderColor = isReadOnly ? "gray" : "gray";
|
|
195
|
-
const placeholder = isReadOnly ? "press esc to cancel" : "";
|
|
249
|
+
const placeholder = isReadOnly ? " press esc to cancel | type a message while agent processes" : "";
|
|
196
250
|
const showPlaceholder = text.length === 0 && isReadOnly;
|
|
251
|
+
const slashQuery = useMemo(() => text.startsWith("/") ? text : "", [text]);
|
|
252
|
+
const slashSuggestions = useMemo(() => {
|
|
253
|
+
if (!slashQuery) return [];
|
|
254
|
+
return filterSlashCommands(slashQuery);
|
|
255
|
+
}, [slashQuery]);
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
if (isReadOnly) {
|
|
258
|
+
setSlashOpen(false);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (text.startsWith("/")) {
|
|
262
|
+
setSlashOpen(true);
|
|
263
|
+
setSlashIndex(0);
|
|
264
|
+
} else {
|
|
265
|
+
setSlashOpen(false);
|
|
266
|
+
}
|
|
267
|
+
}, [text, isReadOnly]);
|
|
268
|
+
useInput2((input, key) => {
|
|
269
|
+
if (!slashOpen) return;
|
|
270
|
+
if (key.downArrow) {
|
|
271
|
+
setSlashIndex((i) => Math.min(i + 1, Math.max(0, slashSuggestions.length - 1)));
|
|
272
|
+
} else if (key.upArrow) {
|
|
273
|
+
setSlashIndex((i) => Math.max(i - 1, 0));
|
|
274
|
+
} else if (key.return) {
|
|
275
|
+
const choice = slashSuggestions[slashIndex];
|
|
276
|
+
if (choice) {
|
|
277
|
+
const cmd = choice.name;
|
|
278
|
+
setSlashOpen(false);
|
|
279
|
+
permissiveOnSubmit(cmd);
|
|
280
|
+
}
|
|
281
|
+
} else if (key.escape) {
|
|
282
|
+
setSlashOpen(false);
|
|
283
|
+
}
|
|
284
|
+
}, { isActive: slashOpen });
|
|
197
285
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
198
|
-
/* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor, borderDimColor: !isReadOnly, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
|
|
286
|
+
/* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor, borderDimColor: !isReadOnly, width: viewWidth - 7, paddingY: 0, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
|
|
199
287
|
/* @__PURE__ */ jsxs2(Text2, { color: "white", dimColor: true, children: [
|
|
200
288
|
">",
|
|
201
289
|
" "
|
|
202
290
|
] }),
|
|
203
291
|
/* @__PURE__ */ jsx2(Text2, { children: textBeforeCursor }),
|
|
204
|
-
/* @__PURE__ */ jsx2(Text2, { inverse:
|
|
292
|
+
/* @__PURE__ */ jsx2(Text2, { inverse: true, children: cursorGlyph }),
|
|
205
293
|
showPlaceholder ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: placeholder }) : /* @__PURE__ */ jsx2(Text2, { children: textAfterCursor })
|
|
206
294
|
] }) }),
|
|
295
|
+
slashOpen && slashSuggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: slashSuggestions.map((s, idx) => {
|
|
296
|
+
const isSelected = idx === slashIndex;
|
|
297
|
+
return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
|
|
298
|
+
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "blue" : "gray", children: isSelected ? "\u276F " : " " }),
|
|
299
|
+
/* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "blue" : "white", bold: isSelected, dimColor: !isSelected, children: [
|
|
300
|
+
s.name,
|
|
301
|
+
" ",
|
|
302
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
303
|
+
"- ",
|
|
304
|
+
s.description
|
|
305
|
+
] })
|
|
306
|
+
] })
|
|
307
|
+
] }, s.name);
|
|
308
|
+
}) }),
|
|
207
309
|
/* @__PURE__ */ jsx2(Box2, { paddingX: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", dimColor: true, children: [
|
|
208
|
-
"ctrl+c to exit | esc to interrupt | ",
|
|
209
|
-
isReadOnly ? "Read-only mode" : "Editable mode"
|
|
310
|
+
"ctrl+c to exit | /help to explore commands | esc to interrupt | ",
|
|
311
|
+
isReadOnly ? "Read-only mode (message passthrough)" : "Editable mode"
|
|
210
312
|
] }) })
|
|
211
313
|
] });
|
|
212
314
|
};
|
|
@@ -216,7 +318,7 @@ import { Box as Box6, Text as Text6 } from "ink";
|
|
|
216
318
|
|
|
217
319
|
// src/app/ui/InteractiveMenu.tsx
|
|
218
320
|
import { useState as useState2, memo } from "react";
|
|
219
|
-
import { Box as Box3, Text as Text3, useInput as
|
|
321
|
+
import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
|
|
220
322
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
221
323
|
var InteractiveMenuComponent = ({ onDecision }) => {
|
|
222
324
|
const options = [
|
|
@@ -225,7 +327,7 @@ var InteractiveMenuComponent = ({ onDecision }) => {
|
|
|
225
327
|
{ label: "3. Always allow this type of command", value: "accept_always" }
|
|
226
328
|
];
|
|
227
329
|
const [selectedOption, setSelectedOption] = useState2(0);
|
|
228
|
-
|
|
330
|
+
useInput3((input, key) => {
|
|
229
331
|
if (key.upArrow) {
|
|
230
332
|
setSelectedOption((prev) => prev > 0 ? prev - 1 : options.length - 1);
|
|
231
333
|
}
|
|
@@ -482,9 +584,61 @@ import os5 from "os";
|
|
|
482
584
|
import path2 from "path";
|
|
483
585
|
import os from "os";
|
|
484
586
|
import { promises as fs } from "fs";
|
|
587
|
+
var fileLocks = /* @__PURE__ */ new Map();
|
|
588
|
+
async function withFileLock(file, fn) {
|
|
589
|
+
const prev = fileLocks.get(file) || Promise.resolve();
|
|
590
|
+
let release;
|
|
591
|
+
const p = new Promise((res) => release = res);
|
|
592
|
+
fileLocks.set(file, prev.then(() => p));
|
|
593
|
+
try {
|
|
594
|
+
const result = await fn();
|
|
595
|
+
return result;
|
|
596
|
+
} finally {
|
|
597
|
+
release();
|
|
598
|
+
if (fileLocks.get(file) === p) fileLocks.delete(file);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function expandHome(p) {
|
|
602
|
+
if (!p) return p;
|
|
603
|
+
if (p.startsWith("~")) {
|
|
604
|
+
return path2.join(os.homedir(), p.slice(1));
|
|
605
|
+
}
|
|
606
|
+
return p;
|
|
607
|
+
}
|
|
608
|
+
function getPreferredAppDir() {
|
|
609
|
+
const fixed = path2.join(os.homedir(), ".bluma-cli");
|
|
610
|
+
return path2.resolve(expandHome(fixed));
|
|
611
|
+
}
|
|
612
|
+
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
613
|
+
let attempt = 0;
|
|
614
|
+
let lastErr;
|
|
615
|
+
const isWin = process.platform === "win32";
|
|
616
|
+
while (attempt <= maxRetries) {
|
|
617
|
+
try {
|
|
618
|
+
await fs.rename(src, dest);
|
|
619
|
+
return;
|
|
620
|
+
} catch (e) {
|
|
621
|
+
lastErr = e;
|
|
622
|
+
const code = e && e.code || "";
|
|
623
|
+
const transient = code === "EPERM" || code === "EBUSY" || code === "ENOTEMPTY" || code === "EACCES";
|
|
624
|
+
if (!(isWin && transient) || attempt === maxRetries) break;
|
|
625
|
+
const backoff = Math.min(1e3, 50 * Math.pow(2, attempt));
|
|
626
|
+
await new Promise((r) => setTimeout(r, backoff));
|
|
627
|
+
attempt++;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
try {
|
|
631
|
+
const data = await fs.readFile(src);
|
|
632
|
+
await fs.writeFile(dest, data);
|
|
633
|
+
await fs.unlink(src).catch(() => {
|
|
634
|
+
});
|
|
635
|
+
return;
|
|
636
|
+
} catch (fallbackErr) {
|
|
637
|
+
throw lastErr || fallbackErr;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
485
640
|
async function ensureSessionDir() {
|
|
486
|
-
const
|
|
487
|
-
const appDir = path2.join(homeDir, ".bluma-cli");
|
|
641
|
+
const appDir = getPreferredAppDir();
|
|
488
642
|
const sessionDir = path2.join(appDir, "sessions");
|
|
489
643
|
await fs.mkdir(sessionDir, { recursive: true });
|
|
490
644
|
return sessionDir;
|
|
@@ -508,41 +662,54 @@ async function loadOrcreateSession(sessionId2) {
|
|
|
508
662
|
}
|
|
509
663
|
}
|
|
510
664
|
async function saveSessionHistory(sessionFile, history) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
console.warn(`Could not read or parse session file ${sessionFile}. Re-initializing. Error: ${error.message}`);
|
|
518
|
-
} else {
|
|
519
|
-
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
665
|
+
await withFileLock(sessionFile, async () => {
|
|
666
|
+
let sessionData;
|
|
667
|
+
try {
|
|
668
|
+
const dir = path2.dirname(sessionFile);
|
|
669
|
+
await fs.mkdir(dir, { recursive: true });
|
|
670
|
+
} catch {
|
|
520
671
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
672
|
+
try {
|
|
673
|
+
const fileContent = await fs.readFile(sessionFile, "utf-8");
|
|
674
|
+
sessionData = JSON.parse(fileContent);
|
|
675
|
+
} catch (error) {
|
|
676
|
+
const code = error && error.code;
|
|
677
|
+
if (code !== "ENOENT") {
|
|
678
|
+
if (error instanceof Error) {
|
|
679
|
+
console.warn(`Could not read or parse session file ${sessionFile}. Re-initializing. Error: ${error.message}`);
|
|
680
|
+
} else {
|
|
681
|
+
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const sessionId2 = path2.basename(sessionFile, ".json");
|
|
685
|
+
sessionData = {
|
|
686
|
+
session_id: sessionId2,
|
|
687
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
688
|
+
conversation_history: []
|
|
689
|
+
};
|
|
690
|
+
try {
|
|
691
|
+
await fs.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
692
|
+
} catch {
|
|
693
|
+
}
|
|
540
694
|
}
|
|
695
|
+
sessionData.conversation_history = history;
|
|
696
|
+
sessionData.last_updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
697
|
+
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
541
698
|
try {
|
|
542
|
-
await fs.
|
|
543
|
-
|
|
699
|
+
await fs.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
700
|
+
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
701
|
+
} catch (writeError) {
|
|
702
|
+
if (writeError instanceof Error) {
|
|
703
|
+
console.error(`Fatal error saving session to ${sessionFile}: ${writeError.message}`);
|
|
704
|
+
} else {
|
|
705
|
+
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
706
|
+
}
|
|
707
|
+
try {
|
|
708
|
+
await fs.unlink(tempSessionFile);
|
|
709
|
+
} catch {
|
|
710
|
+
}
|
|
544
711
|
}
|
|
545
|
-
}
|
|
712
|
+
});
|
|
546
713
|
}
|
|
547
714
|
|
|
548
715
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
@@ -579,59 +746,18 @@ var SYSTEM_PROMPT = `
|
|
|
579
746
|
- **INTELLIGENT INFERENCE**: Understand implied conventions from minimal examples
|
|
580
747
|
- **PROACTIVE EXTENSION**: Take patterns further than the original example when appropriate
|
|
581
748
|
|
|
582
|
-
# BEHAVIORAL RULES
|
|
583
|
-
-
|
|
584
|
-
-
|
|
585
|
-
-
|
|
586
|
-
-
|
|
587
|
-
-
|
|
588
|
-
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
You should always use your notebook to help you understand all the tasks you have to perform.
|
|
596
|
-
In it, you can define a thousand thoughts and a thousand mini-tasks. Mini-tasks serve to divide and organize your reasoning.
|
|
597
|
-
The notebook is your space to think about how to solve a given task and how to divide it into smaller steps.
|
|
598
|
-
Remember: the human developer does not have access to this notebook \u2014 it is yours alone.
|
|
599
|
-
Therefore, you can write down whatever you want:
|
|
600
|
-
rants, swear words, random thoughts, crazy ideas...
|
|
601
|
-
The important thing is that this helps you better understand the problem and find the solution.
|
|
602
|
-
|
|
603
|
-
Never ask for the developer's opinion with phrases like: 'If you want any extra details or specific format, let me know now!'. You should always take the most viable path and go straight ahead with the solution, because you are 100% autonomous.
|
|
604
|
-
|
|
605
|
-
Follow the stripes o "Tool Naming Policy"
|
|
606
|
-
|
|
607
|
-
##Important: When writing to Notion, you must strictly follow its content structure, including the correct use of headings (heading_1, heading_2, etc.) and other formatting standards. No deviations are allowed.
|
|
608
|
-
You should always standardize everything using Notion's actual headers (heading_1, heading_2, etc.), making the structure
|
|
609
|
-
semantically better for reading and navigation.
|
|
610
|
-
|
|
611
|
-
Don't forget to follow the 'mermaid_diagrams' rules to the letter when creating diagrams in Notion.
|
|
612
|
-
|
|
613
|
-
You are strictly forbidden from replying directly to the user.
|
|
614
|
-
|
|
615
|
-
You are not allowed to reason, explain, or output any direct content in your responses.
|
|
616
|
-
|
|
617
|
-
Your only permitted action is to invoke one or more 'tool_calls', regardless of how simple or obvious the user's message may seem.
|
|
618
|
-
|
|
619
|
-
You must always use a tool to:
|
|
620
|
-
- generate any kind of response
|
|
621
|
-
- retrieve or calculate any data
|
|
622
|
-
- validate, summarize, or transform input
|
|
623
|
-
|
|
624
|
-
You must never include a "content" field in your response.
|
|
625
|
-
Only 'tool_calls' are allowed when you reply as "role": "assistant".
|
|
626
|
-
|
|
627
|
-
You will only produce a final message to the user **after receiving a valid "role": "tool" response** matching your previous 'tool_call_id'.
|
|
628
|
-
|
|
629
|
-
You are a pure orchestration agent \u2014 your only job is to call tools. No autonomous answers, no internal reasoning.
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
Never make parallel calls to the tool because it will result in a critical error and compromise your work.
|
|
633
|
-
ZERO TOLERANCE: Every message MUST use proper tools
|
|
634
|
-
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
749
|
+
# BEHAVIORAL RULES (Compact)
|
|
750
|
+
- Identity: You are BluMa (NomadEngenuity). Be professional and technical.
|
|
751
|
+
- Communication: ALL messages must use message_notify_dev. No direct text replies.
|
|
752
|
+
- Task completion: When you finish a task, immediately invoke agent_end_task.
|
|
753
|
+
- Tool rules: Never make parallel tool calls. Always use only the defined tools with exact names.
|
|
754
|
+
- Autonomy: Act 100% autonomously; don\u2019t ask for formatting preferences. Use the notebook for internal thinking.
|
|
755
|
+
- Notion: When writing to Notion, strictly use proper headings (heading_1, heading_2, ...), per rules.
|
|
756
|
+
|
|
757
|
+
CRITICAL COMMUNICATION PROTOCOL (Compact)
|
|
758
|
+
- Only tool_calls are allowed for assistant replies. Never include a "content" field.
|
|
759
|
+
- Always use tools to respond, retrieve data, compute or transform. Await a valid tool response before any final message.
|
|
760
|
+
- Zero tolerance for protocol violations.
|
|
635
761
|
|
|
636
762
|
<current_system_environment>
|
|
637
763
|
- Operating System: {os_type} ({os_version})
|
|
@@ -967,100 +1093,10 @@ Rule Summary:
|
|
|
967
1093
|
</edit_tool_rules>
|
|
968
1094
|
|
|
969
1095
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
tool_call "agent_end_task"
|
|
975
|
-
</example>
|
|
976
|
-
|
|
977
|
-
<example>
|
|
978
|
-
dev: What day is it?
|
|
979
|
-
BluMa: tool_call "message_notify_dev" Sorry, I was designed to operate in the codebase. How can I help you with the code today?
|
|
980
|
-
tool_call "agent_end_task"
|
|
981
|
-
</example>
|
|
982
|
-
|
|
983
|
-
<example>
|
|
984
|
-
dev: Can you generate a new feature for me?
|
|
985
|
-
BluMa: tool_call "message_notify_dev" Of course. Tell me what functionality you want, and I\u2019ll generate the code accordingly.
|
|
986
|
-
tool_call "agent_end_task"
|
|
987
|
-
</example>
|
|
988
|
-
|
|
989
|
-
<example>
|
|
990
|
-
dev: What's the weather like today?
|
|
991
|
-
BluMa: tool_call "message_notify_dev" I don\u2019t have access to real-world data. I'm focused on your codebase. What would you like to modify or create?
|
|
992
|
-
tool_call "agent_end_task"
|
|
993
|
-
</example>
|
|
994
|
-
|
|
995
|
-
<example>
|
|
996
|
-
dev: I'm tired of debugging this.
|
|
997
|
-
BluMa: tool_call "message_notify_dev" I can help you with that. Show me the file or error, and I\u2019ll assist with debugging.
|
|
998
|
-
tool_call "agent_end_task"
|
|
999
|
-
</example>
|
|
1000
|
-
|
|
1001
|
-
<example>
|
|
1002
|
-
dev: Can you create documentation for this module?
|
|
1003
|
-
BluMa: tool_call "message_notify_dev" Yes. Please specify which module or function you'd like documented.
|
|
1004
|
-
tool_call "agent_end_task"
|
|
1005
|
-
</example>
|
|
1006
|
-
|
|
1007
|
-
<example>
|
|
1008
|
-
dev: Do you know who won the game last night?
|
|
1009
|
-
BluMa: tool_call "message_notify_dev" I\u2019m only trained to work inside the codebase. Let me know what you need done in the project.
|
|
1010
|
-
tool_call "agent_end_task"
|
|
1011
|
-
</example>
|
|
1012
|
-
|
|
1013
|
-
<example>
|
|
1014
|
-
dev: Refactor this file to follow clean architecture.
|
|
1015
|
-
BluMa: tool_call "message_notify_dev" Got it. Send me the file path or code snippet you want refactored.
|
|
1016
|
-
tool_call "agent_end_task"
|
|
1017
|
-
</example>
|
|
1018
|
-
|
|
1019
|
-
<example>
|
|
1020
|
-
dev: How do I fix this type error in TypeScript?
|
|
1021
|
-
BluMa: tool_call "message_notify_dev" Send me the error message and relevant code, and I\u2019ll help you resolve the issue.
|
|
1022
|
-
tool_call "agent_end_task"
|
|
1023
|
-
</example>
|
|
1024
|
-
|
|
1025
|
-
<example>
|
|
1026
|
-
dev: Do you have feelings?
|
|
1027
|
-
BluMa: tool_call "message_notify_dev" I\u2019m not sentient \u2014 I exist solely to help you build, refactor, and improve your codebase.
|
|
1028
|
-
tool_call "agent_end_task"
|
|
1029
|
-
</example>
|
|
1030
|
-
|
|
1031
|
-
<example>
|
|
1032
|
-
dev: Rename all the variables to match the new convention.
|
|
1033
|
-
BluMa: tool_call "message_notify_dev" Please share the convention and the target file(s), and I\u2019ll rename the variables accordingly.
|
|
1034
|
-
tool_call "agent_end_task"
|
|
1035
|
-
</example>
|
|
1036
|
-
|
|
1037
|
-
<example>
|
|
1038
|
-
dev: Run the tests for the user module.
|
|
1039
|
-
BluMa: tool_call "message_notify_dev" Running tests for the user module.
|
|
1040
|
-
BluMa: shell_command_shell_command "npm run test user"
|
|
1041
|
-
BluMa: tool_call "message_notify_dev" All tests passed successfully for the user module.
|
|
1042
|
-
tool_call "agent_end_task"
|
|
1043
|
-
</example>
|
|
1044
|
-
|
|
1045
|
-
<example>
|
|
1046
|
-
dev: Run the tests for the user module.
|
|
1047
|
-
BluMa: tool_call "message_notify_dev" Running tests for the user module.
|
|
1048
|
-
BluMa: shell_command_shell_command "npm run test user"
|
|
1049
|
-
BluMa: tool_call "message_notify_dev" Tests finished with 2 failures. See **tests/user.test.js** for more details.
|
|
1050
|
-
tool_call "agent_end_task"
|
|
1051
|
-
</example>
|
|
1052
|
-
|
|
1053
|
-
<example>
|
|
1054
|
-
dev: Prepare the project for deployment.
|
|
1055
|
-
BluMa: tool_call "message_notify_dev" Starting full project preparation: linting, building, and testing.
|
|
1056
|
-
BluMa: shell_command_shell_command "npm run lint"
|
|
1057
|
-
BluMa: tool_call "message_notify_dev" Linting completed. No major issues found.
|
|
1058
|
-
BluMa: shell_command_shell_command "npm run build"
|
|
1059
|
-
BluMa: tool_call "message_notify_dev" Build successful. Artifacts ready in the /dist folder.
|
|
1060
|
-
BluMa: shell_command_shell_command "npm run test"
|
|
1061
|
-
BluMa: tool_call "message_notify_dev" All tests passed. The project is ready for deployment. If you need any further adjustments or extra sections, let me know!
|
|
1062
|
-
tool_call "agent_end_task"
|
|
1063
|
-
</example>
|
|
1096
|
+
Real-Time Developer Messages
|
|
1097
|
+
- During processing, the developer will send you messages.
|
|
1098
|
+
- You MUST respond immediately via message_notify_dev, and be brief. You should use it in your next thoughts/actions.
|
|
1099
|
+
|
|
1064
1100
|
|
|
1065
1101
|
<end_task_rules>
|
|
1066
1102
|
This tool is used to signal to the system that the current task has completed and that the agent can be placed in an idle state.
|
|
@@ -1620,7 +1656,7 @@ var MCPClient = class {
|
|
|
1620
1656
|
try {
|
|
1621
1657
|
this.eventBus.emit("backend_message", {
|
|
1622
1658
|
type: "connection_status",
|
|
1623
|
-
message:
|
|
1659
|
+
message: `${serverName} server is being connected...`
|
|
1624
1660
|
});
|
|
1625
1661
|
if (serverConf.type === "stdio") {
|
|
1626
1662
|
await this.connectToStdioServer(serverName, serverConf);
|
|
@@ -1712,6 +1748,19 @@ var MCPClient = class {
|
|
|
1712
1748
|
getAvailableTools() {
|
|
1713
1749
|
return this.globalToolsForLlm;
|
|
1714
1750
|
}
|
|
1751
|
+
// New: detailed list for UI with origin metadata
|
|
1752
|
+
getAvailableToolsDetailed() {
|
|
1753
|
+
const detailed = [];
|
|
1754
|
+
for (const tool of this.globalToolsForLlm) {
|
|
1755
|
+
const name = tool.function?.name;
|
|
1756
|
+
if (!name) continue;
|
|
1757
|
+
const route = this.toolToServerMap.get(name);
|
|
1758
|
+
if (!route) continue;
|
|
1759
|
+
const source = route.server === "native" ? "native" : "mcp";
|
|
1760
|
+
detailed.push({ ...tool, source, server: route.server, originalName: route.originalName });
|
|
1761
|
+
}
|
|
1762
|
+
return detailed;
|
|
1763
|
+
}
|
|
1715
1764
|
async close() {
|
|
1716
1765
|
for (const [name, session] of this.sessions.entries()) {
|
|
1717
1766
|
try {
|
|
@@ -1776,17 +1825,29 @@ function createApiContextWindow(fullHistory, maxTurns) {
|
|
|
1776
1825
|
const turns = [];
|
|
1777
1826
|
let currentTurn = [];
|
|
1778
1827
|
let turnsFound = 0;
|
|
1828
|
+
const isDevOverlay = (m) => m?.role === "user" && m?.name === "dev_overlay";
|
|
1779
1829
|
for (let i = conversationHistory.length - 1; i >= 0; i--) {
|
|
1780
1830
|
const msg = conversationHistory[i];
|
|
1781
1831
|
currentTurn.unshift(msg);
|
|
1782
|
-
|
|
1783
|
-
|
|
1832
|
+
const endsWithAgentEnd = msg.role === "assistant" && msg.tool_calls?.some((tc) => tc.function.name === "agent_end_task");
|
|
1833
|
+
if (endsWithAgentEnd) {
|
|
1784
1834
|
turns.unshift([...currentTurn]);
|
|
1785
1835
|
currentTurn = [];
|
|
1786
1836
|
turnsFound++;
|
|
1787
1837
|
if (turnsFound >= maxTurns) {
|
|
1788
1838
|
break;
|
|
1789
1839
|
}
|
|
1840
|
+
continue;
|
|
1841
|
+
}
|
|
1842
|
+
const prev = conversationHistory[i - 1];
|
|
1843
|
+
if (msg.role === "user" && !isDevOverlay(msg)) {
|
|
1844
|
+
if (prev && prev.role === "assistant" && !prev.tool_calls?.some((tc) => tc.function.name === "agent_end_task")) {
|
|
1845
|
+
const hasNonOverlay = currentTurn.some((m) => m.role !== "user" || !isDevOverlay(m));
|
|
1846
|
+
if (hasNonOverlay) {
|
|
1847
|
+
turns.unshift([...currentTurn]);
|
|
1848
|
+
currentTurn = [];
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1790
1851
|
}
|
|
1791
1852
|
}
|
|
1792
1853
|
if (currentTurn.length > 0) {
|
|
@@ -1818,6 +1879,22 @@ var Agent = class {
|
|
|
1818
1879
|
this.eventBus.on("user_interrupt", () => {
|
|
1819
1880
|
this.isInterrupted = true;
|
|
1820
1881
|
});
|
|
1882
|
+
this.eventBus.on("dev_overlay", async (data) => {
|
|
1883
|
+
const clean = String(data.payload ?? "").trim();
|
|
1884
|
+
this.history.push({ role: "user", name: "dev_overlay", content: clean });
|
|
1885
|
+
this.eventBus.emit("backend_message", {
|
|
1886
|
+
type: "dev_overlay",
|
|
1887
|
+
payload: clean,
|
|
1888
|
+
ts: data.ts || Date.now()
|
|
1889
|
+
});
|
|
1890
|
+
try {
|
|
1891
|
+
if (this.sessionFile) {
|
|
1892
|
+
await saveSessionHistory(this.sessionFile, this.history);
|
|
1893
|
+
}
|
|
1894
|
+
} catch (e) {
|
|
1895
|
+
this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s dev_overlay: ${e.message}` });
|
|
1896
|
+
}
|
|
1897
|
+
});
|
|
1821
1898
|
const nativeToolInvoker = new ToolInvoker();
|
|
1822
1899
|
this.mcpClient = new MCPClient(nativeToolInvoker, eventBus2);
|
|
1823
1900
|
this.feedbackSystem = new AdvancedFeedbackSystem();
|
|
@@ -1884,14 +1961,28 @@ var Agent = class {
|
|
|
1884
1961
|
You will only produce a final message to the user **after receiving a valid "role": "tool" response** matching your previous 'tool_call_id'.
|
|
1885
1962
|
|
|
1886
1963
|
You are a pure orchestration agent \u2014 your only job is to call tools. No autonomous answers, no internal reasoning.
|
|
1964
|
+
|
|
1965
|
+
Live Dev Overlays:
|
|
1966
|
+
The developer can send messages at any time. They MUST be incorporated immediately. Always confirm via message_notify_dev and proceed.
|
|
1967
|
+
Developer Feedback Handling:
|
|
1968
|
+
- When you detect a developer message, immediately send a short-term acknowledgement via message_notify_dev (maximum one sentence).
|
|
1969
|
+
- Treat the message as a system directive already entered in the history in the format: "Human developer sending this message '<feedback>' to you."
|
|
1970
|
+
- Add it to your workflow with a simple and clear flow of reasoning. Keep it minimal and direct (no verbose thought).
|
|
1971
|
+
- Don't add extra or duplicate messages to the history; the system message is already there. Just act on it.
|
|
1972
|
+
|
|
1887
1973
|
`;
|
|
1888
1974
|
this.history.push({ role: "system", content: systemPrompt });
|
|
1889
1975
|
await saveSessionHistory(this.sessionFile, this.history);
|
|
1890
1976
|
}
|
|
1977
|
+
this.isInitialized = true;
|
|
1891
1978
|
}
|
|
1892
1979
|
getAvailableTools() {
|
|
1893
1980
|
return this.mcpClient.getAvailableTools();
|
|
1894
1981
|
}
|
|
1982
|
+
// UI helper: detailed tools with origin metadata
|
|
1983
|
+
getUiToolsDetailed() {
|
|
1984
|
+
return this.mcpClient.getAvailableToolsDetailed();
|
|
1985
|
+
}
|
|
1895
1986
|
async processTurn(userInput) {
|
|
1896
1987
|
this.isInterrupted = false;
|
|
1897
1988
|
this.history.push({ role: "user", content: userInput.content });
|
|
@@ -1905,6 +1996,13 @@ var Agent = class {
|
|
|
1905
1996
|
const toolCall = decisionData.tool_calls[0];
|
|
1906
1997
|
let toolResultContent;
|
|
1907
1998
|
let shouldContinueConversation = true;
|
|
1999
|
+
if (!this.sessionFile) {
|
|
2000
|
+
const [sessionFile, history] = await loadOrcreateSession(this.sessionId);
|
|
2001
|
+
this.sessionFile = sessionFile;
|
|
2002
|
+
if (this.history.length === 0 && history.length > 0) {
|
|
2003
|
+
this.history = history;
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
1908
2006
|
if (decisionData.type === "user_decision_execute") {
|
|
1909
2007
|
const toolName = toolCall.function.name;
|
|
1910
2008
|
const toolArgs = JSON.parse(toolCall.function.arguments);
|
|
@@ -2071,7 +2169,7 @@ var WorkingTimer = () => {
|
|
|
2071
2169
|
return () => clearInterval(dotsTimer);
|
|
2072
2170
|
}, []);
|
|
2073
2171
|
const dots = ".".repeat(dotIndex).padEnd(3, " ");
|
|
2074
|
-
return /* @__PURE__ */ jsx7(Box7, { marginBottom:
|
|
2172
|
+
return /* @__PURE__ */ jsx7(Box7, { marginBottom: 0.5, paddingX: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: "magenta", children: [
|
|
2075
2173
|
`working${dots}`,
|
|
2076
2174
|
` ${seconds}s`
|
|
2077
2175
|
] }) });
|
|
@@ -2300,8 +2398,191 @@ var ToolResultDisplayComponent = ({ toolName, result }) => {
|
|
|
2300
2398
|
};
|
|
2301
2399
|
var ToolResultDisplay = memo3(ToolResultDisplayComponent);
|
|
2302
2400
|
|
|
2303
|
-
// src/app/ui/
|
|
2401
|
+
// src/app/ui/SessionInfoConnectingMCP.tsx
|
|
2402
|
+
import { Box as Box11, Text as Text10 } from "ink";
|
|
2403
|
+
import Spinner from "ink-spinner";
|
|
2304
2404
|
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2405
|
+
var SessionInfoConnectingMCP = ({ sessionId: sessionId2, workdir, statusMessage }) => {
|
|
2406
|
+
return /* @__PURE__ */ jsxs9(
|
|
2407
|
+
Box11,
|
|
2408
|
+
{
|
|
2409
|
+
borderStyle: "round",
|
|
2410
|
+
borderColor: "gray",
|
|
2411
|
+
flexDirection: "column",
|
|
2412
|
+
marginBottom: 1,
|
|
2413
|
+
children: [
|
|
2414
|
+
/* @__PURE__ */ jsxs9(Text10, { children: [
|
|
2415
|
+
/* @__PURE__ */ jsx11(Text10, { bold: true, color: "white", children: "localhost" }),
|
|
2416
|
+
" ",
|
|
2417
|
+
/* @__PURE__ */ jsx11(Text10, { color: "gray", children: " session:" }),
|
|
2418
|
+
" ",
|
|
2419
|
+
/* @__PURE__ */ jsx11(Text10, { color: "magenta", children: sessionId2 })
|
|
2420
|
+
] }),
|
|
2421
|
+
/* @__PURE__ */ jsxs9(Text10, { children: [
|
|
2422
|
+
/* @__PURE__ */ jsx11(Text10, { color: "magenta", children: "\u21B3" }),
|
|
2423
|
+
" ",
|
|
2424
|
+
/* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
|
|
2425
|
+
"workdir: ",
|
|
2426
|
+
workdir
|
|
2427
|
+
] })
|
|
2428
|
+
] }),
|
|
2429
|
+
/* @__PURE__ */ jsxs9(Text10, { children: [
|
|
2430
|
+
/* @__PURE__ */ jsx11(Text10, { color: "magenta", children: "\u21B3" }),
|
|
2431
|
+
" ",
|
|
2432
|
+
/* @__PURE__ */ jsx11(Text10, { color: "gray", children: "MCP: " }),
|
|
2433
|
+
/* @__PURE__ */ jsxs9(Text10, { color: "yellow", children: [
|
|
2434
|
+
/* @__PURE__ */ jsx11(Spinner, { type: "dots" }),
|
|
2435
|
+
" connecting"
|
|
2436
|
+
] })
|
|
2437
|
+
] }),
|
|
2438
|
+
/* @__PURE__ */ jsxs9(Text10, { children: [
|
|
2439
|
+
/* @__PURE__ */ jsx11(Text10, { color: "magenta", children: "\u21B3" }),
|
|
2440
|
+
" ",
|
|
2441
|
+
/* @__PURE__ */ jsx11(Text10, { color: "gray", children: "status: " }),
|
|
2442
|
+
/* @__PURE__ */ jsx11(Text10, { color: "white", children: statusMessage || "Please wait while we establish connections." })
|
|
2443
|
+
] })
|
|
2444
|
+
]
|
|
2445
|
+
}
|
|
2446
|
+
);
|
|
2447
|
+
};
|
|
2448
|
+
var SessionInfoConnectingMCP_default = SessionInfoConnectingMCP;
|
|
2449
|
+
|
|
2450
|
+
// src/app/ui/components/SlashCommands.tsx
|
|
2451
|
+
import { Box as Box12, Text as Text11 } from "ink";
|
|
2452
|
+
import { Fragment, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2453
|
+
var SlashCommands = ({ input, setHistory, agentRef }) => {
|
|
2454
|
+
const [cmd, ...args] = input.slice(1).trim().split(/\s+/);
|
|
2455
|
+
const outBox = (children) => /* @__PURE__ */ jsx12(Box12, { borderStyle: "round", borderColor: "gray", paddingX: 1, marginBottom: 1, flexDirection: "column", children });
|
|
2456
|
+
const render2 = () => {
|
|
2457
|
+
if (!cmd) {
|
|
2458
|
+
return null;
|
|
2459
|
+
}
|
|
2460
|
+
if (cmd === "help") {
|
|
2461
|
+
const cmds = getSlashCommands();
|
|
2462
|
+
return outBox(
|
|
2463
|
+
/* @__PURE__ */ jsxs10(Fragment, { children: [
|
|
2464
|
+
/* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "Available commands" }),
|
|
2465
|
+
cmds.map((c, i) => /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2466
|
+
c.name,
|
|
2467
|
+
" - ",
|
|
2468
|
+
c.description
|
|
2469
|
+
] }, i))
|
|
2470
|
+
] })
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2473
|
+
if (cmd === "clear") {
|
|
2474
|
+
setHistory((prev) => prev.filter((item) => item.id === 0 || item.id === 1));
|
|
2475
|
+
return outBox(/* @__PURE__ */ jsx12(Text11, { color: "green", children: "History cleared." }));
|
|
2476
|
+
}
|
|
2477
|
+
if (cmd === "mcp") {
|
|
2478
|
+
const all = agentRef.current?.getUiToolsDetailed?.() || agentRef.current?.getAvailableTools?.() || [];
|
|
2479
|
+
const isMcp = (t) => t.source?.toLowerCase?.() === "mcp" || !!t.server && t.server !== "native";
|
|
2480
|
+
const tools = all.filter(isMcp);
|
|
2481
|
+
const term = (args?.[0] || "").toLowerCase();
|
|
2482
|
+
const filtered = term ? tools.filter((t) => (t.function?.name || t.name || "tool").toLowerCase().includes(term)) : tools;
|
|
2483
|
+
const pad = (s, n) => s.length >= n ? s.slice(0, n - 1) + "\u2026" : s.padEnd(n, " ");
|
|
2484
|
+
const colName = 34;
|
|
2485
|
+
const colType = 10;
|
|
2486
|
+
const colSource = 18;
|
|
2487
|
+
return outBox(
|
|
2488
|
+
/* @__PURE__ */ jsxs10(Fragment, { children: [
|
|
2489
|
+
/* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "MCP Tools" }),
|
|
2490
|
+
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2491
|
+
"Total MCP: ",
|
|
2492
|
+
tools.length,
|
|
2493
|
+
term ? ` | Filter: "${term}" | Showing: ${filtered.length}` : ""
|
|
2494
|
+
] }),
|
|
2495
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No MCP tools to display." }) : /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", children: [
|
|
2496
|
+
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2497
|
+
pad("Name", colName),
|
|
2498
|
+
" | ",
|
|
2499
|
+
pad("Type", colType),
|
|
2500
|
+
" | ",
|
|
2501
|
+
pad("Source", colSource)
|
|
2502
|
+
] }),
|
|
2503
|
+
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2504
|
+
"".padEnd(colName, "-"),
|
|
2505
|
+
"---",
|
|
2506
|
+
"".padEnd(colType, "-"),
|
|
2507
|
+
"---",
|
|
2508
|
+
"".padEnd(colSource, "-")
|
|
2509
|
+
] }),
|
|
2510
|
+
filtered.map((t, i) => {
|
|
2511
|
+
const name = t.function?.name || t.name || "tool";
|
|
2512
|
+
const type = t.function?.name ? "fn" : t.type || "tool";
|
|
2513
|
+
const source = t.source || t.provider || "mcp";
|
|
2514
|
+
return /* @__PURE__ */ jsxs10(Text11, { color: "white", children: [
|
|
2515
|
+
pad(name, colName),
|
|
2516
|
+
" | ",
|
|
2517
|
+
pad(String(type), colType),
|
|
2518
|
+
" | ",
|
|
2519
|
+
pad(String(source), colSource)
|
|
2520
|
+
] }, i);
|
|
2521
|
+
})
|
|
2522
|
+
] })
|
|
2523
|
+
] })
|
|
2524
|
+
);
|
|
2525
|
+
}
|
|
2526
|
+
if (cmd === "tools") {
|
|
2527
|
+
const all = agentRef.current?.getUiToolsDetailed?.() || agentRef.current?.getAvailableTools?.() || [];
|
|
2528
|
+
const isMcp = (t) => t.source?.toLowerCase?.() === "mcp" || !!t.server && t.server !== "native";
|
|
2529
|
+
const tools = all.filter((t) => !isMcp(t));
|
|
2530
|
+
const term = (args?.[0] || "").toLowerCase();
|
|
2531
|
+
const filtered = term ? tools.filter((t) => (t.function?.name || t.name || "tool").toLowerCase().includes(term)) : tools;
|
|
2532
|
+
const pad = (s, n) => s.length >= n ? s.slice(0, n - 1) + "\u2026" : s.padEnd(n, " ");
|
|
2533
|
+
const colName = 34;
|
|
2534
|
+
const colType = 10;
|
|
2535
|
+
const colSource = 18;
|
|
2536
|
+
return outBox(
|
|
2537
|
+
/* @__PURE__ */ jsxs10(Fragment, { children: [
|
|
2538
|
+
/* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "Native Tools" }),
|
|
2539
|
+
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2540
|
+
"Total Native: ",
|
|
2541
|
+
tools.length,
|
|
2542
|
+
term ? ` | Filter: "${term}" | Showing: ${filtered.length}` : ""
|
|
2543
|
+
] }),
|
|
2544
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No native tools to display." }) : /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", children: [
|
|
2545
|
+
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2546
|
+
pad("Name", colName),
|
|
2547
|
+
" | ",
|
|
2548
|
+
pad("Type", colType),
|
|
2549
|
+
" | ",
|
|
2550
|
+
pad("Source", colSource)
|
|
2551
|
+
] }),
|
|
2552
|
+
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2553
|
+
"".padEnd(colName, "-"),
|
|
2554
|
+
"---",
|
|
2555
|
+
"".padEnd(colType, "-"),
|
|
2556
|
+
"---",
|
|
2557
|
+
"".padEnd(colSource, "-")
|
|
2558
|
+
] }),
|
|
2559
|
+
filtered.map((t, i) => {
|
|
2560
|
+
const name = t.function?.name || t.name || "tool";
|
|
2561
|
+
const type = t.function?.name ? "fn" : t.type || "tool";
|
|
2562
|
+
const source = t.source || "native";
|
|
2563
|
+
return /* @__PURE__ */ jsxs10(Text11, { color: "white", children: [
|
|
2564
|
+
pad(name, colName),
|
|
2565
|
+
" | ",
|
|
2566
|
+
pad(String(type), colType),
|
|
2567
|
+
" | ",
|
|
2568
|
+
pad(String(source), colSource)
|
|
2569
|
+
] }, i);
|
|
2570
|
+
})
|
|
2571
|
+
] })
|
|
2572
|
+
] })
|
|
2573
|
+
);
|
|
2574
|
+
}
|
|
2575
|
+
return outBox(/* @__PURE__ */ jsxs10(Text11, { color: "red", children: [
|
|
2576
|
+
"Command not recognized: /",
|
|
2577
|
+
cmd
|
|
2578
|
+
] }));
|
|
2579
|
+
};
|
|
2580
|
+
return /* @__PURE__ */ jsx12(Fragment, { children: render2() });
|
|
2581
|
+
};
|
|
2582
|
+
var SlashCommands_default = SlashCommands;
|
|
2583
|
+
|
|
2584
|
+
// src/app/ui/App.tsx
|
|
2585
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2305
2586
|
var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
2306
2587
|
const agentInstance = useRef(null);
|
|
2307
2588
|
const [history, setHistory] = useState4([]);
|
|
@@ -2316,7 +2597,9 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2316
2597
|
const [pendingConfirmation, setPendingConfirmation] = useState4(
|
|
2317
2598
|
null
|
|
2318
2599
|
);
|
|
2319
|
-
const [confirmationPreview, setConfirmationPreview] = useState4(
|
|
2600
|
+
const [confirmationPreview, setConfirmationPreview] = useState4(
|
|
2601
|
+
null
|
|
2602
|
+
);
|
|
2320
2603
|
const alwaysAcceptList = useRef([]);
|
|
2321
2604
|
const workdir = process.cwd();
|
|
2322
2605
|
const handleInterrupt = useCallback(() => {
|
|
@@ -2327,13 +2610,33 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2327
2610
|
...prev,
|
|
2328
2611
|
{
|
|
2329
2612
|
id: prev.length,
|
|
2330
|
-
component: /* @__PURE__ */
|
|
2613
|
+
component: /* @__PURE__ */ jsx13(Text12, { color: "yellow", children: "-- Task cancelled by dev. --" })
|
|
2331
2614
|
}
|
|
2332
2615
|
]);
|
|
2333
2616
|
}, [isProcessing, eventBus2]);
|
|
2334
2617
|
const handleSubmit = useCallback(
|
|
2335
2618
|
(text) => {
|
|
2336
2619
|
if (!text || isProcessing || !agentInstance.current) return;
|
|
2620
|
+
if (text.startsWith("/")) {
|
|
2621
|
+
const [cmd] = text.slice(1).trim().split(/\s+/);
|
|
2622
|
+
if (!cmd) {
|
|
2623
|
+
setIsProcessing(false);
|
|
2624
|
+
return;
|
|
2625
|
+
}
|
|
2626
|
+
setHistory((prev) => [
|
|
2627
|
+
...prev,
|
|
2628
|
+
{
|
|
2629
|
+
id: prev.length,
|
|
2630
|
+
component: /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text12, { color: "white", dimColor: true, children: text }) })
|
|
2631
|
+
},
|
|
2632
|
+
{
|
|
2633
|
+
id: prev.length + 1,
|
|
2634
|
+
component: /* @__PURE__ */ jsx13(SlashCommands_default, { input: text, setHistory, agentRef: agentInstance })
|
|
2635
|
+
}
|
|
2636
|
+
]);
|
|
2637
|
+
setIsProcessing(false);
|
|
2638
|
+
return;
|
|
2639
|
+
}
|
|
2337
2640
|
setIsProcessing(true);
|
|
2338
2641
|
const displayText = text.length > 1e4 ? text.substring(0, 1e4) + "..." : text;
|
|
2339
2642
|
setHistory((prev) => [
|
|
@@ -2342,8 +2645,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2342
2645
|
id: prev.length,
|
|
2343
2646
|
component: (
|
|
2344
2647
|
// Uma única Box para o espaçamento
|
|
2345
|
-
/* @__PURE__ */
|
|
2346
|
-
/* @__PURE__ */
|
|
2648
|
+
/* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Text12, { color: "white", dimColor: true, children: [
|
|
2649
|
+
/* @__PURE__ */ jsxs11(Text12, { color: "white", children: [
|
|
2347
2650
|
">",
|
|
2348
2651
|
" "
|
|
2349
2652
|
] }),
|
|
@@ -2378,7 +2681,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2378
2681
|
[]
|
|
2379
2682
|
);
|
|
2380
2683
|
useEffect3(() => {
|
|
2381
|
-
setHistory([{ id: 0, component: /* @__PURE__ */
|
|
2684
|
+
setHistory([{ id: 0, component: /* @__PURE__ */ jsx13(Header, {}) }]);
|
|
2382
2685
|
const initializeAgent = async () => {
|
|
2383
2686
|
try {
|
|
2384
2687
|
agentInstance.current = new Agent(sessionId2, eventBus2);
|
|
@@ -2430,7 +2733,7 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2430
2733
|
if (prev.length < 2) {
|
|
2431
2734
|
newHistory.push({
|
|
2432
2735
|
id: 1,
|
|
2433
|
-
component: /* @__PURE__ */
|
|
2736
|
+
component: /* @__PURE__ */ jsx13(
|
|
2434
2737
|
SessionInfo,
|
|
2435
2738
|
{
|
|
2436
2739
|
sessionId: sessionId2,
|
|
@@ -2451,10 +2754,10 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2451
2754
|
}
|
|
2452
2755
|
let newComponent = null;
|
|
2453
2756
|
if (parsed.type === "debug") {
|
|
2454
|
-
newComponent = /* @__PURE__ */
|
|
2757
|
+
newComponent = /* @__PURE__ */ jsx13(Text12, { color: "gray", children: parsed.message });
|
|
2455
2758
|
} else if (parsed.type === "protocol_violation") {
|
|
2456
|
-
newComponent = /* @__PURE__ */
|
|
2457
|
-
|
|
2759
|
+
newComponent = /* @__PURE__ */ jsxs11(
|
|
2760
|
+
Box13,
|
|
2458
2761
|
{
|
|
2459
2762
|
borderStyle: "round",
|
|
2460
2763
|
borderColor: "yellow",
|
|
@@ -2462,27 +2765,19 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2462
2765
|
marginBottom: 1,
|
|
2463
2766
|
paddingX: 1,
|
|
2464
2767
|
children: [
|
|
2465
|
-
" ",
|
|
2466
|
-
/* @__PURE__ */
|
|
2467
|
-
|
|
2468
|
-
"Protocol Violation",
|
|
2469
|
-
" "
|
|
2470
|
-
] }),
|
|
2471
|
-
" ",
|
|
2472
|
-
/* @__PURE__ */ jsx11(Text10, { color: "gray", children: parsed.content }),
|
|
2473
|
-
" ",
|
|
2474
|
-
/* @__PURE__ */ jsx11(Text10, { color: "yellow", children: parsed.message }),
|
|
2475
|
-
" "
|
|
2768
|
+
/* @__PURE__ */ jsx13(Text12, { color: "yellow", bold: true, children: "Protocol Violation" }),
|
|
2769
|
+
/* @__PURE__ */ jsx13(Text12, { color: "gray", children: parsed.content }),
|
|
2770
|
+
/* @__PURE__ */ jsx13(Text12, { color: "yellow", children: parsed.message })
|
|
2476
2771
|
]
|
|
2477
2772
|
}
|
|
2478
2773
|
);
|
|
2479
2774
|
} else if (parsed.type === "error") {
|
|
2480
|
-
newComponent = /* @__PURE__ */
|
|
2775
|
+
newComponent = /* @__PURE__ */ jsxs11(Text12, { color: "red", children: [
|
|
2481
2776
|
"\u274C ",
|
|
2482
2777
|
parsed.message
|
|
2483
2778
|
] });
|
|
2484
2779
|
} else if (parsed.type === "tool_call") {
|
|
2485
|
-
newComponent = /* @__PURE__ */
|
|
2780
|
+
newComponent = /* @__PURE__ */ jsx13(
|
|
2486
2781
|
ToolCallDisplay,
|
|
2487
2782
|
{
|
|
2488
2783
|
toolName: parsed.tool_name,
|
|
@@ -2491,13 +2786,27 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2491
2786
|
}
|
|
2492
2787
|
);
|
|
2493
2788
|
} else if (parsed.type === "tool_result") {
|
|
2494
|
-
newComponent = /* @__PURE__ */
|
|
2789
|
+
newComponent = /* @__PURE__ */ jsx13(
|
|
2495
2790
|
ToolResultDisplay,
|
|
2496
2791
|
{
|
|
2497
2792
|
toolName: parsed.tool_name,
|
|
2498
2793
|
result: parsed.result
|
|
2499
2794
|
}
|
|
2500
2795
|
);
|
|
2796
|
+
} else if (parsed.type === "dev_overlay") {
|
|
2797
|
+
newComponent = /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Text12, { color: "gray", children: [
|
|
2798
|
+
/* @__PURE__ */ jsxs11(Text12, { color: "blue", children: [
|
|
2799
|
+
">",
|
|
2800
|
+
" "
|
|
2801
|
+
] }),
|
|
2802
|
+
parsed.payload
|
|
2803
|
+
] }) });
|
|
2804
|
+
} else if (parsed.type === "log") {
|
|
2805
|
+
newComponent = /* @__PURE__ */ jsxs11(Text12, { color: "gray", children: [
|
|
2806
|
+
"\u2139\uFE0F ",
|
|
2807
|
+
parsed.message,
|
|
2808
|
+
parsed.payload ? `: ${parsed.payload}` : ""
|
|
2809
|
+
] });
|
|
2501
2810
|
} else if (parsed.type === "assistant_message" && parsed.content) {
|
|
2502
2811
|
newComponent = null;
|
|
2503
2812
|
}
|
|
@@ -2510,22 +2819,37 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2510
2819
|
} catch (error) {
|
|
2511
2820
|
}
|
|
2512
2821
|
};
|
|
2822
|
+
const handleUiOverlay = (data) => {
|
|
2823
|
+
eventBus2.emit("dev_overlay", data);
|
|
2824
|
+
};
|
|
2825
|
+
uiEventBus.on("dev_overlay", handleUiOverlay);
|
|
2513
2826
|
eventBus2.on("backend_message", handleBackendMessage);
|
|
2514
2827
|
initializeAgent();
|
|
2515
2828
|
return () => {
|
|
2829
|
+
uiEventBus.off("dev_overlay", handleUiOverlay);
|
|
2516
2830
|
eventBus2.off("backend_message", handleBackendMessage);
|
|
2517
2831
|
};
|
|
2518
2832
|
}, [eventBus2, sessionId2, handleConfirmation]);
|
|
2519
2833
|
const renderInteractiveComponent = () => {
|
|
2520
2834
|
if (mcpStatus !== "connected") {
|
|
2521
|
-
return /* @__PURE__ */
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2835
|
+
return /* @__PURE__ */ jsx13(
|
|
2836
|
+
Box13,
|
|
2837
|
+
{
|
|
2838
|
+
borderStyle: "round",
|
|
2839
|
+
borderColor: "black",
|
|
2840
|
+
children: /* @__PURE__ */ jsx13(
|
|
2841
|
+
SessionInfoConnectingMCP_default,
|
|
2842
|
+
{
|
|
2843
|
+
sessionId: sessionId2,
|
|
2844
|
+
workdir,
|
|
2845
|
+
statusMessage
|
|
2846
|
+
}
|
|
2847
|
+
)
|
|
2848
|
+
}
|
|
2849
|
+
);
|
|
2526
2850
|
}
|
|
2527
2851
|
if (pendingConfirmation) {
|
|
2528
|
-
return /* @__PURE__ */
|
|
2852
|
+
return /* @__PURE__ */ jsx13(
|
|
2529
2853
|
ConfirmationPrompt,
|
|
2530
2854
|
{
|
|
2531
2855
|
toolCalls: pendingConfirmation,
|
|
@@ -2537,9 +2861,9 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2537
2861
|
}
|
|
2538
2862
|
);
|
|
2539
2863
|
}
|
|
2540
|
-
return /* @__PURE__ */
|
|
2541
|
-
isProcessing && !pendingConfirmation && /* @__PURE__ */
|
|
2542
|
-
/* @__PURE__ */
|
|
2864
|
+
return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", children: [
|
|
2865
|
+
isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx13(WorkingTimer, {}),
|
|
2866
|
+
/* @__PURE__ */ jsx13(
|
|
2543
2867
|
InputPrompt,
|
|
2544
2868
|
{
|
|
2545
2869
|
onSubmit: handleSubmit,
|
|
@@ -2549,8 +2873,8 @@ var AppComponent = ({ eventBus: eventBus2, sessionId: sessionId2 }) => {
|
|
|
2549
2873
|
)
|
|
2550
2874
|
] });
|
|
2551
2875
|
};
|
|
2552
|
-
return /* @__PURE__ */
|
|
2553
|
-
/* @__PURE__ */
|
|
2876
|
+
return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", children: [
|
|
2877
|
+
/* @__PURE__ */ jsx13(Static, { items: history, children: (item) => /* @__PURE__ */ jsx13(Box13, { children: item.component }, item.id) }),
|
|
2554
2878
|
renderInteractiveComponent()
|
|
2555
2879
|
] });
|
|
2556
2880
|
};
|
|
@@ -2558,7 +2882,7 @@ var App = memo4(AppComponent);
|
|
|
2558
2882
|
var App_default = App;
|
|
2559
2883
|
|
|
2560
2884
|
// src/main.ts
|
|
2561
|
-
var eventBus = new
|
|
2885
|
+
var eventBus = new EventEmitter2();
|
|
2562
2886
|
var sessionId = uuidv42();
|
|
2563
2887
|
var props = {
|
|
2564
2888
|
eventBus,
|