@bike4mind/cli 0.2.8-fix-cli-type-error.17148 → 0.2.10-cj-jira-bulk-transition-edit-issue.17246
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-6FNOJCKV.js} +1 -1
- package/dist/{chunk-YXHQN7SK.js → chunk-H2IH7ZMR.js} +2 -2
- package/dist/{chunk-LIRY24M5.js → chunk-H76XWGY4.js} +2 -2
- package/dist/{chunk-H6SHASMZ.js → chunk-UUC2RE6H.js} +135 -1
- package/dist/{chunk-DNSBC3LO.js → chunk-Z32SRC6S.js} +1 -1
- package/dist/{chunk-3CCBDIFR.js → chunk-ZNJ43SHU.js} +2 -2
- package/dist/{create-PNU64QSF.js → create-ESHR3N56.js} +3 -3
- package/dist/index.js +921 -153
- package/dist/{llmMarkdownGenerator-UQXKK2AS.js → llmMarkdownGenerator-2AFNPQRH.js} +1 -1
- package/dist/{markdownGenerator-FLLZFXMT.js → markdownGenerator-OSAQ2K6N.js} +1 -1
- package/dist/{mementoService-LVFQ5XAW.js → mementoService-7OBYZWX4.js} +3 -3
- package/dist/{src-OYQXRLTM.js → src-NW5CFYN7.js} +7 -1
- package/dist/{src-OH5AAQEW.js → src-QQQ7GNKH.js} +2 -2
- package/dist/{subtractCredits-75VT7HP2.js → subtractCredits-YH3LMUED.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-ZNJ43SHU.js";
|
|
8
|
+
import "./chunk-H76XWGY4.js";
|
|
9
|
+
import "./chunk-H2IH7ZMR.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-Z32SRC6S.js";
|
|
19
19
|
import {
|
|
20
20
|
Logger
|
|
21
21
|
} from "./chunk-AMDXHL6S.js";
|
|
@@ -73,29 +73,30 @@ import {
|
|
|
73
73
|
XAI_IMAGE_MODELS,
|
|
74
74
|
b4mLLMTools,
|
|
75
75
|
getMcpProviderMetadata
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-UUC2RE6H.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 useState8, useEffect as useEffect3, useCallback, useRef as useRef2 } from "react";
|
|
83
|
+
import { render, Box as Box16, Text as Text16, useApp, useInput as useInput6 } from "ink";
|
|
84
|
+
import { execSync } from "child_process";
|
|
84
85
|
import { v4 as uuidv49 } from "uuid";
|
|
85
86
|
|
|
86
87
|
// src/components/App.tsx
|
|
87
|
-
import
|
|
88
|
-
import { Box as
|
|
88
|
+
import React11, { useState as useState4 } from "react";
|
|
89
|
+
import { Box as Box10, Text as Text10, Static } from "ink";
|
|
89
90
|
|
|
90
91
|
// src/components/StatusBar.tsx
|
|
91
92
|
import React from "react";
|
|
92
93
|
import { Box, Text } from "ink";
|
|
93
94
|
var StatusBar = React.memo(function StatusBar2({ sessionName, model, tokenUsage }) {
|
|
94
|
-
return /* @__PURE__ */ React.createElement(Box, {
|
|
95
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", justifyContent: "space-between", width: "100%", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, sessionName), /* @__PURE__ */ React.createElement(Box, { gap: 2 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, tokenUsage.toLocaleString(), " tokens"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, model)));
|
|
95
96
|
});
|
|
96
97
|
|
|
97
98
|
// src/components/InputPrompt.tsx
|
|
98
|
-
import React5, { useState as useState2, useMemo, useEffect } from "react";
|
|
99
|
+
import React5, { useState as useState2, useMemo, useEffect, useRef } from "react";
|
|
99
100
|
import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
|
|
100
101
|
|
|
101
102
|
// src/components/CustomTextInput.tsx
|
|
@@ -732,16 +733,19 @@ function findAtTrigger(value) {
|
|
|
732
733
|
}
|
|
733
734
|
function InputPrompt({
|
|
734
735
|
onSubmit,
|
|
736
|
+
onBashCommand,
|
|
735
737
|
onImageDetected,
|
|
736
738
|
disabled = false,
|
|
737
739
|
history = [],
|
|
738
740
|
prefillInput,
|
|
739
|
-
onPrefillConsumed
|
|
741
|
+
onPrefillConsumed,
|
|
742
|
+
onBashModeChange
|
|
740
743
|
}) {
|
|
741
744
|
const [value, setValue] = useState2("");
|
|
742
745
|
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
743
746
|
const [historyIndex, setHistoryIndex] = useState2(-1);
|
|
744
747
|
const [tempInput, setTempInput] = useState2("");
|
|
748
|
+
const inputKey = useRef(0);
|
|
745
749
|
useEffect(() => {
|
|
746
750
|
if (prefillInput) {
|
|
747
751
|
setValue(prefillInput);
|
|
@@ -750,6 +754,10 @@ function InputPrompt({
|
|
|
750
754
|
}, [prefillInput, onPrefillConsumed]);
|
|
751
755
|
const [fileAutocomplete, setFileAutocomplete] = useState2(null);
|
|
752
756
|
const [fileSelectedIndex, setFileSelectedIndex] = useState2(0);
|
|
757
|
+
const isBashMode = value.startsWith("!");
|
|
758
|
+
useEffect(() => {
|
|
759
|
+
onBashModeChange?.(isBashMode);
|
|
760
|
+
}, [isBashMode, onBashModeChange]);
|
|
753
761
|
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
|
|
754
762
|
const commandQuery = shouldShowCommandAutocomplete ? value.slice(1) : "";
|
|
755
763
|
const filteredCommands = useMemo(() => {
|
|
@@ -856,6 +864,16 @@ function InputPrompt({
|
|
|
856
864
|
onSubmit(commandText);
|
|
857
865
|
return;
|
|
858
866
|
}
|
|
867
|
+
if (isBashMode && onBashCommand) {
|
|
868
|
+
const command = input.slice(1).trim();
|
|
869
|
+
if (command) {
|
|
870
|
+
onBashCommand(command);
|
|
871
|
+
setValue("");
|
|
872
|
+
setHistoryIndex(-1);
|
|
873
|
+
setFileAutocomplete(null);
|
|
874
|
+
}
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
859
877
|
onSubmit(input);
|
|
860
878
|
setValue("");
|
|
861
879
|
setHistoryIndex(-1);
|
|
@@ -886,13 +904,19 @@ function InputPrompt({
|
|
|
886
904
|
setFileAutocomplete(null);
|
|
887
905
|
}
|
|
888
906
|
};
|
|
889
|
-
|
|
907
|
+
const getPlaceholder = () => {
|
|
908
|
+
if (disabled) return "Thinking...";
|
|
909
|
+
if (isBashMode) return "Enter shell command to execute...";
|
|
910
|
+
return "Type your message, /help for commands, @file to reference, or ! for bash";
|
|
911
|
+
};
|
|
912
|
+
return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: isBashMode ? "yellow" : "cyan" }, isBashMode ? "$ " : "> "), /* @__PURE__ */ React5.createElement(
|
|
890
913
|
CustomTextInput,
|
|
891
914
|
{
|
|
915
|
+
key: inputKey.current,
|
|
892
916
|
value,
|
|
893
917
|
onChange: handleChange,
|
|
894
918
|
onSubmit: handleSubmit,
|
|
895
|
-
placeholder:
|
|
919
|
+
placeholder: getPlaceholder(),
|
|
896
920
|
showCursor: !disabled
|
|
897
921
|
}
|
|
898
922
|
)), shouldShowCommandAutocomplete && /* @__PURE__ */ React5.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React5.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
|
|
@@ -916,6 +940,39 @@ var useCliStore = create((set) => ({
|
|
|
916
940
|
// Session state
|
|
917
941
|
session: null,
|
|
918
942
|
setSession: (session) => set({ session }),
|
|
943
|
+
addMessage: (message) => set((state) => {
|
|
944
|
+
if (!state.session) return state;
|
|
945
|
+
return {
|
|
946
|
+
session: {
|
|
947
|
+
...state.session,
|
|
948
|
+
messages: [...state.session.messages, message],
|
|
949
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
}),
|
|
953
|
+
// Pending messages
|
|
954
|
+
pendingMessages: [],
|
|
955
|
+
addPendingMessage: (message) => set((state) => ({ pendingMessages: [...state.pendingMessages, message] })),
|
|
956
|
+
updatePendingMessage: (index, message) => set((state) => {
|
|
957
|
+
const updated = [...state.pendingMessages];
|
|
958
|
+
updated[index] = message;
|
|
959
|
+
return { pendingMessages: updated };
|
|
960
|
+
}),
|
|
961
|
+
clearPendingMessages: () => set({ pendingMessages: [] }),
|
|
962
|
+
completePendingMessage: (index, finalMessage) => set((state) => {
|
|
963
|
+
const pending = [...state.pendingMessages];
|
|
964
|
+
pending.splice(index, 1);
|
|
965
|
+
const session = state.session;
|
|
966
|
+
if (!session) return { pendingMessages: pending };
|
|
967
|
+
return {
|
|
968
|
+
pendingMessages: pending,
|
|
969
|
+
session: {
|
|
970
|
+
...session,
|
|
971
|
+
messages: [...session.messages, finalMessage],
|
|
972
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
}),
|
|
919
976
|
// UI state
|
|
920
977
|
isThinking: false,
|
|
921
978
|
setIsThinking: (thinking) => set({ isThinking: thinking }),
|
|
@@ -923,6 +980,7 @@ var useCliStore = create((set) => ({
|
|
|
923
980
|
addAgentStep: (step) => set((state) => ({
|
|
924
981
|
agentSteps: [...state.agentSteps, step]
|
|
925
982
|
})),
|
|
983
|
+
setAgentSteps: (steps) => set({ agentSteps: steps }),
|
|
926
984
|
clearAgentSteps: () => set({ agentSteps: [] }),
|
|
927
985
|
// Permission prompt
|
|
928
986
|
permissionPrompt: null,
|
|
@@ -942,7 +1000,7 @@ var AgentThinking = React7.memo(function AgentThinking2() {
|
|
|
942
1000
|
if (!isThinking) {
|
|
943
1001
|
return null;
|
|
944
1002
|
}
|
|
945
|
-
return /* @__PURE__ */ React7.createElement(Box6, {
|
|
1003
|
+
return /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
|
|
946
1004
|
});
|
|
947
1005
|
|
|
948
1006
|
// src/components/PermissionPrompt.tsx
|
|
@@ -1214,6 +1272,26 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1214
1272
|
})), /* @__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
1273
|
}
|
|
1216
1274
|
|
|
1275
|
+
// src/components/MessageItem.tsx
|
|
1276
|
+
import React10 from "react";
|
|
1277
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
1278
|
+
function MessageItem({ message }) {
|
|
1279
|
+
const isUser = message.role === "user";
|
|
1280
|
+
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"), isUser && /* @__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) => {
|
|
1281
|
+
if (step.type === "thought") {
|
|
1282
|
+
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 ? "..." : ""}`));
|
|
1283
|
+
}
|
|
1284
|
+
if (step.type === "action") {
|
|
1285
|
+
const toolName = step.metadata?.toolName || "unknown";
|
|
1286
|
+
const toolInput = step.metadata?.toolInput;
|
|
1287
|
+
const observationStep = message.metadata.steps[idx + 1];
|
|
1288
|
+
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1289
|
+
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 ? "..." : ""}`));
|
|
1290
|
+
}
|
|
1291
|
+
return null;
|
|
1292
|
+
}).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`)));
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1217
1295
|
// src/utils/processFileReferences.ts
|
|
1218
1296
|
import * as fs2 from "fs";
|
|
1219
1297
|
import * as path2 from "path";
|
|
@@ -1332,6 +1410,7 @@ function hasFileReferences(message) {
|
|
|
1332
1410
|
function App({
|
|
1333
1411
|
onMessage,
|
|
1334
1412
|
onCommand,
|
|
1413
|
+
onBashCommand,
|
|
1335
1414
|
onPermissionResponse,
|
|
1336
1415
|
onImageDetected,
|
|
1337
1416
|
commandHistory = [],
|
|
@@ -1342,6 +1421,7 @@ function App({
|
|
|
1342
1421
|
onPrefillConsumed
|
|
1343
1422
|
}) {
|
|
1344
1423
|
const messages = useCliStore((state) => state.session?.messages || []);
|
|
1424
|
+
const pendingMessages = useCliStore((state) => state.pendingMessages);
|
|
1345
1425
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
1346
1426
|
const currentModel = useCliStore((state) => state.session?.model || "claude-sonnet-4-5-20250929");
|
|
1347
1427
|
const totalTokens = useCliStore((state) => state.session?.metadata.totalTokens || 0);
|
|
@@ -1351,7 +1431,8 @@ function App({
|
|
|
1351
1431
|
const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
|
|
1352
1432
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
1353
1433
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
1354
|
-
const
|
|
1434
|
+
const [isBashMode, setIsBashMode] = useState4(false);
|
|
1435
|
+
const handleSubmit = React11.useCallback(
|
|
1355
1436
|
async (input) => {
|
|
1356
1437
|
const trimmed = input.trim();
|
|
1357
1438
|
if (!trimmed) return;
|
|
@@ -1380,7 +1461,7 @@ ${errorBlock}`;
|
|
|
1380
1461
|
},
|
|
1381
1462
|
[onMessage, onCommand, setIsThinking]
|
|
1382
1463
|
);
|
|
1383
|
-
return /* @__PURE__ */
|
|
1464
|
+
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, showConfigEditor && config && onSaveConfig ? /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(
|
|
1384
1465
|
ConfigEditor,
|
|
1385
1466
|
{
|
|
1386
1467
|
config,
|
|
@@ -1388,7 +1469,7 @@ ${errorBlock}`;
|
|
|
1388
1469
|
onSave: onSaveConfig,
|
|
1389
1470
|
onClose: () => setShowConfigEditor(false)
|
|
1390
1471
|
}
|
|
1391
|
-
)) : permissionPrompt ? /* @__PURE__ */
|
|
1472
|
+
)) : permissionPrompt ? /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(
|
|
1392
1473
|
PermissionPrompt,
|
|
1393
1474
|
{
|
|
1394
1475
|
toolName: permissionPrompt.toolName,
|
|
@@ -1397,49 +1478,36 @@ ${errorBlock}`;
|
|
|
1397
1478
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
1398
1479
|
onResponse: onPermissionResponse
|
|
1399
1480
|
}
|
|
1400
|
-
)) : /* @__PURE__ */
|
|
1481
|
+
)) : /* @__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(Box10, { borderStyle: "single", borderColor: isBashMode ? "yellow" : "cyan", paddingX: 1 }, /* @__PURE__ */ React11.createElement(
|
|
1401
1482
|
InputPrompt,
|
|
1402
1483
|
{
|
|
1403
1484
|
onSubmit: handleSubmit,
|
|
1485
|
+
onBashCommand,
|
|
1404
1486
|
onImageDetected,
|
|
1405
1487
|
disabled: isThinking,
|
|
1406
1488
|
history: commandHistory,
|
|
1407
1489
|
prefillInput,
|
|
1408
|
-
onPrefillConsumed
|
|
1409
|
-
|
|
1410
|
-
))));
|
|
1411
|
-
}
|
|
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 ? "..." : ""}`));
|
|
1490
|
+
onPrefillConsumed,
|
|
1491
|
+
onBashModeChange: setIsBashMode
|
|
1423
1492
|
}
|
|
1424
|
-
|
|
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`)));
|
|
1493
|
+
)), /* @__PURE__ */ React11.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens })));
|
|
1426
1494
|
}
|
|
1427
1495
|
|
|
1428
1496
|
// src/components/MessageList.tsx
|
|
1429
|
-
import
|
|
1430
|
-
import { Box as
|
|
1497
|
+
import React12 from "react";
|
|
1498
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1431
1499
|
function stripThinkingTags(content) {
|
|
1432
1500
|
return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
1433
1501
|
}
|
|
1434
|
-
var MessageList =
|
|
1502
|
+
var MessageList = React12.memo(
|
|
1435
1503
|
function MessageList2({ messages }) {
|
|
1436
1504
|
if (messages.length === 0) {
|
|
1437
|
-
return /* @__PURE__ */
|
|
1505
|
+
return /* @__PURE__ */ React12.createElement(Box11, { paddingY: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "No messages yet. Type a message to start!"));
|
|
1438
1506
|
}
|
|
1439
|
-
return /* @__PURE__ */
|
|
1507
|
+
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
1508
|
if (step.type === "thought") {
|
|
1441
|
-
return /* @__PURE__ */
|
|
1442
|
-
|
|
1509
|
+
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(
|
|
1510
|
+
Text11,
|
|
1443
1511
|
{
|
|
1444
1512
|
dimColor: true
|
|
1445
1513
|
},
|
|
@@ -1451,10 +1519,10 @@ var MessageList = React11.memo(
|
|
|
1451
1519
|
const toolInput = step.metadata?.toolInput;
|
|
1452
1520
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1453
1521
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1454
|
-
return /* @__PURE__ */
|
|
1522
|
+
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
1523
|
}
|
|
1456
1524
|
return null;
|
|
1457
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1525
|
+
}).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
1526
|
},
|
|
1459
1527
|
(prevProps, nextProps) => {
|
|
1460
1528
|
if (prevProps.messages.length !== nextProps.messages.length) {
|
|
@@ -1465,8 +1533,8 @@ var MessageList = React11.memo(
|
|
|
1465
1533
|
);
|
|
1466
1534
|
|
|
1467
1535
|
// src/components/TrustLocationSelector.tsx
|
|
1468
|
-
import
|
|
1469
|
-
import { Box as
|
|
1536
|
+
import React13 from "react";
|
|
1537
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1470
1538
|
import SelectInput2 from "ink-select-input";
|
|
1471
1539
|
function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
1472
1540
|
const items = [];
|
|
@@ -1487,23 +1555,23 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
1487
1555
|
const handleSelect = (item) => {
|
|
1488
1556
|
onSelect(item.value);
|
|
1489
1557
|
};
|
|
1490
|
-
return /* @__PURE__ */
|
|
1558
|
+
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
1559
|
SelectInput2,
|
|
1492
1560
|
{
|
|
1493
1561
|
items,
|
|
1494
1562
|
onSelect: handleSelect,
|
|
1495
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1563
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1496
1564
|
}
|
|
1497
|
-
), /* @__PURE__ */
|
|
1565
|
+
), /* @__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
1566
|
}
|
|
1499
1567
|
|
|
1500
1568
|
// src/components/RewindSelector.tsx
|
|
1501
|
-
import
|
|
1502
|
-
import { Box as
|
|
1569
|
+
import React14, { useState as useState5 } from "react";
|
|
1570
|
+
import { Box as Box13, Text as Text13, useInput as useInput4 } from "ink";
|
|
1503
1571
|
import SelectInput3 from "ink-select-input";
|
|
1504
1572
|
function RewindSelector({ messages, onSelect, onCancel }) {
|
|
1505
|
-
const [step, setStep] =
|
|
1506
|
-
const [selectedMessageIndex, setSelectedMessageIndex] =
|
|
1573
|
+
const [step, setStep] = useState5("selection");
|
|
1574
|
+
const [selectedMessageIndex, setSelectedMessageIndex] = useState5(null);
|
|
1507
1575
|
useInput4((input, key) => {
|
|
1508
1576
|
if (key.escape) {
|
|
1509
1577
|
if (step === "confirmation") {
|
|
@@ -1541,14 +1609,14 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
1541
1609
|
return sum2 + (msg.metadata?.tokenUsage?.total || 0);
|
|
1542
1610
|
}, 0);
|
|
1543
1611
|
if (step === "selection") {
|
|
1544
|
-
return /* @__PURE__ */
|
|
1612
|
+
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
1613
|
SelectInput3,
|
|
1546
1614
|
{
|
|
1547
1615
|
items,
|
|
1548
1616
|
onSelect: handleSelectionSelect,
|
|
1549
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1617
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1550
1618
|
}
|
|
1551
|
-
), /* @__PURE__ */
|
|
1619
|
+
), /* @__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
1620
|
}
|
|
1553
1621
|
const confirmationItems = [
|
|
1554
1622
|
{ label: "Yes, rewind to this point", value: "confirm" },
|
|
@@ -1556,19 +1624,82 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
1556
1624
|
];
|
|
1557
1625
|
const selectedMessage = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages[selectedMessageIndex] : null;
|
|
1558
1626
|
const selectedPreview = selectedMessage ? selectedMessage.content.length > 50 ? selectedMessage.content.substring(0, 50) + "..." : selectedMessage.content : "";
|
|
1559
|
-
return /* @__PURE__ */
|
|
1627
|
+
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
1628
|
SelectInput3,
|
|
1561
1629
|
{
|
|
1562
1630
|
items: confirmationItems,
|
|
1563
1631
|
onSelect: handleConfirmationSelect,
|
|
1564
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1632
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1633
|
+
}
|
|
1634
|
+
), /* @__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")));
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// src/components/SessionSelector.tsx
|
|
1638
|
+
import React15, { useState as useState6 } from "react";
|
|
1639
|
+
import { Box as Box14, Text as Text14, useInput as useInput5 } from "ink";
|
|
1640
|
+
import SelectInput4 from "ink-select-input";
|
|
1641
|
+
function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
|
|
1642
|
+
const [step, setStep] = useState6("selection");
|
|
1643
|
+
const [selectedSession, setSelectedSession] = useState6(null);
|
|
1644
|
+
useInput5((input, key) => {
|
|
1645
|
+
if (key.escape) {
|
|
1646
|
+
if (step === "confirmation") {
|
|
1647
|
+
setStep("selection");
|
|
1648
|
+
setSelectedSession(null);
|
|
1649
|
+
} else {
|
|
1650
|
+
onCancel();
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
});
|
|
1654
|
+
const hasUnsavedWork = currentSession && currentSession.messages.length > 0;
|
|
1655
|
+
const items = sessions.map((session, index) => {
|
|
1656
|
+
const messageCount = session.messages.length;
|
|
1657
|
+
const lastUpdated = new Date(session.updatedAt).toLocaleString();
|
|
1658
|
+
return {
|
|
1659
|
+
label: `[${index + 1}] ${session.name} (${messageCount} msg, ${session.model})
|
|
1660
|
+
Last updated: ${lastUpdated}`,
|
|
1661
|
+
value: session
|
|
1662
|
+
};
|
|
1663
|
+
});
|
|
1664
|
+
const handleSelectionSelect = (item) => {
|
|
1665
|
+
setSelectedSession(item.value);
|
|
1666
|
+
setStep("confirmation");
|
|
1667
|
+
};
|
|
1668
|
+
const handleConfirmationSelect = (item) => {
|
|
1669
|
+
if (item.value === "confirm" && selectedSession) {
|
|
1670
|
+
onSelect(selectedSession);
|
|
1671
|
+
} else {
|
|
1672
|
+
setStep("selection");
|
|
1673
|
+
setSelectedSession(null);
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
if (step === "selection") {
|
|
1677
|
+
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(
|
|
1678
|
+
SelectInput4,
|
|
1679
|
+
{
|
|
1680
|
+
items,
|
|
1681
|
+
onSelect: handleSelectionSelect,
|
|
1682
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React15.createElement(Box14, null, /* @__PURE__ */ React15.createElement(Text14, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1683
|
+
}
|
|
1684
|
+
), /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
1685
|
+
}
|
|
1686
|
+
const confirmationItems = [
|
|
1687
|
+
{ label: "Yes, resume this session", value: "confirm" },
|
|
1688
|
+
{ label: "No, go back to selection", value: "cancel" }
|
|
1689
|
+
];
|
|
1690
|
+
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(
|
|
1691
|
+
SelectInput4,
|
|
1692
|
+
{
|
|
1693
|
+
items: confirmationItems,
|
|
1694
|
+
onSelect: handleConfirmationSelect,
|
|
1695
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React15.createElement(Box14, null, /* @__PURE__ */ React15.createElement(Text14, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1565
1696
|
}
|
|
1566
|
-
), /* @__PURE__ */
|
|
1697
|
+
), /* @__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
1698
|
}
|
|
1568
1699
|
|
|
1569
1700
|
// src/components/LoginFlow.tsx
|
|
1570
|
-
import
|
|
1571
|
-
import { Box as
|
|
1701
|
+
import React16, { useState as useState7, useEffect as useEffect2 } from "react";
|
|
1702
|
+
import { Box as Box15, Text as Text15 } from "ink";
|
|
1572
1703
|
import Spinner2 from "ink-spinner";
|
|
1573
1704
|
import jwt from "jsonwebtoken";
|
|
1574
1705
|
import open from "open";
|
|
@@ -1684,10 +1815,10 @@ var OAuthClient = class {
|
|
|
1684
1815
|
|
|
1685
1816
|
// src/components/LoginFlow.tsx
|
|
1686
1817
|
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] =
|
|
1818
|
+
const [status, setStatus] = useState7("initiating");
|
|
1819
|
+
const [deviceFlow, setDeviceFlow] = useState7(null);
|
|
1820
|
+
const [statusMessage, setStatusMessage] = useState7("Initiating device authorization...");
|
|
1821
|
+
const [error, setError] = useState7(null);
|
|
1691
1822
|
useEffect2(() => {
|
|
1692
1823
|
const runLoginFlow = async () => {
|
|
1693
1824
|
const oauth = new OAuthClient(apiUrl);
|
|
@@ -1731,15 +1862,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
1731
1862
|
}
|
|
1732
1863
|
}, [deviceFlow, status]);
|
|
1733
1864
|
if (status === "initiating") {
|
|
1734
|
-
return /* @__PURE__ */
|
|
1865
|
+
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
1866
|
}
|
|
1736
1867
|
if (status === "error") {
|
|
1737
|
-
return /* @__PURE__ */
|
|
1868
|
+
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
1869
|
}
|
|
1739
1870
|
if (status === "success") {
|
|
1740
|
-
return /* @__PURE__ */
|
|
1871
|
+
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
1872
|
}
|
|
1742
|
-
return /* @__PURE__ */
|
|
1873
|
+
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
1874
|
}
|
|
1744
1875
|
|
|
1745
1876
|
// src/storage/SessionStore.ts
|
|
@@ -2625,10 +2756,9 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
2625
2756
|
content: query
|
|
2626
2757
|
}
|
|
2627
2758
|
];
|
|
2628
|
-
console.log("[DEBUG ReActAgent] Messages array being sent to LLM:");
|
|
2629
2759
|
messages.forEach((msg, i) => {
|
|
2630
2760
|
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
2631
|
-
|
|
2761
|
+
this.context.logger.debug(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
|
|
2632
2762
|
});
|
|
2633
2763
|
let finalAnswer = "";
|
|
2634
2764
|
let reachedMaxIterations = false;
|
|
@@ -5021,8 +5151,8 @@ async function processAndStoreImages(images, context) {
|
|
|
5021
5151
|
const buffer = await downloadImage(image);
|
|
5022
5152
|
const fileType = await fileTypeFromBuffer2(buffer);
|
|
5023
5153
|
const filename = `${uuidv45()}.${fileType?.ext}`;
|
|
5024
|
-
const
|
|
5025
|
-
return
|
|
5154
|
+
const path15 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
5155
|
+
return path15;
|
|
5026
5156
|
}));
|
|
5027
5157
|
}
|
|
5028
5158
|
async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
|
|
@@ -6234,8 +6364,8 @@ async function processAndStoreImage(imageUrl, context) {
|
|
|
6234
6364
|
const buffer = await downloadImage2(imageUrl);
|
|
6235
6365
|
const fileType = await fileTypeFromBuffer3(buffer);
|
|
6236
6366
|
const filename = `${uuidv46()}.${fileType?.ext}`;
|
|
6237
|
-
const
|
|
6238
|
-
return
|
|
6367
|
+
const path15 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
6368
|
+
return path15;
|
|
6239
6369
|
}
|
|
6240
6370
|
async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
|
|
6241
6371
|
await context.onFinish?.("edit_image", storedImagePath);
|
|
@@ -8647,6 +8777,381 @@ var deleteFileTool = {
|
|
|
8647
8777
|
})
|
|
8648
8778
|
};
|
|
8649
8779
|
|
|
8780
|
+
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
|
|
8781
|
+
import { spawn } from "child_process";
|
|
8782
|
+
import path11 from "path";
|
|
8783
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
8784
|
+
var MAX_OUTPUT_SIZE = 100 * 1024;
|
|
8785
|
+
var DANGEROUS_PATTERNS = [
|
|
8786
|
+
// Destructive file operations
|
|
8787
|
+
{
|
|
8788
|
+
pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*r).*\//i,
|
|
8789
|
+
reason: "Recursive delete with path",
|
|
8790
|
+
block: true
|
|
8791
|
+
},
|
|
8792
|
+
{
|
|
8793
|
+
pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*f).*--no-preserve-root/i,
|
|
8794
|
+
reason: "Force delete without preserve root",
|
|
8795
|
+
block: true
|
|
8796
|
+
},
|
|
8797
|
+
{
|
|
8798
|
+
pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\//i,
|
|
8799
|
+
reason: "Recursive force delete on root paths",
|
|
8800
|
+
block: true
|
|
8801
|
+
},
|
|
8802
|
+
{
|
|
8803
|
+
pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s+\//i,
|
|
8804
|
+
reason: "Force recursive delete on root paths",
|
|
8805
|
+
block: true
|
|
8806
|
+
},
|
|
8807
|
+
// System-level dangerous commands
|
|
8808
|
+
{ pattern: /\bsudo\b/i, reason: "Elevated privileges (sudo)", block: true },
|
|
8809
|
+
{ pattern: /\bsu\s+(-|root)/i, reason: "Switch to root user", block: true },
|
|
8810
|
+
{ pattern: /\bchmod\s+777\b/i, reason: "Overly permissive chmod", block: false },
|
|
8811
|
+
{ pattern: /\bchown\s+-R\s+root/i, reason: "Recursive chown to root", block: true },
|
|
8812
|
+
// Disk/partition operations
|
|
8813
|
+
{ pattern: /\bmkfs\b/i, reason: "Filesystem creation", block: true },
|
|
8814
|
+
{ pattern: /\bfdisk\b/i, reason: "Disk partitioning", block: true },
|
|
8815
|
+
{ pattern: /\bdd\s+.*of=\/dev\//i, reason: "Direct disk write", block: true },
|
|
8816
|
+
// Network attacks
|
|
8817
|
+
{ pattern: /\b(nc|netcat)\s+.*-e\s+\/bin\/(ba)?sh/i, reason: "Reverse shell attempt", block: true },
|
|
8818
|
+
{ pattern: /\bcurl\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
|
|
8819
|
+
{ pattern: /\bwget\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
|
|
8820
|
+
// Fork bombs and resource exhaustion
|
|
8821
|
+
{ pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;/i, reason: "Fork bomb", block: true },
|
|
8822
|
+
{ pattern: /\bwhile\s+true.*do.*done.*&/i, reason: "Infinite loop in background", block: false },
|
|
8823
|
+
// Credential/sensitive data access
|
|
8824
|
+
{ pattern: /\/etc\/shadow/i, reason: "Access to shadow file", block: true },
|
|
8825
|
+
{ pattern: /\/etc\/passwd.*>/i, reason: "Modifying passwd file", block: true },
|
|
8826
|
+
{ pattern: /\baws\s+.*--profile\s+/i, reason: "AWS profile access", block: false },
|
|
8827
|
+
// History/log manipulation
|
|
8828
|
+
{ pattern: /\bhistory\s+-c\b/i, reason: "Clearing shell history", block: false },
|
|
8829
|
+
{ pattern: />\s*\/var\/log\//i, reason: "Overwriting system logs", block: true },
|
|
8830
|
+
// Dangerous redirects
|
|
8831
|
+
{ pattern: />\s*\/dev\/sda/i, reason: "Writing to block device", block: true },
|
|
8832
|
+
{ pattern: />\s*\/dev\/null.*2>&1.*</i, reason: "Potentially hiding output", block: false },
|
|
8833
|
+
// Environment manipulation
|
|
8834
|
+
{ pattern: /\bexport\s+PATH\s*=\s*[^$]/i, reason: "Overwriting PATH", block: false },
|
|
8835
|
+
{ pattern: /\bexport\s+LD_PRELOAD/i, reason: "LD_PRELOAD manipulation", block: true },
|
|
8836
|
+
// Process/system control
|
|
8837
|
+
{ pattern: /\bkill\s+-9\s+(-1|1)\b/i, reason: "Killing all processes", block: true },
|
|
8838
|
+
{ pattern: /\bkillall\s+-9\b/i, reason: "Force killing processes", block: false },
|
|
8839
|
+
{ pattern: /\bshutdown\b/i, reason: "System shutdown", block: true },
|
|
8840
|
+
{ pattern: /\breboot\b/i, reason: "System reboot", block: true },
|
|
8841
|
+
{ pattern: /\binit\s+[06]\b/i, reason: "System runlevel change", block: true }
|
|
8842
|
+
];
|
|
8843
|
+
var SAFE_COMMAND_PREFIXES = [
|
|
8844
|
+
"ls",
|
|
8845
|
+
"cat",
|
|
8846
|
+
"head",
|
|
8847
|
+
"tail",
|
|
8848
|
+
"grep",
|
|
8849
|
+
"find",
|
|
8850
|
+
"echo",
|
|
8851
|
+
"pwd",
|
|
8852
|
+
"whoami",
|
|
8853
|
+
"date",
|
|
8854
|
+
"cal",
|
|
8855
|
+
"wc",
|
|
8856
|
+
"sort",
|
|
8857
|
+
"uniq",
|
|
8858
|
+
"cut",
|
|
8859
|
+
"tr",
|
|
8860
|
+
"sed",
|
|
8861
|
+
"awk",
|
|
8862
|
+
"git",
|
|
8863
|
+
"npm",
|
|
8864
|
+
"pnpm",
|
|
8865
|
+
"yarn",
|
|
8866
|
+
"node",
|
|
8867
|
+
"npx",
|
|
8868
|
+
"tsx",
|
|
8869
|
+
"ts-node",
|
|
8870
|
+
"python",
|
|
8871
|
+
"python3",
|
|
8872
|
+
"pip",
|
|
8873
|
+
"pip3",
|
|
8874
|
+
"docker",
|
|
8875
|
+
"docker-compose",
|
|
8876
|
+
"curl",
|
|
8877
|
+
"wget",
|
|
8878
|
+
// These are safe for fetching, blocked when piped to shell
|
|
8879
|
+
"mkdir",
|
|
8880
|
+
"touch",
|
|
8881
|
+
"cp",
|
|
8882
|
+
"mv",
|
|
8883
|
+
// File operations (still require permission)
|
|
8884
|
+
"which",
|
|
8885
|
+
"whereis",
|
|
8886
|
+
"type",
|
|
8887
|
+
"file",
|
|
8888
|
+
"stat",
|
|
8889
|
+
"env",
|
|
8890
|
+
"printenv",
|
|
8891
|
+
"set",
|
|
8892
|
+
"man",
|
|
8893
|
+
"help",
|
|
8894
|
+
"info",
|
|
8895
|
+
"diff",
|
|
8896
|
+
"comm",
|
|
8897
|
+
"cmp",
|
|
8898
|
+
"tar",
|
|
8899
|
+
"zip",
|
|
8900
|
+
"unzip",
|
|
8901
|
+
"gzip",
|
|
8902
|
+
"gunzip",
|
|
8903
|
+
"ssh",
|
|
8904
|
+
"scp",
|
|
8905
|
+
"rsync",
|
|
8906
|
+
// Network tools
|
|
8907
|
+
"make",
|
|
8908
|
+
"cmake",
|
|
8909
|
+
"cargo",
|
|
8910
|
+
"go",
|
|
8911
|
+
"rustc",
|
|
8912
|
+
"gcc",
|
|
8913
|
+
"g++",
|
|
8914
|
+
// Build tools
|
|
8915
|
+
"jest",
|
|
8916
|
+
"vitest",
|
|
8917
|
+
"mocha",
|
|
8918
|
+
"pytest",
|
|
8919
|
+
// Test runners
|
|
8920
|
+
"eslint",
|
|
8921
|
+
"prettier",
|
|
8922
|
+
"tsc"
|
|
8923
|
+
// Linters/formatters
|
|
8924
|
+
];
|
|
8925
|
+
function checkDangerousPatterns(command) {
|
|
8926
|
+
for (const { pattern, reason, block } of DANGEROUS_PATTERNS) {
|
|
8927
|
+
if (pattern.test(command)) {
|
|
8928
|
+
return { blocked: block, reason };
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
return { blocked: false };
|
|
8932
|
+
}
|
|
8933
|
+
function getBaseCommand(command) {
|
|
8934
|
+
const trimmed = command.trim();
|
|
8935
|
+
const match = trimmed.match(/^(\S+)/);
|
|
8936
|
+
return match ? match[1] : "";
|
|
8937
|
+
}
|
|
8938
|
+
function isSafeCommandPrefix(command) {
|
|
8939
|
+
const baseCommand = getBaseCommand(command);
|
|
8940
|
+
return SAFE_COMMAND_PREFIXES.some((safe) => baseCommand === safe || baseCommand.endsWith(`/${safe}`));
|
|
8941
|
+
}
|
|
8942
|
+
async function executeBashCommand(params) {
|
|
8943
|
+
const { command, cwd: relativeCwd, timeout = DEFAULT_TIMEOUT_MS } = params;
|
|
8944
|
+
if (!command || command.trim().length === 0) {
|
|
8945
|
+
return {
|
|
8946
|
+
stdout: "",
|
|
8947
|
+
stderr: "Error: Command cannot be empty",
|
|
8948
|
+
exitCode: 1,
|
|
8949
|
+
timedOut: false,
|
|
8950
|
+
blocked: true,
|
|
8951
|
+
blockedReason: "Empty command"
|
|
8952
|
+
};
|
|
8953
|
+
}
|
|
8954
|
+
const dangerCheck = checkDangerousPatterns(command);
|
|
8955
|
+
if (dangerCheck.blocked) {
|
|
8956
|
+
return {
|
|
8957
|
+
stdout: "",
|
|
8958
|
+
stderr: `Command blocked for safety: ${dangerCheck.reason}`,
|
|
8959
|
+
exitCode: 1,
|
|
8960
|
+
timedOut: false,
|
|
8961
|
+
blocked: true,
|
|
8962
|
+
blockedReason: dangerCheck.reason
|
|
8963
|
+
};
|
|
8964
|
+
}
|
|
8965
|
+
const baseCwd = process.cwd();
|
|
8966
|
+
const targetCwd = relativeCwd ? path11.resolve(baseCwd, relativeCwd) : baseCwd;
|
|
8967
|
+
const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
|
|
8968
|
+
return new Promise((resolve3) => {
|
|
8969
|
+
let stdout = "";
|
|
8970
|
+
let stderr = "";
|
|
8971
|
+
let timedOut = false;
|
|
8972
|
+
const proc = spawn("bash", ["-c", command], {
|
|
8973
|
+
cwd: targetCwd,
|
|
8974
|
+
env: {
|
|
8975
|
+
...process.env,
|
|
8976
|
+
// Prevent color codes that might be hard to read
|
|
8977
|
+
NO_COLOR: "1",
|
|
8978
|
+
FORCE_COLOR: "0"
|
|
8979
|
+
},
|
|
8980
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
8981
|
+
});
|
|
8982
|
+
const timeoutId = setTimeout(() => {
|
|
8983
|
+
timedOut = true;
|
|
8984
|
+
proc.kill("SIGTERM");
|
|
8985
|
+
setTimeout(() => {
|
|
8986
|
+
if (!proc.killed) {
|
|
8987
|
+
proc.kill("SIGKILL");
|
|
8988
|
+
}
|
|
8989
|
+
}, 5e3);
|
|
8990
|
+
}, effectiveTimeout);
|
|
8991
|
+
proc.stdout.on("data", (data) => {
|
|
8992
|
+
if (stdout.length < MAX_OUTPUT_SIZE) {
|
|
8993
|
+
stdout += data.toString();
|
|
8994
|
+
if (stdout.length > MAX_OUTPUT_SIZE) {
|
|
8995
|
+
stdout = stdout.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
|
|
8996
|
+
}
|
|
8997
|
+
}
|
|
8998
|
+
});
|
|
8999
|
+
proc.stderr.on("data", (data) => {
|
|
9000
|
+
if (stderr.length < MAX_OUTPUT_SIZE) {
|
|
9001
|
+
stderr += data.toString();
|
|
9002
|
+
if (stderr.length > MAX_OUTPUT_SIZE) {
|
|
9003
|
+
stderr = stderr.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
|
|
9004
|
+
}
|
|
9005
|
+
}
|
|
9006
|
+
});
|
|
9007
|
+
proc.on("close", (exitCode) => {
|
|
9008
|
+
clearTimeout(timeoutId);
|
|
9009
|
+
resolve3({
|
|
9010
|
+
stdout: stdout.trim(),
|
|
9011
|
+
stderr: stderr.trim(),
|
|
9012
|
+
exitCode,
|
|
9013
|
+
timedOut,
|
|
9014
|
+
blocked: false
|
|
9015
|
+
});
|
|
9016
|
+
});
|
|
9017
|
+
proc.on("error", (error) => {
|
|
9018
|
+
clearTimeout(timeoutId);
|
|
9019
|
+
resolve3({
|
|
9020
|
+
stdout: "",
|
|
9021
|
+
stderr: `Failed to execute command: ${error.message}`,
|
|
9022
|
+
exitCode: 1,
|
|
9023
|
+
timedOut: false,
|
|
9024
|
+
blocked: false
|
|
9025
|
+
});
|
|
9026
|
+
});
|
|
9027
|
+
});
|
|
9028
|
+
}
|
|
9029
|
+
function formatResult(result, command) {
|
|
9030
|
+
const parts = [];
|
|
9031
|
+
parts.push(`$ ${command}`);
|
|
9032
|
+
parts.push("");
|
|
9033
|
+
if (result.blocked) {
|
|
9034
|
+
parts.push(`BLOCKED: ${result.blockedReason}`);
|
|
9035
|
+
parts.push("");
|
|
9036
|
+
parts.push("This command was blocked for safety reasons.");
|
|
9037
|
+
parts.push("If you believe this is a false positive, please run the command manually.");
|
|
9038
|
+
return parts.join("\n");
|
|
9039
|
+
}
|
|
9040
|
+
if (result.timedOut) {
|
|
9041
|
+
parts.push("WARNING: Command timed out and was terminated.");
|
|
9042
|
+
parts.push("");
|
|
9043
|
+
}
|
|
9044
|
+
if (result.stdout) {
|
|
9045
|
+
parts.push(result.stdout);
|
|
9046
|
+
}
|
|
9047
|
+
if (result.stderr) {
|
|
9048
|
+
if (result.stdout) {
|
|
9049
|
+
parts.push("");
|
|
9050
|
+
}
|
|
9051
|
+
parts.push("STDERR:");
|
|
9052
|
+
parts.push(result.stderr);
|
|
9053
|
+
}
|
|
9054
|
+
if (result.exitCode !== 0 && result.exitCode !== null) {
|
|
9055
|
+
parts.push("");
|
|
9056
|
+
parts.push(`Exit code: ${result.exitCode}`);
|
|
9057
|
+
}
|
|
9058
|
+
if (!result.stdout && !result.stderr && !result.timedOut) {
|
|
9059
|
+
parts.push("(command completed with no output)");
|
|
9060
|
+
}
|
|
9061
|
+
return parts.join("\n");
|
|
9062
|
+
}
|
|
9063
|
+
var bashExecuteTool = {
|
|
9064
|
+
name: "bash_execute",
|
|
9065
|
+
implementation: (context) => ({
|
|
9066
|
+
toolFn: async (value) => {
|
|
9067
|
+
const params = value;
|
|
9068
|
+
const isSafe = isSafeCommandPrefix(params.command);
|
|
9069
|
+
context.logger.info("Bash: Executing command", {
|
|
9070
|
+
command: params.command,
|
|
9071
|
+
cwd: params.cwd || ".",
|
|
9072
|
+
timeout: params.timeout || DEFAULT_TIMEOUT_MS,
|
|
9073
|
+
isSafeCommand: isSafe
|
|
9074
|
+
});
|
|
9075
|
+
if (context.onStart) {
|
|
9076
|
+
await context.onStart("bash_execute", {
|
|
9077
|
+
command: params.command,
|
|
9078
|
+
cwd: params.cwd
|
|
9079
|
+
});
|
|
9080
|
+
}
|
|
9081
|
+
try {
|
|
9082
|
+
const result = await executeBashCommand(params);
|
|
9083
|
+
const formattedResult = formatResult(result, params.command);
|
|
9084
|
+
context.logger.info("Bash: Command completed", {
|
|
9085
|
+
exitCode: result.exitCode,
|
|
9086
|
+
timedOut: result.timedOut,
|
|
9087
|
+
blocked: result.blocked
|
|
9088
|
+
});
|
|
9089
|
+
if (context.onFinish) {
|
|
9090
|
+
await context.onFinish("bash_execute", {
|
|
9091
|
+
command: params.command,
|
|
9092
|
+
exitCode: result.exitCode,
|
|
9093
|
+
blocked: result.blocked
|
|
9094
|
+
});
|
|
9095
|
+
}
|
|
9096
|
+
return formattedResult;
|
|
9097
|
+
} catch (error) {
|
|
9098
|
+
context.logger.error("Bash: Command failed", error);
|
|
9099
|
+
if (context.onFinish) {
|
|
9100
|
+
await context.onFinish("bash_execute", {
|
|
9101
|
+
command: params.command,
|
|
9102
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
9103
|
+
});
|
|
9104
|
+
}
|
|
9105
|
+
throw error;
|
|
9106
|
+
}
|
|
9107
|
+
},
|
|
9108
|
+
toolSchema: {
|
|
9109
|
+
name: "bash_execute",
|
|
9110
|
+
description: `Execute a bash command in the terminal. Use this for running shell commands, scripts, build tools, git operations, and other CLI tasks.
|
|
9111
|
+
|
|
9112
|
+
SAFETY NOTES:
|
|
9113
|
+
- Commands are executed in the current working directory (or specified cwd)
|
|
9114
|
+
- Dangerous commands (sudo, rm -rf /, etc.) are automatically blocked
|
|
9115
|
+
- Commands have a default timeout of 60 seconds (max 5 minutes)
|
|
9116
|
+
- Output is limited to prevent overwhelming responses
|
|
9117
|
+
- This tool ALWAYS requires user permission before execution
|
|
9118
|
+
|
|
9119
|
+
COMMON USE CASES:
|
|
9120
|
+
- Running build commands: npm run build, make, cargo build
|
|
9121
|
+
- Git operations: git status, git log, git diff
|
|
9122
|
+
- Viewing system info: ls, pwd, cat, head, tail
|
|
9123
|
+
- Running tests: npm test, pytest, cargo test
|
|
9124
|
+
- Package management: npm install, pip install
|
|
9125
|
+
- File operations: mkdir, cp, mv (with permission)
|
|
9126
|
+
|
|
9127
|
+
BLOCKED OPERATIONS:
|
|
9128
|
+
- sudo and privilege escalation
|
|
9129
|
+
- Recursive deletes on system paths
|
|
9130
|
+
- Direct disk operations
|
|
9131
|
+
- Fork bombs and resource exhaustion
|
|
9132
|
+
- Piping remote scripts to shell`,
|
|
9133
|
+
parameters: {
|
|
9134
|
+
type: "object",
|
|
9135
|
+
properties: {
|
|
9136
|
+
command: {
|
|
9137
|
+
type: "string",
|
|
9138
|
+
description: "The bash command to execute. Can include pipes, redirects, and chained commands."
|
|
9139
|
+
},
|
|
9140
|
+
cwd: {
|
|
9141
|
+
type: "string",
|
|
9142
|
+
description: "Working directory for the command (optional). Can be relative or absolute path. Defaults to current directory."
|
|
9143
|
+
},
|
|
9144
|
+
timeout: {
|
|
9145
|
+
type: "number",
|
|
9146
|
+
description: "Timeout in milliseconds (optional, default: 60000, max: 300000). Command will be terminated if it exceeds this time."
|
|
9147
|
+
}
|
|
9148
|
+
},
|
|
9149
|
+
required: ["command"]
|
|
9150
|
+
}
|
|
9151
|
+
}
|
|
9152
|
+
})
|
|
9153
|
+
};
|
|
9154
|
+
|
|
8650
9155
|
// ../../b4m-core/packages/services/dist/src/llm/tools/index.js
|
|
8651
9156
|
var tools = {
|
|
8652
9157
|
dice_roll: diceRollTool,
|
|
@@ -8674,7 +9179,9 @@ var tools = {
|
|
|
8674
9179
|
create_file: createFileTool,
|
|
8675
9180
|
glob_files: globFilesTool,
|
|
8676
9181
|
grep_search: grepSearchTool,
|
|
8677
|
-
delete_file: deleteFileTool
|
|
9182
|
+
delete_file: deleteFileTool,
|
|
9183
|
+
// Shell execution
|
|
9184
|
+
bash_execute: bashExecuteTool
|
|
8678
9185
|
};
|
|
8679
9186
|
var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
|
|
8680
9187
|
const context = {
|
|
@@ -8949,7 +9456,7 @@ async function generateFileDeletePreview(args) {
|
|
|
8949
9456
|
if (!existsSync7(args.path)) {
|
|
8950
9457
|
return `[File does not exist: ${args.path}]`;
|
|
8951
9458
|
}
|
|
8952
|
-
const stats = await import("fs/promises").then((
|
|
9459
|
+
const stats = await import("fs/promises").then((fs12) => fs12.stat(args.path));
|
|
8953
9460
|
return `[File will be deleted]
|
|
8954
9461
|
|
|
8955
9462
|
Path: ${args.path}
|
|
@@ -8962,7 +9469,7 @@ Last modified: ${stats.mtime.toLocaleString()}`;
|
|
|
8962
9469
|
|
|
8963
9470
|
// src/utils/Logger.ts
|
|
8964
9471
|
import fs10 from "fs/promises";
|
|
8965
|
-
import
|
|
9472
|
+
import path12 from "path";
|
|
8966
9473
|
import os from "os";
|
|
8967
9474
|
var Logger2 = class _Logger {
|
|
8968
9475
|
constructor() {
|
|
@@ -8985,9 +9492,9 @@ var Logger2 = class _Logger {
|
|
|
8985
9492
|
*/
|
|
8986
9493
|
async initialize(sessionId) {
|
|
8987
9494
|
this.sessionId = sessionId;
|
|
8988
|
-
const debugDir =
|
|
9495
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
8989
9496
|
await fs10.mkdir(debugDir, { recursive: true });
|
|
8990
|
-
this.logFilePath =
|
|
9497
|
+
this.logFilePath = path12.join(debugDir, `${sessionId}.txt`);
|
|
8991
9498
|
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
8992
9499
|
}
|
|
8993
9500
|
/**
|
|
@@ -9197,12 +9704,12 @@ var Logger2 = class _Logger {
|
|
|
9197
9704
|
async cleanupOldLogs() {
|
|
9198
9705
|
if (!this.fileLoggingEnabled) return;
|
|
9199
9706
|
try {
|
|
9200
|
-
const debugDir =
|
|
9707
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9201
9708
|
const files = await fs10.readdir(debugDir);
|
|
9202
9709
|
const now = Date.now();
|
|
9203
9710
|
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
9204
9711
|
for (const file of files) {
|
|
9205
|
-
const filePath =
|
|
9712
|
+
const filePath = path12.join(debugDir, file);
|
|
9206
9713
|
const stats = await fs10.stat(filePath);
|
|
9207
9714
|
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
9208
9715
|
await fs10.unlink(filePath);
|
|
@@ -9214,6 +9721,7 @@ var Logger2 = class _Logger {
|
|
|
9214
9721
|
}
|
|
9215
9722
|
};
|
|
9216
9723
|
var logger = Logger2.getInstance();
|
|
9724
|
+
logger.setVerbose(process.env.B4M_VERBOSE === "1");
|
|
9217
9725
|
|
|
9218
9726
|
// src/llm/ServerToolExecutor.ts
|
|
9219
9727
|
var ServerToolExecutor = class {
|
|
@@ -9267,7 +9775,8 @@ var LOCAL_TOOLS = [
|
|
|
9267
9775
|
"edit_file",
|
|
9268
9776
|
"dice_roll",
|
|
9269
9777
|
"math_evaluate",
|
|
9270
|
-
"current_datetime"
|
|
9778
|
+
"current_datetime",
|
|
9779
|
+
"bash_execute"
|
|
9271
9780
|
];
|
|
9272
9781
|
function isServerTool(toolName) {
|
|
9273
9782
|
return SERVER_TOOLS.includes(toolName);
|
|
@@ -9297,21 +9806,21 @@ var NoOpStorage = class extends BaseStorage {
|
|
|
9297
9806
|
async upload(input, destination, options) {
|
|
9298
9807
|
return `/tmp/${destination}`;
|
|
9299
9808
|
}
|
|
9300
|
-
async download(
|
|
9809
|
+
async download(path15) {
|
|
9301
9810
|
throw new Error("Download not supported in CLI");
|
|
9302
9811
|
}
|
|
9303
|
-
async delete(
|
|
9812
|
+
async delete(path15) {
|
|
9304
9813
|
}
|
|
9305
|
-
async getSignedUrl(
|
|
9306
|
-
return `/tmp/${
|
|
9814
|
+
async getSignedUrl(path15) {
|
|
9815
|
+
return `/tmp/${path15}`;
|
|
9307
9816
|
}
|
|
9308
|
-
getPublicUrl(
|
|
9309
|
-
return `/tmp/${
|
|
9817
|
+
getPublicUrl(path15) {
|
|
9818
|
+
return `/tmp/${path15}`;
|
|
9310
9819
|
}
|
|
9311
|
-
async getPreview(
|
|
9312
|
-
return `/tmp/${
|
|
9820
|
+
async getPreview(path15) {
|
|
9821
|
+
return `/tmp/${path15}`;
|
|
9313
9822
|
}
|
|
9314
|
-
async getMetadata(
|
|
9823
|
+
async getMetadata(path15) {
|
|
9315
9824
|
return { size: 0, contentType: "application/octet-stream" };
|
|
9316
9825
|
}
|
|
9317
9826
|
};
|
|
@@ -9369,6 +9878,10 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
9369
9878
|
} catch (error) {
|
|
9370
9879
|
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9371
9880
|
}
|
|
9881
|
+
} else if (toolName === "bash_execute" && args?.command) {
|
|
9882
|
+
const cwd = args.cwd ? ` (in ${args.cwd})` : "";
|
|
9883
|
+
const timeout = args.timeout ? ` [timeout: ${args.timeout}ms]` : "";
|
|
9884
|
+
preview = `$ ${args.command}${cwd}${timeout}`;
|
|
9372
9885
|
}
|
|
9373
9886
|
const response = await showPermissionPrompt(toolName, args, preview);
|
|
9374
9887
|
if (response.action === "deny") {
|
|
@@ -9439,6 +9952,8 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9439
9952
|
glob_files: {},
|
|
9440
9953
|
grep_search: {},
|
|
9441
9954
|
delete_file: {},
|
|
9955
|
+
// Shell execution (always requires permission)
|
|
9956
|
+
bash_execute: {},
|
|
9442
9957
|
// Server-side tools (executed via /api/ai/v1/tools Lambda)
|
|
9443
9958
|
// Routed by ToolRouter to use B4M company API keys
|
|
9444
9959
|
weather_info: {},
|
|
@@ -9504,6 +10019,7 @@ var DEFAULT_TOOL_CATEGORIES = {
|
|
|
9504
10019
|
create_file: "prompt_always",
|
|
9505
10020
|
delete_file: "prompt_always",
|
|
9506
10021
|
shell_execute: "prompt_always",
|
|
10022
|
+
bash_execute: "prompt_always",
|
|
9507
10023
|
git_commit: "prompt_always",
|
|
9508
10024
|
git_push: "prompt_always",
|
|
9509
10025
|
// ===== PROMPT DEFAULT (Repository read tools) =====
|
|
@@ -9665,10 +10181,122 @@ function getEnvironmentName(configApiConfig) {
|
|
|
9665
10181
|
return "Bike4Mind";
|
|
9666
10182
|
}
|
|
9667
10183
|
|
|
10184
|
+
// src/utils/contextLoader.ts
|
|
10185
|
+
import * as fs11 from "fs";
|
|
10186
|
+
import * as path13 from "path";
|
|
10187
|
+
import { homedir as homedir4 } from "os";
|
|
10188
|
+
var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
|
|
10189
|
+
var PROJECT_CONTEXT_FILES = [
|
|
10190
|
+
"CLAUDE.local.md",
|
|
10191
|
+
// gitignored, highest priority
|
|
10192
|
+
"CLAUDE.md",
|
|
10193
|
+
"AGENTS.md",
|
|
10194
|
+
// cross-tool standard
|
|
10195
|
+
"AI.local.md",
|
|
10196
|
+
// gitignored
|
|
10197
|
+
"AI.md",
|
|
10198
|
+
"INSTRUCTIONS.md"
|
|
10199
|
+
];
|
|
10200
|
+
var GLOBAL_CONTEXT_FILES = ["AI.local.md", "AI.md"];
|
|
10201
|
+
function formatFileSize2(bytes) {
|
|
10202
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
10203
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
10204
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
10205
|
+
}
|
|
10206
|
+
function tryReadContextFile(dir, filename, source) {
|
|
10207
|
+
const filePath = path13.join(dir, filename);
|
|
10208
|
+
try {
|
|
10209
|
+
const stats = fs11.lstatSync(filePath);
|
|
10210
|
+
if (stats.isDirectory()) {
|
|
10211
|
+
return null;
|
|
10212
|
+
}
|
|
10213
|
+
if (stats.isSymbolicLink()) {
|
|
10214
|
+
return {
|
|
10215
|
+
error: `${source === "global" ? "Global" : "Project"} ${filename} is a symlink (not allowed for security)`
|
|
10216
|
+
};
|
|
10217
|
+
}
|
|
10218
|
+
if (stats.size > CONTEXT_FILE_SIZE_LIMIT) {
|
|
10219
|
+
return {
|
|
10220
|
+
error: `${source === "global" ? "Global" : "Project"} ${filename} exceeds 100KB limit (${formatFileSize2(stats.size)})`
|
|
10221
|
+
};
|
|
10222
|
+
}
|
|
10223
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
10224
|
+
return {
|
|
10225
|
+
filename,
|
|
10226
|
+
content,
|
|
10227
|
+
source,
|
|
10228
|
+
path: filePath
|
|
10229
|
+
};
|
|
10230
|
+
} catch (err) {
|
|
10231
|
+
if (err.code === "ENOENT") {
|
|
10232
|
+
return null;
|
|
10233
|
+
}
|
|
10234
|
+
if (err.code === "EACCES") {
|
|
10235
|
+
return {
|
|
10236
|
+
error: `Cannot read ${source} ${filename}: permission denied`
|
|
10237
|
+
};
|
|
10238
|
+
}
|
|
10239
|
+
return {
|
|
10240
|
+
error: `Cannot read ${source} ${filename}: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
10241
|
+
};
|
|
10242
|
+
}
|
|
10243
|
+
}
|
|
10244
|
+
function findContextFile(dir, candidates, source) {
|
|
10245
|
+
for (const filename of candidates) {
|
|
10246
|
+
const result = tryReadContextFile(dir, filename, source);
|
|
10247
|
+
if (result === null) {
|
|
10248
|
+
continue;
|
|
10249
|
+
}
|
|
10250
|
+
if ("error" in result) {
|
|
10251
|
+
return { result: null, error: result.error };
|
|
10252
|
+
}
|
|
10253
|
+
return { result, error: null };
|
|
10254
|
+
}
|
|
10255
|
+
return { result: null, error: null };
|
|
10256
|
+
}
|
|
10257
|
+
function mergeContextContent(global, project) {
|
|
10258
|
+
if (global && project) {
|
|
10259
|
+
return `${global.content}
|
|
10260
|
+
|
|
10261
|
+
---
|
|
10262
|
+
|
|
10263
|
+
${project.content}`;
|
|
10264
|
+
}
|
|
10265
|
+
if (global) {
|
|
10266
|
+
return global.content;
|
|
10267
|
+
}
|
|
10268
|
+
if (project) {
|
|
10269
|
+
return project.content;
|
|
10270
|
+
}
|
|
10271
|
+
return "";
|
|
10272
|
+
}
|
|
10273
|
+
async function loadContextFiles(projectDir) {
|
|
10274
|
+
const errors = [];
|
|
10275
|
+
const globalDir = path13.join(homedir4(), ".bike4mind");
|
|
10276
|
+
const projectDirectory = projectDir || process.cwd();
|
|
10277
|
+
const [globalResult, projectResult] = await Promise.all([
|
|
10278
|
+
Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
|
|
10279
|
+
Promise.resolve(findContextFile(projectDirectory, PROJECT_CONTEXT_FILES, "project"))
|
|
10280
|
+
]);
|
|
10281
|
+
if (globalResult.error) {
|
|
10282
|
+
errors.push(globalResult.error);
|
|
10283
|
+
}
|
|
10284
|
+
if (projectResult.error) {
|
|
10285
|
+
errors.push(projectResult.error);
|
|
10286
|
+
}
|
|
10287
|
+
const mergedContent = mergeContextContent(globalResult.result, projectResult.result);
|
|
10288
|
+
return {
|
|
10289
|
+
globalContext: globalResult.result,
|
|
10290
|
+
projectContext: projectResult.result,
|
|
10291
|
+
mergedContent,
|
|
10292
|
+
errors
|
|
10293
|
+
};
|
|
10294
|
+
}
|
|
10295
|
+
|
|
9668
10296
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
9669
10297
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
9670
10298
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
9671
|
-
import
|
|
10299
|
+
import path14 from "path";
|
|
9672
10300
|
import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
|
|
9673
10301
|
var MCPClient = class {
|
|
9674
10302
|
// Note: This class handles MCP server communication with repository filtering
|
|
@@ -9709,16 +10337,16 @@ var MCPClient = class {
|
|
|
9709
10337
|
const root = process.env.INIT_CWD || process.cwd();
|
|
9710
10338
|
const candidatePaths = [
|
|
9711
10339
|
// When running from SST Lambda with node_modules structure (copyFiles)
|
|
9712
|
-
|
|
10340
|
+
path14.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
|
|
9713
10341
|
// When running from SST Lambda deployed environment (/var/task)
|
|
9714
|
-
|
|
10342
|
+
path14.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9715
10343
|
// When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
|
|
9716
|
-
|
|
10344
|
+
path14.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9717
10345
|
// When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
|
|
9718
|
-
|
|
10346
|
+
path14.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9719
10347
|
// Original paths (backward compatibility)
|
|
9720
|
-
|
|
9721
|
-
|
|
10348
|
+
path14.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
10349
|
+
path14.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
9722
10350
|
];
|
|
9723
10351
|
const serverScriptPath = candidatePaths.find((p) => existsSync8(p));
|
|
9724
10352
|
if (!serverScriptPath) {
|
|
@@ -10522,7 +11150,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
10522
11150
|
// package.json
|
|
10523
11151
|
var package_default = {
|
|
10524
11152
|
name: "@bike4mind/cli",
|
|
10525
|
-
version: "0.2.
|
|
11153
|
+
version: "0.2.10-cj-jira-bulk-transition-edit-issue.17246+e45d9b8e3",
|
|
10526
11154
|
type: "module",
|
|
10527
11155
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
10528
11156
|
license: "UNLICENSED",
|
|
@@ -10577,7 +11205,7 @@ var package_default = {
|
|
|
10577
11205
|
"@google/generative-ai": "^0.21.0",
|
|
10578
11206
|
"@joplin/turndown-plugin-gfm": "^1.0.62",
|
|
10579
11207
|
"@mendable/firecrawl-js": "^1.29.3",
|
|
10580
|
-
"@modelcontextprotocol/sdk": "
|
|
11208
|
+
"@modelcontextprotocol/sdk": "1.20.1",
|
|
10581
11209
|
"@octokit/rest": "^22.0.0",
|
|
10582
11210
|
"@opensearch-project/opensearch": "2.11.0",
|
|
10583
11211
|
"async-mutex": "^0.5.0",
|
|
@@ -10625,11 +11253,11 @@ var package_default = {
|
|
|
10625
11253
|
},
|
|
10626
11254
|
devDependencies: {
|
|
10627
11255
|
"@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.
|
|
11256
|
+
"@bike4mind/common": "2.39.1-cj-jira-bulk-transition-edit-issue.17246+e45d9b8e3",
|
|
11257
|
+
"@bike4mind/mcp": "1.20.4-cj-jira-bulk-transition-edit-issue.17246+e45d9b8e3",
|
|
11258
|
+
"@bike4mind/services": "2.34.2-cj-jira-bulk-transition-edit-issue.17246+e45d9b8e3",
|
|
11259
|
+
"@bike4mind/utils": "2.1.4-cj-jira-bulk-transition-edit-issue.17246+e45d9b8e3",
|
|
11260
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
10633
11261
|
"@types/diff": "^5.0.9",
|
|
10634
11262
|
"@types/jsonwebtoken": "^9.0.4",
|
|
10635
11263
|
"@types/node": "^22.9.0",
|
|
@@ -10641,7 +11269,7 @@ var package_default = {
|
|
|
10641
11269
|
typescript: "^5.9.3",
|
|
10642
11270
|
vitest: "^3.2.4"
|
|
10643
11271
|
},
|
|
10644
|
-
gitHead: "
|
|
11272
|
+
gitHead: "e45d9b8e3754908c59ce7788e730201813053267"
|
|
10645
11273
|
};
|
|
10646
11274
|
|
|
10647
11275
|
// src/config/constants.ts
|
|
@@ -10663,7 +11291,7 @@ var usageCache = null;
|
|
|
10663
11291
|
function CliApp() {
|
|
10664
11292
|
const { exit } = useApp();
|
|
10665
11293
|
const imageRenderer = new ImageRenderer();
|
|
10666
|
-
const [state, setState] =
|
|
11294
|
+
const [state, setState] = useState8({
|
|
10667
11295
|
session: null,
|
|
10668
11296
|
sessionStore: new SessionStore(),
|
|
10669
11297
|
configStore: new ConfigStore(),
|
|
@@ -10677,12 +11305,13 @@ function CliApp() {
|
|
|
10677
11305
|
permissionManager: null,
|
|
10678
11306
|
permissionPrompt: null,
|
|
10679
11307
|
trustLocationSelector: null,
|
|
10680
|
-
rewindSelector: null
|
|
11308
|
+
rewindSelector: null,
|
|
11309
|
+
sessionSelector: null
|
|
10681
11310
|
});
|
|
10682
|
-
const [isInitialized, setIsInitialized] =
|
|
10683
|
-
const [initError, setInitError] =
|
|
10684
|
-
const [commandHistory, setCommandHistory] =
|
|
10685
|
-
const imageStoreInitPromise =
|
|
11311
|
+
const [isInitialized, setIsInitialized] = useState8(false);
|
|
11312
|
+
const [initError, setInitError] = useState8(null);
|
|
11313
|
+
const [commandHistory, setCommandHistory] = useState8([]);
|
|
11314
|
+
const imageStoreInitPromise = useRef2(null);
|
|
10686
11315
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
10687
11316
|
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
10688
11317
|
const setShowConfigEditor = useCliStore((state2) => state2.setShowConfigEditor);
|
|
@@ -10722,7 +11351,7 @@ function CliApp() {
|
|
|
10722
11351
|
process.exit(0);
|
|
10723
11352
|
}, 100);
|
|
10724
11353
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
10725
|
-
|
|
11354
|
+
useInput6((input, key) => {
|
|
10726
11355
|
if (key.ctrl && input === "c") {
|
|
10727
11356
|
const now = Date.now();
|
|
10728
11357
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
@@ -10804,13 +11433,11 @@ function CliApp() {
|
|
|
10804
11433
|
if (models.length === 0) {
|
|
10805
11434
|
throw new Error("No models available from server.");
|
|
10806
11435
|
}
|
|
10807
|
-
|
|
11436
|
+
logger.debug(`\u{1F4CB} Available models: ${models.map((m) => m.id).join(", ")}`);
|
|
10808
11437
|
const modelInfo = models.find((m) => m.id === config.defaultModel) || models[0];
|
|
10809
11438
|
if (modelInfo.id !== config.defaultModel) {
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
} else {
|
|
10813
|
-
console.log(`\u{1F916} Using model: ${modelInfo.id}`);
|
|
11439
|
+
logger.warn(`\u26A0\uFE0F Requested model '${config.defaultModel}' not available`);
|
|
11440
|
+
logger.warn(`\u{1F916} Using fallback model: ${modelInfo.id}`);
|
|
10814
11441
|
}
|
|
10815
11442
|
llm.currentModel = modelInfo.id;
|
|
10816
11443
|
const newSession = {
|
|
@@ -10829,9 +11456,6 @@ function CliApp() {
|
|
|
10829
11456
|
};
|
|
10830
11457
|
await logger.initialize(newSession.id);
|
|
10831
11458
|
logger.setVerbose(process.env.B4M_VERBOSE === "1");
|
|
10832
|
-
logger.info(`API: ${apiBaseURL}`);
|
|
10833
|
-
logger.info(`Environment: ${envName}`);
|
|
10834
|
-
logger.info(`Model: ${modelInfo.id}`);
|
|
10835
11459
|
logger.debug("=== Session Configuration Complete ===");
|
|
10836
11460
|
logger.cleanupOldLogs().catch(() => {
|
|
10837
11461
|
});
|
|
@@ -10880,7 +11504,7 @@ function CliApp() {
|
|
|
10880
11504
|
state.configStore,
|
|
10881
11505
|
apiClient
|
|
10882
11506
|
);
|
|
10883
|
-
console.log(`\u{1F6E0}\uFE0F
|
|
11507
|
+
console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
|
|
10884
11508
|
const mcpManager = new McpManager(config);
|
|
10885
11509
|
await mcpManager.initialize();
|
|
10886
11510
|
const mcpTools = mcpManager.getTools();
|
|
@@ -10891,7 +11515,24 @@ function CliApp() {
|
|
|
10891
11515
|
});
|
|
10892
11516
|
}
|
|
10893
11517
|
const allTools = [...b4mTools, ...mcpTools];
|
|
10894
|
-
|
|
11518
|
+
const projectDir = state.configStore.getProjectConfigDir();
|
|
11519
|
+
const contextResult = await loadContextFiles(projectDir);
|
|
11520
|
+
if (contextResult.globalContext) {
|
|
11521
|
+
console.log(`\u{1F4C4} Global context: ${contextResult.globalContext.filename}`);
|
|
11522
|
+
}
|
|
11523
|
+
if (contextResult.projectContext) {
|
|
11524
|
+
console.log(`\u{1F4C4} Project context: ${contextResult.projectContext.filename}`);
|
|
11525
|
+
}
|
|
11526
|
+
for (const error of contextResult.errors) {
|
|
11527
|
+
console.log(`\u26A0\uFE0F Context file error: ${error}`);
|
|
11528
|
+
}
|
|
11529
|
+
const contextSection = contextResult.mergedContent ? `
|
|
11530
|
+
|
|
11531
|
+
## Project Context
|
|
11532
|
+
|
|
11533
|
+
Follow these project-specific instructions:
|
|
11534
|
+
|
|
11535
|
+
${contextResult.mergedContent}` : "";
|
|
10895
11536
|
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
11537
|
|
|
10897
11538
|
CORE BEHAVIOR:
|
|
@@ -10921,7 +11562,7 @@ EXAMPLES:
|
|
|
10921
11562
|
- "enhance README" \u2192 file_read \u2192 generate \u2192 create_file
|
|
10922
11563
|
- "what packages installed?" \u2192 glob_files "**/package.json" \u2192 file_read
|
|
10923
11564
|
|
|
10924
|
-
Remember: Use context from previous messages to understand follow-up questions
|
|
11565
|
+
Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
|
|
10925
11566
|
const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
|
|
10926
11567
|
const agent = new ReActAgent({
|
|
10927
11568
|
userId: config.userId,
|
|
@@ -10949,6 +11590,7 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10949
11590
|
}));
|
|
10950
11591
|
setStoreSession(newSession);
|
|
10951
11592
|
setIsInitialized(true);
|
|
11593
|
+
console.log("\n");
|
|
10952
11594
|
} catch (error) {
|
|
10953
11595
|
console.error("Initialization error:", error);
|
|
10954
11596
|
setInitError(error instanceof Error ? error.message : "Unknown error");
|
|
@@ -10976,6 +11618,22 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10976
11618
|
console.log("\u{1F4A1} Run /login to authenticate with your B4M account.\n");
|
|
10977
11619
|
return;
|
|
10978
11620
|
}
|
|
11621
|
+
const currentSteps = [];
|
|
11622
|
+
const stepHandler = (step) => {
|
|
11623
|
+
currentSteps.push(step);
|
|
11624
|
+
const { pendingMessages, updatePendingMessage } = useCliStore.getState();
|
|
11625
|
+
const lastIdx = pendingMessages.length - 1;
|
|
11626
|
+
if (lastIdx >= 0 && pendingMessages[lastIdx].role === "assistant") {
|
|
11627
|
+
updatePendingMessage(lastIdx, {
|
|
11628
|
+
...pendingMessages[lastIdx],
|
|
11629
|
+
metadata: {
|
|
11630
|
+
...pendingMessages[lastIdx].metadata,
|
|
11631
|
+
steps: [...currentSteps]
|
|
11632
|
+
}
|
|
11633
|
+
});
|
|
11634
|
+
}
|
|
11635
|
+
};
|
|
11636
|
+
state.agent.on("action", stepHandler);
|
|
10979
11637
|
try {
|
|
10980
11638
|
let messageContent = message;
|
|
10981
11639
|
let userMessageContent = message;
|
|
@@ -10989,6 +11647,22 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10989
11647
|
content: userMessageContent,
|
|
10990
11648
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10991
11649
|
};
|
|
11650
|
+
const pendingAssistantMessage = {
|
|
11651
|
+
role: "assistant",
|
|
11652
|
+
content: "...",
|
|
11653
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11654
|
+
metadata: {
|
|
11655
|
+
steps: []
|
|
11656
|
+
}
|
|
11657
|
+
};
|
|
11658
|
+
const sessionWithUserMessage = {
|
|
11659
|
+
...state.session,
|
|
11660
|
+
messages: [...state.session.messages, userMessage],
|
|
11661
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11662
|
+
};
|
|
11663
|
+
setState((prev) => ({ ...prev, session: sessionWithUserMessage }));
|
|
11664
|
+
setStoreSession(sessionWithUserMessage);
|
|
11665
|
+
useCliStore.getState().addPendingMessage(pendingAssistantMessage);
|
|
10992
11666
|
const recentMessages = state.session.messages.slice(-20);
|
|
10993
11667
|
const previousMessages = recentMessages.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => ({
|
|
10994
11668
|
role: msg.role,
|
|
@@ -11002,37 +11676,37 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
11002
11676
|
console.log("\n\u26A0\uFE0F Action denied by user\n");
|
|
11003
11677
|
}
|
|
11004
11678
|
const successfulToolCalls = result.steps.filter((s) => s.type === "observation").length;
|
|
11005
|
-
const
|
|
11679
|
+
const finalAssistantMessage = {
|
|
11006
11680
|
role: "assistant",
|
|
11007
11681
|
content: result.finalAnswer,
|
|
11008
|
-
timestamp:
|
|
11682
|
+
timestamp: pendingAssistantMessage.timestamp,
|
|
11009
11683
|
metadata: {
|
|
11010
11684
|
tokenUsage: {
|
|
11011
11685
|
prompt: 0,
|
|
11012
|
-
// TODO: Extract from result
|
|
11013
11686
|
completion: 0,
|
|
11014
11687
|
total: result.completionInfo.totalTokens
|
|
11015
11688
|
},
|
|
11016
11689
|
steps: result.steps,
|
|
11017
11690
|
// Complete history: thoughts, actions, observations
|
|
11018
11691
|
permissionDenied
|
|
11019
|
-
// Mark if this ended due to permission denial
|
|
11020
11692
|
}
|
|
11021
11693
|
};
|
|
11694
|
+
useCliStore.getState().completePendingMessage(0, finalAssistantMessage);
|
|
11695
|
+
const currentSession = useCliStore.getState().session;
|
|
11696
|
+
if (!currentSession) return;
|
|
11022
11697
|
const updatedSession = {
|
|
11023
|
-
...
|
|
11024
|
-
messages: [...state.session.messages, userMessage, assistantMessage],
|
|
11025
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11698
|
+
...currentSession,
|
|
11026
11699
|
metadata: {
|
|
11027
|
-
...
|
|
11028
|
-
totalTokens:
|
|
11029
|
-
toolCallCount:
|
|
11700
|
+
...currentSession.metadata,
|
|
11701
|
+
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
11702
|
+
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
|
|
11030
11703
|
}
|
|
11031
11704
|
};
|
|
11032
11705
|
setState((prev) => ({ ...prev, session: updatedSession }));
|
|
11033
11706
|
setStoreSession(updatedSession);
|
|
11034
11707
|
await state.sessionStore.save(updatedSession);
|
|
11035
11708
|
} catch (error) {
|
|
11709
|
+
useCliStore.getState().clearPendingMessages();
|
|
11036
11710
|
if (error instanceof Error) {
|
|
11037
11711
|
if (error.message.includes("Authentication failed") || error.message.includes("Authentication expired")) {
|
|
11038
11712
|
console.log("\n\u274C Authentication failed");
|
|
@@ -11041,8 +11715,50 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
11041
11715
|
}
|
|
11042
11716
|
}
|
|
11043
11717
|
console.error("Error processing message:", error);
|
|
11718
|
+
} finally {
|
|
11719
|
+
state.agent.off("action", stepHandler);
|
|
11044
11720
|
}
|
|
11045
11721
|
};
|
|
11722
|
+
const handleBashCommand = useCallback(
|
|
11723
|
+
(command) => {
|
|
11724
|
+
if (!state.session) return;
|
|
11725
|
+
let output;
|
|
11726
|
+
let isError = false;
|
|
11727
|
+
try {
|
|
11728
|
+
output = execSync(command, {
|
|
11729
|
+
encoding: "utf-8",
|
|
11730
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
11731
|
+
cwd: process.cwd(),
|
|
11732
|
+
maxBuffer: 10 * 1024 * 1024
|
|
11733
|
+
// 10MB buffer
|
|
11734
|
+
});
|
|
11735
|
+
} catch (error) {
|
|
11736
|
+
const execError = error;
|
|
11737
|
+
output = execError.stderr || execError.message || "Command failed";
|
|
11738
|
+
isError = true;
|
|
11739
|
+
}
|
|
11740
|
+
const userMessage = {
|
|
11741
|
+
role: "user",
|
|
11742
|
+
content: `$ ${command}`,
|
|
11743
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11744
|
+
};
|
|
11745
|
+
const assistantMessage = {
|
|
11746
|
+
role: "assistant",
|
|
11747
|
+
content: isError ? `\u274C Error:
|
|
11748
|
+
${output}` : output.trim() || "(no output)",
|
|
11749
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11750
|
+
};
|
|
11751
|
+
const updatedSession = {
|
|
11752
|
+
...state.session,
|
|
11753
|
+
messages: [...state.session.messages, userMessage, assistantMessage],
|
|
11754
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11755
|
+
};
|
|
11756
|
+
setState((prev) => ({ ...prev, session: updatedSession }));
|
|
11757
|
+
setStoreSession(updatedSession);
|
|
11758
|
+
state.sessionStore.save(updatedSession);
|
|
11759
|
+
},
|
|
11760
|
+
[state.session, state.sessionStore]
|
|
11761
|
+
);
|
|
11046
11762
|
const handleImageDetected = async (imageData) => {
|
|
11047
11763
|
try {
|
|
11048
11764
|
let imageStore = state.imageStore;
|
|
@@ -11143,15 +11859,45 @@ Project Configuration:
|
|
|
11143
11859
|
await state.sessionStore.save(state.session);
|
|
11144
11860
|
console.log(`\u2705 Session saved as "${sessionName}"`);
|
|
11145
11861
|
break;
|
|
11146
|
-
case "sessions":
|
|
11862
|
+
case "sessions": {
|
|
11147
11863
|
const sessions = await state.sessionStore.list();
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
console.log(
|
|
11151
|
-
|
|
11152
|
-
}
|
|
11153
|
-
|
|
11864
|
+
if (sessions.length === 0) {
|
|
11865
|
+
console.log("\n\u{1F4DA} No saved sessions found.");
|
|
11866
|
+
console.log("\u{1F4A1} Use /save <name> to save your current session.\n");
|
|
11867
|
+
break;
|
|
11868
|
+
}
|
|
11869
|
+
const handleSessionSelect = async (selectedSession) => {
|
|
11870
|
+
setState((prev) => ({ ...prev, sessionSelector: null }));
|
|
11871
|
+
if (!selectedSession) {
|
|
11872
|
+
console.log("\u274C Session selection cancelled");
|
|
11873
|
+
return;
|
|
11874
|
+
}
|
|
11875
|
+
const loadedSession = await state.sessionStore.load(selectedSession.id);
|
|
11876
|
+
if (!loadedSession) {
|
|
11877
|
+
console.log(`\u274C Failed to load session: ${selectedSession.name}`);
|
|
11878
|
+
console.log(" The session file may be corrupted or deleted.");
|
|
11879
|
+
return;
|
|
11880
|
+
}
|
|
11881
|
+
await logger.initialize(loadedSession.id);
|
|
11882
|
+
logger.debug("=== Session Resumed ===");
|
|
11883
|
+
setState((prev) => ({ ...prev, session: loadedSession }));
|
|
11884
|
+
setStoreSession(loadedSession);
|
|
11885
|
+
useCliStore.getState().clearAgentSteps();
|
|
11886
|
+
useCliStore.getState().clearPendingMessages();
|
|
11887
|
+
usageCache = null;
|
|
11888
|
+
console.log(`
|
|
11889
|
+
\u2705 Session resumed: "${loadedSession.name}"`);
|
|
11890
|
+
console.log(
|
|
11891
|
+
`\u{1F4DD} ${loadedSession.messages.length} messages | \u{1F916} ${loadedSession.model} | \u{1F4CA} ${loadedSession.metadata.totalTokens.toLocaleString()} tokens
|
|
11892
|
+
`
|
|
11893
|
+
);
|
|
11894
|
+
};
|
|
11895
|
+
setState((prev) => ({
|
|
11896
|
+
...prev,
|
|
11897
|
+
sessionSelector: { sessions, resolve: handleSessionSelect }
|
|
11898
|
+
}));
|
|
11154
11899
|
break;
|
|
11900
|
+
}
|
|
11155
11901
|
case "config": {
|
|
11156
11902
|
setShowConfigEditor(true);
|
|
11157
11903
|
break;
|
|
@@ -11373,6 +12119,7 @@ Project Configuration:
|
|
|
11373
12119
|
setState((prev) => ({ ...prev, session: newSession }));
|
|
11374
12120
|
setStoreSession(newSession);
|
|
11375
12121
|
useCliStore.getState().clearAgentSteps();
|
|
12122
|
+
useCliStore.getState().clearPendingMessages();
|
|
11376
12123
|
usageCache = null;
|
|
11377
12124
|
console.log("New session started.");
|
|
11378
12125
|
console.log(`
|
|
@@ -11425,6 +12172,7 @@ Project Configuration:
|
|
|
11425
12172
|
prefillInput: prefillContent
|
|
11426
12173
|
}));
|
|
11427
12174
|
setStoreSession(rewindedSession);
|
|
12175
|
+
useCliStore.getState().clearPendingMessages();
|
|
11428
12176
|
await state.sessionStore.save(rewindedSession);
|
|
11429
12177
|
console.log("\u2705 Conversation rewound successfully");
|
|
11430
12178
|
console.log(`\u{1F4CA} Current state: ${rewindedMessages.length} messages, ${newMetadata.totalTokens} tokens`);
|
|
@@ -11592,12 +12340,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11592
12340
|
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
11593
12341
|
};
|
|
11594
12342
|
if (initError) {
|
|
11595
|
-
return /* @__PURE__ */
|
|
12343
|
+
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
12344
|
}
|
|
11597
12345
|
if (state.trustLocationSelector) {
|
|
11598
12346
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
11599
12347
|
const inProject = projectDir !== null;
|
|
11600
|
-
return /* @__PURE__ */
|
|
12348
|
+
return /* @__PURE__ */ React17.createElement(
|
|
11601
12349
|
TrustLocationSelector,
|
|
11602
12350
|
{
|
|
11603
12351
|
inProject,
|
|
@@ -11615,7 +12363,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11615
12363
|
);
|
|
11616
12364
|
}
|
|
11617
12365
|
if (state.rewindSelector && state.session) {
|
|
11618
|
-
return /* @__PURE__ */
|
|
12366
|
+
return /* @__PURE__ */ React17.createElement(
|
|
11619
12367
|
RewindSelector,
|
|
11620
12368
|
{
|
|
11621
12369
|
messages: state.session.messages,
|
|
@@ -11632,9 +12380,28 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11632
12380
|
}
|
|
11633
12381
|
);
|
|
11634
12382
|
}
|
|
12383
|
+
if (state.sessionSelector) {
|
|
12384
|
+
return /* @__PURE__ */ React17.createElement(
|
|
12385
|
+
SessionSelector,
|
|
12386
|
+
{
|
|
12387
|
+
sessions: state.sessionSelector.sessions,
|
|
12388
|
+
currentSession: state.session,
|
|
12389
|
+
onSelect: (session) => {
|
|
12390
|
+
if (state.sessionSelector) {
|
|
12391
|
+
state.sessionSelector.resolve(session);
|
|
12392
|
+
}
|
|
12393
|
+
},
|
|
12394
|
+
onCancel: () => {
|
|
12395
|
+
if (state.sessionSelector) {
|
|
12396
|
+
state.sessionSelector.resolve(null);
|
|
12397
|
+
}
|
|
12398
|
+
}
|
|
12399
|
+
}
|
|
12400
|
+
);
|
|
12401
|
+
}
|
|
11635
12402
|
if (state.showLoginFlow) {
|
|
11636
12403
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
11637
|
-
return /* @__PURE__ */
|
|
12404
|
+
return /* @__PURE__ */ React17.createElement(
|
|
11638
12405
|
LoginFlow,
|
|
11639
12406
|
{
|
|
11640
12407
|
apiUrl: loginApiUrl,
|
|
@@ -11657,13 +12424,14 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11657
12424
|
);
|
|
11658
12425
|
}
|
|
11659
12426
|
if (!isInitialized) {
|
|
11660
|
-
return /* @__PURE__ */
|
|
12427
|
+
return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React17.createElement(Text16, null, "\u{1F680} Initializing..."));
|
|
11661
12428
|
}
|
|
11662
|
-
return /* @__PURE__ */
|
|
12429
|
+
return /* @__PURE__ */ React17.createElement(
|
|
11663
12430
|
App,
|
|
11664
12431
|
{
|
|
11665
12432
|
onMessage: handleMessage,
|
|
11666
12433
|
onCommand: handleCommand,
|
|
12434
|
+
onBashCommand: handleBashCommand,
|
|
11667
12435
|
onImageDetected: handleImageDetected,
|
|
11668
12436
|
commandHistory,
|
|
11669
12437
|
config: state.config,
|
|
@@ -11692,12 +12460,12 @@ var BANNER = `
|
|
|
11692
12460
|
\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
11693
12461
|
`;
|
|
11694
12462
|
console.log("\x1B[36m\x1B[1m%s\x1B[0m", BANNER);
|
|
11695
|
-
console.log(`\x1B[2m v${package_default.version} - AI-Powered CLI\x1B[0m
|
|
11696
|
-
|
|
12463
|
+
console.log(`\x1B[2m v${package_default.version} - AI-Powered CLI\x1B[0m`);
|
|
12464
|
+
console.log(" /help for more information\n");
|
|
11697
12465
|
var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "development";
|
|
11698
12466
|
if (isDevMode) {
|
|
11699
|
-
|
|
12467
|
+
logger.debug("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
11700
12468
|
}
|
|
11701
|
-
render(/* @__PURE__ */
|
|
12469
|
+
render(/* @__PURE__ */ React17.createElement(CliApp, null), {
|
|
11702
12470
|
exitOnCtrlC: false
|
|
11703
12471
|
});
|