@bike4mind/cli 0.2.8-fix-cli-type-error.17148 → 0.2.10-fix-b4m-cli-flicker.17230
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 +48 -0
- package/dist/{artifactExtractor-DNE5C6DY.js → artifactExtractor-JAGU7QRL.js} +1 -1
- package/dist/{chunk-YXHQN7SK.js → chunk-AQBZVAYO.js} +2 -2
- package/dist/{chunk-H6SHASMZ.js → chunk-DJPXSSP4.js} +13 -1
- package/dist/{chunk-DNSBC3LO.js → chunk-GCVIRGIN.js} +1 -1
- package/dist/{chunk-3CCBDIFR.js → chunk-GO75FMLY.js} +2 -2
- package/dist/{chunk-LIRY24M5.js → chunk-VZU4Z7WI.js} +2 -2
- package/dist/{create-PNU64QSF.js → create-ZDOTF7XD.js} +3 -3
- package/dist/index.js +826 -131
- package/dist/{llmMarkdownGenerator-UQXKK2AS.js → llmMarkdownGenerator-LXUGJ7QH.js} +1 -1
- package/dist/{markdownGenerator-FLLZFXMT.js → markdownGenerator-IMDG6OX6.js} +1 -1
- package/dist/{mementoService-LVFQ5XAW.js → mementoService-TURUS3OH.js} +3 -3
- package/dist/{src-OH5AAQEW.js → src-6ZRXGE5J.js} +2 -2
- package/dist/{src-OYQXRLTM.js → src-YNY32ELR.js} +3 -1
- package/dist/{subtractCredits-75VT7HP2.js → subtractCredits-GOVKM3IR.js} +3 -3
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
getEffectiveApiKey,
|
|
5
5
|
getOpenWeatherKey,
|
|
6
6
|
getSerperKey
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-GO75FMLY.js";
|
|
8
|
+
import "./chunk-VZU4Z7WI.js";
|
|
9
|
+
import "./chunk-AQBZVAYO.js";
|
|
10
10
|
import {
|
|
11
11
|
BFLImageService,
|
|
12
12
|
BaseStorage,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
OpenAIBackend,
|
|
16
16
|
OpenAIImageService,
|
|
17
17
|
XAIImageService
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-GCVIRGIN.js";
|
|
19
19
|
import {
|
|
20
20
|
Logger
|
|
21
21
|
} from "./chunk-AMDXHL6S.js";
|
|
@@ -73,19 +73,19 @@ import {
|
|
|
73
73
|
XAI_IMAGE_MODELS,
|
|
74
74
|
b4mLLMTools,
|
|
75
75
|
getMcpProviderMetadata
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-DJPXSSP4.js";
|
|
77
77
|
import {
|
|
78
78
|
__require
|
|
79
79
|
} from "./chunk-PDX44BCA.js";
|
|
80
80
|
|
|
81
81
|
// src/index.tsx
|
|
82
|
-
import
|
|
83
|
-
import { render, Box as
|
|
82
|
+
import React17, { useState as useState7, useEffect as useEffect3, useCallback, useRef } from "react";
|
|
83
|
+
import { render, Box as Box16, Text as Text16, useApp, useInput as useInput6 } from "ink";
|
|
84
84
|
import { v4 as uuidv49 } from "uuid";
|
|
85
85
|
|
|
86
86
|
// src/components/App.tsx
|
|
87
|
-
import
|
|
88
|
-
import { Box as
|
|
87
|
+
import React11 from "react";
|
|
88
|
+
import { Box as Box10, Text as Text10, Static } from "ink";
|
|
89
89
|
|
|
90
90
|
// src/components/StatusBar.tsx
|
|
91
91
|
import React from "react";
|
|
@@ -742,6 +742,7 @@ function InputPrompt({
|
|
|
742
742
|
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
743
743
|
const [historyIndex, setHistoryIndex] = useState2(-1);
|
|
744
744
|
const [tempInput, setTempInput] = useState2("");
|
|
745
|
+
const [inputKey, setInputKey] = useState2(0);
|
|
745
746
|
useEffect(() => {
|
|
746
747
|
if (prefillInput) {
|
|
747
748
|
setValue(prefillInput);
|
|
@@ -889,10 +890,11 @@ function InputPrompt({
|
|
|
889
890
|
return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(
|
|
890
891
|
CustomTextInput,
|
|
891
892
|
{
|
|
893
|
+
key: inputKey,
|
|
892
894
|
value,
|
|
893
895
|
onChange: handleChange,
|
|
894
896
|
onSubmit: handleSubmit,
|
|
895
|
-
placeholder: disabled ? "
|
|
897
|
+
placeholder: disabled ? "" : "Type your message, /help for commands, or @file to reference",
|
|
896
898
|
showCursor: !disabled
|
|
897
899
|
}
|
|
898
900
|
)), shouldShowCommandAutocomplete && /* @__PURE__ */ React5.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React5.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
|
|
@@ -916,6 +918,29 @@ var useCliStore = create((set) => ({
|
|
|
916
918
|
// Session state
|
|
917
919
|
session: null,
|
|
918
920
|
setSession: (session) => set({ session }),
|
|
921
|
+
// Pending messages
|
|
922
|
+
pendingMessages: [],
|
|
923
|
+
addPendingMessage: (message) => set((state) => ({ pendingMessages: [...state.pendingMessages, message] })),
|
|
924
|
+
updatePendingMessage: (index, message) => set((state) => {
|
|
925
|
+
const updated = [...state.pendingMessages];
|
|
926
|
+
updated[index] = message;
|
|
927
|
+
return { pendingMessages: updated };
|
|
928
|
+
}),
|
|
929
|
+
clearPendingMessages: () => set({ pendingMessages: [] }),
|
|
930
|
+
completePendingMessage: (index, finalMessage) => set((state) => {
|
|
931
|
+
const pending = [...state.pendingMessages];
|
|
932
|
+
pending.splice(index, 1);
|
|
933
|
+
const session = state.session;
|
|
934
|
+
if (!session) return { pendingMessages: pending };
|
|
935
|
+
return {
|
|
936
|
+
pendingMessages: pending,
|
|
937
|
+
session: {
|
|
938
|
+
...session,
|
|
939
|
+
messages: [...session.messages, finalMessage],
|
|
940
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
}),
|
|
919
944
|
// UI state
|
|
920
945
|
isThinking: false,
|
|
921
946
|
setIsThinking: (thinking) => set({ isThinking: thinking }),
|
|
@@ -923,6 +948,7 @@ var useCliStore = create((set) => ({
|
|
|
923
948
|
addAgentStep: (step) => set((state) => ({
|
|
924
949
|
agentSteps: [...state.agentSteps, step]
|
|
925
950
|
})),
|
|
951
|
+
setAgentSteps: (steps) => set({ agentSteps: steps }),
|
|
926
952
|
clearAgentSteps: () => set({ agentSteps: [] }),
|
|
927
953
|
// Permission prompt
|
|
928
954
|
permissionPrompt: null,
|
|
@@ -942,7 +968,7 @@ var AgentThinking = React7.memo(function AgentThinking2() {
|
|
|
942
968
|
if (!isThinking) {
|
|
943
969
|
return null;
|
|
944
970
|
}
|
|
945
|
-
return /* @__PURE__ */ React7.createElement(Box6, {
|
|
971
|
+
return /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
|
|
946
972
|
});
|
|
947
973
|
|
|
948
974
|
// src/components/PermissionPrompt.tsx
|
|
@@ -1214,6 +1240,26 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1214
1240
|
})), /* @__PURE__ */ React9.createElement(Box8, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u2191/\u2193: Navigate | Space/\u2190/\u2192: Change | q/Esc: Save & Exit")));
|
|
1215
1241
|
}
|
|
1216
1242
|
|
|
1243
|
+
// src/components/MessageItem.tsx
|
|
1244
|
+
import React10 from "react";
|
|
1245
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
1246
|
+
function MessageItem({ message }) {
|
|
1247
|
+
const isUser = message.role === "user";
|
|
1248
|
+
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: isUser ? "cyan" : "green" }, isUser ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1249
|
+
if (step.type === "thought") {
|
|
1250
|
+
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
|
|
1251
|
+
}
|
|
1252
|
+
if (step.type === "action") {
|
|
1253
|
+
const toolName = step.metadata?.toolName || "unknown";
|
|
1254
|
+
const toolInput = step.metadata?.toolInput;
|
|
1255
|
+
const observationStep = message.metadata.steps[idx + 1];
|
|
1256
|
+
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1257
|
+
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1258
|
+
}
|
|
1259
|
+
return null;
|
|
1260
|
+
}).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: message.metadata?.steps?.length ? 1 : 0 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React10.createElement(Text9, null, message.content)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1217
1263
|
// src/utils/processFileReferences.ts
|
|
1218
1264
|
import * as fs2 from "fs";
|
|
1219
1265
|
import * as path2 from "path";
|
|
@@ -1342,6 +1388,7 @@ function App({
|
|
|
1342
1388
|
onPrefillConsumed
|
|
1343
1389
|
}) {
|
|
1344
1390
|
const messages = useCliStore((state) => state.session?.messages || []);
|
|
1391
|
+
const pendingMessages = useCliStore((state) => state.pendingMessages);
|
|
1345
1392
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
1346
1393
|
const currentModel = useCliStore((state) => state.session?.model || "claude-sonnet-4-5-20250929");
|
|
1347
1394
|
const totalTokens = useCliStore((state) => state.session?.metadata.totalTokens || 0);
|
|
@@ -1351,7 +1398,7 @@ function App({
|
|
|
1351
1398
|
const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
|
|
1352
1399
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
1353
1400
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
1354
|
-
const handleSubmit =
|
|
1401
|
+
const handleSubmit = React11.useCallback(
|
|
1355
1402
|
async (input) => {
|
|
1356
1403
|
const trimmed = input.trim();
|
|
1357
1404
|
if (!trimmed) return;
|
|
@@ -1380,7 +1427,7 @@ ${errorBlock}`;
|
|
|
1380
1427
|
},
|
|
1381
1428
|
[onMessage, onCommand, setIsThinking]
|
|
1382
1429
|
);
|
|
1383
|
-
return /* @__PURE__ */
|
|
1430
|
+
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, showConfigEditor && config && onSaveConfig ? /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(
|
|
1384
1431
|
ConfigEditor,
|
|
1385
1432
|
{
|
|
1386
1433
|
config,
|
|
@@ -1388,7 +1435,7 @@ ${errorBlock}`;
|
|
|
1388
1435
|
onSave: onSaveConfig,
|
|
1389
1436
|
onClose: () => setShowConfigEditor(false)
|
|
1390
1437
|
}
|
|
1391
|
-
)) : permissionPrompt ? /* @__PURE__ */
|
|
1438
|
+
)) : permissionPrompt ? /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(
|
|
1392
1439
|
PermissionPrompt,
|
|
1393
1440
|
{
|
|
1394
1441
|
toolName: permissionPrompt.toolName,
|
|
@@ -1397,7 +1444,7 @@ ${errorBlock}`;
|
|
|
1397
1444
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
1398
1445
|
onResponse: onPermissionResponse
|
|
1399
1446
|
}
|
|
1400
|
-
)) : /* @__PURE__ */
|
|
1447
|
+
)) : /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React11.createElement(Box10, { key: `${message.timestamp}-${message.role}-${index}`, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(MessageItem, { message }))), /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, pendingMessages.map((message, index) => /* @__PURE__ */ React11.createElement(Box10, { key: `pending-${message.timestamp}-${index}`, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(MessageItem, { message })))), /* @__PURE__ */ React11.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React11.createElement(Box10, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React11.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), /* @__PURE__ */ React11.createElement(Box10, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React11.createElement(
|
|
1401
1448
|
InputPrompt,
|
|
1402
1449
|
{
|
|
1403
1450
|
onSubmit: handleSubmit,
|
|
@@ -1409,37 +1456,22 @@ ${errorBlock}`;
|
|
|
1409
1456
|
}
|
|
1410
1457
|
))));
|
|
1411
1458
|
}
|
|
1412
|
-
function MessageItem({ message }) {
|
|
1413
|
-
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React10.createElement(Text9, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1414
|
-
if (step.type === "thought") {
|
|
1415
|
-
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
|
|
1416
|
-
}
|
|
1417
|
-
if (step.type === "action") {
|
|
1418
|
-
const toolName = step.metadata?.toolName || "unknown";
|
|
1419
|
-
const toolInput = step.metadata?.toolInput;
|
|
1420
|
-
const observationStep = message.metadata.steps[idx + 1];
|
|
1421
|
-
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1422
|
-
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1423
|
-
}
|
|
1424
|
-
return null;
|
|
1425
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
1426
|
-
}
|
|
1427
1459
|
|
|
1428
1460
|
// src/components/MessageList.tsx
|
|
1429
|
-
import
|
|
1430
|
-
import { Box as
|
|
1461
|
+
import React12 from "react";
|
|
1462
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1431
1463
|
function stripThinkingTags(content) {
|
|
1432
1464
|
return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
1433
1465
|
}
|
|
1434
|
-
var MessageList =
|
|
1466
|
+
var MessageList = React12.memo(
|
|
1435
1467
|
function MessageList2({ messages }) {
|
|
1436
1468
|
if (messages.length === 0) {
|
|
1437
|
-
return /* @__PURE__ */
|
|
1469
|
+
return /* @__PURE__ */ React12.createElement(Box11, { paddingY: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "No messages yet. Type a message to start!"));
|
|
1438
1470
|
}
|
|
1439
|
-
return /* @__PURE__ */
|
|
1471
|
+
return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React12.createElement(Box11, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React12.createElement(Box11, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "\u26A0\uFE0F ", stripThinkingTags(message.content)) : /* @__PURE__ */ React12.createElement(Text11, null, stripThinkingTags(message.content))), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React12.createElement(Box11, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " tools used,", " ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1440
1472
|
if (step.type === "thought") {
|
|
1441
|
-
return /* @__PURE__ */
|
|
1442
|
-
|
|
1473
|
+
return /* @__PURE__ */ React12.createElement(Box11, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text11, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React12.createElement(
|
|
1474
|
+
Text11,
|
|
1443
1475
|
{
|
|
1444
1476
|
dimColor: true
|
|
1445
1477
|
},
|
|
@@ -1451,10 +1483,10 @@ var MessageList = React11.memo(
|
|
|
1451
1483
|
const toolInput = step.metadata?.toolInput;
|
|
1452
1484
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1453
1485
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1454
|
-
return /* @__PURE__ */
|
|
1486
|
+
return /* @__PURE__ */ React12.createElement(Box11, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1455
1487
|
}
|
|
1456
1488
|
return null;
|
|
1457
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1489
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React12.createElement(Box11, { paddingLeft: 2 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
|
|
1458
1490
|
},
|
|
1459
1491
|
(prevProps, nextProps) => {
|
|
1460
1492
|
if (prevProps.messages.length !== nextProps.messages.length) {
|
|
@@ -1465,8 +1497,8 @@ var MessageList = React11.memo(
|
|
|
1465
1497
|
);
|
|
1466
1498
|
|
|
1467
1499
|
// src/components/TrustLocationSelector.tsx
|
|
1468
|
-
import
|
|
1469
|
-
import { Box as
|
|
1500
|
+
import React13 from "react";
|
|
1501
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1470
1502
|
import SelectInput2 from "ink-select-input";
|
|
1471
1503
|
function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
1472
1504
|
const items = [];
|
|
@@ -1487,19 +1519,19 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
1487
1519
|
const handleSelect = (item) => {
|
|
1488
1520
|
onSelect(item.value);
|
|
1489
1521
|
};
|
|
1490
|
-
return /* @__PURE__ */
|
|
1522
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { bold: true }, "Where should this rule be saved?")), /* @__PURE__ */ React13.createElement(
|
|
1491
1523
|
SelectInput2,
|
|
1492
1524
|
{
|
|
1493
1525
|
items,
|
|
1494
1526
|
onSelect: handleSelect,
|
|
1495
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1527
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1496
1528
|
}
|
|
1497
|
-
), /* @__PURE__ */
|
|
1529
|
+
), /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Ctrl+C to cancel")));
|
|
1498
1530
|
}
|
|
1499
1531
|
|
|
1500
1532
|
// src/components/RewindSelector.tsx
|
|
1501
|
-
import
|
|
1502
|
-
import { Box as
|
|
1533
|
+
import React14, { useState as useState4 } from "react";
|
|
1534
|
+
import { Box as Box13, Text as Text13, useInput as useInput4 } from "ink";
|
|
1503
1535
|
import SelectInput3 from "ink-select-input";
|
|
1504
1536
|
function RewindSelector({ messages, onSelect, onCancel }) {
|
|
1505
1537
|
const [step, setStep] = useState4("selection");
|
|
@@ -1541,14 +1573,14 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
1541
1573
|
return sum2 + (msg.metadata?.tokenUsage?.total || 0);
|
|
1542
1574
|
}, 0);
|
|
1543
1575
|
if (step === "selection") {
|
|
1544
|
-
return /* @__PURE__ */
|
|
1576
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text13, { bold: true }, "Select a point to rewind to:")), /* @__PURE__ */ React14.createElement(
|
|
1545
1577
|
SelectInput3,
|
|
1546
1578
|
{
|
|
1547
1579
|
items,
|
|
1548
1580
|
onSelect: handleSelectionSelect,
|
|
1549
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1581
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1550
1582
|
}
|
|
1551
|
-
), /* @__PURE__ */
|
|
1583
|
+
), /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
1552
1584
|
}
|
|
1553
1585
|
const confirmationItems = [
|
|
1554
1586
|
{ label: "Yes, rewind to this point", value: "confirm" },
|
|
@@ -1556,19 +1588,82 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
1556
1588
|
];
|
|
1557
1589
|
const selectedMessage = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages[selectedMessageIndex] : null;
|
|
1558
1590
|
const selectedPreview = selectedMessage ? selectedMessage.content.length > 50 ? selectedMessage.content.substring(0, 50) + "..." : selectedMessage.content : "";
|
|
1559
|
-
return /* @__PURE__ */
|
|
1591
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { bold: true }, "Confirm rewind operation:"), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "Message: ", selectedPreview), /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, "This will remove ", messagesToRemove.length, " message(s) (", tokensToRemove.toLocaleString(), " tokens)"), /* @__PURE__ */ React14.createElement(Text13, { color: "cyan" }, "The selected message will be placed in your input for editing.")), /* @__PURE__ */ React14.createElement(
|
|
1560
1592
|
SelectInput3,
|
|
1561
1593
|
{
|
|
1562
1594
|
items: confirmationItems,
|
|
1563
1595
|
onSelect: handleConfirmationSelect,
|
|
1564
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1596
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1597
|
+
}
|
|
1598
|
+
), /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// src/components/SessionSelector.tsx
|
|
1602
|
+
import React15, { useState as useState5 } from "react";
|
|
1603
|
+
import { Box as Box14, Text as Text14, useInput as useInput5 } from "ink";
|
|
1604
|
+
import SelectInput4 from "ink-select-input";
|
|
1605
|
+
function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
|
|
1606
|
+
const [step, setStep] = useState5("selection");
|
|
1607
|
+
const [selectedSession, setSelectedSession] = useState5(null);
|
|
1608
|
+
useInput5((input, key) => {
|
|
1609
|
+
if (key.escape) {
|
|
1610
|
+
if (step === "confirmation") {
|
|
1611
|
+
setStep("selection");
|
|
1612
|
+
setSelectedSession(null);
|
|
1613
|
+
} else {
|
|
1614
|
+
onCancel();
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
const hasUnsavedWork = currentSession && currentSession.messages.length > 0;
|
|
1619
|
+
const items = sessions.map((session, index) => {
|
|
1620
|
+
const messageCount = session.messages.length;
|
|
1621
|
+
const lastUpdated = new Date(session.updatedAt).toLocaleString();
|
|
1622
|
+
return {
|
|
1623
|
+
label: `[${index + 1}] ${session.name} (${messageCount} msg, ${session.model})
|
|
1624
|
+
Last updated: ${lastUpdated}`,
|
|
1625
|
+
value: session
|
|
1626
|
+
};
|
|
1627
|
+
});
|
|
1628
|
+
const handleSelectionSelect = (item) => {
|
|
1629
|
+
setSelectedSession(item.value);
|
|
1630
|
+
setStep("confirmation");
|
|
1631
|
+
};
|
|
1632
|
+
const handleConfirmationSelect = (item) => {
|
|
1633
|
+
if (item.value === "confirm" && selectedSession) {
|
|
1634
|
+
onSelect(selectedSession);
|
|
1635
|
+
} else {
|
|
1636
|
+
setStep("selection");
|
|
1637
|
+
setSelectedSession(null);
|
|
1638
|
+
}
|
|
1639
|
+
};
|
|
1640
|
+
if (step === "selection") {
|
|
1641
|
+
return /* @__PURE__ */ React15.createElement(Box14, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React15.createElement(Box14, { marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text14, { bold: true }, "Select a session to resume:")), /* @__PURE__ */ React15.createElement(
|
|
1642
|
+
SelectInput4,
|
|
1643
|
+
{
|
|
1644
|
+
items,
|
|
1645
|
+
onSelect: handleSelectionSelect,
|
|
1646
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React15.createElement(Box14, null, /* @__PURE__ */ React15.createElement(Text14, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1647
|
+
}
|
|
1648
|
+
), /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
1649
|
+
}
|
|
1650
|
+
const confirmationItems = [
|
|
1651
|
+
{ label: "Yes, resume this session", value: "confirm" },
|
|
1652
|
+
{ label: "No, go back to selection", value: "cancel" }
|
|
1653
|
+
];
|
|
1654
|
+
return /* @__PURE__ */ React15.createElement(Box14, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React15.createElement(Box14, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React15.createElement(Text14, { bold: true }, 'Resume session: "', selectedSession?.name, '"'), /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, selectedSession?.messages.length, " messages | ", selectedSession?.model, " |", " ", selectedSession?.metadata?.totalTokens?.toLocaleString() ?? 0, " tokens"), hasUnsavedWork && /* @__PURE__ */ React15.createElement(Text14, { color: "yellow" }, "Warning: Your current session has unsaved messages. Use /save first if needed.")), /* @__PURE__ */ React15.createElement(
|
|
1655
|
+
SelectInput4,
|
|
1656
|
+
{
|
|
1657
|
+
items: confirmationItems,
|
|
1658
|
+
onSelect: handleConfirmationSelect,
|
|
1659
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React15.createElement(Box14, null, /* @__PURE__ */ React15.createElement(Text14, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1565
1660
|
}
|
|
1566
|
-
), /* @__PURE__ */
|
|
1661
|
+
), /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
|
|
1567
1662
|
}
|
|
1568
1663
|
|
|
1569
1664
|
// src/components/LoginFlow.tsx
|
|
1570
|
-
import
|
|
1571
|
-
import { Box as
|
|
1665
|
+
import React16, { useState as useState6, useEffect as useEffect2 } from "react";
|
|
1666
|
+
import { Box as Box15, Text as Text15 } from "ink";
|
|
1572
1667
|
import Spinner2 from "ink-spinner";
|
|
1573
1668
|
import jwt from "jsonwebtoken";
|
|
1574
1669
|
import open from "open";
|
|
@@ -1684,10 +1779,10 @@ var OAuthClient = class {
|
|
|
1684
1779
|
|
|
1685
1780
|
// src/components/LoginFlow.tsx
|
|
1686
1781
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
1687
|
-
const [status, setStatus] =
|
|
1688
|
-
const [deviceFlow, setDeviceFlow] =
|
|
1689
|
-
const [statusMessage, setStatusMessage] =
|
|
1690
|
-
const [error, setError] =
|
|
1782
|
+
const [status, setStatus] = useState6("initiating");
|
|
1783
|
+
const [deviceFlow, setDeviceFlow] = useState6(null);
|
|
1784
|
+
const [statusMessage, setStatusMessage] = useState6("Initiating device authorization...");
|
|
1785
|
+
const [error, setError] = useState6(null);
|
|
1691
1786
|
useEffect2(() => {
|
|
1692
1787
|
const runLoginFlow = async () => {
|
|
1693
1788
|
const oauth = new OAuthClient(apiUrl);
|
|
@@ -1731,15 +1826,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
1731
1826
|
}
|
|
1732
1827
|
}, [deviceFlow, status]);
|
|
1733
1828
|
if (status === "initiating") {
|
|
1734
|
-
return /* @__PURE__ */
|
|
1829
|
+
return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { color: "cyan" }, /* @__PURE__ */ React16.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
|
|
1735
1830
|
}
|
|
1736
1831
|
if (status === "error") {
|
|
1737
|
-
return /* @__PURE__ */
|
|
1832
|
+
return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React16.createElement(Box15, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text15, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { color: "red" }, error)));
|
|
1738
1833
|
}
|
|
1739
1834
|
if (status === "success") {
|
|
1740
|
-
return /* @__PURE__ */
|
|
1835
|
+
return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React16.createElement(Box15, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text15, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "You can now use B4M CLI with your account.")));
|
|
1741
1836
|
}
|
|
1742
|
-
return /* @__PURE__ */
|
|
1837
|
+
return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React16.createElement(Box15, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text15, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React16.createElement(Box15, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text15, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React16.createElement(Box15, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React16.createElement(Text15, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React16.createElement(Box15, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text15, null, "And enter this code when prompted:")), /* @__PURE__ */ React16.createElement(Box15, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React16.createElement(Text15, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, { color: "cyan" }, /* @__PURE__ */ React16.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
|
|
1743
1838
|
}
|
|
1744
1839
|
|
|
1745
1840
|
// src/storage/SessionStore.ts
|
|
@@ -2625,10 +2720,9 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
2625
2720
|
content: query
|
|
2626
2721
|
}
|
|
2627
2722
|
];
|
|
2628
|
-
console.log("[DEBUG ReActAgent] Messages array being sent to LLM:");
|
|
2629
2723
|
messages.forEach((msg, i) => {
|
|
2630
2724
|
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
2631
|
-
|
|
2725
|
+
this.context.logger.debug(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
|
|
2632
2726
|
});
|
|
2633
2727
|
let finalAnswer = "";
|
|
2634
2728
|
let reachedMaxIterations = false;
|
|
@@ -5021,8 +5115,8 @@ async function processAndStoreImages(images, context) {
|
|
|
5021
5115
|
const buffer = await downloadImage(image);
|
|
5022
5116
|
const fileType = await fileTypeFromBuffer2(buffer);
|
|
5023
5117
|
const filename = `${uuidv45()}.${fileType?.ext}`;
|
|
5024
|
-
const
|
|
5025
|
-
return
|
|
5118
|
+
const path15 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
5119
|
+
return path15;
|
|
5026
5120
|
}));
|
|
5027
5121
|
}
|
|
5028
5122
|
async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
|
|
@@ -6234,8 +6328,8 @@ async function processAndStoreImage(imageUrl, context) {
|
|
|
6234
6328
|
const buffer = await downloadImage2(imageUrl);
|
|
6235
6329
|
const fileType = await fileTypeFromBuffer3(buffer);
|
|
6236
6330
|
const filename = `${uuidv46()}.${fileType?.ext}`;
|
|
6237
|
-
const
|
|
6238
|
-
return
|
|
6331
|
+
const path15 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
6332
|
+
return path15;
|
|
6239
6333
|
}
|
|
6240
6334
|
async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
|
|
6241
6335
|
await context.onFinish?.("edit_image", storedImagePath);
|
|
@@ -8647,6 +8741,381 @@ var deleteFileTool = {
|
|
|
8647
8741
|
})
|
|
8648
8742
|
};
|
|
8649
8743
|
|
|
8744
|
+
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
|
|
8745
|
+
import { spawn } from "child_process";
|
|
8746
|
+
import path11 from "path";
|
|
8747
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
8748
|
+
var MAX_OUTPUT_SIZE = 100 * 1024;
|
|
8749
|
+
var DANGEROUS_PATTERNS = [
|
|
8750
|
+
// Destructive file operations
|
|
8751
|
+
{
|
|
8752
|
+
pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*r).*\//i,
|
|
8753
|
+
reason: "Recursive delete with path",
|
|
8754
|
+
block: true
|
|
8755
|
+
},
|
|
8756
|
+
{
|
|
8757
|
+
pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*f).*--no-preserve-root/i,
|
|
8758
|
+
reason: "Force delete without preserve root",
|
|
8759
|
+
block: true
|
|
8760
|
+
},
|
|
8761
|
+
{
|
|
8762
|
+
pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\//i,
|
|
8763
|
+
reason: "Recursive force delete on root paths",
|
|
8764
|
+
block: true
|
|
8765
|
+
},
|
|
8766
|
+
{
|
|
8767
|
+
pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s+\//i,
|
|
8768
|
+
reason: "Force recursive delete on root paths",
|
|
8769
|
+
block: true
|
|
8770
|
+
},
|
|
8771
|
+
// System-level dangerous commands
|
|
8772
|
+
{ pattern: /\bsudo\b/i, reason: "Elevated privileges (sudo)", block: true },
|
|
8773
|
+
{ pattern: /\bsu\s+(-|root)/i, reason: "Switch to root user", block: true },
|
|
8774
|
+
{ pattern: /\bchmod\s+777\b/i, reason: "Overly permissive chmod", block: false },
|
|
8775
|
+
{ pattern: /\bchown\s+-R\s+root/i, reason: "Recursive chown to root", block: true },
|
|
8776
|
+
// Disk/partition operations
|
|
8777
|
+
{ pattern: /\bmkfs\b/i, reason: "Filesystem creation", block: true },
|
|
8778
|
+
{ pattern: /\bfdisk\b/i, reason: "Disk partitioning", block: true },
|
|
8779
|
+
{ pattern: /\bdd\s+.*of=\/dev\//i, reason: "Direct disk write", block: true },
|
|
8780
|
+
// Network attacks
|
|
8781
|
+
{ pattern: /\b(nc|netcat)\s+.*-e\s+\/bin\/(ba)?sh/i, reason: "Reverse shell attempt", block: true },
|
|
8782
|
+
{ pattern: /\bcurl\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
|
|
8783
|
+
{ pattern: /\bwget\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
|
|
8784
|
+
// Fork bombs and resource exhaustion
|
|
8785
|
+
{ pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;/i, reason: "Fork bomb", block: true },
|
|
8786
|
+
{ pattern: /\bwhile\s+true.*do.*done.*&/i, reason: "Infinite loop in background", block: false },
|
|
8787
|
+
// Credential/sensitive data access
|
|
8788
|
+
{ pattern: /\/etc\/shadow/i, reason: "Access to shadow file", block: true },
|
|
8789
|
+
{ pattern: /\/etc\/passwd.*>/i, reason: "Modifying passwd file", block: true },
|
|
8790
|
+
{ pattern: /\baws\s+.*--profile\s+/i, reason: "AWS profile access", block: false },
|
|
8791
|
+
// History/log manipulation
|
|
8792
|
+
{ pattern: /\bhistory\s+-c\b/i, reason: "Clearing shell history", block: false },
|
|
8793
|
+
{ pattern: />\s*\/var\/log\//i, reason: "Overwriting system logs", block: true },
|
|
8794
|
+
// Dangerous redirects
|
|
8795
|
+
{ pattern: />\s*\/dev\/sda/i, reason: "Writing to block device", block: true },
|
|
8796
|
+
{ pattern: />\s*\/dev\/null.*2>&1.*</i, reason: "Potentially hiding output", block: false },
|
|
8797
|
+
// Environment manipulation
|
|
8798
|
+
{ pattern: /\bexport\s+PATH\s*=\s*[^$]/i, reason: "Overwriting PATH", block: false },
|
|
8799
|
+
{ pattern: /\bexport\s+LD_PRELOAD/i, reason: "LD_PRELOAD manipulation", block: true },
|
|
8800
|
+
// Process/system control
|
|
8801
|
+
{ pattern: /\bkill\s+-9\s+(-1|1)\b/i, reason: "Killing all processes", block: true },
|
|
8802
|
+
{ pattern: /\bkillall\s+-9\b/i, reason: "Force killing processes", block: false },
|
|
8803
|
+
{ pattern: /\bshutdown\b/i, reason: "System shutdown", block: true },
|
|
8804
|
+
{ pattern: /\breboot\b/i, reason: "System reboot", block: true },
|
|
8805
|
+
{ pattern: /\binit\s+[06]\b/i, reason: "System runlevel change", block: true }
|
|
8806
|
+
];
|
|
8807
|
+
var SAFE_COMMAND_PREFIXES = [
|
|
8808
|
+
"ls",
|
|
8809
|
+
"cat",
|
|
8810
|
+
"head",
|
|
8811
|
+
"tail",
|
|
8812
|
+
"grep",
|
|
8813
|
+
"find",
|
|
8814
|
+
"echo",
|
|
8815
|
+
"pwd",
|
|
8816
|
+
"whoami",
|
|
8817
|
+
"date",
|
|
8818
|
+
"cal",
|
|
8819
|
+
"wc",
|
|
8820
|
+
"sort",
|
|
8821
|
+
"uniq",
|
|
8822
|
+
"cut",
|
|
8823
|
+
"tr",
|
|
8824
|
+
"sed",
|
|
8825
|
+
"awk",
|
|
8826
|
+
"git",
|
|
8827
|
+
"npm",
|
|
8828
|
+
"pnpm",
|
|
8829
|
+
"yarn",
|
|
8830
|
+
"node",
|
|
8831
|
+
"npx",
|
|
8832
|
+
"tsx",
|
|
8833
|
+
"ts-node",
|
|
8834
|
+
"python",
|
|
8835
|
+
"python3",
|
|
8836
|
+
"pip",
|
|
8837
|
+
"pip3",
|
|
8838
|
+
"docker",
|
|
8839
|
+
"docker-compose",
|
|
8840
|
+
"curl",
|
|
8841
|
+
"wget",
|
|
8842
|
+
// These are safe for fetching, blocked when piped to shell
|
|
8843
|
+
"mkdir",
|
|
8844
|
+
"touch",
|
|
8845
|
+
"cp",
|
|
8846
|
+
"mv",
|
|
8847
|
+
// File operations (still require permission)
|
|
8848
|
+
"which",
|
|
8849
|
+
"whereis",
|
|
8850
|
+
"type",
|
|
8851
|
+
"file",
|
|
8852
|
+
"stat",
|
|
8853
|
+
"env",
|
|
8854
|
+
"printenv",
|
|
8855
|
+
"set",
|
|
8856
|
+
"man",
|
|
8857
|
+
"help",
|
|
8858
|
+
"info",
|
|
8859
|
+
"diff",
|
|
8860
|
+
"comm",
|
|
8861
|
+
"cmp",
|
|
8862
|
+
"tar",
|
|
8863
|
+
"zip",
|
|
8864
|
+
"unzip",
|
|
8865
|
+
"gzip",
|
|
8866
|
+
"gunzip",
|
|
8867
|
+
"ssh",
|
|
8868
|
+
"scp",
|
|
8869
|
+
"rsync",
|
|
8870
|
+
// Network tools
|
|
8871
|
+
"make",
|
|
8872
|
+
"cmake",
|
|
8873
|
+
"cargo",
|
|
8874
|
+
"go",
|
|
8875
|
+
"rustc",
|
|
8876
|
+
"gcc",
|
|
8877
|
+
"g++",
|
|
8878
|
+
// Build tools
|
|
8879
|
+
"jest",
|
|
8880
|
+
"vitest",
|
|
8881
|
+
"mocha",
|
|
8882
|
+
"pytest",
|
|
8883
|
+
// Test runners
|
|
8884
|
+
"eslint",
|
|
8885
|
+
"prettier",
|
|
8886
|
+
"tsc"
|
|
8887
|
+
// Linters/formatters
|
|
8888
|
+
];
|
|
8889
|
+
function checkDangerousPatterns(command) {
|
|
8890
|
+
for (const { pattern, reason, block } of DANGEROUS_PATTERNS) {
|
|
8891
|
+
if (pattern.test(command)) {
|
|
8892
|
+
return { blocked: block, reason };
|
|
8893
|
+
}
|
|
8894
|
+
}
|
|
8895
|
+
return { blocked: false };
|
|
8896
|
+
}
|
|
8897
|
+
function getBaseCommand(command) {
|
|
8898
|
+
const trimmed = command.trim();
|
|
8899
|
+
const match = trimmed.match(/^(\S+)/);
|
|
8900
|
+
return match ? match[1] : "";
|
|
8901
|
+
}
|
|
8902
|
+
function isSafeCommandPrefix(command) {
|
|
8903
|
+
const baseCommand = getBaseCommand(command);
|
|
8904
|
+
return SAFE_COMMAND_PREFIXES.some((safe) => baseCommand === safe || baseCommand.endsWith(`/${safe}`));
|
|
8905
|
+
}
|
|
8906
|
+
async function executeBashCommand(params) {
|
|
8907
|
+
const { command, cwd: relativeCwd, timeout = DEFAULT_TIMEOUT_MS } = params;
|
|
8908
|
+
if (!command || command.trim().length === 0) {
|
|
8909
|
+
return {
|
|
8910
|
+
stdout: "",
|
|
8911
|
+
stderr: "Error: Command cannot be empty",
|
|
8912
|
+
exitCode: 1,
|
|
8913
|
+
timedOut: false,
|
|
8914
|
+
blocked: true,
|
|
8915
|
+
blockedReason: "Empty command"
|
|
8916
|
+
};
|
|
8917
|
+
}
|
|
8918
|
+
const dangerCheck = checkDangerousPatterns(command);
|
|
8919
|
+
if (dangerCheck.blocked) {
|
|
8920
|
+
return {
|
|
8921
|
+
stdout: "",
|
|
8922
|
+
stderr: `Command blocked for safety: ${dangerCheck.reason}`,
|
|
8923
|
+
exitCode: 1,
|
|
8924
|
+
timedOut: false,
|
|
8925
|
+
blocked: true,
|
|
8926
|
+
blockedReason: dangerCheck.reason
|
|
8927
|
+
};
|
|
8928
|
+
}
|
|
8929
|
+
const baseCwd = process.cwd();
|
|
8930
|
+
const targetCwd = relativeCwd ? path11.resolve(baseCwd, relativeCwd) : baseCwd;
|
|
8931
|
+
const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
|
|
8932
|
+
return new Promise((resolve3) => {
|
|
8933
|
+
let stdout = "";
|
|
8934
|
+
let stderr = "";
|
|
8935
|
+
let timedOut = false;
|
|
8936
|
+
const proc = spawn("bash", ["-c", command], {
|
|
8937
|
+
cwd: targetCwd,
|
|
8938
|
+
env: {
|
|
8939
|
+
...process.env,
|
|
8940
|
+
// Prevent color codes that might be hard to read
|
|
8941
|
+
NO_COLOR: "1",
|
|
8942
|
+
FORCE_COLOR: "0"
|
|
8943
|
+
},
|
|
8944
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
8945
|
+
});
|
|
8946
|
+
const timeoutId = setTimeout(() => {
|
|
8947
|
+
timedOut = true;
|
|
8948
|
+
proc.kill("SIGTERM");
|
|
8949
|
+
setTimeout(() => {
|
|
8950
|
+
if (!proc.killed) {
|
|
8951
|
+
proc.kill("SIGKILL");
|
|
8952
|
+
}
|
|
8953
|
+
}, 5e3);
|
|
8954
|
+
}, effectiveTimeout);
|
|
8955
|
+
proc.stdout.on("data", (data) => {
|
|
8956
|
+
if (stdout.length < MAX_OUTPUT_SIZE) {
|
|
8957
|
+
stdout += data.toString();
|
|
8958
|
+
if (stdout.length > MAX_OUTPUT_SIZE) {
|
|
8959
|
+
stdout = stdout.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
|
|
8960
|
+
}
|
|
8961
|
+
}
|
|
8962
|
+
});
|
|
8963
|
+
proc.stderr.on("data", (data) => {
|
|
8964
|
+
if (stderr.length < MAX_OUTPUT_SIZE) {
|
|
8965
|
+
stderr += data.toString();
|
|
8966
|
+
if (stderr.length > MAX_OUTPUT_SIZE) {
|
|
8967
|
+
stderr = stderr.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8970
|
+
});
|
|
8971
|
+
proc.on("close", (exitCode) => {
|
|
8972
|
+
clearTimeout(timeoutId);
|
|
8973
|
+
resolve3({
|
|
8974
|
+
stdout: stdout.trim(),
|
|
8975
|
+
stderr: stderr.trim(),
|
|
8976
|
+
exitCode,
|
|
8977
|
+
timedOut,
|
|
8978
|
+
blocked: false
|
|
8979
|
+
});
|
|
8980
|
+
});
|
|
8981
|
+
proc.on("error", (error) => {
|
|
8982
|
+
clearTimeout(timeoutId);
|
|
8983
|
+
resolve3({
|
|
8984
|
+
stdout: "",
|
|
8985
|
+
stderr: `Failed to execute command: ${error.message}`,
|
|
8986
|
+
exitCode: 1,
|
|
8987
|
+
timedOut: false,
|
|
8988
|
+
blocked: false
|
|
8989
|
+
});
|
|
8990
|
+
});
|
|
8991
|
+
});
|
|
8992
|
+
}
|
|
8993
|
+
function formatResult(result, command) {
|
|
8994
|
+
const parts = [];
|
|
8995
|
+
parts.push(`$ ${command}`);
|
|
8996
|
+
parts.push("");
|
|
8997
|
+
if (result.blocked) {
|
|
8998
|
+
parts.push(`BLOCKED: ${result.blockedReason}`);
|
|
8999
|
+
parts.push("");
|
|
9000
|
+
parts.push("This command was blocked for safety reasons.");
|
|
9001
|
+
parts.push("If you believe this is a false positive, please run the command manually.");
|
|
9002
|
+
return parts.join("\n");
|
|
9003
|
+
}
|
|
9004
|
+
if (result.timedOut) {
|
|
9005
|
+
parts.push("WARNING: Command timed out and was terminated.");
|
|
9006
|
+
parts.push("");
|
|
9007
|
+
}
|
|
9008
|
+
if (result.stdout) {
|
|
9009
|
+
parts.push(result.stdout);
|
|
9010
|
+
}
|
|
9011
|
+
if (result.stderr) {
|
|
9012
|
+
if (result.stdout) {
|
|
9013
|
+
parts.push("");
|
|
9014
|
+
}
|
|
9015
|
+
parts.push("STDERR:");
|
|
9016
|
+
parts.push(result.stderr);
|
|
9017
|
+
}
|
|
9018
|
+
if (result.exitCode !== 0 && result.exitCode !== null) {
|
|
9019
|
+
parts.push("");
|
|
9020
|
+
parts.push(`Exit code: ${result.exitCode}`);
|
|
9021
|
+
}
|
|
9022
|
+
if (!result.stdout && !result.stderr && !result.timedOut) {
|
|
9023
|
+
parts.push("(command completed with no output)");
|
|
9024
|
+
}
|
|
9025
|
+
return parts.join("\n");
|
|
9026
|
+
}
|
|
9027
|
+
var bashExecuteTool = {
|
|
9028
|
+
name: "bash_execute",
|
|
9029
|
+
implementation: (context) => ({
|
|
9030
|
+
toolFn: async (value) => {
|
|
9031
|
+
const params = value;
|
|
9032
|
+
const isSafe = isSafeCommandPrefix(params.command);
|
|
9033
|
+
context.logger.info("Bash: Executing command", {
|
|
9034
|
+
command: params.command,
|
|
9035
|
+
cwd: params.cwd || ".",
|
|
9036
|
+
timeout: params.timeout || DEFAULT_TIMEOUT_MS,
|
|
9037
|
+
isSafeCommand: isSafe
|
|
9038
|
+
});
|
|
9039
|
+
if (context.onStart) {
|
|
9040
|
+
await context.onStart("bash_execute", {
|
|
9041
|
+
command: params.command,
|
|
9042
|
+
cwd: params.cwd
|
|
9043
|
+
});
|
|
9044
|
+
}
|
|
9045
|
+
try {
|
|
9046
|
+
const result = await executeBashCommand(params);
|
|
9047
|
+
const formattedResult = formatResult(result, params.command);
|
|
9048
|
+
context.logger.info("Bash: Command completed", {
|
|
9049
|
+
exitCode: result.exitCode,
|
|
9050
|
+
timedOut: result.timedOut,
|
|
9051
|
+
blocked: result.blocked
|
|
9052
|
+
});
|
|
9053
|
+
if (context.onFinish) {
|
|
9054
|
+
await context.onFinish("bash_execute", {
|
|
9055
|
+
command: params.command,
|
|
9056
|
+
exitCode: result.exitCode,
|
|
9057
|
+
blocked: result.blocked
|
|
9058
|
+
});
|
|
9059
|
+
}
|
|
9060
|
+
return formattedResult;
|
|
9061
|
+
} catch (error) {
|
|
9062
|
+
context.logger.error("Bash: Command failed", error);
|
|
9063
|
+
if (context.onFinish) {
|
|
9064
|
+
await context.onFinish("bash_execute", {
|
|
9065
|
+
command: params.command,
|
|
9066
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
9067
|
+
});
|
|
9068
|
+
}
|
|
9069
|
+
throw error;
|
|
9070
|
+
}
|
|
9071
|
+
},
|
|
9072
|
+
toolSchema: {
|
|
9073
|
+
name: "bash_execute",
|
|
9074
|
+
description: `Execute a bash command in the terminal. Use this for running shell commands, scripts, build tools, git operations, and other CLI tasks.
|
|
9075
|
+
|
|
9076
|
+
SAFETY NOTES:
|
|
9077
|
+
- Commands are executed in the current working directory (or specified cwd)
|
|
9078
|
+
- Dangerous commands (sudo, rm -rf /, etc.) are automatically blocked
|
|
9079
|
+
- Commands have a default timeout of 60 seconds (max 5 minutes)
|
|
9080
|
+
- Output is limited to prevent overwhelming responses
|
|
9081
|
+
- This tool ALWAYS requires user permission before execution
|
|
9082
|
+
|
|
9083
|
+
COMMON USE CASES:
|
|
9084
|
+
- Running build commands: npm run build, make, cargo build
|
|
9085
|
+
- Git operations: git status, git log, git diff
|
|
9086
|
+
- Viewing system info: ls, pwd, cat, head, tail
|
|
9087
|
+
- Running tests: npm test, pytest, cargo test
|
|
9088
|
+
- Package management: npm install, pip install
|
|
9089
|
+
- File operations: mkdir, cp, mv (with permission)
|
|
9090
|
+
|
|
9091
|
+
BLOCKED OPERATIONS:
|
|
9092
|
+
- sudo and privilege escalation
|
|
9093
|
+
- Recursive deletes on system paths
|
|
9094
|
+
- Direct disk operations
|
|
9095
|
+
- Fork bombs and resource exhaustion
|
|
9096
|
+
- Piping remote scripts to shell`,
|
|
9097
|
+
parameters: {
|
|
9098
|
+
type: "object",
|
|
9099
|
+
properties: {
|
|
9100
|
+
command: {
|
|
9101
|
+
type: "string",
|
|
9102
|
+
description: "The bash command to execute. Can include pipes, redirects, and chained commands."
|
|
9103
|
+
},
|
|
9104
|
+
cwd: {
|
|
9105
|
+
type: "string",
|
|
9106
|
+
description: "Working directory for the command (optional). Can be relative or absolute path. Defaults to current directory."
|
|
9107
|
+
},
|
|
9108
|
+
timeout: {
|
|
9109
|
+
type: "number",
|
|
9110
|
+
description: "Timeout in milliseconds (optional, default: 60000, max: 300000). Command will be terminated if it exceeds this time."
|
|
9111
|
+
}
|
|
9112
|
+
},
|
|
9113
|
+
required: ["command"]
|
|
9114
|
+
}
|
|
9115
|
+
}
|
|
9116
|
+
})
|
|
9117
|
+
};
|
|
9118
|
+
|
|
8650
9119
|
// ../../b4m-core/packages/services/dist/src/llm/tools/index.js
|
|
8651
9120
|
var tools = {
|
|
8652
9121
|
dice_roll: diceRollTool,
|
|
@@ -8674,7 +9143,9 @@ var tools = {
|
|
|
8674
9143
|
create_file: createFileTool,
|
|
8675
9144
|
glob_files: globFilesTool,
|
|
8676
9145
|
grep_search: grepSearchTool,
|
|
8677
|
-
delete_file: deleteFileTool
|
|
9146
|
+
delete_file: deleteFileTool,
|
|
9147
|
+
// Shell execution
|
|
9148
|
+
bash_execute: bashExecuteTool
|
|
8678
9149
|
};
|
|
8679
9150
|
var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
|
|
8680
9151
|
const context = {
|
|
@@ -8949,7 +9420,7 @@ async function generateFileDeletePreview(args) {
|
|
|
8949
9420
|
if (!existsSync7(args.path)) {
|
|
8950
9421
|
return `[File does not exist: ${args.path}]`;
|
|
8951
9422
|
}
|
|
8952
|
-
const stats = await import("fs/promises").then((
|
|
9423
|
+
const stats = await import("fs/promises").then((fs12) => fs12.stat(args.path));
|
|
8953
9424
|
return `[File will be deleted]
|
|
8954
9425
|
|
|
8955
9426
|
Path: ${args.path}
|
|
@@ -8962,7 +9433,7 @@ Last modified: ${stats.mtime.toLocaleString()}`;
|
|
|
8962
9433
|
|
|
8963
9434
|
// src/utils/Logger.ts
|
|
8964
9435
|
import fs10 from "fs/promises";
|
|
8965
|
-
import
|
|
9436
|
+
import path12 from "path";
|
|
8966
9437
|
import os from "os";
|
|
8967
9438
|
var Logger2 = class _Logger {
|
|
8968
9439
|
constructor() {
|
|
@@ -8985,9 +9456,9 @@ var Logger2 = class _Logger {
|
|
|
8985
9456
|
*/
|
|
8986
9457
|
async initialize(sessionId) {
|
|
8987
9458
|
this.sessionId = sessionId;
|
|
8988
|
-
const debugDir =
|
|
9459
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
8989
9460
|
await fs10.mkdir(debugDir, { recursive: true });
|
|
8990
|
-
this.logFilePath =
|
|
9461
|
+
this.logFilePath = path12.join(debugDir, `${sessionId}.txt`);
|
|
8991
9462
|
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
8992
9463
|
}
|
|
8993
9464
|
/**
|
|
@@ -9197,12 +9668,12 @@ var Logger2 = class _Logger {
|
|
|
9197
9668
|
async cleanupOldLogs() {
|
|
9198
9669
|
if (!this.fileLoggingEnabled) return;
|
|
9199
9670
|
try {
|
|
9200
|
-
const debugDir =
|
|
9671
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9201
9672
|
const files = await fs10.readdir(debugDir);
|
|
9202
9673
|
const now = Date.now();
|
|
9203
9674
|
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
9204
9675
|
for (const file of files) {
|
|
9205
|
-
const filePath =
|
|
9676
|
+
const filePath = path12.join(debugDir, file);
|
|
9206
9677
|
const stats = await fs10.stat(filePath);
|
|
9207
9678
|
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
9208
9679
|
await fs10.unlink(filePath);
|
|
@@ -9267,7 +9738,8 @@ var LOCAL_TOOLS = [
|
|
|
9267
9738
|
"edit_file",
|
|
9268
9739
|
"dice_roll",
|
|
9269
9740
|
"math_evaluate",
|
|
9270
|
-
"current_datetime"
|
|
9741
|
+
"current_datetime",
|
|
9742
|
+
"bash_execute"
|
|
9271
9743
|
];
|
|
9272
9744
|
function isServerTool(toolName) {
|
|
9273
9745
|
return SERVER_TOOLS.includes(toolName);
|
|
@@ -9297,21 +9769,21 @@ var NoOpStorage = class extends BaseStorage {
|
|
|
9297
9769
|
async upload(input, destination, options) {
|
|
9298
9770
|
return `/tmp/${destination}`;
|
|
9299
9771
|
}
|
|
9300
|
-
async download(
|
|
9772
|
+
async download(path15) {
|
|
9301
9773
|
throw new Error("Download not supported in CLI");
|
|
9302
9774
|
}
|
|
9303
|
-
async delete(
|
|
9775
|
+
async delete(path15) {
|
|
9304
9776
|
}
|
|
9305
|
-
async getSignedUrl(
|
|
9306
|
-
return `/tmp/${
|
|
9777
|
+
async getSignedUrl(path15) {
|
|
9778
|
+
return `/tmp/${path15}`;
|
|
9307
9779
|
}
|
|
9308
|
-
getPublicUrl(
|
|
9309
|
-
return `/tmp/${
|
|
9780
|
+
getPublicUrl(path15) {
|
|
9781
|
+
return `/tmp/${path15}`;
|
|
9310
9782
|
}
|
|
9311
|
-
async getPreview(
|
|
9312
|
-
return `/tmp/${
|
|
9783
|
+
async getPreview(path15) {
|
|
9784
|
+
return `/tmp/${path15}`;
|
|
9313
9785
|
}
|
|
9314
|
-
async getMetadata(
|
|
9786
|
+
async getMetadata(path15) {
|
|
9315
9787
|
return { size: 0, contentType: "application/octet-stream" };
|
|
9316
9788
|
}
|
|
9317
9789
|
};
|
|
@@ -9369,6 +9841,10 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
9369
9841
|
} catch (error) {
|
|
9370
9842
|
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9371
9843
|
}
|
|
9844
|
+
} else if (toolName === "bash_execute" && args?.command) {
|
|
9845
|
+
const cwd = args.cwd ? ` (in ${args.cwd})` : "";
|
|
9846
|
+
const timeout = args.timeout ? ` [timeout: ${args.timeout}ms]` : "";
|
|
9847
|
+
preview = `$ ${args.command}${cwd}${timeout}`;
|
|
9372
9848
|
}
|
|
9373
9849
|
const response = await showPermissionPrompt(toolName, args, preview);
|
|
9374
9850
|
if (response.action === "deny") {
|
|
@@ -9439,6 +9915,8 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9439
9915
|
glob_files: {},
|
|
9440
9916
|
grep_search: {},
|
|
9441
9917
|
delete_file: {},
|
|
9918
|
+
// Shell execution (always requires permission)
|
|
9919
|
+
bash_execute: {},
|
|
9442
9920
|
// Server-side tools (executed via /api/ai/v1/tools Lambda)
|
|
9443
9921
|
// Routed by ToolRouter to use B4M company API keys
|
|
9444
9922
|
weather_info: {},
|
|
@@ -9504,6 +9982,7 @@ var DEFAULT_TOOL_CATEGORIES = {
|
|
|
9504
9982
|
create_file: "prompt_always",
|
|
9505
9983
|
delete_file: "prompt_always",
|
|
9506
9984
|
shell_execute: "prompt_always",
|
|
9985
|
+
bash_execute: "prompt_always",
|
|
9507
9986
|
git_commit: "prompt_always",
|
|
9508
9987
|
git_push: "prompt_always",
|
|
9509
9988
|
// ===== PROMPT DEFAULT (Repository read tools) =====
|
|
@@ -9665,10 +10144,122 @@ function getEnvironmentName(configApiConfig) {
|
|
|
9665
10144
|
return "Bike4Mind";
|
|
9666
10145
|
}
|
|
9667
10146
|
|
|
10147
|
+
// src/utils/contextLoader.ts
|
|
10148
|
+
import * as fs11 from "fs";
|
|
10149
|
+
import * as path13 from "path";
|
|
10150
|
+
import { homedir as homedir4 } from "os";
|
|
10151
|
+
var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
|
|
10152
|
+
var PROJECT_CONTEXT_FILES = [
|
|
10153
|
+
"CLAUDE.local.md",
|
|
10154
|
+
// gitignored, highest priority
|
|
10155
|
+
"CLAUDE.md",
|
|
10156
|
+
"AGENTS.md",
|
|
10157
|
+
// cross-tool standard
|
|
10158
|
+
"AI.local.md",
|
|
10159
|
+
// gitignored
|
|
10160
|
+
"AI.md",
|
|
10161
|
+
"INSTRUCTIONS.md"
|
|
10162
|
+
];
|
|
10163
|
+
var GLOBAL_CONTEXT_FILES = ["AI.local.md", "AI.md"];
|
|
10164
|
+
function formatFileSize2(bytes) {
|
|
10165
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
10166
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
10167
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
10168
|
+
}
|
|
10169
|
+
function tryReadContextFile(dir, filename, source) {
|
|
10170
|
+
const filePath = path13.join(dir, filename);
|
|
10171
|
+
try {
|
|
10172
|
+
const stats = fs11.lstatSync(filePath);
|
|
10173
|
+
if (stats.isDirectory()) {
|
|
10174
|
+
return null;
|
|
10175
|
+
}
|
|
10176
|
+
if (stats.isSymbolicLink()) {
|
|
10177
|
+
return {
|
|
10178
|
+
error: `${source === "global" ? "Global" : "Project"} ${filename} is a symlink (not allowed for security)`
|
|
10179
|
+
};
|
|
10180
|
+
}
|
|
10181
|
+
if (stats.size > CONTEXT_FILE_SIZE_LIMIT) {
|
|
10182
|
+
return {
|
|
10183
|
+
error: `${source === "global" ? "Global" : "Project"} ${filename} exceeds 100KB limit (${formatFileSize2(stats.size)})`
|
|
10184
|
+
};
|
|
10185
|
+
}
|
|
10186
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
10187
|
+
return {
|
|
10188
|
+
filename,
|
|
10189
|
+
content,
|
|
10190
|
+
source,
|
|
10191
|
+
path: filePath
|
|
10192
|
+
};
|
|
10193
|
+
} catch (err) {
|
|
10194
|
+
if (err.code === "ENOENT") {
|
|
10195
|
+
return null;
|
|
10196
|
+
}
|
|
10197
|
+
if (err.code === "EACCES") {
|
|
10198
|
+
return {
|
|
10199
|
+
error: `Cannot read ${source} ${filename}: permission denied`
|
|
10200
|
+
};
|
|
10201
|
+
}
|
|
10202
|
+
return {
|
|
10203
|
+
error: `Cannot read ${source} ${filename}: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
10204
|
+
};
|
|
10205
|
+
}
|
|
10206
|
+
}
|
|
10207
|
+
function findContextFile(dir, candidates, source) {
|
|
10208
|
+
for (const filename of candidates) {
|
|
10209
|
+
const result = tryReadContextFile(dir, filename, source);
|
|
10210
|
+
if (result === null) {
|
|
10211
|
+
continue;
|
|
10212
|
+
}
|
|
10213
|
+
if ("error" in result) {
|
|
10214
|
+
return { result: null, error: result.error };
|
|
10215
|
+
}
|
|
10216
|
+
return { result, error: null };
|
|
10217
|
+
}
|
|
10218
|
+
return { result: null, error: null };
|
|
10219
|
+
}
|
|
10220
|
+
function mergeContextContent(global, project) {
|
|
10221
|
+
if (global && project) {
|
|
10222
|
+
return `${global.content}
|
|
10223
|
+
|
|
10224
|
+
---
|
|
10225
|
+
|
|
10226
|
+
${project.content}`;
|
|
10227
|
+
}
|
|
10228
|
+
if (global) {
|
|
10229
|
+
return global.content;
|
|
10230
|
+
}
|
|
10231
|
+
if (project) {
|
|
10232
|
+
return project.content;
|
|
10233
|
+
}
|
|
10234
|
+
return "";
|
|
10235
|
+
}
|
|
10236
|
+
async function loadContextFiles(projectDir) {
|
|
10237
|
+
const errors = [];
|
|
10238
|
+
const globalDir = path13.join(homedir4(), ".bike4mind");
|
|
10239
|
+
const projectDirectory = projectDir || process.cwd();
|
|
10240
|
+
const [globalResult, projectResult] = await Promise.all([
|
|
10241
|
+
Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
|
|
10242
|
+
Promise.resolve(findContextFile(projectDirectory, PROJECT_CONTEXT_FILES, "project"))
|
|
10243
|
+
]);
|
|
10244
|
+
if (globalResult.error) {
|
|
10245
|
+
errors.push(globalResult.error);
|
|
10246
|
+
}
|
|
10247
|
+
if (projectResult.error) {
|
|
10248
|
+
errors.push(projectResult.error);
|
|
10249
|
+
}
|
|
10250
|
+
const mergedContent = mergeContextContent(globalResult.result, projectResult.result);
|
|
10251
|
+
return {
|
|
10252
|
+
globalContext: globalResult.result,
|
|
10253
|
+
projectContext: projectResult.result,
|
|
10254
|
+
mergedContent,
|
|
10255
|
+
errors
|
|
10256
|
+
};
|
|
10257
|
+
}
|
|
10258
|
+
|
|
9668
10259
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
9669
10260
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
9670
10261
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
9671
|
-
import
|
|
10262
|
+
import path14 from "path";
|
|
9672
10263
|
import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
|
|
9673
10264
|
var MCPClient = class {
|
|
9674
10265
|
// Note: This class handles MCP server communication with repository filtering
|
|
@@ -9709,16 +10300,16 @@ var MCPClient = class {
|
|
|
9709
10300
|
const root = process.env.INIT_CWD || process.cwd();
|
|
9710
10301
|
const candidatePaths = [
|
|
9711
10302
|
// When running from SST Lambda with node_modules structure (copyFiles)
|
|
9712
|
-
|
|
10303
|
+
path14.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
|
|
9713
10304
|
// When running from SST Lambda deployed environment (/var/task)
|
|
9714
|
-
|
|
10305
|
+
path14.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9715
10306
|
// When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
|
|
9716
|
-
|
|
10307
|
+
path14.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9717
10308
|
// When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
|
|
9718
|
-
|
|
10309
|
+
path14.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9719
10310
|
// Original paths (backward compatibility)
|
|
9720
|
-
|
|
9721
|
-
|
|
10311
|
+
path14.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
10312
|
+
path14.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
9722
10313
|
];
|
|
9723
10314
|
const serverScriptPath = candidatePaths.find((p) => existsSync8(p));
|
|
9724
10315
|
if (!serverScriptPath) {
|
|
@@ -10522,7 +11113,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
10522
11113
|
// package.json
|
|
10523
11114
|
var package_default = {
|
|
10524
11115
|
name: "@bike4mind/cli",
|
|
10525
|
-
version: "0.2.
|
|
11116
|
+
version: "0.2.10-fix-b4m-cli-flicker.17230+316343590",
|
|
10526
11117
|
type: "module",
|
|
10527
11118
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
10528
11119
|
license: "UNLICENSED",
|
|
@@ -10577,7 +11168,7 @@ var package_default = {
|
|
|
10577
11168
|
"@google/generative-ai": "^0.21.0",
|
|
10578
11169
|
"@joplin/turndown-plugin-gfm": "^1.0.62",
|
|
10579
11170
|
"@mendable/firecrawl-js": "^1.29.3",
|
|
10580
|
-
"@modelcontextprotocol/sdk": "
|
|
11171
|
+
"@modelcontextprotocol/sdk": "1.20.1",
|
|
10581
11172
|
"@octokit/rest": "^22.0.0",
|
|
10582
11173
|
"@opensearch-project/opensearch": "2.11.0",
|
|
10583
11174
|
"async-mutex": "^0.5.0",
|
|
@@ -10625,11 +11216,11 @@ var package_default = {
|
|
|
10625
11216
|
},
|
|
10626
11217
|
devDependencies: {
|
|
10627
11218
|
"@bike4mind/agents": "0.1.0",
|
|
10628
|
-
"@bike4mind/common": "2.
|
|
10629
|
-
"@bike4mind/mcp": "1.20.
|
|
10630
|
-
"@bike4mind/services": "2.
|
|
10631
|
-
"@bike4mind/utils": "2.1.
|
|
10632
|
-
"@types/better-sqlite3": "^7.6.
|
|
11219
|
+
"@bike4mind/common": "2.39.1-fix-b4m-cli-flicker.17230+316343590",
|
|
11220
|
+
"@bike4mind/mcp": "1.20.4-fix-b4m-cli-flicker.17230+316343590",
|
|
11221
|
+
"@bike4mind/services": "2.34.2-fix-b4m-cli-flicker.17230+316343590",
|
|
11222
|
+
"@bike4mind/utils": "2.1.4-fix-b4m-cli-flicker.17230+316343590",
|
|
11223
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
10633
11224
|
"@types/diff": "^5.0.9",
|
|
10634
11225
|
"@types/jsonwebtoken": "^9.0.4",
|
|
10635
11226
|
"@types/node": "^22.9.0",
|
|
@@ -10641,7 +11232,7 @@ var package_default = {
|
|
|
10641
11232
|
typescript: "^5.9.3",
|
|
10642
11233
|
vitest: "^3.2.4"
|
|
10643
11234
|
},
|
|
10644
|
-
gitHead: "
|
|
11235
|
+
gitHead: "316343590be145ba6cfa891271ed491b27f9c7d0"
|
|
10645
11236
|
};
|
|
10646
11237
|
|
|
10647
11238
|
// src/config/constants.ts
|
|
@@ -10663,7 +11254,7 @@ var usageCache = null;
|
|
|
10663
11254
|
function CliApp() {
|
|
10664
11255
|
const { exit } = useApp();
|
|
10665
11256
|
const imageRenderer = new ImageRenderer();
|
|
10666
|
-
const [state, setState] =
|
|
11257
|
+
const [state, setState] = useState7({
|
|
10667
11258
|
session: null,
|
|
10668
11259
|
sessionStore: new SessionStore(),
|
|
10669
11260
|
configStore: new ConfigStore(),
|
|
@@ -10677,11 +11268,12 @@ function CliApp() {
|
|
|
10677
11268
|
permissionManager: null,
|
|
10678
11269
|
permissionPrompt: null,
|
|
10679
11270
|
trustLocationSelector: null,
|
|
10680
|
-
rewindSelector: null
|
|
11271
|
+
rewindSelector: null,
|
|
11272
|
+
sessionSelector: null
|
|
10681
11273
|
});
|
|
10682
|
-
const [isInitialized, setIsInitialized] =
|
|
10683
|
-
const [initError, setInitError] =
|
|
10684
|
-
const [commandHistory, setCommandHistory] =
|
|
11274
|
+
const [isInitialized, setIsInitialized] = useState7(false);
|
|
11275
|
+
const [initError, setInitError] = useState7(null);
|
|
11276
|
+
const [commandHistory, setCommandHistory] = useState7([]);
|
|
10685
11277
|
const imageStoreInitPromise = useRef(null);
|
|
10686
11278
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
10687
11279
|
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
@@ -10722,7 +11314,7 @@ function CliApp() {
|
|
|
10722
11314
|
process.exit(0);
|
|
10723
11315
|
}, 100);
|
|
10724
11316
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
10725
|
-
|
|
11317
|
+
useInput6((input, key) => {
|
|
10726
11318
|
if (key.ctrl && input === "c") {
|
|
10727
11319
|
const now = Date.now();
|
|
10728
11320
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
@@ -10892,6 +11484,24 @@ function CliApp() {
|
|
|
10892
11484
|
}
|
|
10893
11485
|
const allTools = [...b4mTools, ...mcpTools];
|
|
10894
11486
|
console.log(`\u2705 Total: ${allTools.length} tool(s) available`);
|
|
11487
|
+
const projectDir = state.configStore.getProjectConfigDir();
|
|
11488
|
+
const contextResult = await loadContextFiles(projectDir);
|
|
11489
|
+
if (contextResult.globalContext) {
|
|
11490
|
+
console.log(`\u{1F4C4} Global context: ${contextResult.globalContext.filename}`);
|
|
11491
|
+
}
|
|
11492
|
+
if (contextResult.projectContext) {
|
|
11493
|
+
console.log(`\u{1F4C4} Project context: ${contextResult.projectContext.filename}`);
|
|
11494
|
+
}
|
|
11495
|
+
for (const error of contextResult.errors) {
|
|
11496
|
+
console.log(`\u26A0\uFE0F Context file error: ${error}`);
|
|
11497
|
+
}
|
|
11498
|
+
const contextSection = contextResult.mergedContent ? `
|
|
11499
|
+
|
|
11500
|
+
## Project Context
|
|
11501
|
+
|
|
11502
|
+
Follow these project-specific instructions:
|
|
11503
|
+
|
|
11504
|
+
${contextResult.mergedContent}` : "";
|
|
10895
11505
|
const cliSystemPrompt = `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
|
|
10896
11506
|
|
|
10897
11507
|
CORE BEHAVIOR:
|
|
@@ -10921,7 +11531,7 @@ EXAMPLES:
|
|
|
10921
11531
|
- "enhance README" \u2192 file_read \u2192 generate \u2192 create_file
|
|
10922
11532
|
- "what packages installed?" \u2192 glob_files "**/package.json" \u2192 file_read
|
|
10923
11533
|
|
|
10924
|
-
Remember: Use context from previous messages to understand follow-up questions
|
|
11534
|
+
Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
|
|
10925
11535
|
const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
|
|
10926
11536
|
const agent = new ReActAgent({
|
|
10927
11537
|
userId: config.userId,
|
|
@@ -10976,6 +11586,22 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10976
11586
|
console.log("\u{1F4A1} Run /login to authenticate with your B4M account.\n");
|
|
10977
11587
|
return;
|
|
10978
11588
|
}
|
|
11589
|
+
const currentSteps = [];
|
|
11590
|
+
const stepHandler = (step) => {
|
|
11591
|
+
currentSteps.push(step);
|
|
11592
|
+
const { pendingMessages, updatePendingMessage } = useCliStore.getState();
|
|
11593
|
+
const lastIdx = pendingMessages.length - 1;
|
|
11594
|
+
if (lastIdx >= 0 && pendingMessages[lastIdx].role === "assistant") {
|
|
11595
|
+
updatePendingMessage(lastIdx, {
|
|
11596
|
+
...pendingMessages[lastIdx],
|
|
11597
|
+
metadata: {
|
|
11598
|
+
...pendingMessages[lastIdx].metadata,
|
|
11599
|
+
steps: [...currentSteps]
|
|
11600
|
+
}
|
|
11601
|
+
});
|
|
11602
|
+
}
|
|
11603
|
+
};
|
|
11604
|
+
state.agent.on("action", stepHandler);
|
|
10979
11605
|
try {
|
|
10980
11606
|
let messageContent = message;
|
|
10981
11607
|
let userMessageContent = message;
|
|
@@ -10989,6 +11615,22 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10989
11615
|
content: userMessageContent,
|
|
10990
11616
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10991
11617
|
};
|
|
11618
|
+
const pendingAssistantMessage = {
|
|
11619
|
+
role: "assistant",
|
|
11620
|
+
content: "...",
|
|
11621
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11622
|
+
metadata: {
|
|
11623
|
+
steps: []
|
|
11624
|
+
}
|
|
11625
|
+
};
|
|
11626
|
+
const sessionWithUserMessage = {
|
|
11627
|
+
...state.session,
|
|
11628
|
+
messages: [...state.session.messages, userMessage],
|
|
11629
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11630
|
+
};
|
|
11631
|
+
setState((prev) => ({ ...prev, session: sessionWithUserMessage }));
|
|
11632
|
+
setStoreSession(sessionWithUserMessage);
|
|
11633
|
+
useCliStore.getState().addPendingMessage(pendingAssistantMessage);
|
|
10992
11634
|
const recentMessages = state.session.messages.slice(-20);
|
|
10993
11635
|
const previousMessages = recentMessages.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => ({
|
|
10994
11636
|
role: msg.role,
|
|
@@ -11002,37 +11644,37 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
11002
11644
|
console.log("\n\u26A0\uFE0F Action denied by user\n");
|
|
11003
11645
|
}
|
|
11004
11646
|
const successfulToolCalls = result.steps.filter((s) => s.type === "observation").length;
|
|
11005
|
-
const
|
|
11647
|
+
const finalAssistantMessage = {
|
|
11006
11648
|
role: "assistant",
|
|
11007
11649
|
content: result.finalAnswer,
|
|
11008
|
-
timestamp:
|
|
11650
|
+
timestamp: pendingAssistantMessage.timestamp,
|
|
11009
11651
|
metadata: {
|
|
11010
11652
|
tokenUsage: {
|
|
11011
11653
|
prompt: 0,
|
|
11012
|
-
// TODO: Extract from result
|
|
11013
11654
|
completion: 0,
|
|
11014
11655
|
total: result.completionInfo.totalTokens
|
|
11015
11656
|
},
|
|
11016
11657
|
steps: result.steps,
|
|
11017
11658
|
// Complete history: thoughts, actions, observations
|
|
11018
11659
|
permissionDenied
|
|
11019
|
-
// Mark if this ended due to permission denial
|
|
11020
11660
|
}
|
|
11021
11661
|
};
|
|
11662
|
+
useCliStore.getState().completePendingMessage(0, finalAssistantMessage);
|
|
11663
|
+
const currentSession = useCliStore.getState().session;
|
|
11664
|
+
if (!currentSession) return;
|
|
11022
11665
|
const updatedSession = {
|
|
11023
|
-
...
|
|
11024
|
-
messages: [...state.session.messages, userMessage, assistantMessage],
|
|
11025
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11666
|
+
...currentSession,
|
|
11026
11667
|
metadata: {
|
|
11027
|
-
...
|
|
11028
|
-
totalTokens:
|
|
11029
|
-
toolCallCount:
|
|
11668
|
+
...currentSession.metadata,
|
|
11669
|
+
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
11670
|
+
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
|
|
11030
11671
|
}
|
|
11031
11672
|
};
|
|
11032
11673
|
setState((prev) => ({ ...prev, session: updatedSession }));
|
|
11033
11674
|
setStoreSession(updatedSession);
|
|
11034
11675
|
await state.sessionStore.save(updatedSession);
|
|
11035
11676
|
} catch (error) {
|
|
11677
|
+
useCliStore.getState().clearPendingMessages();
|
|
11036
11678
|
if (error instanceof Error) {
|
|
11037
11679
|
if (error.message.includes("Authentication failed") || error.message.includes("Authentication expired")) {
|
|
11038
11680
|
console.log("\n\u274C Authentication failed");
|
|
@@ -11041,6 +11683,8 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
11041
11683
|
}
|
|
11042
11684
|
}
|
|
11043
11685
|
console.error("Error processing message:", error);
|
|
11686
|
+
} finally {
|
|
11687
|
+
state.agent.off("action", stepHandler);
|
|
11044
11688
|
}
|
|
11045
11689
|
};
|
|
11046
11690
|
const handleImageDetected = async (imageData) => {
|
|
@@ -11143,15 +11787,45 @@ Project Configuration:
|
|
|
11143
11787
|
await state.sessionStore.save(state.session);
|
|
11144
11788
|
console.log(`\u2705 Session saved as "${sessionName}"`);
|
|
11145
11789
|
break;
|
|
11146
|
-
case "sessions":
|
|
11790
|
+
case "sessions": {
|
|
11147
11791
|
const sessions = await state.sessionStore.list();
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
console.log(
|
|
11151
|
-
|
|
11152
|
-
}
|
|
11153
|
-
|
|
11792
|
+
if (sessions.length === 0) {
|
|
11793
|
+
console.log("\n\u{1F4DA} No saved sessions found.");
|
|
11794
|
+
console.log("\u{1F4A1} Use /save <name> to save your current session.\n");
|
|
11795
|
+
break;
|
|
11796
|
+
}
|
|
11797
|
+
const handleSessionSelect = async (selectedSession) => {
|
|
11798
|
+
setState((prev) => ({ ...prev, sessionSelector: null }));
|
|
11799
|
+
if (!selectedSession) {
|
|
11800
|
+
console.log("\u274C Session selection cancelled");
|
|
11801
|
+
return;
|
|
11802
|
+
}
|
|
11803
|
+
const loadedSession = await state.sessionStore.load(selectedSession.id);
|
|
11804
|
+
if (!loadedSession) {
|
|
11805
|
+
console.log(`\u274C Failed to load session: ${selectedSession.name}`);
|
|
11806
|
+
console.log(" The session file may be corrupted or deleted.");
|
|
11807
|
+
return;
|
|
11808
|
+
}
|
|
11809
|
+
await logger.initialize(loadedSession.id);
|
|
11810
|
+
logger.debug("=== Session Resumed ===");
|
|
11811
|
+
setState((prev) => ({ ...prev, session: loadedSession }));
|
|
11812
|
+
setStoreSession(loadedSession);
|
|
11813
|
+
useCliStore.getState().clearAgentSteps();
|
|
11814
|
+
useCliStore.getState().clearPendingMessages();
|
|
11815
|
+
usageCache = null;
|
|
11816
|
+
console.log(`
|
|
11817
|
+
\u2705 Session resumed: "${loadedSession.name}"`);
|
|
11818
|
+
console.log(
|
|
11819
|
+
`\u{1F4DD} ${loadedSession.messages.length} messages | \u{1F916} ${loadedSession.model} | \u{1F4CA} ${loadedSession.metadata.totalTokens.toLocaleString()} tokens
|
|
11820
|
+
`
|
|
11821
|
+
);
|
|
11822
|
+
};
|
|
11823
|
+
setState((prev) => ({
|
|
11824
|
+
...prev,
|
|
11825
|
+
sessionSelector: { sessions, resolve: handleSessionSelect }
|
|
11826
|
+
}));
|
|
11154
11827
|
break;
|
|
11828
|
+
}
|
|
11155
11829
|
case "config": {
|
|
11156
11830
|
setShowConfigEditor(true);
|
|
11157
11831
|
break;
|
|
@@ -11373,6 +12047,7 @@ Project Configuration:
|
|
|
11373
12047
|
setState((prev) => ({ ...prev, session: newSession }));
|
|
11374
12048
|
setStoreSession(newSession);
|
|
11375
12049
|
useCliStore.getState().clearAgentSteps();
|
|
12050
|
+
useCliStore.getState().clearPendingMessages();
|
|
11376
12051
|
usageCache = null;
|
|
11377
12052
|
console.log("New session started.");
|
|
11378
12053
|
console.log(`
|
|
@@ -11425,6 +12100,7 @@ Project Configuration:
|
|
|
11425
12100
|
prefillInput: prefillContent
|
|
11426
12101
|
}));
|
|
11427
12102
|
setStoreSession(rewindedSession);
|
|
12103
|
+
useCliStore.getState().clearPendingMessages();
|
|
11428
12104
|
await state.sessionStore.save(rewindedSession);
|
|
11429
12105
|
console.log("\u2705 Conversation rewound successfully");
|
|
11430
12106
|
console.log(`\u{1F4CA} Current state: ${rewindedMessages.length} messages, ${newMetadata.totalTokens} tokens`);
|
|
@@ -11592,12 +12268,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11592
12268
|
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
11593
12269
|
};
|
|
11594
12270
|
if (initError) {
|
|
11595
|
-
return /* @__PURE__ */
|
|
12271
|
+
return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React17.createElement(Text16, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React17.createElement(Text16, null, initError), /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
|
|
11596
12272
|
}
|
|
11597
12273
|
if (state.trustLocationSelector) {
|
|
11598
12274
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
11599
12275
|
const inProject = projectDir !== null;
|
|
11600
|
-
return /* @__PURE__ */
|
|
12276
|
+
return /* @__PURE__ */ React17.createElement(
|
|
11601
12277
|
TrustLocationSelector,
|
|
11602
12278
|
{
|
|
11603
12279
|
inProject,
|
|
@@ -11615,7 +12291,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11615
12291
|
);
|
|
11616
12292
|
}
|
|
11617
12293
|
if (state.rewindSelector && state.session) {
|
|
11618
|
-
return /* @__PURE__ */
|
|
12294
|
+
return /* @__PURE__ */ React17.createElement(
|
|
11619
12295
|
RewindSelector,
|
|
11620
12296
|
{
|
|
11621
12297
|
messages: state.session.messages,
|
|
@@ -11632,9 +12308,28 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11632
12308
|
}
|
|
11633
12309
|
);
|
|
11634
12310
|
}
|
|
12311
|
+
if (state.sessionSelector) {
|
|
12312
|
+
return /* @__PURE__ */ React17.createElement(
|
|
12313
|
+
SessionSelector,
|
|
12314
|
+
{
|
|
12315
|
+
sessions: state.sessionSelector.sessions,
|
|
12316
|
+
currentSession: state.session,
|
|
12317
|
+
onSelect: (session) => {
|
|
12318
|
+
if (state.sessionSelector) {
|
|
12319
|
+
state.sessionSelector.resolve(session);
|
|
12320
|
+
}
|
|
12321
|
+
},
|
|
12322
|
+
onCancel: () => {
|
|
12323
|
+
if (state.sessionSelector) {
|
|
12324
|
+
state.sessionSelector.resolve(null);
|
|
12325
|
+
}
|
|
12326
|
+
}
|
|
12327
|
+
}
|
|
12328
|
+
);
|
|
12329
|
+
}
|
|
11635
12330
|
if (state.showLoginFlow) {
|
|
11636
12331
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
11637
|
-
return /* @__PURE__ */
|
|
12332
|
+
return /* @__PURE__ */ React17.createElement(
|
|
11638
12333
|
LoginFlow,
|
|
11639
12334
|
{
|
|
11640
12335
|
apiUrl: loginApiUrl,
|
|
@@ -11657,9 +12352,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11657
12352
|
);
|
|
11658
12353
|
}
|
|
11659
12354
|
if (!isInitialized) {
|
|
11660
|
-
return /* @__PURE__ */
|
|
12355
|
+
return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React17.createElement(Text16, null, "\u{1F680} Initializing..."));
|
|
11661
12356
|
}
|
|
11662
|
-
return /* @__PURE__ */
|
|
12357
|
+
return /* @__PURE__ */ React17.createElement(
|
|
11663
12358
|
App,
|
|
11664
12359
|
{
|
|
11665
12360
|
onMessage: handleMessage,
|
|
@@ -11698,6 +12393,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
11698
12393
|
if (isDevMode) {
|
|
11699
12394
|
console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
11700
12395
|
}
|
|
11701
|
-
render(/* @__PURE__ */
|
|
12396
|
+
render(/* @__PURE__ */ React17.createElement(CliApp, null), {
|
|
11702
12397
|
exitOnCtrlC: false
|
|
11703
12398
|
});
|