@bike4mind/cli 0.2.7-ja-fix-confluence-table-data-5752.17142 → 0.2.8-cli-bash-tools.17146
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 +1 -0
- package/dist/{artifactExtractor-6IAPZV52.js → artifactExtractor-LF5DAVZL.js} +1 -1
- package/dist/{chunk-GPTQ7T7Y.js → chunk-6S7HV2GF.js} +2 -2
- package/dist/{chunk-6ATNPERY.js → chunk-F2K7OCDJ.js} +2 -2
- package/dist/{chunk-F2LBB4TN.js → chunk-HD4QQGS6.js} +33 -116
- package/dist/{chunk-OK3T7VKG.js → chunk-RJYG7BUV.js} +2 -2
- package/dist/{chunk-KPBWTYME.js → chunk-SE2T5GED.js} +1 -1
- package/dist/{create-OF43XXQD.js → create-S7N2FBWR.js} +3 -3
- package/dist/index.js +1163 -466
- package/dist/{llmMarkdownGenerator-FDUGMOSN.js → llmMarkdownGenerator-VY7FSNOI.js} +1 -1
- package/dist/{markdownGenerator-LX7EKZVE.js → markdownGenerator-KES2TDKF.js} +1 -1
- package/dist/{mementoService-IVQOF6B3.js → mementoService-S4OOMZIT.js} +3 -3
- package/dist/{src-5AUILBQJ.js → src-7PSZ4VA4.js} +2 -2
- package/dist/{src-AGEG6AP6.js → src-VFRCY2I4.js} +3 -7
- package/dist/{subtractCredits-647ODWTM.js → subtractCredits-DD7PBL22.js} +3 -3
- package/package.json +6 -6
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-6S7HV2GF.js";
|
|
8
|
+
import "./chunk-F2K7OCDJ.js";
|
|
9
|
+
import "./chunk-RJYG7BUV.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-SE2T5GED.js";
|
|
19
19
|
import {
|
|
20
20
|
Logger
|
|
21
21
|
} from "./chunk-AMDXHL6S.js";
|
|
@@ -73,19 +73,19 @@ import {
|
|
|
73
73
|
XAI_IMAGE_MODELS,
|
|
74
74
|
b4mLLMTools,
|
|
75
75
|
getMcpProviderMetadata
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-HD4QQGS6.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 React14, { useState as useState5, useEffect as useEffect3, useCallback, useRef } from "react";
|
|
83
|
+
import { render, Box as Box14, Text as Text13, useApp, useInput as useInput4 } from "ink";
|
|
84
84
|
import { v4 as uuidv49 } from "uuid";
|
|
85
85
|
|
|
86
86
|
// src/components/App.tsx
|
|
87
87
|
import React9 from "react";
|
|
88
|
-
import { Box as Box9, Text as Text8
|
|
88
|
+
import { Box as Box9, Text as Text8 } from "ink";
|
|
89
89
|
|
|
90
90
|
// src/components/StatusBar.tsx
|
|
91
91
|
import React from "react";
|
|
@@ -389,6 +389,11 @@ var COMMANDS = [
|
|
|
389
389
|
name: "clear",
|
|
390
390
|
description: "Start a new session",
|
|
391
391
|
aliases: ["new"]
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: "rewind",
|
|
395
|
+
description: "Rewind conversation to a previous point",
|
|
396
|
+
aliases: ["undo"]
|
|
392
397
|
}
|
|
393
398
|
];
|
|
394
399
|
|
|
@@ -625,12 +630,26 @@ function findAtTrigger(value) {
|
|
|
625
630
|
}
|
|
626
631
|
return null;
|
|
627
632
|
}
|
|
628
|
-
function InputPrompt({
|
|
633
|
+
function InputPrompt({
|
|
634
|
+
onSubmit,
|
|
635
|
+
onImageDetected,
|
|
636
|
+
disabled = false,
|
|
637
|
+
history = [],
|
|
638
|
+
prefillInput,
|
|
639
|
+
onPrefillConsumed
|
|
640
|
+
}) {
|
|
629
641
|
const [value, setValue] = useState("");
|
|
630
642
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
631
643
|
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
632
644
|
const [tempInput, setTempInput] = useState("");
|
|
633
645
|
const [inputKey, setInputKey] = useState(0);
|
|
646
|
+
useEffect(() => {
|
|
647
|
+
if (prefillInput) {
|
|
648
|
+
setValue(prefillInput);
|
|
649
|
+
setInputKey((prev) => prev + 1);
|
|
650
|
+
onPrefillConsumed?.();
|
|
651
|
+
}
|
|
652
|
+
}, [prefillInput, onPrefillConsumed]);
|
|
634
653
|
const [fileAutocomplete, setFileAutocomplete] = useState(null);
|
|
635
654
|
const [fileSelectedIndex, setFileSelectedIndex] = useState(0);
|
|
636
655
|
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
|
|
@@ -779,7 +798,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
779
798
|
value,
|
|
780
799
|
onChange: handleChange,
|
|
781
800
|
onSubmit: handleSubmit,
|
|
782
|
-
placeholder: disabled ? "
|
|
801
|
+
placeholder: disabled ? "" : "Type your message, /help for commands, or @file to reference",
|
|
783
802
|
showCursor: !disabled
|
|
784
803
|
}
|
|
785
804
|
)), shouldShowCommandAutocomplete && /* @__PURE__ */ React4.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React4.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
|
|
@@ -810,6 +829,7 @@ var useCliStore = create((set) => ({
|
|
|
810
829
|
addAgentStep: (step) => set((state) => ({
|
|
811
830
|
agentSteps: [...state.agentSteps, step]
|
|
812
831
|
})),
|
|
832
|
+
setAgentSteps: (steps) => set({ agentSteps: steps }),
|
|
813
833
|
clearAgentSteps: () => set({ agentSteps: [] }),
|
|
814
834
|
// Permission prompt
|
|
815
835
|
permissionPrompt: null,
|
|
@@ -829,7 +849,7 @@ var AgentThinking = React6.memo(function AgentThinking2() {
|
|
|
829
849
|
if (!isThinking) {
|
|
830
850
|
return null;
|
|
831
851
|
}
|
|
832
|
-
return /* @__PURE__ */ React6.createElement(Box6, {
|
|
852
|
+
return /* @__PURE__ */ React6.createElement(Box6, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React6.createElement(ThoughtStream, { steps: agentSteps }));
|
|
833
853
|
});
|
|
834
854
|
|
|
835
855
|
// src/components/PermissionPrompt.tsx
|
|
@@ -1224,7 +1244,9 @@ function App({
|
|
|
1224
1244
|
commandHistory = [],
|
|
1225
1245
|
config,
|
|
1226
1246
|
availableModels = [],
|
|
1227
|
-
onSaveConfig
|
|
1247
|
+
onSaveConfig,
|
|
1248
|
+
prefillInput,
|
|
1249
|
+
onPrefillConsumed
|
|
1228
1250
|
}) {
|
|
1229
1251
|
const messages = useCliStore((state) => state.session?.messages || []);
|
|
1230
1252
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
@@ -1282,18 +1304,21 @@ ${errorBlock}`;
|
|
|
1282
1304
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
1283
1305
|
onResponse: onPermissionResponse
|
|
1284
1306
|
}
|
|
1285
|
-
)) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
|
|
1307
|
+
)) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, messages.map((message, index) => /* @__PURE__ */ React9.createElement(Box9, { key: `${message.timestamp}-${message.role}-${index}`, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(MessageItem, { message })))), /* @__PURE__ */ React9.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React9.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React9.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
|
|
1286
1308
|
InputPrompt,
|
|
1287
1309
|
{
|
|
1288
1310
|
onSubmit: handleSubmit,
|
|
1289
1311
|
onImageDetected,
|
|
1290
1312
|
disabled: isThinking,
|
|
1291
|
-
history: commandHistory
|
|
1313
|
+
history: commandHistory,
|
|
1314
|
+
prefillInput,
|
|
1315
|
+
onPrefillConsumed
|
|
1292
1316
|
}
|
|
1293
1317
|
))));
|
|
1294
1318
|
}
|
|
1295
1319
|
function MessageItem({ message }) {
|
|
1296
|
-
|
|
1320
|
+
const isUser = message.role === "user";
|
|
1321
|
+
return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: isUser ? "cyan" : "green" }, isUser ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { 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__ */ React9.createElement(Text8, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1297
1322
|
if (step.type === "thought") {
|
|
1298
1323
|
return /* @__PURE__ */ React9.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
|
|
1299
1324
|
}
|
|
@@ -1305,7 +1330,7 @@ function MessageItem({ message }) {
|
|
|
1305
1330
|
return /* @__PURE__ */ React9.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React9.createElement(Text8, { 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 ? "..." : ""}`));
|
|
1306
1331
|
}
|
|
1307
1332
|
return null;
|
|
1308
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
1333
|
+
}).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2, marginTop: message.metadata?.steps?.length ? 1 : 0 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React9.createElement(Text8, null, message.content)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
1309
1334
|
}
|
|
1310
1335
|
|
|
1311
1336
|
// src/components/MessageList.tsx
|
|
@@ -1380,9 +1405,78 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
1380
1405
|
), /* @__PURE__ */ React11.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Ctrl+C to cancel")));
|
|
1381
1406
|
}
|
|
1382
1407
|
|
|
1408
|
+
// src/components/RewindSelector.tsx
|
|
1409
|
+
import React12, { useState as useState3 } from "react";
|
|
1410
|
+
import { Box as Box12, Text as Text11, useInput as useInput3 } from "ink";
|
|
1411
|
+
import SelectInput3 from "ink-select-input";
|
|
1412
|
+
function RewindSelector({ messages, onSelect, onCancel }) {
|
|
1413
|
+
const [step, setStep] = useState3("selection");
|
|
1414
|
+
const [selectedMessageIndex, setSelectedMessageIndex] = useState3(null);
|
|
1415
|
+
useInput3((input, key) => {
|
|
1416
|
+
if (key.escape) {
|
|
1417
|
+
if (step === "confirmation") {
|
|
1418
|
+
setStep("selection");
|
|
1419
|
+
setSelectedMessageIndex(null);
|
|
1420
|
+
} else {
|
|
1421
|
+
onCancel();
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
});
|
|
1425
|
+
const userMessages = messages.map((msg, idx) => ({ msg, originalIndex: idx })).filter(({ msg }) => msg.role === "user");
|
|
1426
|
+
const items = userMessages.map(({ msg, originalIndex }, index) => {
|
|
1427
|
+
const preview = msg.content.length > 50 ? msg.content.substring(0, 50) + "..." : msg.content;
|
|
1428
|
+
const timestamp = new Date(msg.timestamp).toLocaleString();
|
|
1429
|
+
const position = index + 1;
|
|
1430
|
+
return {
|
|
1431
|
+
label: `[${position}] ${timestamp} - ${preview}`,
|
|
1432
|
+
value: originalIndex
|
|
1433
|
+
};
|
|
1434
|
+
});
|
|
1435
|
+
const handleSelectionSelect = (item) => {
|
|
1436
|
+
setSelectedMessageIndex(item.value);
|
|
1437
|
+
setStep("confirmation");
|
|
1438
|
+
};
|
|
1439
|
+
const handleConfirmationSelect = (item) => {
|
|
1440
|
+
if (item.value === "confirm" && selectedMessageIndex !== null) {
|
|
1441
|
+
onSelect(selectedMessageIndex);
|
|
1442
|
+
} else {
|
|
1443
|
+
setStep("selection");
|
|
1444
|
+
setSelectedMessageIndex(null);
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
const messagesToRemove = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages.slice(selectedMessageIndex) : [];
|
|
1448
|
+
const tokensToRemove = messagesToRemove.reduce((sum2, msg) => {
|
|
1449
|
+
return sum2 + (msg.metadata?.tokenUsage?.total || 0);
|
|
1450
|
+
}, 0);
|
|
1451
|
+
if (step === "selection") {
|
|
1452
|
+
return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Select a point to rewind to:")), /* @__PURE__ */ React12.createElement(
|
|
1453
|
+
SelectInput3,
|
|
1454
|
+
{
|
|
1455
|
+
items,
|
|
1456
|
+
onSelect: handleSelectionSelect,
|
|
1457
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1458
|
+
}
|
|
1459
|
+
), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
1460
|
+
}
|
|
1461
|
+
const confirmationItems = [
|
|
1462
|
+
{ label: "Yes, rewind to this point", value: "confirm" },
|
|
1463
|
+
{ label: "No, go back to selection", value: "cancel" }
|
|
1464
|
+
];
|
|
1465
|
+
const selectedMessage = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages[selectedMessageIndex] : null;
|
|
1466
|
+
const selectedPreview = selectedMessage ? selectedMessage.content.length > 50 ? selectedMessage.content.substring(0, 50) + "..." : selectedMessage.content : "";
|
|
1467
|
+
return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Confirm rewind operation:"), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Message: ", selectedPreview), /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "This will remove ", messagesToRemove.length, " message(s) (", tokensToRemove.toLocaleString(), " tokens)"), /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, "The selected message will be placed in your input for editing.")), /* @__PURE__ */ React12.createElement(
|
|
1468
|
+
SelectInput3,
|
|
1469
|
+
{
|
|
1470
|
+
items: confirmationItems,
|
|
1471
|
+
onSelect: handleConfirmationSelect,
|
|
1472
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1473
|
+
}
|
|
1474
|
+
), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1383
1477
|
// src/components/LoginFlow.tsx
|
|
1384
|
-
import
|
|
1385
|
-
import { Box as
|
|
1478
|
+
import React13, { useState as useState4, useEffect as useEffect2 } from "react";
|
|
1479
|
+
import { Box as Box13, Text as Text12 } from "ink";
|
|
1386
1480
|
import Spinner2 from "ink-spinner";
|
|
1387
1481
|
import jwt from "jsonwebtoken";
|
|
1388
1482
|
import open from "open";
|
|
@@ -1498,10 +1592,10 @@ var OAuthClient = class {
|
|
|
1498
1592
|
|
|
1499
1593
|
// src/components/LoginFlow.tsx
|
|
1500
1594
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
1501
|
-
const [status, setStatus] =
|
|
1502
|
-
const [deviceFlow, setDeviceFlow] =
|
|
1503
|
-
const [statusMessage, setStatusMessage] =
|
|
1504
|
-
const [error, setError] =
|
|
1595
|
+
const [status, setStatus] = useState4("initiating");
|
|
1596
|
+
const [deviceFlow, setDeviceFlow] = useState4(null);
|
|
1597
|
+
const [statusMessage, setStatusMessage] = useState4("Initiating device authorization...");
|
|
1598
|
+
const [error, setError] = useState4(null);
|
|
1505
1599
|
useEffect2(() => {
|
|
1506
1600
|
const runLoginFlow = async () => {
|
|
1507
1601
|
const oauth = new OAuthClient(apiUrl);
|
|
@@ -1545,15 +1639,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
1545
1639
|
}
|
|
1546
1640
|
}, [deviceFlow, status]);
|
|
1547
1641
|
if (status === "initiating") {
|
|
1548
|
-
return /* @__PURE__ */
|
|
1642
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box13, null, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
|
|
1549
1643
|
}
|
|
1550
1644
|
if (status === "error") {
|
|
1551
|
-
return /* @__PURE__ */
|
|
1645
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React13.createElement(Box13, null, /* @__PURE__ */ React13.createElement(Text12, { color: "red" }, error)));
|
|
1552
1646
|
}
|
|
1553
1647
|
if (status === "success") {
|
|
1554
|
-
return /* @__PURE__ */
|
|
1648
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React13.createElement(Box13, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "You can now use B4M CLI with your account.")));
|
|
1555
1649
|
}
|
|
1556
|
-
return /* @__PURE__ */
|
|
1650
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "And enter this code when prompted:")), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
|
|
1557
1651
|
}
|
|
1558
1652
|
|
|
1559
1653
|
// src/storage/SessionStore.ts
|
|
@@ -2439,10 +2533,9 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
2439
2533
|
content: query
|
|
2440
2534
|
}
|
|
2441
2535
|
];
|
|
2442
|
-
console.log("[DEBUG ReActAgent] Messages array being sent to LLM:");
|
|
2443
2536
|
messages.forEach((msg, i) => {
|
|
2444
2537
|
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
2445
|
-
|
|
2538
|
+
this.context.logger.debug(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
|
|
2446
2539
|
});
|
|
2447
2540
|
let finalAnswer = "";
|
|
2448
2541
|
let reachedMaxIterations = false;
|
|
@@ -4835,8 +4928,8 @@ async function processAndStoreImages(images, context) {
|
|
|
4835
4928
|
const buffer = await downloadImage(image);
|
|
4836
4929
|
const fileType = await fileTypeFromBuffer2(buffer);
|
|
4837
4930
|
const filename = `${uuidv45()}.${fileType?.ext}`;
|
|
4838
|
-
const
|
|
4839
|
-
return
|
|
4931
|
+
const path14 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
4932
|
+
return path14;
|
|
4840
4933
|
}));
|
|
4841
4934
|
}
|
|
4842
4935
|
async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
|
|
@@ -6048,8 +6141,8 @@ async function processAndStoreImage(imageUrl, context) {
|
|
|
6048
6141
|
const buffer = await downloadImage2(imageUrl);
|
|
6049
6142
|
const fileType = await fileTypeFromBuffer3(buffer);
|
|
6050
6143
|
const filename = `${uuidv46()}.${fileType?.ext}`;
|
|
6051
|
-
const
|
|
6052
|
-
return
|
|
6144
|
+
const path14 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
6145
|
+
return path14;
|
|
6053
6146
|
}
|
|
6054
6147
|
async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
|
|
6055
6148
|
await context.onFinish?.("edit_image", storedImagePath);
|
|
@@ -8461,6 +8554,381 @@ var deleteFileTool = {
|
|
|
8461
8554
|
})
|
|
8462
8555
|
};
|
|
8463
8556
|
|
|
8557
|
+
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
|
|
8558
|
+
import { spawn } from "child_process";
|
|
8559
|
+
import path11 from "path";
|
|
8560
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
8561
|
+
var MAX_OUTPUT_SIZE = 100 * 1024;
|
|
8562
|
+
var DANGEROUS_PATTERNS = [
|
|
8563
|
+
// Destructive file operations
|
|
8564
|
+
{
|
|
8565
|
+
pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*r).*\//i,
|
|
8566
|
+
reason: "Recursive delete with path",
|
|
8567
|
+
block: true
|
|
8568
|
+
},
|
|
8569
|
+
{
|
|
8570
|
+
pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*f).*--no-preserve-root/i,
|
|
8571
|
+
reason: "Force delete without preserve root",
|
|
8572
|
+
block: true
|
|
8573
|
+
},
|
|
8574
|
+
{
|
|
8575
|
+
pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\//i,
|
|
8576
|
+
reason: "Recursive force delete on root paths",
|
|
8577
|
+
block: true
|
|
8578
|
+
},
|
|
8579
|
+
{
|
|
8580
|
+
pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s+\//i,
|
|
8581
|
+
reason: "Force recursive delete on root paths",
|
|
8582
|
+
block: true
|
|
8583
|
+
},
|
|
8584
|
+
// System-level dangerous commands
|
|
8585
|
+
{ pattern: /\bsudo\b/i, reason: "Elevated privileges (sudo)", block: true },
|
|
8586
|
+
{ pattern: /\bsu\s+(-|root)/i, reason: "Switch to root user", block: true },
|
|
8587
|
+
{ pattern: /\bchmod\s+777\b/i, reason: "Overly permissive chmod", block: false },
|
|
8588
|
+
{ pattern: /\bchown\s+-R\s+root/i, reason: "Recursive chown to root", block: true },
|
|
8589
|
+
// Disk/partition operations
|
|
8590
|
+
{ pattern: /\bmkfs\b/i, reason: "Filesystem creation", block: true },
|
|
8591
|
+
{ pattern: /\bfdisk\b/i, reason: "Disk partitioning", block: true },
|
|
8592
|
+
{ pattern: /\bdd\s+.*of=\/dev\//i, reason: "Direct disk write", block: true },
|
|
8593
|
+
// Network attacks
|
|
8594
|
+
{ pattern: /\b(nc|netcat)\s+.*-e\s+\/bin\/(ba)?sh/i, reason: "Reverse shell attempt", block: true },
|
|
8595
|
+
{ pattern: /\bcurl\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
|
|
8596
|
+
{ pattern: /\bwget\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
|
|
8597
|
+
// Fork bombs and resource exhaustion
|
|
8598
|
+
{ pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;/i, reason: "Fork bomb", block: true },
|
|
8599
|
+
{ pattern: /\bwhile\s+true.*do.*done.*&/i, reason: "Infinite loop in background", block: false },
|
|
8600
|
+
// Credential/sensitive data access
|
|
8601
|
+
{ pattern: /\/etc\/shadow/i, reason: "Access to shadow file", block: true },
|
|
8602
|
+
{ pattern: /\/etc\/passwd.*>/i, reason: "Modifying passwd file", block: true },
|
|
8603
|
+
{ pattern: /\baws\s+.*--profile\s+/i, reason: "AWS profile access", block: false },
|
|
8604
|
+
// History/log manipulation
|
|
8605
|
+
{ pattern: /\bhistory\s+-c\b/i, reason: "Clearing shell history", block: false },
|
|
8606
|
+
{ pattern: />\s*\/var\/log\//i, reason: "Overwriting system logs", block: true },
|
|
8607
|
+
// Dangerous redirects
|
|
8608
|
+
{ pattern: />\s*\/dev\/sda/i, reason: "Writing to block device", block: true },
|
|
8609
|
+
{ pattern: />\s*\/dev\/null.*2>&1.*</i, reason: "Potentially hiding output", block: false },
|
|
8610
|
+
// Environment manipulation
|
|
8611
|
+
{ pattern: /\bexport\s+PATH\s*=\s*[^$]/i, reason: "Overwriting PATH", block: false },
|
|
8612
|
+
{ pattern: /\bexport\s+LD_PRELOAD/i, reason: "LD_PRELOAD manipulation", block: true },
|
|
8613
|
+
// Process/system control
|
|
8614
|
+
{ pattern: /\bkill\s+-9\s+(-1|1)\b/i, reason: "Killing all processes", block: true },
|
|
8615
|
+
{ pattern: /\bkillall\s+-9\b/i, reason: "Force killing processes", block: false },
|
|
8616
|
+
{ pattern: /\bshutdown\b/i, reason: "System shutdown", block: true },
|
|
8617
|
+
{ pattern: /\breboot\b/i, reason: "System reboot", block: true },
|
|
8618
|
+
{ pattern: /\binit\s+[06]\b/i, reason: "System runlevel change", block: true }
|
|
8619
|
+
];
|
|
8620
|
+
var SAFE_COMMAND_PREFIXES = [
|
|
8621
|
+
"ls",
|
|
8622
|
+
"cat",
|
|
8623
|
+
"head",
|
|
8624
|
+
"tail",
|
|
8625
|
+
"grep",
|
|
8626
|
+
"find",
|
|
8627
|
+
"echo",
|
|
8628
|
+
"pwd",
|
|
8629
|
+
"whoami",
|
|
8630
|
+
"date",
|
|
8631
|
+
"cal",
|
|
8632
|
+
"wc",
|
|
8633
|
+
"sort",
|
|
8634
|
+
"uniq",
|
|
8635
|
+
"cut",
|
|
8636
|
+
"tr",
|
|
8637
|
+
"sed",
|
|
8638
|
+
"awk",
|
|
8639
|
+
"git",
|
|
8640
|
+
"npm",
|
|
8641
|
+
"pnpm",
|
|
8642
|
+
"yarn",
|
|
8643
|
+
"node",
|
|
8644
|
+
"npx",
|
|
8645
|
+
"tsx",
|
|
8646
|
+
"ts-node",
|
|
8647
|
+
"python",
|
|
8648
|
+
"python3",
|
|
8649
|
+
"pip",
|
|
8650
|
+
"pip3",
|
|
8651
|
+
"docker",
|
|
8652
|
+
"docker-compose",
|
|
8653
|
+
"curl",
|
|
8654
|
+
"wget",
|
|
8655
|
+
// These are safe for fetching, blocked when piped to shell
|
|
8656
|
+
"mkdir",
|
|
8657
|
+
"touch",
|
|
8658
|
+
"cp",
|
|
8659
|
+
"mv",
|
|
8660
|
+
// File operations (still require permission)
|
|
8661
|
+
"which",
|
|
8662
|
+
"whereis",
|
|
8663
|
+
"type",
|
|
8664
|
+
"file",
|
|
8665
|
+
"stat",
|
|
8666
|
+
"env",
|
|
8667
|
+
"printenv",
|
|
8668
|
+
"set",
|
|
8669
|
+
"man",
|
|
8670
|
+
"help",
|
|
8671
|
+
"info",
|
|
8672
|
+
"diff",
|
|
8673
|
+
"comm",
|
|
8674
|
+
"cmp",
|
|
8675
|
+
"tar",
|
|
8676
|
+
"zip",
|
|
8677
|
+
"unzip",
|
|
8678
|
+
"gzip",
|
|
8679
|
+
"gunzip",
|
|
8680
|
+
"ssh",
|
|
8681
|
+
"scp",
|
|
8682
|
+
"rsync",
|
|
8683
|
+
// Network tools
|
|
8684
|
+
"make",
|
|
8685
|
+
"cmake",
|
|
8686
|
+
"cargo",
|
|
8687
|
+
"go",
|
|
8688
|
+
"rustc",
|
|
8689
|
+
"gcc",
|
|
8690
|
+
"g++",
|
|
8691
|
+
// Build tools
|
|
8692
|
+
"jest",
|
|
8693
|
+
"vitest",
|
|
8694
|
+
"mocha",
|
|
8695
|
+
"pytest",
|
|
8696
|
+
// Test runners
|
|
8697
|
+
"eslint",
|
|
8698
|
+
"prettier",
|
|
8699
|
+
"tsc"
|
|
8700
|
+
// Linters/formatters
|
|
8701
|
+
];
|
|
8702
|
+
function checkDangerousPatterns(command) {
|
|
8703
|
+
for (const { pattern, reason, block } of DANGEROUS_PATTERNS) {
|
|
8704
|
+
if (pattern.test(command)) {
|
|
8705
|
+
return { blocked: block, reason };
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8708
|
+
return { blocked: false };
|
|
8709
|
+
}
|
|
8710
|
+
function getBaseCommand(command) {
|
|
8711
|
+
const trimmed = command.trim();
|
|
8712
|
+
const match = trimmed.match(/^(\S+)/);
|
|
8713
|
+
return match ? match[1] : "";
|
|
8714
|
+
}
|
|
8715
|
+
function isSafeCommandPrefix(command) {
|
|
8716
|
+
const baseCommand = getBaseCommand(command);
|
|
8717
|
+
return SAFE_COMMAND_PREFIXES.some((safe) => baseCommand === safe || baseCommand.endsWith(`/${safe}`));
|
|
8718
|
+
}
|
|
8719
|
+
async function executeBashCommand(params) {
|
|
8720
|
+
const { command, cwd: relativeCwd, timeout = DEFAULT_TIMEOUT_MS } = params;
|
|
8721
|
+
if (!command || command.trim().length === 0) {
|
|
8722
|
+
return {
|
|
8723
|
+
stdout: "",
|
|
8724
|
+
stderr: "Error: Command cannot be empty",
|
|
8725
|
+
exitCode: 1,
|
|
8726
|
+
timedOut: false,
|
|
8727
|
+
blocked: true,
|
|
8728
|
+
blockedReason: "Empty command"
|
|
8729
|
+
};
|
|
8730
|
+
}
|
|
8731
|
+
const dangerCheck = checkDangerousPatterns(command);
|
|
8732
|
+
if (dangerCheck.blocked) {
|
|
8733
|
+
return {
|
|
8734
|
+
stdout: "",
|
|
8735
|
+
stderr: `Command blocked for safety: ${dangerCheck.reason}`,
|
|
8736
|
+
exitCode: 1,
|
|
8737
|
+
timedOut: false,
|
|
8738
|
+
blocked: true,
|
|
8739
|
+
blockedReason: dangerCheck.reason
|
|
8740
|
+
};
|
|
8741
|
+
}
|
|
8742
|
+
const baseCwd = process.cwd();
|
|
8743
|
+
const targetCwd = relativeCwd ? path11.resolve(baseCwd, relativeCwd) : baseCwd;
|
|
8744
|
+
const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
|
|
8745
|
+
return new Promise((resolve3) => {
|
|
8746
|
+
let stdout = "";
|
|
8747
|
+
let stderr = "";
|
|
8748
|
+
let timedOut = false;
|
|
8749
|
+
const proc = spawn("bash", ["-c", command], {
|
|
8750
|
+
cwd: targetCwd,
|
|
8751
|
+
env: {
|
|
8752
|
+
...process.env,
|
|
8753
|
+
// Prevent color codes that might be hard to read
|
|
8754
|
+
NO_COLOR: "1",
|
|
8755
|
+
FORCE_COLOR: "0"
|
|
8756
|
+
},
|
|
8757
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
8758
|
+
});
|
|
8759
|
+
const timeoutId = setTimeout(() => {
|
|
8760
|
+
timedOut = true;
|
|
8761
|
+
proc.kill("SIGTERM");
|
|
8762
|
+
setTimeout(() => {
|
|
8763
|
+
if (!proc.killed) {
|
|
8764
|
+
proc.kill("SIGKILL");
|
|
8765
|
+
}
|
|
8766
|
+
}, 5e3);
|
|
8767
|
+
}, effectiveTimeout);
|
|
8768
|
+
proc.stdout.on("data", (data) => {
|
|
8769
|
+
if (stdout.length < MAX_OUTPUT_SIZE) {
|
|
8770
|
+
stdout += data.toString();
|
|
8771
|
+
if (stdout.length > MAX_OUTPUT_SIZE) {
|
|
8772
|
+
stdout = stdout.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
|
|
8773
|
+
}
|
|
8774
|
+
}
|
|
8775
|
+
});
|
|
8776
|
+
proc.stderr.on("data", (data) => {
|
|
8777
|
+
if (stderr.length < MAX_OUTPUT_SIZE) {
|
|
8778
|
+
stderr += data.toString();
|
|
8779
|
+
if (stderr.length > MAX_OUTPUT_SIZE) {
|
|
8780
|
+
stderr = stderr.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
|
|
8781
|
+
}
|
|
8782
|
+
}
|
|
8783
|
+
});
|
|
8784
|
+
proc.on("close", (exitCode) => {
|
|
8785
|
+
clearTimeout(timeoutId);
|
|
8786
|
+
resolve3({
|
|
8787
|
+
stdout: stdout.trim(),
|
|
8788
|
+
stderr: stderr.trim(),
|
|
8789
|
+
exitCode,
|
|
8790
|
+
timedOut,
|
|
8791
|
+
blocked: false
|
|
8792
|
+
});
|
|
8793
|
+
});
|
|
8794
|
+
proc.on("error", (error) => {
|
|
8795
|
+
clearTimeout(timeoutId);
|
|
8796
|
+
resolve3({
|
|
8797
|
+
stdout: "",
|
|
8798
|
+
stderr: `Failed to execute command: ${error.message}`,
|
|
8799
|
+
exitCode: 1,
|
|
8800
|
+
timedOut: false,
|
|
8801
|
+
blocked: false
|
|
8802
|
+
});
|
|
8803
|
+
});
|
|
8804
|
+
});
|
|
8805
|
+
}
|
|
8806
|
+
function formatResult(result, command) {
|
|
8807
|
+
const parts = [];
|
|
8808
|
+
parts.push(`$ ${command}`);
|
|
8809
|
+
parts.push("");
|
|
8810
|
+
if (result.blocked) {
|
|
8811
|
+
parts.push(`BLOCKED: ${result.blockedReason}`);
|
|
8812
|
+
parts.push("");
|
|
8813
|
+
parts.push("This command was blocked for safety reasons.");
|
|
8814
|
+
parts.push("If you believe this is a false positive, please run the command manually.");
|
|
8815
|
+
return parts.join("\n");
|
|
8816
|
+
}
|
|
8817
|
+
if (result.timedOut) {
|
|
8818
|
+
parts.push("WARNING: Command timed out and was terminated.");
|
|
8819
|
+
parts.push("");
|
|
8820
|
+
}
|
|
8821
|
+
if (result.stdout) {
|
|
8822
|
+
parts.push(result.stdout);
|
|
8823
|
+
}
|
|
8824
|
+
if (result.stderr) {
|
|
8825
|
+
if (result.stdout) {
|
|
8826
|
+
parts.push("");
|
|
8827
|
+
}
|
|
8828
|
+
parts.push("STDERR:");
|
|
8829
|
+
parts.push(result.stderr);
|
|
8830
|
+
}
|
|
8831
|
+
if (result.exitCode !== 0 && result.exitCode !== null) {
|
|
8832
|
+
parts.push("");
|
|
8833
|
+
parts.push(`Exit code: ${result.exitCode}`);
|
|
8834
|
+
}
|
|
8835
|
+
if (!result.stdout && !result.stderr && !result.timedOut) {
|
|
8836
|
+
parts.push("(command completed with no output)");
|
|
8837
|
+
}
|
|
8838
|
+
return parts.join("\n");
|
|
8839
|
+
}
|
|
8840
|
+
var bashExecuteTool = {
|
|
8841
|
+
name: "bash_execute",
|
|
8842
|
+
implementation: (context) => ({
|
|
8843
|
+
toolFn: async (value) => {
|
|
8844
|
+
const params = value;
|
|
8845
|
+
const isSafe = isSafeCommandPrefix(params.command);
|
|
8846
|
+
context.logger.info("Bash: Executing command", {
|
|
8847
|
+
command: params.command,
|
|
8848
|
+
cwd: params.cwd || ".",
|
|
8849
|
+
timeout: params.timeout || DEFAULT_TIMEOUT_MS,
|
|
8850
|
+
isSafeCommand: isSafe
|
|
8851
|
+
});
|
|
8852
|
+
if (context.onStart) {
|
|
8853
|
+
await context.onStart("bash_execute", {
|
|
8854
|
+
command: params.command,
|
|
8855
|
+
cwd: params.cwd
|
|
8856
|
+
});
|
|
8857
|
+
}
|
|
8858
|
+
try {
|
|
8859
|
+
const result = await executeBashCommand(params);
|
|
8860
|
+
const formattedResult = formatResult(result, params.command);
|
|
8861
|
+
context.logger.info("Bash: Command completed", {
|
|
8862
|
+
exitCode: result.exitCode,
|
|
8863
|
+
timedOut: result.timedOut,
|
|
8864
|
+
blocked: result.blocked
|
|
8865
|
+
});
|
|
8866
|
+
if (context.onFinish) {
|
|
8867
|
+
await context.onFinish("bash_execute", {
|
|
8868
|
+
command: params.command,
|
|
8869
|
+
exitCode: result.exitCode,
|
|
8870
|
+
blocked: result.blocked
|
|
8871
|
+
});
|
|
8872
|
+
}
|
|
8873
|
+
return formattedResult;
|
|
8874
|
+
} catch (error) {
|
|
8875
|
+
context.logger.error("Bash: Command failed", error);
|
|
8876
|
+
if (context.onFinish) {
|
|
8877
|
+
await context.onFinish("bash_execute", {
|
|
8878
|
+
command: params.command,
|
|
8879
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8880
|
+
});
|
|
8881
|
+
}
|
|
8882
|
+
throw error;
|
|
8883
|
+
}
|
|
8884
|
+
},
|
|
8885
|
+
toolSchema: {
|
|
8886
|
+
name: "bash_execute",
|
|
8887
|
+
description: `Execute a bash command in the terminal. Use this for running shell commands, scripts, build tools, git operations, and other CLI tasks.
|
|
8888
|
+
|
|
8889
|
+
SAFETY NOTES:
|
|
8890
|
+
- Commands are executed in the current working directory (or specified cwd)
|
|
8891
|
+
- Dangerous commands (sudo, rm -rf /, etc.) are automatically blocked
|
|
8892
|
+
- Commands have a default timeout of 60 seconds (max 5 minutes)
|
|
8893
|
+
- Output is limited to prevent overwhelming responses
|
|
8894
|
+
- This tool ALWAYS requires user permission before execution
|
|
8895
|
+
|
|
8896
|
+
COMMON USE CASES:
|
|
8897
|
+
- Running build commands: npm run build, make, cargo build
|
|
8898
|
+
- Git operations: git status, git log, git diff
|
|
8899
|
+
- Viewing system info: ls, pwd, cat, head, tail
|
|
8900
|
+
- Running tests: npm test, pytest, cargo test
|
|
8901
|
+
- Package management: npm install, pip install
|
|
8902
|
+
- File operations: mkdir, cp, mv (with permission)
|
|
8903
|
+
|
|
8904
|
+
BLOCKED OPERATIONS:
|
|
8905
|
+
- sudo and privilege escalation
|
|
8906
|
+
- Recursive deletes on system paths
|
|
8907
|
+
- Direct disk operations
|
|
8908
|
+
- Fork bombs and resource exhaustion
|
|
8909
|
+
- Piping remote scripts to shell`,
|
|
8910
|
+
parameters: {
|
|
8911
|
+
type: "object",
|
|
8912
|
+
properties: {
|
|
8913
|
+
command: {
|
|
8914
|
+
type: "string",
|
|
8915
|
+
description: "The bash command to execute. Can include pipes, redirects, and chained commands."
|
|
8916
|
+
},
|
|
8917
|
+
cwd: {
|
|
8918
|
+
type: "string",
|
|
8919
|
+
description: "Working directory for the command (optional). Can be relative or absolute path. Defaults to current directory."
|
|
8920
|
+
},
|
|
8921
|
+
timeout: {
|
|
8922
|
+
type: "number",
|
|
8923
|
+
description: "Timeout in milliseconds (optional, default: 60000, max: 300000). Command will be terminated if it exceeds this time."
|
|
8924
|
+
}
|
|
8925
|
+
},
|
|
8926
|
+
required: ["command"]
|
|
8927
|
+
}
|
|
8928
|
+
}
|
|
8929
|
+
})
|
|
8930
|
+
};
|
|
8931
|
+
|
|
8464
8932
|
// ../../b4m-core/packages/services/dist/src/llm/tools/index.js
|
|
8465
8933
|
var tools = {
|
|
8466
8934
|
dice_roll: diceRollTool,
|
|
@@ -8488,7 +8956,9 @@ var tools = {
|
|
|
8488
8956
|
create_file: createFileTool,
|
|
8489
8957
|
glob_files: globFilesTool,
|
|
8490
8958
|
grep_search: grepSearchTool,
|
|
8491
|
-
delete_file: deleteFileTool
|
|
8959
|
+
delete_file: deleteFileTool,
|
|
8960
|
+
// Shell execution
|
|
8961
|
+
bash_execute: bashExecuteTool
|
|
8492
8962
|
};
|
|
8493
8963
|
var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
|
|
8494
8964
|
const context = {
|
|
@@ -8713,6 +9183,17 @@ var MementoEvalResponseSchema = z142.object({
|
|
|
8713
9183
|
// ../../b4m-core/packages/services/dist/src/auth/AccessTokenGeneratorService.js
|
|
8714
9184
|
import jwt2 from "jsonwebtoken";
|
|
8715
9185
|
|
|
9186
|
+
// ../../b4m-core/packages/services/dist/src/cliTools/types.js
|
|
9187
|
+
var ToolErrorType;
|
|
9188
|
+
(function(ToolErrorType2) {
|
|
9189
|
+
ToolErrorType2["INVALID_INPUT"] = "INVALID_INPUT";
|
|
9190
|
+
ToolErrorType2["API_KEY_MISSING"] = "API_KEY_MISSING";
|
|
9191
|
+
ToolErrorType2["EXTERNAL_API_ERROR"] = "EXTERNAL_API_ERROR";
|
|
9192
|
+
ToolErrorType2["RATE_LIMIT"] = "RATE_LIMIT";
|
|
9193
|
+
ToolErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
9194
|
+
ToolErrorType2["UNKNOWN"] = "UNKNOWN";
|
|
9195
|
+
})(ToolErrorType || (ToolErrorType = {}));
|
|
9196
|
+
|
|
8716
9197
|
// src/utils/diffPreview.ts
|
|
8717
9198
|
import * as Diff from "diff";
|
|
8718
9199
|
import { readFile } from "fs/promises";
|
|
@@ -8763,74 +9244,406 @@ Last modified: ${stats.mtime.toLocaleString()}`;
|
|
|
8763
9244
|
}
|
|
8764
9245
|
}
|
|
8765
9246
|
|
|
8766
|
-
// src/utils/
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
9247
|
+
// src/utils/Logger.ts
|
|
9248
|
+
import fs10 from "fs/promises";
|
|
9249
|
+
import path12 from "path";
|
|
9250
|
+
import os from "os";
|
|
9251
|
+
var Logger2 = class _Logger {
|
|
9252
|
+
constructor() {
|
|
9253
|
+
this.logFilePath = null;
|
|
9254
|
+
this.sessionId = null;
|
|
9255
|
+
this.fileLoggingEnabled = true;
|
|
9256
|
+
this.consoleVerbose = false;
|
|
8775
9257
|
}
|
|
8776
|
-
|
|
8777
|
-
|
|
9258
|
+
static {
|
|
9259
|
+
this.instance = null;
|
|
8778
9260
|
}
|
|
8779
|
-
|
|
8780
|
-
|
|
9261
|
+
static getInstance() {
|
|
9262
|
+
if (!_Logger.instance) {
|
|
9263
|
+
_Logger.instance = new _Logger();
|
|
9264
|
+
}
|
|
9265
|
+
return _Logger.instance;
|
|
8781
9266
|
}
|
|
8782
|
-
|
|
8783
|
-
|
|
9267
|
+
/**
|
|
9268
|
+
* Initialize the logger with a session ID
|
|
9269
|
+
*/
|
|
9270
|
+
async initialize(sessionId) {
|
|
9271
|
+
this.sessionId = sessionId;
|
|
9272
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9273
|
+
await fs10.mkdir(debugDir, { recursive: true });
|
|
9274
|
+
this.logFilePath = path12.join(debugDir, `${sessionId}.txt`);
|
|
9275
|
+
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
8784
9276
|
}
|
|
8785
|
-
|
|
8786
|
-
|
|
9277
|
+
/**
|
|
9278
|
+
* Set whether verbose console logging is enabled
|
|
9279
|
+
*/
|
|
9280
|
+
setVerbose(enabled) {
|
|
9281
|
+
this.consoleVerbose = enabled;
|
|
8787
9282
|
}
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
9283
|
+
/**
|
|
9284
|
+
* Set whether file logging is enabled
|
|
9285
|
+
*/
|
|
9286
|
+
setFileLoggingEnabled(enabled) {
|
|
9287
|
+
this.fileLoggingEnabled = enabled;
|
|
8792
9288
|
}
|
|
8793
|
-
|
|
8794
|
-
|
|
9289
|
+
/**
|
|
9290
|
+
* DEBUG level - verbose-only console, always file
|
|
9291
|
+
*/
|
|
9292
|
+
debug(message) {
|
|
9293
|
+
this.writeToFile("DEBUG", message).catch(() => {
|
|
9294
|
+
});
|
|
9295
|
+
if (this.consoleVerbose) {
|
|
9296
|
+
console.log(message);
|
|
9297
|
+
}
|
|
8795
9298
|
}
|
|
8796
|
-
|
|
9299
|
+
/**
|
|
9300
|
+
* INFO level - always shown to user
|
|
9301
|
+
*/
|
|
9302
|
+
info(message) {
|
|
9303
|
+
this.writeToFile("INFO", message).catch(() => {
|
|
9304
|
+
});
|
|
9305
|
+
console.log(message);
|
|
8797
9306
|
}
|
|
8798
|
-
|
|
9307
|
+
/**
|
|
9308
|
+
* WARN level - always shown to user
|
|
9309
|
+
*/
|
|
9310
|
+
warn(message) {
|
|
9311
|
+
this.writeToFile("WARN", message).catch(() => {
|
|
9312
|
+
});
|
|
9313
|
+
console.warn(message);
|
|
8799
9314
|
}
|
|
8800
|
-
|
|
9315
|
+
/**
|
|
9316
|
+
* ERROR level - always shown to user
|
|
9317
|
+
*/
|
|
9318
|
+
error(message, err) {
|
|
9319
|
+
this.writeToFile("ERROR", message).catch(() => {
|
|
9320
|
+
});
|
|
9321
|
+
if (err) {
|
|
9322
|
+
this.logErrorDetailsToFile(err).catch(() => {
|
|
9323
|
+
});
|
|
9324
|
+
}
|
|
9325
|
+
console.error(message);
|
|
8801
9326
|
}
|
|
8802
|
-
|
|
9327
|
+
/**
|
|
9328
|
+
* Write log entry to file
|
|
9329
|
+
*/
|
|
9330
|
+
async writeToFile(level, message) {
|
|
9331
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
9332
|
+
return;
|
|
9333
|
+
}
|
|
9334
|
+
try {
|
|
9335
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
9336
|
+
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
9337
|
+
`;
|
|
9338
|
+
await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
9339
|
+
} catch (error) {
|
|
9340
|
+
console.error("File logging failed:", error);
|
|
9341
|
+
}
|
|
8803
9342
|
}
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
9343
|
+
/**
|
|
9344
|
+
* Log error details to file
|
|
9345
|
+
*/
|
|
9346
|
+
async logErrorDetailsToFile(err) {
|
|
9347
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
9348
|
+
return;
|
|
9349
|
+
}
|
|
9350
|
+
try {
|
|
9351
|
+
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
9352
|
+
const response = err.response;
|
|
9353
|
+
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
9354
|
+
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
9355
|
+
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
9356
|
+
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
9357
|
+
if (response.data) {
|
|
9358
|
+
const errorText = this.extractErrorMessage(response.data);
|
|
9359
|
+
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
9360
|
+
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
9361
|
+
const parsedError = this.parseHtmlError(errorText);
|
|
9362
|
+
if (parsedError) {
|
|
9363
|
+
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
9364
|
+
}
|
|
9365
|
+
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
9366
|
+
} else {
|
|
9367
|
+
const preview = this.truncate(errorText, 500);
|
|
9368
|
+
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
9369
|
+
}
|
|
9370
|
+
} else {
|
|
9371
|
+
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
8825
9372
|
}
|
|
8826
|
-
}
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
9373
|
+
}
|
|
9374
|
+
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
9375
|
+
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
9376
|
+
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
9377
|
+
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
9378
|
+
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
9379
|
+
}
|
|
9380
|
+
} catch (error) {
|
|
9381
|
+
}
|
|
9382
|
+
}
|
|
9383
|
+
/**
|
|
9384
|
+
* Safely stringify object, handling circular references
|
|
9385
|
+
*/
|
|
9386
|
+
safeStringify(obj) {
|
|
9387
|
+
try {
|
|
9388
|
+
return JSON.stringify(obj);
|
|
9389
|
+
} catch (error) {
|
|
9390
|
+
if (error instanceof Error && error.message.includes("circular")) {
|
|
9391
|
+
try {
|
|
9392
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
9393
|
+
return JSON.stringify(obj, (key, value) => {
|
|
9394
|
+
if (typeof value === "object" && value !== null) {
|
|
9395
|
+
if (seen.has(value)) {
|
|
9396
|
+
return "[Circular]";
|
|
9397
|
+
}
|
|
9398
|
+
seen.add(value);
|
|
9399
|
+
}
|
|
9400
|
+
return value;
|
|
9401
|
+
});
|
|
9402
|
+
} catch {
|
|
9403
|
+
return "[Unable to stringify]";
|
|
9404
|
+
}
|
|
9405
|
+
}
|
|
9406
|
+
return "[Stringify error]";
|
|
9407
|
+
}
|
|
9408
|
+
}
|
|
9409
|
+
/**
|
|
9410
|
+
* Extract readable text from error response data
|
|
9411
|
+
*/
|
|
9412
|
+
extractErrorMessage(data) {
|
|
9413
|
+
if (Buffer.isBuffer(data)) {
|
|
9414
|
+
return data.toString("utf-8");
|
|
9415
|
+
}
|
|
9416
|
+
if (typeof data === "string") {
|
|
9417
|
+
return data;
|
|
9418
|
+
}
|
|
9419
|
+
if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
|
|
9420
|
+
const chunks = [];
|
|
9421
|
+
for (const chunk of data._readableState.buffer) {
|
|
9422
|
+
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
9423
|
+
chunks.push(Buffer.from(chunk.data));
|
|
9424
|
+
} else if (Buffer.isBuffer(chunk)) {
|
|
9425
|
+
chunks.push(chunk);
|
|
9426
|
+
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
9427
|
+
if (Buffer.isBuffer(chunk.data)) {
|
|
9428
|
+
chunks.push(chunk.data);
|
|
9429
|
+
} else if (Array.isArray(chunk.data)) {
|
|
9430
|
+
chunks.push(Buffer.from(chunk.data));
|
|
9431
|
+
}
|
|
9432
|
+
}
|
|
9433
|
+
}
|
|
9434
|
+
if (chunks.length > 0) {
|
|
9435
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
9436
|
+
}
|
|
9437
|
+
}
|
|
9438
|
+
return this.safeStringify(data);
|
|
9439
|
+
}
|
|
9440
|
+
/**
|
|
9441
|
+
* Parse HTML error page to extract error message
|
|
9442
|
+
*/
|
|
9443
|
+
parseHtmlError(html) {
|
|
9444
|
+
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
|
|
9445
|
+
const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
|
|
9446
|
+
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
9447
|
+
if (titleMatch && titleMatch[1] !== "Error") {
|
|
9448
|
+
return titleMatch[1].trim();
|
|
9449
|
+
}
|
|
9450
|
+
if (h1Match) {
|
|
9451
|
+
return h1Match[1].trim();
|
|
9452
|
+
}
|
|
9453
|
+
if (bodyMatch) {
|
|
9454
|
+
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
9455
|
+
return text.substring(0, 200);
|
|
9456
|
+
}
|
|
9457
|
+
return null;
|
|
9458
|
+
}
|
|
9459
|
+
/**
|
|
9460
|
+
* Truncate string to max length with ellipsis
|
|
9461
|
+
*/
|
|
9462
|
+
truncate(str, maxLength) {
|
|
9463
|
+
if (str.length <= maxLength) {
|
|
9464
|
+
return str;
|
|
9465
|
+
}
|
|
9466
|
+
return str.substring(0, maxLength) + "... [truncated]";
|
|
9467
|
+
}
|
|
9468
|
+
/**
|
|
9469
|
+
* Format bytes to human-readable size
|
|
9470
|
+
*/
|
|
9471
|
+
formatBytes(bytes) {
|
|
9472
|
+
if (bytes === 0) return "0 B";
|
|
9473
|
+
const k = 1024;
|
|
9474
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
9475
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
9476
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
9477
|
+
}
|
|
9478
|
+
/**
|
|
9479
|
+
* Clean up old debug logs (older than 30 days)
|
|
9480
|
+
*/
|
|
9481
|
+
async cleanupOldLogs() {
|
|
9482
|
+
if (!this.fileLoggingEnabled) return;
|
|
9483
|
+
try {
|
|
9484
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9485
|
+
const files = await fs10.readdir(debugDir);
|
|
9486
|
+
const now = Date.now();
|
|
9487
|
+
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
9488
|
+
for (const file of files) {
|
|
9489
|
+
const filePath = path12.join(debugDir, file);
|
|
9490
|
+
const stats = await fs10.stat(filePath);
|
|
9491
|
+
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
9492
|
+
await fs10.unlink(filePath);
|
|
9493
|
+
}
|
|
9494
|
+
}
|
|
9495
|
+
} catch (error) {
|
|
9496
|
+
console.error("Failed to cleanup old logs:", error);
|
|
9497
|
+
}
|
|
9498
|
+
}
|
|
9499
|
+
};
|
|
9500
|
+
var logger = Logger2.getInstance();
|
|
9501
|
+
|
|
9502
|
+
// src/llm/ServerToolExecutor.ts
|
|
9503
|
+
var ServerToolExecutor = class {
|
|
9504
|
+
constructor(apiClient) {
|
|
9505
|
+
this.toolsEndpoint = "/api/ai/v1/tools";
|
|
9506
|
+
this.apiClient = apiClient;
|
|
9507
|
+
}
|
|
9508
|
+
/**
|
|
9509
|
+
* Execute a tool server-side
|
|
9510
|
+
*
|
|
9511
|
+
* @param toolName - Name of the tool to execute (e.g., 'weather_info', 'web_search')
|
|
9512
|
+
* @param input - Tool input parameters
|
|
9513
|
+
* @returns Tool execution result (content or error)
|
|
9514
|
+
*/
|
|
9515
|
+
async executeTool(toolName, input) {
|
|
9516
|
+
logger.debug(`[ServerToolExecutor] Executing tool: ${toolName}`);
|
|
9517
|
+
logger.debug(`[ServerToolExecutor] Endpoint: ${this.toolsEndpoint}`);
|
|
9518
|
+
logger.debug(`[ServerToolExecutor] Input: ${JSON.stringify(input)}`);
|
|
9519
|
+
try {
|
|
9520
|
+
const result = await this.apiClient.post(this.toolsEndpoint, {
|
|
9521
|
+
toolName,
|
|
9522
|
+
input
|
|
9523
|
+
});
|
|
9524
|
+
logger.debug(`[ServerToolExecutor] Response: ${JSON.stringify(result)}`);
|
|
9525
|
+
if (!result.success) {
|
|
9526
|
+
const errorMessage = result.error || "Tool execution failed";
|
|
9527
|
+
logger.error(`[ServerToolExecutor] Tool ${toolName} failed: ${errorMessage}`);
|
|
9528
|
+
return `Error executing ${toolName}: ${errorMessage}`;
|
|
9529
|
+
}
|
|
9530
|
+
logger.debug(`[ServerToolExecutor] Tool ${toolName} succeeded in ${result.executionTimeMs}ms`);
|
|
9531
|
+
return result.content || "";
|
|
9532
|
+
} catch (error) {
|
|
9533
|
+
const err = error;
|
|
9534
|
+
const errorMessage = err?.response?.data?.error || err?.message || "Unknown error";
|
|
9535
|
+
const status = err?.response?.status || "unknown";
|
|
9536
|
+
logger.error(`[ServerToolExecutor] Request failed for ${toolName}: ${errorMessage} (status: ${status})`);
|
|
9537
|
+
logger.error(`[ServerToolExecutor] Full error: ${JSON.stringify(err, null, 2)}`);
|
|
9538
|
+
return `Error executing ${toolName}: ${errorMessage}`;
|
|
9539
|
+
}
|
|
9540
|
+
}
|
|
9541
|
+
};
|
|
9542
|
+
|
|
9543
|
+
// src/llm/ToolRouter.ts
|
|
9544
|
+
var SERVER_TOOLS = ["weather_info", "web_search"];
|
|
9545
|
+
var LOCAL_TOOLS = [
|
|
9546
|
+
"file_read",
|
|
9547
|
+
"create_file",
|
|
9548
|
+
"glob_files",
|
|
9549
|
+
"grep_search",
|
|
9550
|
+
"delete_file",
|
|
9551
|
+
"edit_file",
|
|
9552
|
+
"dice_roll",
|
|
9553
|
+
"math_evaluate",
|
|
9554
|
+
"current_datetime"
|
|
9555
|
+
];
|
|
9556
|
+
function isServerTool(toolName) {
|
|
9557
|
+
return SERVER_TOOLS.includes(toolName);
|
|
9558
|
+
}
|
|
9559
|
+
function isLocalTool(toolName) {
|
|
9560
|
+
return LOCAL_TOOLS.includes(toolName);
|
|
9561
|
+
}
|
|
9562
|
+
async function executeTool(toolName, input, apiClient, localToolFn) {
|
|
9563
|
+
if (isServerTool(toolName)) {
|
|
9564
|
+
logger.debug(`[ToolRouter] Routing ${toolName} to server`);
|
|
9565
|
+
const executor = new ServerToolExecutor(apiClient);
|
|
9566
|
+
return await executor.executeTool(toolName, input);
|
|
9567
|
+
} else if (isLocalTool(toolName)) {
|
|
9568
|
+
logger.debug(`[ToolRouter] Executing ${toolName} locally`);
|
|
9569
|
+
if (!localToolFn) {
|
|
9570
|
+
throw new Error(`Local tool ${toolName} has no implementation`);
|
|
9571
|
+
}
|
|
9572
|
+
const result = await localToolFn(input);
|
|
9573
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
9574
|
+
} else {
|
|
9575
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
9576
|
+
}
|
|
9577
|
+
}
|
|
9578
|
+
|
|
9579
|
+
// src/utils/toolsAdapter.ts
|
|
9580
|
+
var NoOpStorage = class extends BaseStorage {
|
|
9581
|
+
async upload(input, destination, options) {
|
|
9582
|
+
return `/tmp/${destination}`;
|
|
9583
|
+
}
|
|
9584
|
+
async download(path14) {
|
|
9585
|
+
throw new Error("Download not supported in CLI");
|
|
9586
|
+
}
|
|
9587
|
+
async delete(path14) {
|
|
9588
|
+
}
|
|
9589
|
+
async getSignedUrl(path14) {
|
|
9590
|
+
return `/tmp/${path14}`;
|
|
9591
|
+
}
|
|
9592
|
+
getPublicUrl(path14) {
|
|
9593
|
+
return `/tmp/${path14}`;
|
|
9594
|
+
}
|
|
9595
|
+
async getPreview(path14) {
|
|
9596
|
+
return `/tmp/${path14}`;
|
|
9597
|
+
}
|
|
9598
|
+
async getMetadata(path14) {
|
|
9599
|
+
return { size: 0, contentType: "application/octet-stream" };
|
|
9600
|
+
}
|
|
9601
|
+
};
|
|
9602
|
+
var CliLogger = class extends Logger {
|
|
9603
|
+
constructor() {
|
|
9604
|
+
super({ logInJson: false });
|
|
9605
|
+
}
|
|
9606
|
+
// Override all logging methods to be silent
|
|
9607
|
+
info(...args) {
|
|
9608
|
+
}
|
|
9609
|
+
warn(...args) {
|
|
9610
|
+
}
|
|
9611
|
+
error(...args) {
|
|
9612
|
+
}
|
|
9613
|
+
debug(...args) {
|
|
9614
|
+
}
|
|
9615
|
+
log(...args) {
|
|
9616
|
+
}
|
|
9617
|
+
};
|
|
9618
|
+
function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
|
|
9619
|
+
const originalFn = tool.toolFn;
|
|
9620
|
+
const toolName = tool.toolSchema.name;
|
|
9621
|
+
return {
|
|
9622
|
+
...tool,
|
|
9623
|
+
toolFn: async (args) => {
|
|
9624
|
+
if (!permissionManager.needsPermission(toolName)) {
|
|
9625
|
+
const result2 = await executeTool(toolName, args, apiClient, originalFn);
|
|
9626
|
+
agentContext.observationQueue.push({ toolName, result: result2 });
|
|
9627
|
+
return result2;
|
|
9628
|
+
}
|
|
9629
|
+
let preview;
|
|
9630
|
+
if (toolName === "edit_file" && args?.path && args?.content) {
|
|
9631
|
+
try {
|
|
9632
|
+
preview = await generateFileDiffPreview({
|
|
9633
|
+
path: args.path,
|
|
9634
|
+
content: args.content
|
|
9635
|
+
});
|
|
9636
|
+
} catch (error) {
|
|
9637
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9638
|
+
}
|
|
9639
|
+
} else if (toolName === "create_file" && args?.path && args?.content) {
|
|
9640
|
+
try {
|
|
9641
|
+
preview = await generateFileDiffPreview({
|
|
9642
|
+
path: args.path,
|
|
9643
|
+
content: args.content
|
|
9644
|
+
});
|
|
9645
|
+
} catch (error) {
|
|
9646
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
8834
9647
|
}
|
|
8835
9648
|
} else if (toolName === "delete_file" && args?.path) {
|
|
8836
9649
|
try {
|
|
@@ -8840,6 +9653,10 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
8840
9653
|
} catch (error) {
|
|
8841
9654
|
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
8842
9655
|
}
|
|
9656
|
+
} else if (toolName === "bash_execute" && args?.command) {
|
|
9657
|
+
const cwd = args.cwd ? ` (in ${args.cwd})` : "";
|
|
9658
|
+
const timeout = args.timeout ? ` [timeout: ${args.timeout}ms]` : "";
|
|
9659
|
+
preview = `$ ${args.command}${cwd}${timeout}`;
|
|
8843
9660
|
}
|
|
8844
9661
|
const response = await showPermissionPrompt(toolName, args, preview);
|
|
8845
9662
|
if (response.action === "deny") {
|
|
@@ -8866,13 +9683,13 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
8866
9683
|
}
|
|
8867
9684
|
}
|
|
8868
9685
|
}
|
|
8869
|
-
const result = await
|
|
9686
|
+
const result = await executeTool(toolName, args, apiClient, originalFn);
|
|
8870
9687
|
agentContext.observationQueue.push({ toolName, result });
|
|
8871
9688
|
return result;
|
|
8872
9689
|
}
|
|
8873
9690
|
};
|
|
8874
9691
|
}
|
|
8875
|
-
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore,
|
|
9692
|
+
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
|
|
8876
9693
|
const logger2 = new CliLogger();
|
|
8877
9694
|
const storage = new NoOpStorage();
|
|
8878
9695
|
const user = {
|
|
@@ -8889,18 +9706,6 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
8889
9706
|
};
|
|
8890
9707
|
const mockAdminSettings = {
|
|
8891
9708
|
findBySettingName: async (settingName) => {
|
|
8892
|
-
if (settingName === "OpenWeatherKey") {
|
|
8893
|
-
const key = toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY;
|
|
8894
|
-
if (key) {
|
|
8895
|
-
return { settingValue: key };
|
|
8896
|
-
}
|
|
8897
|
-
}
|
|
8898
|
-
if (settingName === "SerperKey") {
|
|
8899
|
-
const key = toolApiKeys?.serper || process.env.SERPER_API_KEY;
|
|
8900
|
-
if (key) {
|
|
8901
|
-
return { settingValue: key };
|
|
8902
|
-
}
|
|
8903
|
-
}
|
|
8904
9709
|
return null;
|
|
8905
9710
|
}
|
|
8906
9711
|
};
|
|
@@ -8910,27 +9715,24 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
8910
9715
|
adminSettings: mockAdminSettings
|
|
8911
9716
|
}
|
|
8912
9717
|
};
|
|
8913
|
-
const hasSerperKey = !!(toolApiKeys?.serper || process.env.SERPER_API_KEY);
|
|
8914
|
-
const hasOpenWeatherKey = !!(toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY);
|
|
8915
9718
|
const toolConfig = {
|
|
8916
|
-
//
|
|
9719
|
+
// Local-only tools (no external API keys needed)
|
|
8917
9720
|
dice_roll: {},
|
|
8918
9721
|
math_evaluate: {},
|
|
8919
9722
|
current_datetime: {},
|
|
8920
9723
|
prompt_enhancement: {},
|
|
8921
|
-
// File operation tools (CLI-specific)
|
|
9724
|
+
// File operation tools (CLI-specific, local execution)
|
|
8922
9725
|
file_read: {},
|
|
8923
9726
|
create_file: {},
|
|
8924
9727
|
glob_files: {},
|
|
8925
9728
|
grep_search: {},
|
|
8926
9729
|
delete_file: {},
|
|
8927
|
-
//
|
|
8928
|
-
|
|
8929
|
-
//
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
// Requires SERPER_API_KEY
|
|
9730
|
+
// Shell execution (always requires permission)
|
|
9731
|
+
bash_execute: {},
|
|
9732
|
+
// Server-side tools (executed via /api/ai/v1/tools Lambda)
|
|
9733
|
+
// Routed by ToolRouter to use B4M company API keys
|
|
9734
|
+
weather_info: {},
|
|
9735
|
+
web_search: {}
|
|
8934
9736
|
// Disable web-only tools
|
|
8935
9737
|
// blog_publish: undefined,
|
|
8936
9738
|
// blog_edit: undefined,
|
|
@@ -8940,6 +9742,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
8940
9742
|
// mermaid_chart: undefined,
|
|
8941
9743
|
// recharts: undefined,
|
|
8942
9744
|
// edit_file: undefined,
|
|
9745
|
+
// deep_research: undefined,
|
|
8943
9746
|
};
|
|
8944
9747
|
const toolsMap = generateTools(
|
|
8945
9748
|
userId,
|
|
@@ -8957,7 +9760,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
8957
9760
|
model
|
|
8958
9761
|
);
|
|
8959
9762
|
const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
|
|
8960
|
-
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore)
|
|
9763
|
+
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
|
|
8961
9764
|
);
|
|
8962
9765
|
return { tools: tools2, agentContext };
|
|
8963
9766
|
}
|
|
@@ -8991,6 +9794,7 @@ var DEFAULT_TOOL_CATEGORIES = {
|
|
|
8991
9794
|
create_file: "prompt_always",
|
|
8992
9795
|
delete_file: "prompt_always",
|
|
8993
9796
|
shell_execute: "prompt_always",
|
|
9797
|
+
bash_execute: "prompt_always",
|
|
8994
9798
|
git_commit: "prompt_always",
|
|
8995
9799
|
git_push: "prompt_always",
|
|
8996
9800
|
// ===== PROMPT DEFAULT (Repository read tools) =====
|
|
@@ -9067,350 +9871,95 @@ var PermissionManager = class {
|
|
|
9067
9871
|
if (!canTrustTool(toolName, categoryMap)) {
|
|
9068
9872
|
return false;
|
|
9069
9873
|
}
|
|
9070
|
-
this.trustedTools.add(toolName);
|
|
9071
|
-
return true;
|
|
9072
|
-
}
|
|
9073
|
-
/**
|
|
9074
|
-
* Remove a tool from the trusted list
|
|
9075
|
-
*/
|
|
9076
|
-
untrustTool(toolName) {
|
|
9077
|
-
this.trustedTools.delete(toolName);
|
|
9078
|
-
}
|
|
9079
|
-
/**
|
|
9080
|
-
* Get list of all trusted tools
|
|
9081
|
-
*/
|
|
9082
|
-
getTrustedTools() {
|
|
9083
|
-
return Array.from(this.trustedTools).sort();
|
|
9084
|
-
}
|
|
9085
|
-
/**
|
|
9086
|
-
* Check if a tool is currently trusted
|
|
9087
|
-
*/
|
|
9088
|
-
isTrusted(toolName) {
|
|
9089
|
-
return this.trustedTools.has(toolName);
|
|
9090
|
-
}
|
|
9091
|
-
/**
|
|
9092
|
-
* Get the category for a tool
|
|
9093
|
-
*/
|
|
9094
|
-
getCategory(toolName) {
|
|
9095
|
-
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9096
|
-
return getToolCategory(toolName, categoryMap);
|
|
9097
|
-
}
|
|
9098
|
-
/**
|
|
9099
|
-
* Check if a tool can be trusted (not in prompt_always category or denied by project)
|
|
9100
|
-
*/
|
|
9101
|
-
canBeTrusted(toolName) {
|
|
9102
|
-
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9103
|
-
if (this.deniedTools.has(toolName)) {
|
|
9104
|
-
return false;
|
|
9105
|
-
}
|
|
9106
|
-
return canTrustTool(toolName, categoryMap);
|
|
9107
|
-
}
|
|
9108
|
-
/**
|
|
9109
|
-
* Check if a tool is denied by project config
|
|
9110
|
-
*/
|
|
9111
|
-
isDenied(toolName) {
|
|
9112
|
-
return this.deniedTools.has(toolName);
|
|
9113
|
-
}
|
|
9114
|
-
/**
|
|
9115
|
-
* Get list of denied tools
|
|
9116
|
-
*/
|
|
9117
|
-
getDeniedTools() {
|
|
9118
|
-
return Array.from(this.deniedTools).sort();
|
|
9119
|
-
}
|
|
9120
|
-
/**
|
|
9121
|
-
* Clear all trusted tools
|
|
9122
|
-
*/
|
|
9123
|
-
clearTrustedTools() {
|
|
9124
|
-
this.trustedTools.clear();
|
|
9125
|
-
}
|
|
9126
|
-
/**
|
|
9127
|
-
* Get statistics about permissions
|
|
9128
|
-
*/
|
|
9129
|
-
getStats() {
|
|
9130
|
-
return {
|
|
9131
|
-
trustedCount: this.trustedTools.size,
|
|
9132
|
-
autoApproveCount: 0,
|
|
9133
|
-
// TODO: Would need full tool list
|
|
9134
|
-
promptAlwaysCount: 0,
|
|
9135
|
-
promptDefaultCount: 0
|
|
9136
|
-
};
|
|
9137
|
-
}
|
|
9138
|
-
};
|
|
9139
|
-
|
|
9140
|
-
// src/utils/apiUrl.ts
|
|
9141
|
-
var BIKE4MIND_URL = "https://app.bike4mind.com";
|
|
9142
|
-
function getApiUrl(configApiConfig) {
|
|
9143
|
-
if (configApiConfig?.customUrl) {
|
|
9144
|
-
return configApiConfig.customUrl;
|
|
9145
|
-
}
|
|
9146
|
-
return BIKE4MIND_URL;
|
|
9147
|
-
}
|
|
9148
|
-
function getEnvironmentName(configApiConfig) {
|
|
9149
|
-
if (configApiConfig?.customUrl) {
|
|
9150
|
-
return "Self-Hosted";
|
|
9151
|
-
}
|
|
9152
|
-
return "Bike4Mind";
|
|
9153
|
-
}
|
|
9154
|
-
|
|
9155
|
-
// src/utils/Logger.ts
|
|
9156
|
-
import fs10 from "fs/promises";
|
|
9157
|
-
import path11 from "path";
|
|
9158
|
-
import os from "os";
|
|
9159
|
-
var Logger2 = class _Logger {
|
|
9160
|
-
constructor() {
|
|
9161
|
-
this.logFilePath = null;
|
|
9162
|
-
this.sessionId = null;
|
|
9163
|
-
this.fileLoggingEnabled = true;
|
|
9164
|
-
this.consoleVerbose = false;
|
|
9165
|
-
}
|
|
9166
|
-
static {
|
|
9167
|
-
this.instance = null;
|
|
9168
|
-
}
|
|
9169
|
-
static getInstance() {
|
|
9170
|
-
if (!_Logger.instance) {
|
|
9171
|
-
_Logger.instance = new _Logger();
|
|
9172
|
-
}
|
|
9173
|
-
return _Logger.instance;
|
|
9174
|
-
}
|
|
9175
|
-
/**
|
|
9176
|
-
* Initialize the logger with a session ID
|
|
9177
|
-
*/
|
|
9178
|
-
async initialize(sessionId) {
|
|
9179
|
-
this.sessionId = sessionId;
|
|
9180
|
-
const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
|
|
9181
|
-
await fs10.mkdir(debugDir, { recursive: true });
|
|
9182
|
-
this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
|
|
9183
|
-
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
9184
|
-
}
|
|
9185
|
-
/**
|
|
9186
|
-
* Set whether verbose console logging is enabled
|
|
9187
|
-
*/
|
|
9188
|
-
setVerbose(enabled) {
|
|
9189
|
-
this.consoleVerbose = enabled;
|
|
9190
|
-
}
|
|
9191
|
-
/**
|
|
9192
|
-
* Set whether file logging is enabled
|
|
9193
|
-
*/
|
|
9194
|
-
setFileLoggingEnabled(enabled) {
|
|
9195
|
-
this.fileLoggingEnabled = enabled;
|
|
9196
|
-
}
|
|
9197
|
-
/**
|
|
9198
|
-
* DEBUG level - verbose-only console, always file
|
|
9199
|
-
*/
|
|
9200
|
-
debug(message) {
|
|
9201
|
-
this.writeToFile("DEBUG", message).catch(() => {
|
|
9202
|
-
});
|
|
9203
|
-
if (this.consoleVerbose) {
|
|
9204
|
-
console.log(message);
|
|
9205
|
-
}
|
|
9206
|
-
}
|
|
9207
|
-
/**
|
|
9208
|
-
* INFO level - always shown to user
|
|
9209
|
-
*/
|
|
9210
|
-
info(message) {
|
|
9211
|
-
this.writeToFile("INFO", message).catch(() => {
|
|
9212
|
-
});
|
|
9213
|
-
console.log(message);
|
|
9214
|
-
}
|
|
9215
|
-
/**
|
|
9216
|
-
* WARN level - always shown to user
|
|
9217
|
-
*/
|
|
9218
|
-
warn(message) {
|
|
9219
|
-
this.writeToFile("WARN", message).catch(() => {
|
|
9220
|
-
});
|
|
9221
|
-
console.warn(message);
|
|
9222
|
-
}
|
|
9223
|
-
/**
|
|
9224
|
-
* ERROR level - always shown to user
|
|
9225
|
-
*/
|
|
9226
|
-
error(message, err) {
|
|
9227
|
-
this.writeToFile("ERROR", message).catch(() => {
|
|
9228
|
-
});
|
|
9229
|
-
if (err) {
|
|
9230
|
-
this.logErrorDetailsToFile(err).catch(() => {
|
|
9231
|
-
});
|
|
9232
|
-
}
|
|
9233
|
-
console.error(message);
|
|
9234
|
-
}
|
|
9235
|
-
/**
|
|
9236
|
-
* Write log entry to file
|
|
9237
|
-
*/
|
|
9238
|
-
async writeToFile(level, message) {
|
|
9239
|
-
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
9240
|
-
return;
|
|
9241
|
-
}
|
|
9242
|
-
try {
|
|
9243
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
9244
|
-
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
9245
|
-
`;
|
|
9246
|
-
await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
9247
|
-
} catch (error) {
|
|
9248
|
-
console.error("File logging failed:", error);
|
|
9249
|
-
}
|
|
9874
|
+
this.trustedTools.add(toolName);
|
|
9875
|
+
return true;
|
|
9250
9876
|
}
|
|
9251
9877
|
/**
|
|
9252
|
-
*
|
|
9878
|
+
* Remove a tool from the trusted list
|
|
9253
9879
|
*/
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
return;
|
|
9257
|
-
}
|
|
9258
|
-
try {
|
|
9259
|
-
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
9260
|
-
const response = err.response;
|
|
9261
|
-
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
9262
|
-
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
9263
|
-
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
9264
|
-
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
9265
|
-
if (response.data) {
|
|
9266
|
-
const errorText = this.extractErrorMessage(response.data);
|
|
9267
|
-
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
9268
|
-
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
9269
|
-
const parsedError = this.parseHtmlError(errorText);
|
|
9270
|
-
if (parsedError) {
|
|
9271
|
-
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
9272
|
-
}
|
|
9273
|
-
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
9274
|
-
} else {
|
|
9275
|
-
const preview = this.truncate(errorText, 500);
|
|
9276
|
-
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
9277
|
-
}
|
|
9278
|
-
} else {
|
|
9279
|
-
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
9280
|
-
}
|
|
9281
|
-
}
|
|
9282
|
-
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
9283
|
-
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
9284
|
-
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
9285
|
-
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
9286
|
-
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
9287
|
-
}
|
|
9288
|
-
} catch (error) {
|
|
9289
|
-
}
|
|
9880
|
+
untrustTool(toolName) {
|
|
9881
|
+
this.trustedTools.delete(toolName);
|
|
9290
9882
|
}
|
|
9291
9883
|
/**
|
|
9292
|
-
*
|
|
9884
|
+
* Get list of all trusted tools
|
|
9293
9885
|
*/
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
return JSON.stringify(obj);
|
|
9297
|
-
} catch (error) {
|
|
9298
|
-
if (error instanceof Error && error.message.includes("circular")) {
|
|
9299
|
-
try {
|
|
9300
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
9301
|
-
return JSON.stringify(obj, (key, value) => {
|
|
9302
|
-
if (typeof value === "object" && value !== null) {
|
|
9303
|
-
if (seen.has(value)) {
|
|
9304
|
-
return "[Circular]";
|
|
9305
|
-
}
|
|
9306
|
-
seen.add(value);
|
|
9307
|
-
}
|
|
9308
|
-
return value;
|
|
9309
|
-
});
|
|
9310
|
-
} catch {
|
|
9311
|
-
return "[Unable to stringify]";
|
|
9312
|
-
}
|
|
9313
|
-
}
|
|
9314
|
-
return "[Stringify error]";
|
|
9315
|
-
}
|
|
9886
|
+
getTrustedTools() {
|
|
9887
|
+
return Array.from(this.trustedTools).sort();
|
|
9316
9888
|
}
|
|
9317
9889
|
/**
|
|
9318
|
-
*
|
|
9890
|
+
* Check if a tool is currently trusted
|
|
9319
9891
|
*/
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
return data.toString("utf-8");
|
|
9323
|
-
}
|
|
9324
|
-
if (typeof data === "string") {
|
|
9325
|
-
return data;
|
|
9326
|
-
}
|
|
9327
|
-
if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
|
|
9328
|
-
const chunks = [];
|
|
9329
|
-
for (const chunk of data._readableState.buffer) {
|
|
9330
|
-
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
9331
|
-
chunks.push(Buffer.from(chunk.data));
|
|
9332
|
-
} else if (Buffer.isBuffer(chunk)) {
|
|
9333
|
-
chunks.push(chunk);
|
|
9334
|
-
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
9335
|
-
if (Buffer.isBuffer(chunk.data)) {
|
|
9336
|
-
chunks.push(chunk.data);
|
|
9337
|
-
} else if (Array.isArray(chunk.data)) {
|
|
9338
|
-
chunks.push(Buffer.from(chunk.data));
|
|
9339
|
-
}
|
|
9340
|
-
}
|
|
9341
|
-
}
|
|
9342
|
-
if (chunks.length > 0) {
|
|
9343
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
9344
|
-
}
|
|
9345
|
-
}
|
|
9346
|
-
return this.safeStringify(data);
|
|
9892
|
+
isTrusted(toolName) {
|
|
9893
|
+
return this.trustedTools.has(toolName);
|
|
9347
9894
|
}
|
|
9348
9895
|
/**
|
|
9349
|
-
*
|
|
9896
|
+
* Get the category for a tool
|
|
9350
9897
|
*/
|
|
9351
|
-
|
|
9352
|
-
const
|
|
9353
|
-
|
|
9354
|
-
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
9355
|
-
if (titleMatch && titleMatch[1] !== "Error") {
|
|
9356
|
-
return titleMatch[1].trim();
|
|
9357
|
-
}
|
|
9358
|
-
if (h1Match) {
|
|
9359
|
-
return h1Match[1].trim();
|
|
9360
|
-
}
|
|
9361
|
-
if (bodyMatch) {
|
|
9362
|
-
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
9363
|
-
return text.substring(0, 200);
|
|
9364
|
-
}
|
|
9365
|
-
return null;
|
|
9898
|
+
getCategory(toolName) {
|
|
9899
|
+
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9900
|
+
return getToolCategory(toolName, categoryMap);
|
|
9366
9901
|
}
|
|
9367
9902
|
/**
|
|
9368
|
-
*
|
|
9903
|
+
* Check if a tool can be trusted (not in prompt_always category or denied by project)
|
|
9369
9904
|
*/
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9905
|
+
canBeTrusted(toolName) {
|
|
9906
|
+
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9907
|
+
if (this.deniedTools.has(toolName)) {
|
|
9908
|
+
return false;
|
|
9373
9909
|
}
|
|
9374
|
-
return
|
|
9910
|
+
return canTrustTool(toolName, categoryMap);
|
|
9375
9911
|
}
|
|
9376
9912
|
/**
|
|
9377
|
-
*
|
|
9913
|
+
* Check if a tool is denied by project config
|
|
9378
9914
|
*/
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
const k = 1024;
|
|
9382
|
-
const sizes = ["B", "KB", "MB", "GB"];
|
|
9383
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
9384
|
-
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
9915
|
+
isDenied(toolName) {
|
|
9916
|
+
return this.deniedTools.has(toolName);
|
|
9385
9917
|
}
|
|
9386
9918
|
/**
|
|
9387
|
-
*
|
|
9919
|
+
* Get list of denied tools
|
|
9388
9920
|
*/
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9921
|
+
getDeniedTools() {
|
|
9922
|
+
return Array.from(this.deniedTools).sort();
|
|
9923
|
+
}
|
|
9924
|
+
/**
|
|
9925
|
+
* Clear all trusted tools
|
|
9926
|
+
*/
|
|
9927
|
+
clearTrustedTools() {
|
|
9928
|
+
this.trustedTools.clear();
|
|
9929
|
+
}
|
|
9930
|
+
/**
|
|
9931
|
+
* Get statistics about permissions
|
|
9932
|
+
*/
|
|
9933
|
+
getStats() {
|
|
9934
|
+
return {
|
|
9935
|
+
trustedCount: this.trustedTools.size,
|
|
9936
|
+
autoApproveCount: 0,
|
|
9937
|
+
// TODO: Would need full tool list
|
|
9938
|
+
promptAlwaysCount: 0,
|
|
9939
|
+
promptDefaultCount: 0
|
|
9940
|
+
};
|
|
9406
9941
|
}
|
|
9407
9942
|
};
|
|
9408
|
-
|
|
9943
|
+
|
|
9944
|
+
// src/utils/apiUrl.ts
|
|
9945
|
+
var BIKE4MIND_URL = "https://app.bike4mind.com";
|
|
9946
|
+
function getApiUrl(configApiConfig) {
|
|
9947
|
+
if (configApiConfig?.customUrl) {
|
|
9948
|
+
return configApiConfig.customUrl;
|
|
9949
|
+
}
|
|
9950
|
+
return BIKE4MIND_URL;
|
|
9951
|
+
}
|
|
9952
|
+
function getEnvironmentName(configApiConfig) {
|
|
9953
|
+
if (configApiConfig?.customUrl) {
|
|
9954
|
+
return "Self-Hosted";
|
|
9955
|
+
}
|
|
9956
|
+
return "Bike4Mind";
|
|
9957
|
+
}
|
|
9409
9958
|
|
|
9410
9959
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
9411
9960
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
9412
9961
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
9413
|
-
import
|
|
9962
|
+
import path13 from "path";
|
|
9414
9963
|
import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
|
|
9415
9964
|
var MCPClient = class {
|
|
9416
9965
|
// Note: This class handles MCP server communication with repository filtering
|
|
@@ -9451,16 +10000,16 @@ var MCPClient = class {
|
|
|
9451
10000
|
const root = process.env.INIT_CWD || process.cwd();
|
|
9452
10001
|
const candidatePaths = [
|
|
9453
10002
|
// When running from SST Lambda with node_modules structure (copyFiles)
|
|
9454
|
-
|
|
10003
|
+
path13.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
|
|
9455
10004
|
// When running from SST Lambda deployed environment (/var/task)
|
|
9456
|
-
|
|
10005
|
+
path13.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9457
10006
|
// When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
|
|
9458
|
-
|
|
10007
|
+
path13.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9459
10008
|
// When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
|
|
9460
|
-
|
|
10009
|
+
path13.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9461
10010
|
// Original paths (backward compatibility)
|
|
9462
|
-
|
|
9463
|
-
|
|
10011
|
+
path13.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
10012
|
+
path13.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
9464
10013
|
];
|
|
9465
10014
|
const serverScriptPath = candidatePaths.find((p) => existsSync8(p));
|
|
9466
10015
|
if (!serverScriptPath) {
|
|
@@ -10210,7 +10759,10 @@ var ApiClient = class {
|
|
|
10210
10759
|
* Make a POST request
|
|
10211
10760
|
*/
|
|
10212
10761
|
async post(url, data, config) {
|
|
10762
|
+
logger.debug(`[ApiClient] POST ${this.client.defaults.baseURL}${url}`);
|
|
10763
|
+
logger.debug(`[ApiClient] Request body: ${JSON.stringify(data)}`);
|
|
10213
10764
|
const response = await this.client.post(url, data, config);
|
|
10765
|
+
logger.debug(`[ApiClient] Response status: ${response.status}`);
|
|
10214
10766
|
return response.data;
|
|
10215
10767
|
}
|
|
10216
10768
|
/**
|
|
@@ -10261,7 +10813,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
10261
10813
|
// package.json
|
|
10262
10814
|
var package_default = {
|
|
10263
10815
|
name: "@bike4mind/cli",
|
|
10264
|
-
version: "0.2.
|
|
10816
|
+
version: "0.2.8-cli-bash-tools.17146+9129ce1ea",
|
|
10265
10817
|
type: "module",
|
|
10266
10818
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
10267
10819
|
license: "UNLICENSED",
|
|
@@ -10364,10 +10916,10 @@ var package_default = {
|
|
|
10364
10916
|
},
|
|
10365
10917
|
devDependencies: {
|
|
10366
10918
|
"@bike4mind/agents": "0.1.0",
|
|
10367
|
-
"@bike4mind/common": "2.38.
|
|
10368
|
-
"@bike4mind/mcp": "1.20.
|
|
10369
|
-
"@bike4mind/services": "2.33.
|
|
10370
|
-
"@bike4mind/utils": "2.1.
|
|
10919
|
+
"@bike4mind/common": "2.38.2-cli-bash-tools.17146+9129ce1ea",
|
|
10920
|
+
"@bike4mind/mcp": "1.20.2-cli-bash-tools.17146+9129ce1ea",
|
|
10921
|
+
"@bike4mind/services": "2.33.2-cli-bash-tools.17146+9129ce1ea",
|
|
10922
|
+
"@bike4mind/utils": "2.1.3-cli-bash-tools.17146+9129ce1ea",
|
|
10371
10923
|
"@types/better-sqlite3": "^7.6.12",
|
|
10372
10924
|
"@types/diff": "^5.0.9",
|
|
10373
10925
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -10380,7 +10932,7 @@ var package_default = {
|
|
|
10380
10932
|
typescript: "^5.9.3",
|
|
10381
10933
|
vitest: "^3.2.4"
|
|
10382
10934
|
},
|
|
10383
|
-
gitHead: "
|
|
10935
|
+
gitHead: "9129ce1ea5d8589dec80ac92bf32ca954c6844fc"
|
|
10384
10936
|
};
|
|
10385
10937
|
|
|
10386
10938
|
// src/config/constants.ts
|
|
@@ -10402,7 +10954,7 @@ var usageCache = null;
|
|
|
10402
10954
|
function CliApp() {
|
|
10403
10955
|
const { exit } = useApp();
|
|
10404
10956
|
const imageRenderer = new ImageRenderer();
|
|
10405
|
-
const [state, setState] =
|
|
10957
|
+
const [state, setState] = useState5({
|
|
10406
10958
|
session: null,
|
|
10407
10959
|
sessionStore: new SessionStore(),
|
|
10408
10960
|
configStore: new ConfigStore(),
|
|
@@ -10415,11 +10967,12 @@ function CliApp() {
|
|
|
10415
10967
|
mcpManager: null,
|
|
10416
10968
|
permissionManager: null,
|
|
10417
10969
|
permissionPrompt: null,
|
|
10418
|
-
trustLocationSelector: null
|
|
10970
|
+
trustLocationSelector: null,
|
|
10971
|
+
rewindSelector: null
|
|
10419
10972
|
});
|
|
10420
|
-
const [isInitialized, setIsInitialized] =
|
|
10421
|
-
const [initError, setInitError] =
|
|
10422
|
-
const [commandHistory, setCommandHistory] =
|
|
10973
|
+
const [isInitialized, setIsInitialized] = useState5(false);
|
|
10974
|
+
const [initError, setInitError] = useState5(null);
|
|
10975
|
+
const [commandHistory, setCommandHistory] = useState5([]);
|
|
10423
10976
|
const imageStoreInitPromise = useRef(null);
|
|
10424
10977
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
10425
10978
|
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
@@ -10460,7 +11013,7 @@ function CliApp() {
|
|
|
10460
11013
|
process.exit(0);
|
|
10461
11014
|
}, 100);
|
|
10462
11015
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
10463
|
-
|
|
11016
|
+
useInput4((input, key) => {
|
|
10464
11017
|
if (key.ctrl && input === "c") {
|
|
10465
11018
|
const now = Date.now();
|
|
10466
11019
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
@@ -10604,10 +11157,6 @@ function CliApp() {
|
|
|
10604
11157
|
setPermissionPrompt(prompt);
|
|
10605
11158
|
});
|
|
10606
11159
|
};
|
|
10607
|
-
console.log("\u{1F511} Tool API Keys:", {
|
|
10608
|
-
openweather: config.toolApiKeys?.openweather ? "\u2713 configured" : "\u2717 missing",
|
|
10609
|
-
serper: config.toolApiKeys?.serper ? "\u2713 configured" : "\u2717 missing"
|
|
10610
|
-
});
|
|
10611
11160
|
const agentContext = {
|
|
10612
11161
|
currentAgent: null,
|
|
10613
11162
|
observationQueue: []
|
|
@@ -10620,7 +11169,7 @@ function CliApp() {
|
|
|
10620
11169
|
promptFn,
|
|
10621
11170
|
agentContext,
|
|
10622
11171
|
state.configStore,
|
|
10623
|
-
|
|
11172
|
+
apiClient
|
|
10624
11173
|
);
|
|
10625
11174
|
console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
|
|
10626
11175
|
const mcpManager = new McpManager(config);
|
|
@@ -10718,6 +11267,29 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10718
11267
|
console.log("\u{1F4A1} Run /login to authenticate with your B4M account.\n");
|
|
10719
11268
|
return;
|
|
10720
11269
|
}
|
|
11270
|
+
const currentSteps = [];
|
|
11271
|
+
const stepHandler = (step) => {
|
|
11272
|
+
currentSteps.push(step);
|
|
11273
|
+
const currentSession = useCliStore.getState().session;
|
|
11274
|
+
if (currentSession) {
|
|
11275
|
+
const messages = [...currentSession.messages];
|
|
11276
|
+
const lastMessage = messages[messages.length - 1];
|
|
11277
|
+
if (lastMessage && lastMessage.role === "assistant") {
|
|
11278
|
+
messages[messages.length - 1] = {
|
|
11279
|
+
...lastMessage,
|
|
11280
|
+
metadata: {
|
|
11281
|
+
...lastMessage.metadata,
|
|
11282
|
+
steps: [...currentSteps]
|
|
11283
|
+
}
|
|
11284
|
+
};
|
|
11285
|
+
setStoreSession({
|
|
11286
|
+
...currentSession,
|
|
11287
|
+
messages
|
|
11288
|
+
});
|
|
11289
|
+
}
|
|
11290
|
+
}
|
|
11291
|
+
};
|
|
11292
|
+
state.agent.on("action", stepHandler);
|
|
10721
11293
|
try {
|
|
10722
11294
|
let messageContent = message;
|
|
10723
11295
|
let userMessageContent = message;
|
|
@@ -10731,6 +11303,21 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10731
11303
|
content: userMessageContent,
|
|
10732
11304
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10733
11305
|
};
|
|
11306
|
+
const pendingAssistantMessage = {
|
|
11307
|
+
role: "assistant",
|
|
11308
|
+
content: "...",
|
|
11309
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11310
|
+
metadata: {
|
|
11311
|
+
steps: []
|
|
11312
|
+
}
|
|
11313
|
+
};
|
|
11314
|
+
const sessionWithMessages = {
|
|
11315
|
+
...state.session,
|
|
11316
|
+
messages: [...state.session.messages, userMessage, pendingAssistantMessage],
|
|
11317
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11318
|
+
};
|
|
11319
|
+
setState((prev) => ({ ...prev, session: sessionWithMessages }));
|
|
11320
|
+
setStoreSession(sessionWithMessages);
|
|
10734
11321
|
const recentMessages = state.session.messages.slice(-20);
|
|
10735
11322
|
const previousMessages = recentMessages.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => ({
|
|
10736
11323
|
role: msg.role,
|
|
@@ -10744,26 +11331,28 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10744
11331
|
console.log("\n\u26A0\uFE0F Action denied by user\n");
|
|
10745
11332
|
}
|
|
10746
11333
|
const successfulToolCalls = result.steps.filter((s) => s.type === "observation").length;
|
|
10747
|
-
const
|
|
11334
|
+
const currentSession = useCliStore.getState().session;
|
|
11335
|
+
if (!currentSession) return;
|
|
11336
|
+
const finalMessages = [...currentSession.messages];
|
|
11337
|
+
const finalAssistantMessage = {
|
|
10748
11338
|
role: "assistant",
|
|
10749
11339
|
content: result.finalAnswer,
|
|
10750
|
-
timestamp:
|
|
11340
|
+
timestamp: pendingAssistantMessage.timestamp,
|
|
10751
11341
|
metadata: {
|
|
10752
11342
|
tokenUsage: {
|
|
10753
11343
|
prompt: 0,
|
|
10754
|
-
// TODO: Extract from result
|
|
10755
11344
|
completion: 0,
|
|
10756
11345
|
total: result.completionInfo.totalTokens
|
|
10757
11346
|
},
|
|
10758
11347
|
steps: result.steps,
|
|
10759
11348
|
// Complete history: thoughts, actions, observations
|
|
10760
11349
|
permissionDenied
|
|
10761
|
-
// Mark if this ended due to permission denial
|
|
10762
11350
|
}
|
|
10763
11351
|
};
|
|
11352
|
+
finalMessages[finalMessages.length - 1] = finalAssistantMessage;
|
|
10764
11353
|
const updatedSession = {
|
|
10765
|
-
...
|
|
10766
|
-
messages:
|
|
11354
|
+
...currentSession,
|
|
11355
|
+
messages: finalMessages,
|
|
10767
11356
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10768
11357
|
metadata: {
|
|
10769
11358
|
...state.session.metadata,
|
|
@@ -10783,6 +11372,8 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10783
11372
|
}
|
|
10784
11373
|
}
|
|
10785
11374
|
console.error("Error processing message:", error);
|
|
11375
|
+
} finally {
|
|
11376
|
+
state.agent.off("action", stepHandler);
|
|
10786
11377
|
}
|
|
10787
11378
|
};
|
|
10788
11379
|
const handleImageDetected = async (imageData) => {
|
|
@@ -10814,6 +11405,30 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10814
11405
|
throw error;
|
|
10815
11406
|
}
|
|
10816
11407
|
};
|
|
11408
|
+
const recalculateSessionMetadata = (messages) => {
|
|
11409
|
+
let totalTokens = 0;
|
|
11410
|
+
let totalCost = 0;
|
|
11411
|
+
let toolCallCount = 0;
|
|
11412
|
+
for (const msg of messages) {
|
|
11413
|
+
if (msg.metadata) {
|
|
11414
|
+
if (msg.metadata.tokenUsage) {
|
|
11415
|
+
totalTokens += msg.metadata.tokenUsage.total || 0;
|
|
11416
|
+
}
|
|
11417
|
+
if (msg.metadata.cost) {
|
|
11418
|
+
totalCost += msg.metadata.cost;
|
|
11419
|
+
}
|
|
11420
|
+
if (msg.metadata.steps) {
|
|
11421
|
+
const observations = msg.metadata.steps.filter((s) => s.type === "observation");
|
|
11422
|
+
toolCallCount += observations.length;
|
|
11423
|
+
}
|
|
11424
|
+
}
|
|
11425
|
+
}
|
|
11426
|
+
return {
|
|
11427
|
+
totalTokens,
|
|
11428
|
+
totalCost,
|
|
11429
|
+
toolCallCount
|
|
11430
|
+
};
|
|
11431
|
+
};
|
|
10817
11432
|
const handleCommand = async (command, args) => {
|
|
10818
11433
|
switch (command) {
|
|
10819
11434
|
case "help":
|
|
@@ -10822,6 +11437,7 @@ Available commands:
|
|
|
10822
11437
|
/help - Show this help message
|
|
10823
11438
|
/exit - Exit the CLI
|
|
10824
11439
|
/clear - Start a new session
|
|
11440
|
+
/rewind - Rewind conversation to a previous point
|
|
10825
11441
|
/login - Authenticate with your B4M account
|
|
10826
11442
|
/logout - Clear authentication and sign out
|
|
10827
11443
|
/whoami - Show current authenticated user
|
|
@@ -11097,6 +11713,65 @@ Project Configuration:
|
|
|
11097
11713
|
`);
|
|
11098
11714
|
break;
|
|
11099
11715
|
}
|
|
11716
|
+
case "rewind":
|
|
11717
|
+
case "undo": {
|
|
11718
|
+
if (!state.session) {
|
|
11719
|
+
console.log("No active session to rewind");
|
|
11720
|
+
return;
|
|
11721
|
+
}
|
|
11722
|
+
if (state.session.messages.length === 0) {
|
|
11723
|
+
console.log("\u26A0\uFE0F Conversation is empty. Nothing to rewind.");
|
|
11724
|
+
return;
|
|
11725
|
+
}
|
|
11726
|
+
const userMessages = state.session.messages.filter((msg) => msg.role === "user");
|
|
11727
|
+
if (userMessages.length === 0) {
|
|
11728
|
+
console.log("\u26A0\uFE0F No user messages found. Nothing to rewind.");
|
|
11729
|
+
return;
|
|
11730
|
+
}
|
|
11731
|
+
if (userMessages.length === 1) {
|
|
11732
|
+
console.log("\u26A0\uFE0F Only one exchange in the conversation. Use /clear to start a new session instead.");
|
|
11733
|
+
return;
|
|
11734
|
+
}
|
|
11735
|
+
const handleRewindSelect = async (messageIndex) => {
|
|
11736
|
+
setState((prev) => ({ ...prev, rewindSelector: null }));
|
|
11737
|
+
if (messageIndex === null) {
|
|
11738
|
+
console.log("\u274C Rewind operation cancelled");
|
|
11739
|
+
return;
|
|
11740
|
+
}
|
|
11741
|
+
if (!state.session) {
|
|
11742
|
+
console.log("\u274C No active session");
|
|
11743
|
+
return;
|
|
11744
|
+
}
|
|
11745
|
+
const selectedMessage = state.session.messages[messageIndex];
|
|
11746
|
+
const prefillContent = selectedMessage?.content || "";
|
|
11747
|
+
const rewindedMessages = state.session.messages.slice(0, messageIndex);
|
|
11748
|
+
const newMetadata = recalculateSessionMetadata(rewindedMessages);
|
|
11749
|
+
const rewindedSession = {
|
|
11750
|
+
...state.session,
|
|
11751
|
+
messages: rewindedMessages,
|
|
11752
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11753
|
+
metadata: newMetadata
|
|
11754
|
+
};
|
|
11755
|
+
setState((prev) => ({
|
|
11756
|
+
...prev,
|
|
11757
|
+
session: rewindedSession,
|
|
11758
|
+
prefillInput: prefillContent
|
|
11759
|
+
}));
|
|
11760
|
+
setStoreSession(rewindedSession);
|
|
11761
|
+
await state.sessionStore.save(rewindedSession);
|
|
11762
|
+
console.log("\u2705 Conversation rewound successfully");
|
|
11763
|
+
console.log(`\u{1F4CA} Current state: ${rewindedMessages.length} messages, ${newMetadata.totalTokens} tokens`);
|
|
11764
|
+
console.log(`\u{1F4DD} Your message has been placed in the input. Edit and send when ready.
|
|
11765
|
+
`);
|
|
11766
|
+
};
|
|
11767
|
+
setState((prev) => ({
|
|
11768
|
+
...prev,
|
|
11769
|
+
rewindSelector: {
|
|
11770
|
+
resolve: handleRewindSelect
|
|
11771
|
+
}
|
|
11772
|
+
}));
|
|
11773
|
+
break;
|
|
11774
|
+
}
|
|
11100
11775
|
case "usage": {
|
|
11101
11776
|
const usageAuthTokens = await state.configStore.getAuthTokens();
|
|
11102
11777
|
if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
@@ -11250,12 +11925,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11250
11925
|
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
11251
11926
|
};
|
|
11252
11927
|
if (initError) {
|
|
11253
|
-
return /* @__PURE__ */
|
|
11928
|
+
return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React14.createElement(Text13, null, initError), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
|
|
11254
11929
|
}
|
|
11255
11930
|
if (state.trustLocationSelector) {
|
|
11256
11931
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
11257
11932
|
const inProject = projectDir !== null;
|
|
11258
|
-
return /* @__PURE__ */
|
|
11933
|
+
return /* @__PURE__ */ React14.createElement(
|
|
11259
11934
|
TrustLocationSelector,
|
|
11260
11935
|
{
|
|
11261
11936
|
inProject,
|
|
@@ -11272,9 +11947,27 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11272
11947
|
}
|
|
11273
11948
|
);
|
|
11274
11949
|
}
|
|
11950
|
+
if (state.rewindSelector && state.session) {
|
|
11951
|
+
return /* @__PURE__ */ React14.createElement(
|
|
11952
|
+
RewindSelector,
|
|
11953
|
+
{
|
|
11954
|
+
messages: state.session.messages,
|
|
11955
|
+
onSelect: (messageIndex) => {
|
|
11956
|
+
if (state.rewindSelector) {
|
|
11957
|
+
state.rewindSelector.resolve(messageIndex);
|
|
11958
|
+
}
|
|
11959
|
+
},
|
|
11960
|
+
onCancel: () => {
|
|
11961
|
+
if (state.rewindSelector) {
|
|
11962
|
+
state.rewindSelector.resolve(null);
|
|
11963
|
+
}
|
|
11964
|
+
}
|
|
11965
|
+
}
|
|
11966
|
+
);
|
|
11967
|
+
}
|
|
11275
11968
|
if (state.showLoginFlow) {
|
|
11276
11969
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
11277
|
-
return /* @__PURE__ */
|
|
11970
|
+
return /* @__PURE__ */ React14.createElement(
|
|
11278
11971
|
LoginFlow,
|
|
11279
11972
|
{
|
|
11280
11973
|
apiUrl: loginApiUrl,
|
|
@@ -11297,9 +11990,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11297
11990
|
);
|
|
11298
11991
|
}
|
|
11299
11992
|
if (!isInitialized) {
|
|
11300
|
-
return /* @__PURE__ */
|
|
11993
|
+
return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, "\u{1F680} Initializing..."));
|
|
11301
11994
|
}
|
|
11302
|
-
return /* @__PURE__ */
|
|
11995
|
+
return /* @__PURE__ */ React14.createElement(
|
|
11303
11996
|
App,
|
|
11304
11997
|
{
|
|
11305
11998
|
onMessage: handleMessage,
|
|
@@ -11309,6 +12002,10 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11309
12002
|
config: state.config,
|
|
11310
12003
|
availableModels: state.availableModels,
|
|
11311
12004
|
onSaveConfig: handleSaveConfig,
|
|
12005
|
+
prefillInput: state.prefillInput,
|
|
12006
|
+
onPrefillConsumed: () => {
|
|
12007
|
+
setState((prev) => ({ ...prev, prefillInput: void 0 }));
|
|
12008
|
+
},
|
|
11312
12009
|
onPermissionResponse: (response) => {
|
|
11313
12010
|
if (state.permissionPrompt) {
|
|
11314
12011
|
state.permissionPrompt.resolve({ action: response });
|
|
@@ -11334,6 +12031,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
11334
12031
|
if (isDevMode) {
|
|
11335
12032
|
console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
11336
12033
|
}
|
|
11337
|
-
render(/* @__PURE__ */
|
|
12034
|
+
render(/* @__PURE__ */ React14.createElement(CliApp, null), {
|
|
11338
12035
|
exitOnCtrlC: false
|
|
11339
12036
|
});
|