@bike4mind/cli 0.2.7-feature-notebook-splash-redesign.17129 → 0.2.8-cli-bash-tools.17140
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-DNE5C6DY.js → artifactExtractor-LF5DAVZL.js} +1 -1
- package/dist/{chunk-3CCBDIFR.js → chunk-6S7HV2GF.js} +2 -2
- package/dist/{chunk-LIRY24M5.js → chunk-F2K7OCDJ.js} +2 -2
- package/dist/{chunk-H6SHASMZ.js → chunk-HD4QQGS6.js} +3 -1
- package/dist/{chunk-YXHQN7SK.js → chunk-RJYG7BUV.js} +2 -2
- package/dist/{chunk-DNSBC3LO.js → chunk-SE2T5GED.js} +1 -1
- package/dist/{create-PNU64QSF.js → create-S7N2FBWR.js} +3 -3
- package/dist/index.js +705 -79
- package/dist/{llmMarkdownGenerator-UQXKK2AS.js → llmMarkdownGenerator-VY7FSNOI.js} +1 -1
- package/dist/{markdownGenerator-FLLZFXMT.js → markdownGenerator-KES2TDKF.js} +1 -1
- package/dist/{mementoService-LVFQ5XAW.js → mementoService-S4OOMZIT.js} +3 -3
- package/dist/{src-OH5AAQEW.js → src-7PSZ4VA4.js} +2 -2
- package/dist/{src-OYQXRLTM.js → src-VFRCY2I4.js} +1 -1
- package/dist/{subtractCredits-75VT7HP2.js → subtractCredits-DD7PBL22.js} +3 -3
- package/package.json +7 -7
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 = {
|
|
@@ -8768,21 +9238,21 @@ var NoOpStorage = class extends BaseStorage {
|
|
|
8768
9238
|
async upload(input, destination, options) {
|
|
8769
9239
|
return `/tmp/${destination}`;
|
|
8770
9240
|
}
|
|
8771
|
-
async download(
|
|
9241
|
+
async download(path14) {
|
|
8772
9242
|
throw new Error("Download not supported in CLI");
|
|
8773
9243
|
}
|
|
8774
|
-
async delete(
|
|
9244
|
+
async delete(path14) {
|
|
8775
9245
|
}
|
|
8776
|
-
async getSignedUrl(
|
|
8777
|
-
return `/tmp/${
|
|
9246
|
+
async getSignedUrl(path14) {
|
|
9247
|
+
return `/tmp/${path14}`;
|
|
8778
9248
|
}
|
|
8779
|
-
getPublicUrl(
|
|
8780
|
-
return `/tmp/${
|
|
9249
|
+
getPublicUrl(path14) {
|
|
9250
|
+
return `/tmp/${path14}`;
|
|
8781
9251
|
}
|
|
8782
|
-
async getPreview(
|
|
8783
|
-
return `/tmp/${
|
|
9252
|
+
async getPreview(path14) {
|
|
9253
|
+
return `/tmp/${path14}`;
|
|
8784
9254
|
}
|
|
8785
|
-
async getMetadata(
|
|
9255
|
+
async getMetadata(path14) {
|
|
8786
9256
|
return { size: 0, contentType: "application/octet-stream" };
|
|
8787
9257
|
}
|
|
8788
9258
|
};
|
|
@@ -8840,6 +9310,10 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
8840
9310
|
} catch (error) {
|
|
8841
9311
|
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
8842
9312
|
}
|
|
9313
|
+
} else if (toolName === "bash_execute" && args?.command) {
|
|
9314
|
+
const cwd = args.cwd ? ` (in ${args.cwd})` : "";
|
|
9315
|
+
const timeout = args.timeout ? ` [timeout: ${args.timeout}ms]` : "";
|
|
9316
|
+
preview = `$ ${args.command}${cwd}${timeout}`;
|
|
8843
9317
|
}
|
|
8844
9318
|
const response = await showPermissionPrompt(toolName, args, preview);
|
|
8845
9319
|
if (response.action === "deny") {
|
|
@@ -8924,6 +9398,8 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
8924
9398
|
glob_files: {},
|
|
8925
9399
|
grep_search: {},
|
|
8926
9400
|
delete_file: {},
|
|
9401
|
+
// Shell execution (always requires permission)
|
|
9402
|
+
bash_execute: {},
|
|
8927
9403
|
// Tools requiring API keys (only include if API key is available)
|
|
8928
9404
|
...hasSerperKey && { web_search: {} },
|
|
8929
9405
|
// Requires SERPER_API_KEY
|
|
@@ -8991,6 +9467,7 @@ var DEFAULT_TOOL_CATEGORIES = {
|
|
|
8991
9467
|
create_file: "prompt_always",
|
|
8992
9468
|
delete_file: "prompt_always",
|
|
8993
9469
|
shell_execute: "prompt_always",
|
|
9470
|
+
bash_execute: "prompt_always",
|
|
8994
9471
|
git_commit: "prompt_always",
|
|
8995
9472
|
git_push: "prompt_always",
|
|
8996
9473
|
// ===== PROMPT DEFAULT (Repository read tools) =====
|
|
@@ -9154,7 +9631,7 @@ function getEnvironmentName(configApiConfig) {
|
|
|
9154
9631
|
|
|
9155
9632
|
// src/utils/Logger.ts
|
|
9156
9633
|
import fs10 from "fs/promises";
|
|
9157
|
-
import
|
|
9634
|
+
import path12 from "path";
|
|
9158
9635
|
import os from "os";
|
|
9159
9636
|
var Logger2 = class _Logger {
|
|
9160
9637
|
constructor() {
|
|
@@ -9177,9 +9654,9 @@ var Logger2 = class _Logger {
|
|
|
9177
9654
|
*/
|
|
9178
9655
|
async initialize(sessionId) {
|
|
9179
9656
|
this.sessionId = sessionId;
|
|
9180
|
-
const debugDir =
|
|
9657
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9181
9658
|
await fs10.mkdir(debugDir, { recursive: true });
|
|
9182
|
-
this.logFilePath =
|
|
9659
|
+
this.logFilePath = path12.join(debugDir, `${sessionId}.txt`);
|
|
9183
9660
|
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
9184
9661
|
}
|
|
9185
9662
|
/**
|
|
@@ -9389,12 +9866,12 @@ var Logger2 = class _Logger {
|
|
|
9389
9866
|
async cleanupOldLogs() {
|
|
9390
9867
|
if (!this.fileLoggingEnabled) return;
|
|
9391
9868
|
try {
|
|
9392
|
-
const debugDir =
|
|
9869
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9393
9870
|
const files = await fs10.readdir(debugDir);
|
|
9394
9871
|
const now = Date.now();
|
|
9395
9872
|
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
9396
9873
|
for (const file of files) {
|
|
9397
|
-
const filePath =
|
|
9874
|
+
const filePath = path12.join(debugDir, file);
|
|
9398
9875
|
const stats = await fs10.stat(filePath);
|
|
9399
9876
|
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
9400
9877
|
await fs10.unlink(filePath);
|
|
@@ -9410,7 +9887,7 @@ var logger = Logger2.getInstance();
|
|
|
9410
9887
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
9411
9888
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
9412
9889
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
9413
|
-
import
|
|
9890
|
+
import path13 from "path";
|
|
9414
9891
|
import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
|
|
9415
9892
|
var MCPClient = class {
|
|
9416
9893
|
// Note: This class handles MCP server communication with repository filtering
|
|
@@ -9451,16 +9928,16 @@ var MCPClient = class {
|
|
|
9451
9928
|
const root = process.env.INIT_CWD || process.cwd();
|
|
9452
9929
|
const candidatePaths = [
|
|
9453
9930
|
// When running from SST Lambda with node_modules structure (copyFiles)
|
|
9454
|
-
|
|
9931
|
+
path13.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
|
|
9455
9932
|
// When running from SST Lambda deployed environment (/var/task)
|
|
9456
|
-
|
|
9933
|
+
path13.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9457
9934
|
// When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
|
|
9458
|
-
|
|
9935
|
+
path13.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9459
9936
|
// When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
|
|
9460
|
-
|
|
9937
|
+
path13.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9461
9938
|
// Original paths (backward compatibility)
|
|
9462
|
-
|
|
9463
|
-
|
|
9939
|
+
path13.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9940
|
+
path13.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
9464
9941
|
];
|
|
9465
9942
|
const serverScriptPath = candidatePaths.find((p) => existsSync8(p));
|
|
9466
9943
|
if (!serverScriptPath) {
|
|
@@ -10261,7 +10738,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
10261
10738
|
// package.json
|
|
10262
10739
|
var package_default = {
|
|
10263
10740
|
name: "@bike4mind/cli",
|
|
10264
|
-
version: "0.2.
|
|
10741
|
+
version: "0.2.8-cli-bash-tools.17140+f678d9888",
|
|
10265
10742
|
type: "module",
|
|
10266
10743
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
10267
10744
|
license: "UNLICENSED",
|
|
@@ -10364,11 +10841,11 @@ var package_default = {
|
|
|
10364
10841
|
},
|
|
10365
10842
|
devDependencies: {
|
|
10366
10843
|
"@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.
|
|
10371
|
-
"@types/better-sqlite3": "^7.6.
|
|
10844
|
+
"@bike4mind/common": "2.38.2-cli-bash-tools.17140+f678d9888",
|
|
10845
|
+
"@bike4mind/mcp": "1.20.2-cli-bash-tools.17140+f678d9888",
|
|
10846
|
+
"@bike4mind/services": "2.33.2-cli-bash-tools.17140+f678d9888",
|
|
10847
|
+
"@bike4mind/utils": "2.1.3-cli-bash-tools.17140+f678d9888",
|
|
10848
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
10372
10849
|
"@types/diff": "^5.0.9",
|
|
10373
10850
|
"@types/jsonwebtoken": "^9.0.4",
|
|
10374
10851
|
"@types/node": "^22.9.0",
|
|
@@ -10380,7 +10857,7 @@ var package_default = {
|
|
|
10380
10857
|
typescript: "^5.9.3",
|
|
10381
10858
|
vitest: "^3.2.4"
|
|
10382
10859
|
},
|
|
10383
|
-
gitHead: "
|
|
10860
|
+
gitHead: "f678d98888d841ba0496d91a1225dd89edb21e35"
|
|
10384
10861
|
};
|
|
10385
10862
|
|
|
10386
10863
|
// src/config/constants.ts
|
|
@@ -10402,7 +10879,7 @@ var usageCache = null;
|
|
|
10402
10879
|
function CliApp() {
|
|
10403
10880
|
const { exit } = useApp();
|
|
10404
10881
|
const imageRenderer = new ImageRenderer();
|
|
10405
|
-
const [state, setState] =
|
|
10882
|
+
const [state, setState] = useState5({
|
|
10406
10883
|
session: null,
|
|
10407
10884
|
sessionStore: new SessionStore(),
|
|
10408
10885
|
configStore: new ConfigStore(),
|
|
@@ -10415,11 +10892,12 @@ function CliApp() {
|
|
|
10415
10892
|
mcpManager: null,
|
|
10416
10893
|
permissionManager: null,
|
|
10417
10894
|
permissionPrompt: null,
|
|
10418
|
-
trustLocationSelector: null
|
|
10895
|
+
trustLocationSelector: null,
|
|
10896
|
+
rewindSelector: null
|
|
10419
10897
|
});
|
|
10420
|
-
const [isInitialized, setIsInitialized] =
|
|
10421
|
-
const [initError, setInitError] =
|
|
10422
|
-
const [commandHistory, setCommandHistory] =
|
|
10898
|
+
const [isInitialized, setIsInitialized] = useState5(false);
|
|
10899
|
+
const [initError, setInitError] = useState5(null);
|
|
10900
|
+
const [commandHistory, setCommandHistory] = useState5([]);
|
|
10423
10901
|
const imageStoreInitPromise = useRef(null);
|
|
10424
10902
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
10425
10903
|
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
@@ -10460,7 +10938,7 @@ function CliApp() {
|
|
|
10460
10938
|
process.exit(0);
|
|
10461
10939
|
}, 100);
|
|
10462
10940
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
10463
|
-
|
|
10941
|
+
useInput4((input, key) => {
|
|
10464
10942
|
if (key.ctrl && input === "c") {
|
|
10465
10943
|
const now = Date.now();
|
|
10466
10944
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
@@ -10718,6 +11196,29 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10718
11196
|
console.log("\u{1F4A1} Run /login to authenticate with your B4M account.\n");
|
|
10719
11197
|
return;
|
|
10720
11198
|
}
|
|
11199
|
+
const currentSteps = [];
|
|
11200
|
+
const stepHandler = (step) => {
|
|
11201
|
+
currentSteps.push(step);
|
|
11202
|
+
const currentSession = useCliStore.getState().session;
|
|
11203
|
+
if (currentSession) {
|
|
11204
|
+
const messages = [...currentSession.messages];
|
|
11205
|
+
const lastMessage = messages[messages.length - 1];
|
|
11206
|
+
if (lastMessage && lastMessage.role === "assistant") {
|
|
11207
|
+
messages[messages.length - 1] = {
|
|
11208
|
+
...lastMessage,
|
|
11209
|
+
metadata: {
|
|
11210
|
+
...lastMessage.metadata,
|
|
11211
|
+
steps: [...currentSteps]
|
|
11212
|
+
}
|
|
11213
|
+
};
|
|
11214
|
+
setStoreSession({
|
|
11215
|
+
...currentSession,
|
|
11216
|
+
messages
|
|
11217
|
+
});
|
|
11218
|
+
}
|
|
11219
|
+
}
|
|
11220
|
+
};
|
|
11221
|
+
state.agent.on("action", stepHandler);
|
|
10721
11222
|
try {
|
|
10722
11223
|
let messageContent = message;
|
|
10723
11224
|
let userMessageContent = message;
|
|
@@ -10731,6 +11232,21 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10731
11232
|
content: userMessageContent,
|
|
10732
11233
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10733
11234
|
};
|
|
11235
|
+
const pendingAssistantMessage = {
|
|
11236
|
+
role: "assistant",
|
|
11237
|
+
content: "...",
|
|
11238
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11239
|
+
metadata: {
|
|
11240
|
+
steps: []
|
|
11241
|
+
}
|
|
11242
|
+
};
|
|
11243
|
+
const sessionWithMessages = {
|
|
11244
|
+
...state.session,
|
|
11245
|
+
messages: [...state.session.messages, userMessage, pendingAssistantMessage],
|
|
11246
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11247
|
+
};
|
|
11248
|
+
setState((prev) => ({ ...prev, session: sessionWithMessages }));
|
|
11249
|
+
setStoreSession(sessionWithMessages);
|
|
10734
11250
|
const recentMessages = state.session.messages.slice(-20);
|
|
10735
11251
|
const previousMessages = recentMessages.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => ({
|
|
10736
11252
|
role: msg.role,
|
|
@@ -10744,26 +11260,28 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10744
11260
|
console.log("\n\u26A0\uFE0F Action denied by user\n");
|
|
10745
11261
|
}
|
|
10746
11262
|
const successfulToolCalls = result.steps.filter((s) => s.type === "observation").length;
|
|
10747
|
-
const
|
|
11263
|
+
const currentSession = useCliStore.getState().session;
|
|
11264
|
+
if (!currentSession) return;
|
|
11265
|
+
const finalMessages = [...currentSession.messages];
|
|
11266
|
+
const finalAssistantMessage = {
|
|
10748
11267
|
role: "assistant",
|
|
10749
11268
|
content: result.finalAnswer,
|
|
10750
|
-
timestamp:
|
|
11269
|
+
timestamp: pendingAssistantMessage.timestamp,
|
|
10751
11270
|
metadata: {
|
|
10752
11271
|
tokenUsage: {
|
|
10753
11272
|
prompt: 0,
|
|
10754
|
-
// TODO: Extract from result
|
|
10755
11273
|
completion: 0,
|
|
10756
11274
|
total: result.completionInfo.totalTokens
|
|
10757
11275
|
},
|
|
10758
11276
|
steps: result.steps,
|
|
10759
11277
|
// Complete history: thoughts, actions, observations
|
|
10760
11278
|
permissionDenied
|
|
10761
|
-
// Mark if this ended due to permission denial
|
|
10762
11279
|
}
|
|
10763
11280
|
};
|
|
11281
|
+
finalMessages[finalMessages.length - 1] = finalAssistantMessage;
|
|
10764
11282
|
const updatedSession = {
|
|
10765
|
-
...
|
|
10766
|
-
messages:
|
|
11283
|
+
...currentSession,
|
|
11284
|
+
messages: finalMessages,
|
|
10767
11285
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10768
11286
|
metadata: {
|
|
10769
11287
|
...state.session.metadata,
|
|
@@ -10783,6 +11301,8 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10783
11301
|
}
|
|
10784
11302
|
}
|
|
10785
11303
|
console.error("Error processing message:", error);
|
|
11304
|
+
} finally {
|
|
11305
|
+
state.agent.off("action", stepHandler);
|
|
10786
11306
|
}
|
|
10787
11307
|
};
|
|
10788
11308
|
const handleImageDetected = async (imageData) => {
|
|
@@ -10814,6 +11334,30 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10814
11334
|
throw error;
|
|
10815
11335
|
}
|
|
10816
11336
|
};
|
|
11337
|
+
const recalculateSessionMetadata = (messages) => {
|
|
11338
|
+
let totalTokens = 0;
|
|
11339
|
+
let totalCost = 0;
|
|
11340
|
+
let toolCallCount = 0;
|
|
11341
|
+
for (const msg of messages) {
|
|
11342
|
+
if (msg.metadata) {
|
|
11343
|
+
if (msg.metadata.tokenUsage) {
|
|
11344
|
+
totalTokens += msg.metadata.tokenUsage.total || 0;
|
|
11345
|
+
}
|
|
11346
|
+
if (msg.metadata.cost) {
|
|
11347
|
+
totalCost += msg.metadata.cost;
|
|
11348
|
+
}
|
|
11349
|
+
if (msg.metadata.steps) {
|
|
11350
|
+
const observations = msg.metadata.steps.filter((s) => s.type === "observation");
|
|
11351
|
+
toolCallCount += observations.length;
|
|
11352
|
+
}
|
|
11353
|
+
}
|
|
11354
|
+
}
|
|
11355
|
+
return {
|
|
11356
|
+
totalTokens,
|
|
11357
|
+
totalCost,
|
|
11358
|
+
toolCallCount
|
|
11359
|
+
};
|
|
11360
|
+
};
|
|
10817
11361
|
const handleCommand = async (command, args) => {
|
|
10818
11362
|
switch (command) {
|
|
10819
11363
|
case "help":
|
|
@@ -10822,6 +11366,7 @@ Available commands:
|
|
|
10822
11366
|
/help - Show this help message
|
|
10823
11367
|
/exit - Exit the CLI
|
|
10824
11368
|
/clear - Start a new session
|
|
11369
|
+
/rewind - Rewind conversation to a previous point
|
|
10825
11370
|
/login - Authenticate with your B4M account
|
|
10826
11371
|
/logout - Clear authentication and sign out
|
|
10827
11372
|
/whoami - Show current authenticated user
|
|
@@ -11097,6 +11642,65 @@ Project Configuration:
|
|
|
11097
11642
|
`);
|
|
11098
11643
|
break;
|
|
11099
11644
|
}
|
|
11645
|
+
case "rewind":
|
|
11646
|
+
case "undo": {
|
|
11647
|
+
if (!state.session) {
|
|
11648
|
+
console.log("No active session to rewind");
|
|
11649
|
+
return;
|
|
11650
|
+
}
|
|
11651
|
+
if (state.session.messages.length === 0) {
|
|
11652
|
+
console.log("\u26A0\uFE0F Conversation is empty. Nothing to rewind.");
|
|
11653
|
+
return;
|
|
11654
|
+
}
|
|
11655
|
+
const userMessages = state.session.messages.filter((msg) => msg.role === "user");
|
|
11656
|
+
if (userMessages.length === 0) {
|
|
11657
|
+
console.log("\u26A0\uFE0F No user messages found. Nothing to rewind.");
|
|
11658
|
+
return;
|
|
11659
|
+
}
|
|
11660
|
+
if (userMessages.length === 1) {
|
|
11661
|
+
console.log("\u26A0\uFE0F Only one exchange in the conversation. Use /clear to start a new session instead.");
|
|
11662
|
+
return;
|
|
11663
|
+
}
|
|
11664
|
+
const handleRewindSelect = async (messageIndex) => {
|
|
11665
|
+
setState((prev) => ({ ...prev, rewindSelector: null }));
|
|
11666
|
+
if (messageIndex === null) {
|
|
11667
|
+
console.log("\u274C Rewind operation cancelled");
|
|
11668
|
+
return;
|
|
11669
|
+
}
|
|
11670
|
+
if (!state.session) {
|
|
11671
|
+
console.log("\u274C No active session");
|
|
11672
|
+
return;
|
|
11673
|
+
}
|
|
11674
|
+
const selectedMessage = state.session.messages[messageIndex];
|
|
11675
|
+
const prefillContent = selectedMessage?.content || "";
|
|
11676
|
+
const rewindedMessages = state.session.messages.slice(0, messageIndex);
|
|
11677
|
+
const newMetadata = recalculateSessionMetadata(rewindedMessages);
|
|
11678
|
+
const rewindedSession = {
|
|
11679
|
+
...state.session,
|
|
11680
|
+
messages: rewindedMessages,
|
|
11681
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11682
|
+
metadata: newMetadata
|
|
11683
|
+
};
|
|
11684
|
+
setState((prev) => ({
|
|
11685
|
+
...prev,
|
|
11686
|
+
session: rewindedSession,
|
|
11687
|
+
prefillInput: prefillContent
|
|
11688
|
+
}));
|
|
11689
|
+
setStoreSession(rewindedSession);
|
|
11690
|
+
await state.sessionStore.save(rewindedSession);
|
|
11691
|
+
console.log("\u2705 Conversation rewound successfully");
|
|
11692
|
+
console.log(`\u{1F4CA} Current state: ${rewindedMessages.length} messages, ${newMetadata.totalTokens} tokens`);
|
|
11693
|
+
console.log(`\u{1F4DD} Your message has been placed in the input. Edit and send when ready.
|
|
11694
|
+
`);
|
|
11695
|
+
};
|
|
11696
|
+
setState((prev) => ({
|
|
11697
|
+
...prev,
|
|
11698
|
+
rewindSelector: {
|
|
11699
|
+
resolve: handleRewindSelect
|
|
11700
|
+
}
|
|
11701
|
+
}));
|
|
11702
|
+
break;
|
|
11703
|
+
}
|
|
11100
11704
|
case "usage": {
|
|
11101
11705
|
const usageAuthTokens = await state.configStore.getAuthTokens();
|
|
11102
11706
|
if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
@@ -11250,12 +11854,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11250
11854
|
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
11251
11855
|
};
|
|
11252
11856
|
if (initError) {
|
|
11253
|
-
return /* @__PURE__ */
|
|
11857
|
+
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
11858
|
}
|
|
11255
11859
|
if (state.trustLocationSelector) {
|
|
11256
11860
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
11257
11861
|
const inProject = projectDir !== null;
|
|
11258
|
-
return /* @__PURE__ */
|
|
11862
|
+
return /* @__PURE__ */ React14.createElement(
|
|
11259
11863
|
TrustLocationSelector,
|
|
11260
11864
|
{
|
|
11261
11865
|
inProject,
|
|
@@ -11272,9 +11876,27 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11272
11876
|
}
|
|
11273
11877
|
);
|
|
11274
11878
|
}
|
|
11879
|
+
if (state.rewindSelector && state.session) {
|
|
11880
|
+
return /* @__PURE__ */ React14.createElement(
|
|
11881
|
+
RewindSelector,
|
|
11882
|
+
{
|
|
11883
|
+
messages: state.session.messages,
|
|
11884
|
+
onSelect: (messageIndex) => {
|
|
11885
|
+
if (state.rewindSelector) {
|
|
11886
|
+
state.rewindSelector.resolve(messageIndex);
|
|
11887
|
+
}
|
|
11888
|
+
},
|
|
11889
|
+
onCancel: () => {
|
|
11890
|
+
if (state.rewindSelector) {
|
|
11891
|
+
state.rewindSelector.resolve(null);
|
|
11892
|
+
}
|
|
11893
|
+
}
|
|
11894
|
+
}
|
|
11895
|
+
);
|
|
11896
|
+
}
|
|
11275
11897
|
if (state.showLoginFlow) {
|
|
11276
11898
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
11277
|
-
return /* @__PURE__ */
|
|
11899
|
+
return /* @__PURE__ */ React14.createElement(
|
|
11278
11900
|
LoginFlow,
|
|
11279
11901
|
{
|
|
11280
11902
|
apiUrl: loginApiUrl,
|
|
@@ -11297,9 +11919,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11297
11919
|
);
|
|
11298
11920
|
}
|
|
11299
11921
|
if (!isInitialized) {
|
|
11300
|
-
return /* @__PURE__ */
|
|
11922
|
+
return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, "\u{1F680} Initializing..."));
|
|
11301
11923
|
}
|
|
11302
|
-
return /* @__PURE__ */
|
|
11924
|
+
return /* @__PURE__ */ React14.createElement(
|
|
11303
11925
|
App,
|
|
11304
11926
|
{
|
|
11305
11927
|
onMessage: handleMessage,
|
|
@@ -11309,6 +11931,10 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11309
11931
|
config: state.config,
|
|
11310
11932
|
availableModels: state.availableModels,
|
|
11311
11933
|
onSaveConfig: handleSaveConfig,
|
|
11934
|
+
prefillInput: state.prefillInput,
|
|
11935
|
+
onPrefillConsumed: () => {
|
|
11936
|
+
setState((prev) => ({ ...prev, prefillInput: void 0 }));
|
|
11937
|
+
},
|
|
11312
11938
|
onPermissionResponse: (response) => {
|
|
11313
11939
|
if (state.permissionPrompt) {
|
|
11314
11940
|
state.permissionPrompt.resolve({ action: response });
|
|
@@ -11334,6 +11960,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
11334
11960
|
if (isDevMode) {
|
|
11335
11961
|
console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
11336
11962
|
}
|
|
11337
|
-
render(/* @__PURE__ */
|
|
11963
|
+
render(/* @__PURE__ */ React14.createElement(CliApp, null), {
|
|
11338
11964
|
exitOnCtrlC: false
|
|
11339
11965
|
});
|