@bike4mind/cli 0.2.7-feat-cli-rewind-command.17128 → 0.2.7-feat-cli-tool-lambda-v2.17131
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/dist/index.js +554 -682
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -79,8 +79,8 @@ import {
|
|
|
79
79
|
} from "./chunk-PDX44BCA.js";
|
|
80
80
|
|
|
81
81
|
// src/index.tsx
|
|
82
|
-
import
|
|
83
|
-
import { render, Box as
|
|
82
|
+
import React13, { useState as useState4, useEffect as useEffect3, useCallback, useRef } from "react";
|
|
83
|
+
import { render, Box as Box13, Text as Text12, useApp, useInput as useInput3 } from "ink";
|
|
84
84
|
import { v4 as uuidv49 } from "uuid";
|
|
85
85
|
|
|
86
86
|
// src/components/App.tsx
|
|
@@ -389,11 +389,6 @@ 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"]
|
|
397
392
|
}
|
|
398
393
|
];
|
|
399
394
|
|
|
@@ -630,26 +625,12 @@ function findAtTrigger(value) {
|
|
|
630
625
|
}
|
|
631
626
|
return null;
|
|
632
627
|
}
|
|
633
|
-
function InputPrompt({
|
|
634
|
-
onSubmit,
|
|
635
|
-
onImageDetected,
|
|
636
|
-
disabled = false,
|
|
637
|
-
history = [],
|
|
638
|
-
prefillInput,
|
|
639
|
-
onPrefillConsumed
|
|
640
|
-
}) {
|
|
628
|
+
function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = [] }) {
|
|
641
629
|
const [value, setValue] = useState("");
|
|
642
630
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
643
631
|
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
644
632
|
const [tempInput, setTempInput] = useState("");
|
|
645
633
|
const [inputKey, setInputKey] = useState(0);
|
|
646
|
-
useEffect(() => {
|
|
647
|
-
if (prefillInput) {
|
|
648
|
-
setValue(prefillInput);
|
|
649
|
-
setInputKey((prev) => prev + 1);
|
|
650
|
-
onPrefillConsumed?.();
|
|
651
|
-
}
|
|
652
|
-
}, [prefillInput, onPrefillConsumed]);
|
|
653
634
|
const [fileAutocomplete, setFileAutocomplete] = useState(null);
|
|
654
635
|
const [fileSelectedIndex, setFileSelectedIndex] = useState(0);
|
|
655
636
|
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
|
|
@@ -1243,9 +1224,7 @@ function App({
|
|
|
1243
1224
|
commandHistory = [],
|
|
1244
1225
|
config,
|
|
1245
1226
|
availableModels = [],
|
|
1246
|
-
onSaveConfig
|
|
1247
|
-
prefillInput,
|
|
1248
|
-
onPrefillConsumed
|
|
1227
|
+
onSaveConfig
|
|
1249
1228
|
}) {
|
|
1250
1229
|
const messages = useCliStore((state) => state.session?.messages || []);
|
|
1251
1230
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
@@ -1309,9 +1288,7 @@ ${errorBlock}`;
|
|
|
1309
1288
|
onSubmit: handleSubmit,
|
|
1310
1289
|
onImageDetected,
|
|
1311
1290
|
disabled: isThinking,
|
|
1312
|
-
history: commandHistory
|
|
1313
|
-
prefillInput,
|
|
1314
|
-
onPrefillConsumed
|
|
1291
|
+
history: commandHistory
|
|
1315
1292
|
}
|
|
1316
1293
|
))));
|
|
1317
1294
|
}
|
|
@@ -1403,78 +1380,9 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
1403
1380
|
), /* @__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")));
|
|
1404
1381
|
}
|
|
1405
1382
|
|
|
1406
|
-
// src/components/RewindSelector.tsx
|
|
1407
|
-
import React12, { useState as useState3 } from "react";
|
|
1408
|
-
import { Box as Box12, Text as Text11, useInput as useInput3 } from "ink";
|
|
1409
|
-
import SelectInput3 from "ink-select-input";
|
|
1410
|
-
function RewindSelector({ messages, onSelect, onCancel }) {
|
|
1411
|
-
const [step, setStep] = useState3("selection");
|
|
1412
|
-
const [selectedMessageIndex, setSelectedMessageIndex] = useState3(null);
|
|
1413
|
-
useInput3((input, key) => {
|
|
1414
|
-
if (key.escape) {
|
|
1415
|
-
if (step === "confirmation") {
|
|
1416
|
-
setStep("selection");
|
|
1417
|
-
setSelectedMessageIndex(null);
|
|
1418
|
-
} else {
|
|
1419
|
-
onCancel();
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
});
|
|
1423
|
-
const userMessages = messages.map((msg, idx) => ({ msg, originalIndex: idx })).filter(({ msg }) => msg.role === "user");
|
|
1424
|
-
const items = userMessages.map(({ msg, originalIndex }, index) => {
|
|
1425
|
-
const preview = msg.content.length > 50 ? msg.content.substring(0, 50) + "..." : msg.content;
|
|
1426
|
-
const timestamp = new Date(msg.timestamp).toLocaleString();
|
|
1427
|
-
const position = index + 1;
|
|
1428
|
-
return {
|
|
1429
|
-
label: `[${position}] ${timestamp} - ${preview}`,
|
|
1430
|
-
value: originalIndex
|
|
1431
|
-
};
|
|
1432
|
-
});
|
|
1433
|
-
const handleSelectionSelect = (item) => {
|
|
1434
|
-
setSelectedMessageIndex(item.value);
|
|
1435
|
-
setStep("confirmation");
|
|
1436
|
-
};
|
|
1437
|
-
const handleConfirmationSelect = (item) => {
|
|
1438
|
-
if (item.value === "confirm" && selectedMessageIndex !== null) {
|
|
1439
|
-
onSelect(selectedMessageIndex);
|
|
1440
|
-
} else {
|
|
1441
|
-
setStep("selection");
|
|
1442
|
-
setSelectedMessageIndex(null);
|
|
1443
|
-
}
|
|
1444
|
-
};
|
|
1445
|
-
const messagesToRemove = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages.slice(selectedMessageIndex) : [];
|
|
1446
|
-
const tokensToRemove = messagesToRemove.reduce((sum2, msg) => {
|
|
1447
|
-
return sum2 + (msg.metadata?.tokenUsage?.total || 0);
|
|
1448
|
-
}, 0);
|
|
1449
|
-
if (step === "selection") {
|
|
1450
|
-
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(
|
|
1451
|
-
SelectInput3,
|
|
1452
|
-
{
|
|
1453
|
-
items,
|
|
1454
|
-
onSelect: handleSelectionSelect,
|
|
1455
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1456
|
-
}
|
|
1457
|
-
), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
1458
|
-
}
|
|
1459
|
-
const confirmationItems = [
|
|
1460
|
-
{ label: "Yes, rewind to this point", value: "confirm" },
|
|
1461
|
-
{ label: "No, go back to selection", value: "cancel" }
|
|
1462
|
-
];
|
|
1463
|
-
const selectedMessage = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages[selectedMessageIndex] : null;
|
|
1464
|
-
const selectedPreview = selectedMessage ? selectedMessage.content.length > 50 ? selectedMessage.content.substring(0, 50) + "..." : selectedMessage.content : "";
|
|
1465
|
-
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(
|
|
1466
|
-
SelectInput3,
|
|
1467
|
-
{
|
|
1468
|
-
items: confirmationItems,
|
|
1469
|
-
onSelect: handleConfirmationSelect,
|
|
1470
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1471
|
-
}
|
|
1472
|
-
), /* @__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")));
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
1383
|
// src/components/LoginFlow.tsx
|
|
1476
|
-
import
|
|
1477
|
-
import { Box as
|
|
1384
|
+
import React12, { useState as useState3, useEffect as useEffect2 } from "react";
|
|
1385
|
+
import { Box as Box12, Text as Text11 } from "ink";
|
|
1478
1386
|
import Spinner2 from "ink-spinner";
|
|
1479
1387
|
import jwt from "jsonwebtoken";
|
|
1480
1388
|
import open from "open";
|
|
@@ -1590,10 +1498,10 @@ var OAuthClient = class {
|
|
|
1590
1498
|
|
|
1591
1499
|
// src/components/LoginFlow.tsx
|
|
1592
1500
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
1593
|
-
const [status, setStatus] =
|
|
1594
|
-
const [deviceFlow, setDeviceFlow] =
|
|
1595
|
-
const [statusMessage, setStatusMessage] =
|
|
1596
|
-
const [error, setError] =
|
|
1501
|
+
const [status, setStatus] = useState3("initiating");
|
|
1502
|
+
const [deviceFlow, setDeviceFlow] = useState3(null);
|
|
1503
|
+
const [statusMessage, setStatusMessage] = useState3("Initiating device authorization...");
|
|
1504
|
+
const [error, setError] = useState3(null);
|
|
1597
1505
|
useEffect2(() => {
|
|
1598
1506
|
const runLoginFlow = async () => {
|
|
1599
1507
|
const oauth = new OAuthClient(apiUrl);
|
|
@@ -1637,15 +1545,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
1637
1545
|
}
|
|
1638
1546
|
}, [deviceFlow, status]);
|
|
1639
1547
|
if (status === "initiating") {
|
|
1640
|
-
return /* @__PURE__ */
|
|
1548
|
+
return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, /* @__PURE__ */ React12.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
|
|
1641
1549
|
}
|
|
1642
1550
|
if (status === "error") {
|
|
1643
|
-
return /* @__PURE__ */
|
|
1551
|
+
return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: "red" }, error)));
|
|
1644
1552
|
}
|
|
1645
1553
|
if (status === "success") {
|
|
1646
|
-
return /* @__PURE__ */
|
|
1554
|
+
return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "You can now use B4M CLI with your account.")));
|
|
1647
1555
|
}
|
|
1648
|
-
return /* @__PURE__ */
|
|
1556
|
+
return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React12.createElement(Text11, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, null, "And enter this code when prompted:")), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React12.createElement(Text11, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, /* @__PURE__ */ React12.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
|
|
1649
1557
|
}
|
|
1650
1558
|
|
|
1651
1559
|
// src/storage/SessionStore.ts
|
|
@@ -8805,6 +8713,17 @@ var MementoEvalResponseSchema = z142.object({
|
|
|
8805
8713
|
// ../../b4m-core/packages/services/dist/src/auth/AccessTokenGeneratorService.js
|
|
8806
8714
|
import jwt2 from "jsonwebtoken";
|
|
8807
8715
|
|
|
8716
|
+
// ../../b4m-core/packages/services/dist/src/cliTools/types.js
|
|
8717
|
+
var ToolErrorType;
|
|
8718
|
+
(function(ToolErrorType2) {
|
|
8719
|
+
ToolErrorType2["INVALID_INPUT"] = "INVALID_INPUT";
|
|
8720
|
+
ToolErrorType2["API_KEY_MISSING"] = "API_KEY_MISSING";
|
|
8721
|
+
ToolErrorType2["EXTERNAL_API_ERROR"] = "EXTERNAL_API_ERROR";
|
|
8722
|
+
ToolErrorType2["RATE_LIMIT"] = "RATE_LIMIT";
|
|
8723
|
+
ToolErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
8724
|
+
ToolErrorType2["UNKNOWN"] = "UNKNOWN";
|
|
8725
|
+
})(ToolErrorType || (ToolErrorType = {}));
|
|
8726
|
+
|
|
8808
8727
|
// src/utils/diffPreview.ts
|
|
8809
8728
|
import * as Diff from "diff";
|
|
8810
8729
|
import { readFile } from "fs/promises";
|
|
@@ -8855,174 +8774,489 @@ Last modified: ${stats.mtime.toLocaleString()}`;
|
|
|
8855
8774
|
}
|
|
8856
8775
|
}
|
|
8857
8776
|
|
|
8858
|
-
// src/utils/
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8777
|
+
// src/utils/Logger.ts
|
|
8778
|
+
import fs10 from "fs/promises";
|
|
8779
|
+
import path11 from "path";
|
|
8780
|
+
import os from "os";
|
|
8781
|
+
var Logger2 = class _Logger {
|
|
8782
|
+
constructor() {
|
|
8783
|
+
this.logFilePath = null;
|
|
8784
|
+
this.sessionId = null;
|
|
8785
|
+
this.fileLoggingEnabled = true;
|
|
8786
|
+
this.consoleVerbose = false;
|
|
8867
8787
|
}
|
|
8868
|
-
|
|
8869
|
-
|
|
8788
|
+
static {
|
|
8789
|
+
this.instance = null;
|
|
8870
8790
|
}
|
|
8871
|
-
|
|
8872
|
-
|
|
8791
|
+
static getInstance() {
|
|
8792
|
+
if (!_Logger.instance) {
|
|
8793
|
+
_Logger.instance = new _Logger();
|
|
8794
|
+
}
|
|
8795
|
+
return _Logger.instance;
|
|
8873
8796
|
}
|
|
8874
|
-
|
|
8875
|
-
|
|
8797
|
+
/**
|
|
8798
|
+
* Initialize the logger with a session ID
|
|
8799
|
+
*/
|
|
8800
|
+
async initialize(sessionId) {
|
|
8801
|
+
this.sessionId = sessionId;
|
|
8802
|
+
const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
|
|
8803
|
+
await fs10.mkdir(debugDir, { recursive: true });
|
|
8804
|
+
this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
|
|
8805
|
+
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
8876
8806
|
}
|
|
8877
|
-
|
|
8878
|
-
|
|
8807
|
+
/**
|
|
8808
|
+
* Set whether verbose console logging is enabled
|
|
8809
|
+
*/
|
|
8810
|
+
setVerbose(enabled) {
|
|
8811
|
+
this.consoleVerbose = enabled;
|
|
8879
8812
|
}
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8813
|
+
/**
|
|
8814
|
+
* Set whether file logging is enabled
|
|
8815
|
+
*/
|
|
8816
|
+
setFileLoggingEnabled(enabled) {
|
|
8817
|
+
this.fileLoggingEnabled = enabled;
|
|
8884
8818
|
}
|
|
8885
|
-
|
|
8886
|
-
|
|
8819
|
+
/**
|
|
8820
|
+
* DEBUG level - verbose-only console, always file
|
|
8821
|
+
*/
|
|
8822
|
+
debug(message) {
|
|
8823
|
+
this.writeToFile("DEBUG", message).catch(() => {
|
|
8824
|
+
});
|
|
8825
|
+
if (this.consoleVerbose) {
|
|
8826
|
+
console.log(message);
|
|
8827
|
+
}
|
|
8887
8828
|
}
|
|
8888
|
-
|
|
8829
|
+
/**
|
|
8830
|
+
* INFO level - always shown to user
|
|
8831
|
+
*/
|
|
8832
|
+
info(message) {
|
|
8833
|
+
this.writeToFile("INFO", message).catch(() => {
|
|
8834
|
+
});
|
|
8835
|
+
console.log(message);
|
|
8889
8836
|
}
|
|
8890
|
-
|
|
8837
|
+
/**
|
|
8838
|
+
* WARN level - always shown to user
|
|
8839
|
+
*/
|
|
8840
|
+
warn(message) {
|
|
8841
|
+
this.writeToFile("WARN", message).catch(() => {
|
|
8842
|
+
});
|
|
8843
|
+
console.warn(message);
|
|
8891
8844
|
}
|
|
8892
|
-
|
|
8845
|
+
/**
|
|
8846
|
+
* ERROR level - always shown to user
|
|
8847
|
+
*/
|
|
8848
|
+
error(message, err) {
|
|
8849
|
+
this.writeToFile("ERROR", message).catch(() => {
|
|
8850
|
+
});
|
|
8851
|
+
if (err) {
|
|
8852
|
+
this.logErrorDetailsToFile(err).catch(() => {
|
|
8853
|
+
});
|
|
8854
|
+
}
|
|
8855
|
+
console.error(message);
|
|
8893
8856
|
}
|
|
8894
|
-
|
|
8857
|
+
/**
|
|
8858
|
+
* Write log entry to file
|
|
8859
|
+
*/
|
|
8860
|
+
async writeToFile(level, message) {
|
|
8861
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
8862
|
+
return;
|
|
8863
|
+
}
|
|
8864
|
+
try {
|
|
8865
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
8866
|
+
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
8867
|
+
`;
|
|
8868
|
+
await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
8869
|
+
} catch (error) {
|
|
8870
|
+
console.error("File logging failed:", error);
|
|
8871
|
+
}
|
|
8895
8872
|
}
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
}
|
|
8918
|
-
} else if (toolName === "create_file" && args?.path && args?.content) {
|
|
8919
|
-
try {
|
|
8920
|
-
preview = await generateFileDiffPreview({
|
|
8921
|
-
path: args.path,
|
|
8922
|
-
content: args.content
|
|
8923
|
-
});
|
|
8924
|
-
} catch (error) {
|
|
8925
|
-
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
8926
|
-
}
|
|
8927
|
-
} else if (toolName === "delete_file" && args?.path) {
|
|
8928
|
-
try {
|
|
8929
|
-
preview = await generateFileDeletePreview({
|
|
8930
|
-
path: args.path
|
|
8931
|
-
});
|
|
8932
|
-
} catch (error) {
|
|
8933
|
-
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
8934
|
-
}
|
|
8935
|
-
}
|
|
8936
|
-
const response = await showPermissionPrompt(toolName, args, preview);
|
|
8937
|
-
if (response.action === "deny") {
|
|
8938
|
-
throw new PermissionDeniedError(toolName, args);
|
|
8939
|
-
}
|
|
8940
|
-
if (response.action === "allow-always") {
|
|
8941
|
-
const canTrust = permissionManager.trustTool(toolName);
|
|
8942
|
-
if (canTrust) {
|
|
8943
|
-
const projectDir = configStore.getProjectConfigDir();
|
|
8944
|
-
if (projectDir) {
|
|
8945
|
-
try {
|
|
8946
|
-
await configStore.initProjectConfig();
|
|
8947
|
-
const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
|
|
8948
|
-
const updatedLocal = {
|
|
8949
|
-
...existingLocal,
|
|
8950
|
-
trustedTools: [...existingLocal.trustedTools || [], toolName]
|
|
8951
|
-
};
|
|
8952
|
-
await configStore.saveProjectLocalConfig(updatedLocal);
|
|
8953
|
-
} catch (error) {
|
|
8954
|
-
await configStore.trustTool(toolName);
|
|
8873
|
+
/**
|
|
8874
|
+
* Log error details to file
|
|
8875
|
+
*/
|
|
8876
|
+
async logErrorDetailsToFile(err) {
|
|
8877
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
8878
|
+
return;
|
|
8879
|
+
}
|
|
8880
|
+
try {
|
|
8881
|
+
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
8882
|
+
const response = err.response;
|
|
8883
|
+
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
8884
|
+
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
8885
|
+
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
8886
|
+
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
8887
|
+
if (response.data) {
|
|
8888
|
+
const errorText = this.extractErrorMessage(response.data);
|
|
8889
|
+
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
8890
|
+
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
8891
|
+
const parsedError = this.parseHtmlError(errorText);
|
|
8892
|
+
if (parsedError) {
|
|
8893
|
+
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
8955
8894
|
}
|
|
8895
|
+
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
8956
8896
|
} else {
|
|
8957
|
-
|
|
8897
|
+
const preview = this.truncate(errorText, 500);
|
|
8898
|
+
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
8958
8899
|
}
|
|
8900
|
+
} else {
|
|
8901
|
+
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
8959
8902
|
}
|
|
8960
8903
|
}
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
};
|
|
8966
|
-
}
|
|
8967
|
-
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, toolApiKeys) {
|
|
8968
|
-
const logger2 = new CliLogger();
|
|
8969
|
-
const storage = new NoOpStorage();
|
|
8970
|
-
const user = {
|
|
8971
|
-
_id: userId,
|
|
8972
|
-
email: "cli-user@bike4mind.local",
|
|
8973
|
-
firstName: "CLI",
|
|
8974
|
-
lastName: "User"
|
|
8975
|
-
};
|
|
8976
|
-
const statusUpdate = async (q, status) => {
|
|
8977
|
-
};
|
|
8978
|
-
const onStart = async (toolName, data) => {
|
|
8979
|
-
};
|
|
8980
|
-
const onFinish = async (toolName, data) => {
|
|
8981
|
-
};
|
|
8982
|
-
const mockAdminSettings = {
|
|
8983
|
-
findBySettingName: async (settingName) => {
|
|
8984
|
-
if (settingName === "OpenWeatherKey") {
|
|
8985
|
-
const key = toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY;
|
|
8986
|
-
if (key) {
|
|
8987
|
-
return { settingValue: key };
|
|
8988
|
-
}
|
|
8989
|
-
}
|
|
8990
|
-
if (settingName === "SerperKey") {
|
|
8991
|
-
const key = toolApiKeys?.serper || process.env.SERPER_API_KEY;
|
|
8992
|
-
if (key) {
|
|
8993
|
-
return { settingValue: key };
|
|
8994
|
-
}
|
|
8904
|
+
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
8905
|
+
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
8906
|
+
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
8907
|
+
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
8908
|
+
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
8995
8909
|
}
|
|
8996
|
-
|
|
8910
|
+
} catch (error) {
|
|
8997
8911
|
}
|
|
8998
|
-
}
|
|
8999
|
-
|
|
8912
|
+
}
|
|
8913
|
+
/**
|
|
8914
|
+
* Safely stringify object, handling circular references
|
|
8915
|
+
*/
|
|
8916
|
+
safeStringify(obj) {
|
|
8917
|
+
try {
|
|
8918
|
+
return JSON.stringify(obj);
|
|
8919
|
+
} catch (error) {
|
|
8920
|
+
if (error instanceof Error && error.message.includes("circular")) {
|
|
8921
|
+
try {
|
|
8922
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
8923
|
+
return JSON.stringify(obj, (key, value) => {
|
|
8924
|
+
if (typeof value === "object" && value !== null) {
|
|
8925
|
+
if (seen.has(value)) {
|
|
8926
|
+
return "[Circular]";
|
|
8927
|
+
}
|
|
8928
|
+
seen.add(value);
|
|
8929
|
+
}
|
|
8930
|
+
return value;
|
|
8931
|
+
});
|
|
8932
|
+
} catch {
|
|
8933
|
+
return "[Unable to stringify]";
|
|
8934
|
+
}
|
|
8935
|
+
}
|
|
8936
|
+
return "[Stringify error]";
|
|
8937
|
+
}
|
|
8938
|
+
}
|
|
8939
|
+
/**
|
|
8940
|
+
* Extract readable text from error response data
|
|
8941
|
+
*/
|
|
8942
|
+
extractErrorMessage(data) {
|
|
8943
|
+
if (Buffer.isBuffer(data)) {
|
|
8944
|
+
return data.toString("utf-8");
|
|
8945
|
+
}
|
|
8946
|
+
if (typeof data === "string") {
|
|
8947
|
+
return data;
|
|
8948
|
+
}
|
|
8949
|
+
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) {
|
|
8950
|
+
const chunks = [];
|
|
8951
|
+
for (const chunk of data._readableState.buffer) {
|
|
8952
|
+
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
8953
|
+
chunks.push(Buffer.from(chunk.data));
|
|
8954
|
+
} else if (Buffer.isBuffer(chunk)) {
|
|
8955
|
+
chunks.push(chunk);
|
|
8956
|
+
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
8957
|
+
if (Buffer.isBuffer(chunk.data)) {
|
|
8958
|
+
chunks.push(chunk.data);
|
|
8959
|
+
} else if (Array.isArray(chunk.data)) {
|
|
8960
|
+
chunks.push(Buffer.from(chunk.data));
|
|
8961
|
+
}
|
|
8962
|
+
}
|
|
8963
|
+
}
|
|
8964
|
+
if (chunks.length > 0) {
|
|
8965
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
8966
|
+
}
|
|
8967
|
+
}
|
|
8968
|
+
return this.safeStringify(data);
|
|
8969
|
+
}
|
|
8970
|
+
/**
|
|
8971
|
+
* Parse HTML error page to extract error message
|
|
8972
|
+
*/
|
|
8973
|
+
parseHtmlError(html) {
|
|
8974
|
+
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
|
|
8975
|
+
const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
|
|
8976
|
+
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
8977
|
+
if (titleMatch && titleMatch[1] !== "Error") {
|
|
8978
|
+
return titleMatch[1].trim();
|
|
8979
|
+
}
|
|
8980
|
+
if (h1Match) {
|
|
8981
|
+
return h1Match[1].trim();
|
|
8982
|
+
}
|
|
8983
|
+
if (bodyMatch) {
|
|
8984
|
+
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
8985
|
+
return text.substring(0, 200);
|
|
8986
|
+
}
|
|
8987
|
+
return null;
|
|
8988
|
+
}
|
|
8989
|
+
/**
|
|
8990
|
+
* Truncate string to max length with ellipsis
|
|
8991
|
+
*/
|
|
8992
|
+
truncate(str, maxLength) {
|
|
8993
|
+
if (str.length <= maxLength) {
|
|
8994
|
+
return str;
|
|
8995
|
+
}
|
|
8996
|
+
return str.substring(0, maxLength) + "... [truncated]";
|
|
8997
|
+
}
|
|
8998
|
+
/**
|
|
8999
|
+
* Format bytes to human-readable size
|
|
9000
|
+
*/
|
|
9001
|
+
formatBytes(bytes) {
|
|
9002
|
+
if (bytes === 0) return "0 B";
|
|
9003
|
+
const k = 1024;
|
|
9004
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
9005
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
9006
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
9007
|
+
}
|
|
9008
|
+
/**
|
|
9009
|
+
* Clean up old debug logs (older than 30 days)
|
|
9010
|
+
*/
|
|
9011
|
+
async cleanupOldLogs() {
|
|
9012
|
+
if (!this.fileLoggingEnabled) return;
|
|
9013
|
+
try {
|
|
9014
|
+
const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
|
|
9015
|
+
const files = await fs10.readdir(debugDir);
|
|
9016
|
+
const now = Date.now();
|
|
9017
|
+
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
9018
|
+
for (const file of files) {
|
|
9019
|
+
const filePath = path11.join(debugDir, file);
|
|
9020
|
+
const stats = await fs10.stat(filePath);
|
|
9021
|
+
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
9022
|
+
await fs10.unlink(filePath);
|
|
9023
|
+
}
|
|
9024
|
+
}
|
|
9025
|
+
} catch (error) {
|
|
9026
|
+
console.error("Failed to cleanup old logs:", error);
|
|
9027
|
+
}
|
|
9028
|
+
}
|
|
9029
|
+
};
|
|
9030
|
+
var logger = Logger2.getInstance();
|
|
9031
|
+
|
|
9032
|
+
// src/llm/ServerToolExecutor.ts
|
|
9033
|
+
var ServerToolExecutor = class {
|
|
9034
|
+
constructor(apiClient) {
|
|
9035
|
+
this.toolsEndpoint = "/api/ai/v1/tools";
|
|
9036
|
+
this.apiClient = apiClient;
|
|
9037
|
+
}
|
|
9038
|
+
/**
|
|
9039
|
+
* Execute a tool server-side
|
|
9040
|
+
*
|
|
9041
|
+
* @param toolName - Name of the tool to execute (e.g., 'weather_info', 'web_search')
|
|
9042
|
+
* @param input - Tool input parameters
|
|
9043
|
+
* @returns Tool execution result (content or error)
|
|
9044
|
+
*/
|
|
9045
|
+
async executeTool(toolName, input) {
|
|
9046
|
+
logger.debug(`[ServerToolExecutor] Executing tool: ${toolName}`);
|
|
9047
|
+
logger.debug(`[ServerToolExecutor] Endpoint: ${this.toolsEndpoint}`);
|
|
9048
|
+
logger.debug(`[ServerToolExecutor] Input: ${JSON.stringify(input)}`);
|
|
9049
|
+
try {
|
|
9050
|
+
const result = await this.apiClient.post(this.toolsEndpoint, {
|
|
9051
|
+
toolName,
|
|
9052
|
+
input
|
|
9053
|
+
});
|
|
9054
|
+
logger.debug(`[ServerToolExecutor] Response: ${JSON.stringify(result)}`);
|
|
9055
|
+
if (!result.success) {
|
|
9056
|
+
const errorMessage = result.error || "Tool execution failed";
|
|
9057
|
+
logger.error(`[ServerToolExecutor] Tool ${toolName} failed: ${errorMessage}`);
|
|
9058
|
+
return `Error executing ${toolName}: ${errorMessage}`;
|
|
9059
|
+
}
|
|
9060
|
+
logger.debug(`[ServerToolExecutor] Tool ${toolName} succeeded in ${result.executionTimeMs}ms`);
|
|
9061
|
+
return result.content || "";
|
|
9062
|
+
} catch (error) {
|
|
9063
|
+
const err = error;
|
|
9064
|
+
const errorMessage = err?.response?.data?.error || err?.message || "Unknown error";
|
|
9065
|
+
const status = err?.response?.status || "unknown";
|
|
9066
|
+
logger.error(`[ServerToolExecutor] Request failed for ${toolName}: ${errorMessage} (status: ${status})`);
|
|
9067
|
+
logger.error(`[ServerToolExecutor] Full error: ${JSON.stringify(err, null, 2)}`);
|
|
9068
|
+
return `Error executing ${toolName}: ${errorMessage}`;
|
|
9069
|
+
}
|
|
9070
|
+
}
|
|
9071
|
+
};
|
|
9072
|
+
|
|
9073
|
+
// src/llm/ToolRouter.ts
|
|
9074
|
+
var SERVER_TOOLS = ["weather_info", "web_search"];
|
|
9075
|
+
var LOCAL_TOOLS = [
|
|
9076
|
+
"file_read",
|
|
9077
|
+
"create_file",
|
|
9078
|
+
"glob_files",
|
|
9079
|
+
"grep_search",
|
|
9080
|
+
"delete_file",
|
|
9081
|
+
"edit_file",
|
|
9082
|
+
"dice_roll",
|
|
9083
|
+
"math_evaluate",
|
|
9084
|
+
"current_datetime"
|
|
9085
|
+
];
|
|
9086
|
+
function isServerTool(toolName) {
|
|
9087
|
+
return SERVER_TOOLS.includes(toolName);
|
|
9088
|
+
}
|
|
9089
|
+
function isLocalTool(toolName) {
|
|
9090
|
+
return LOCAL_TOOLS.includes(toolName);
|
|
9091
|
+
}
|
|
9092
|
+
async function executeTool(toolName, input, apiClient, localToolFn) {
|
|
9093
|
+
if (isServerTool(toolName)) {
|
|
9094
|
+
logger.debug(`[ToolRouter] Routing ${toolName} to server`);
|
|
9095
|
+
const executor = new ServerToolExecutor(apiClient);
|
|
9096
|
+
return await executor.executeTool(toolName, input);
|
|
9097
|
+
} else if (isLocalTool(toolName)) {
|
|
9098
|
+
logger.debug(`[ToolRouter] Executing ${toolName} locally`);
|
|
9099
|
+
if (!localToolFn) {
|
|
9100
|
+
throw new Error(`Local tool ${toolName} has no implementation`);
|
|
9101
|
+
}
|
|
9102
|
+
const result = await localToolFn(input);
|
|
9103
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
9104
|
+
} else {
|
|
9105
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
9106
|
+
}
|
|
9107
|
+
}
|
|
9108
|
+
|
|
9109
|
+
// src/utils/toolsAdapter.ts
|
|
9110
|
+
var NoOpStorage = class extends BaseStorage {
|
|
9111
|
+
async upload(input, destination, options) {
|
|
9112
|
+
return `/tmp/${destination}`;
|
|
9113
|
+
}
|
|
9114
|
+
async download(path13) {
|
|
9115
|
+
throw new Error("Download not supported in CLI");
|
|
9116
|
+
}
|
|
9117
|
+
async delete(path13) {
|
|
9118
|
+
}
|
|
9119
|
+
async getSignedUrl(path13) {
|
|
9120
|
+
return `/tmp/${path13}`;
|
|
9121
|
+
}
|
|
9122
|
+
getPublicUrl(path13) {
|
|
9123
|
+
return `/tmp/${path13}`;
|
|
9124
|
+
}
|
|
9125
|
+
async getPreview(path13) {
|
|
9126
|
+
return `/tmp/${path13}`;
|
|
9127
|
+
}
|
|
9128
|
+
async getMetadata(path13) {
|
|
9129
|
+
return { size: 0, contentType: "application/octet-stream" };
|
|
9130
|
+
}
|
|
9131
|
+
};
|
|
9132
|
+
var CliLogger = class extends Logger {
|
|
9133
|
+
constructor() {
|
|
9134
|
+
super({ logInJson: false });
|
|
9135
|
+
}
|
|
9136
|
+
// Override all logging methods to be silent
|
|
9137
|
+
info(...args) {
|
|
9138
|
+
}
|
|
9139
|
+
warn(...args) {
|
|
9140
|
+
}
|
|
9141
|
+
error(...args) {
|
|
9142
|
+
}
|
|
9143
|
+
debug(...args) {
|
|
9144
|
+
}
|
|
9145
|
+
log(...args) {
|
|
9146
|
+
}
|
|
9147
|
+
};
|
|
9148
|
+
function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
|
|
9149
|
+
const originalFn = tool.toolFn;
|
|
9150
|
+
const toolName = tool.toolSchema.name;
|
|
9151
|
+
return {
|
|
9152
|
+
...tool,
|
|
9153
|
+
toolFn: async (args) => {
|
|
9154
|
+
if (!permissionManager.needsPermission(toolName)) {
|
|
9155
|
+
const result2 = await executeTool(toolName, args, apiClient, originalFn);
|
|
9156
|
+
agentContext.observationQueue.push({ toolName, result: result2 });
|
|
9157
|
+
return result2;
|
|
9158
|
+
}
|
|
9159
|
+
let preview;
|
|
9160
|
+
if (toolName === "edit_file" && args?.path && args?.content) {
|
|
9161
|
+
try {
|
|
9162
|
+
preview = await generateFileDiffPreview({
|
|
9163
|
+
path: args.path,
|
|
9164
|
+
content: args.content
|
|
9165
|
+
});
|
|
9166
|
+
} catch (error) {
|
|
9167
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9168
|
+
}
|
|
9169
|
+
} else if (toolName === "create_file" && args?.path && args?.content) {
|
|
9170
|
+
try {
|
|
9171
|
+
preview = await generateFileDiffPreview({
|
|
9172
|
+
path: args.path,
|
|
9173
|
+
content: args.content
|
|
9174
|
+
});
|
|
9175
|
+
} catch (error) {
|
|
9176
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9177
|
+
}
|
|
9178
|
+
} else if (toolName === "delete_file" && args?.path) {
|
|
9179
|
+
try {
|
|
9180
|
+
preview = await generateFileDeletePreview({
|
|
9181
|
+
path: args.path
|
|
9182
|
+
});
|
|
9183
|
+
} catch (error) {
|
|
9184
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9185
|
+
}
|
|
9186
|
+
}
|
|
9187
|
+
const response = await showPermissionPrompt(toolName, args, preview);
|
|
9188
|
+
if (response.action === "deny") {
|
|
9189
|
+
throw new PermissionDeniedError(toolName, args);
|
|
9190
|
+
}
|
|
9191
|
+
if (response.action === "allow-always") {
|
|
9192
|
+
const canTrust = permissionManager.trustTool(toolName);
|
|
9193
|
+
if (canTrust) {
|
|
9194
|
+
const projectDir = configStore.getProjectConfigDir();
|
|
9195
|
+
if (projectDir) {
|
|
9196
|
+
try {
|
|
9197
|
+
await configStore.initProjectConfig();
|
|
9198
|
+
const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
|
|
9199
|
+
const updatedLocal = {
|
|
9200
|
+
...existingLocal,
|
|
9201
|
+
trustedTools: [...existingLocal.trustedTools || [], toolName]
|
|
9202
|
+
};
|
|
9203
|
+
await configStore.saveProjectLocalConfig(updatedLocal);
|
|
9204
|
+
} catch (error) {
|
|
9205
|
+
await configStore.trustTool(toolName);
|
|
9206
|
+
}
|
|
9207
|
+
} else {
|
|
9208
|
+
await configStore.trustTool(toolName);
|
|
9209
|
+
}
|
|
9210
|
+
}
|
|
9211
|
+
}
|
|
9212
|
+
const result = await executeTool(toolName, args, apiClient, originalFn);
|
|
9213
|
+
agentContext.observationQueue.push({ toolName, result });
|
|
9214
|
+
return result;
|
|
9215
|
+
}
|
|
9216
|
+
};
|
|
9217
|
+
}
|
|
9218
|
+
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
|
|
9219
|
+
const logger2 = new CliLogger();
|
|
9220
|
+
const storage = new NoOpStorage();
|
|
9221
|
+
const user = {
|
|
9222
|
+
_id: userId,
|
|
9223
|
+
email: "cli-user@bike4mind.local",
|
|
9224
|
+
firstName: "CLI",
|
|
9225
|
+
lastName: "User"
|
|
9226
|
+
};
|
|
9227
|
+
const statusUpdate = async (q, status) => {
|
|
9228
|
+
};
|
|
9229
|
+
const onStart = async (toolName, data) => {
|
|
9230
|
+
};
|
|
9231
|
+
const onFinish = async (toolName, data) => {
|
|
9232
|
+
};
|
|
9233
|
+
const mockAdminSettings = {
|
|
9234
|
+
findBySettingName: async (settingName) => {
|
|
9235
|
+
return null;
|
|
9236
|
+
}
|
|
9237
|
+
};
|
|
9238
|
+
const dbAdapters = {
|
|
9000
9239
|
db: {
|
|
9001
9240
|
apiKeys: null,
|
|
9002
9241
|
adminSettings: mockAdminSettings
|
|
9003
9242
|
}
|
|
9004
9243
|
};
|
|
9005
|
-
const hasSerperKey = !!(toolApiKeys?.serper || process.env.SERPER_API_KEY);
|
|
9006
|
-
const hasOpenWeatherKey = !!(toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY);
|
|
9007
9244
|
const toolConfig = {
|
|
9008
|
-
//
|
|
9245
|
+
// Local-only tools (no external API keys needed)
|
|
9009
9246
|
dice_roll: {},
|
|
9010
9247
|
math_evaluate: {},
|
|
9011
9248
|
current_datetime: {},
|
|
9012
9249
|
prompt_enhancement: {},
|
|
9013
|
-
// File operation tools (CLI-specific)
|
|
9250
|
+
// File operation tools (CLI-specific, local execution)
|
|
9014
9251
|
file_read: {},
|
|
9015
9252
|
create_file: {},
|
|
9016
9253
|
glob_files: {},
|
|
9017
9254
|
grep_search: {},
|
|
9018
9255
|
delete_file: {},
|
|
9019
|
-
//
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
// Requires OPENWEATHER_API_KEY
|
|
9024
|
-
...hasSerperKey && { deep_research: {} }
|
|
9025
|
-
// Requires SERPER_API_KEY
|
|
9256
|
+
// Server-side tools (executed via /api/ai/v1/tools Lambda)
|
|
9257
|
+
// Routed by ToolRouter to use B4M company API keys
|
|
9258
|
+
weather_info: {},
|
|
9259
|
+
web_search: {}
|
|
9026
9260
|
// Disable web-only tools
|
|
9027
9261
|
// blog_publish: undefined,
|
|
9028
9262
|
// blog_edit: undefined,
|
|
@@ -9032,6 +9266,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9032
9266
|
// mermaid_chart: undefined,
|
|
9033
9267
|
// recharts: undefined,
|
|
9034
9268
|
// edit_file: undefined,
|
|
9269
|
+
// deep_research: undefined,
|
|
9035
9270
|
};
|
|
9036
9271
|
const toolsMap = generateTools(
|
|
9037
9272
|
userId,
|
|
@@ -9049,7 +9284,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9049
9284
|
model
|
|
9050
9285
|
);
|
|
9051
9286
|
const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
|
|
9052
|
-
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore)
|
|
9287
|
+
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
|
|
9053
9288
|
);
|
|
9054
9289
|
return { tools: tools2, agentContext };
|
|
9055
9290
|
}
|
|
@@ -9159,345 +9394,90 @@ var PermissionManager = class {
|
|
|
9159
9394
|
if (!canTrustTool(toolName, categoryMap)) {
|
|
9160
9395
|
return false;
|
|
9161
9396
|
}
|
|
9162
|
-
this.trustedTools.add(toolName);
|
|
9163
|
-
return true;
|
|
9164
|
-
}
|
|
9165
|
-
/**
|
|
9166
|
-
* Remove a tool from the trusted list
|
|
9167
|
-
*/
|
|
9168
|
-
untrustTool(toolName) {
|
|
9169
|
-
this.trustedTools.delete(toolName);
|
|
9170
|
-
}
|
|
9171
|
-
/**
|
|
9172
|
-
* Get list of all trusted tools
|
|
9173
|
-
*/
|
|
9174
|
-
getTrustedTools() {
|
|
9175
|
-
return Array.from(this.trustedTools).sort();
|
|
9176
|
-
}
|
|
9177
|
-
/**
|
|
9178
|
-
* Check if a tool is currently trusted
|
|
9179
|
-
*/
|
|
9180
|
-
isTrusted(toolName) {
|
|
9181
|
-
return this.trustedTools.has(toolName);
|
|
9182
|
-
}
|
|
9183
|
-
/**
|
|
9184
|
-
* Get the category for a tool
|
|
9185
|
-
*/
|
|
9186
|
-
getCategory(toolName) {
|
|
9187
|
-
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9188
|
-
return getToolCategory(toolName, categoryMap);
|
|
9189
|
-
}
|
|
9190
|
-
/**
|
|
9191
|
-
* Check if a tool can be trusted (not in prompt_always category or denied by project)
|
|
9192
|
-
*/
|
|
9193
|
-
canBeTrusted(toolName) {
|
|
9194
|
-
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9195
|
-
if (this.deniedTools.has(toolName)) {
|
|
9196
|
-
return false;
|
|
9197
|
-
}
|
|
9198
|
-
return canTrustTool(toolName, categoryMap);
|
|
9199
|
-
}
|
|
9200
|
-
/**
|
|
9201
|
-
* Check if a tool is denied by project config
|
|
9202
|
-
*/
|
|
9203
|
-
isDenied(toolName) {
|
|
9204
|
-
return this.deniedTools.has(toolName);
|
|
9205
|
-
}
|
|
9206
|
-
/**
|
|
9207
|
-
* Get list of denied tools
|
|
9208
|
-
*/
|
|
9209
|
-
getDeniedTools() {
|
|
9210
|
-
return Array.from(this.deniedTools).sort();
|
|
9211
|
-
}
|
|
9212
|
-
/**
|
|
9213
|
-
* Clear all trusted tools
|
|
9214
|
-
*/
|
|
9215
|
-
clearTrustedTools() {
|
|
9216
|
-
this.trustedTools.clear();
|
|
9217
|
-
}
|
|
9218
|
-
/**
|
|
9219
|
-
* Get statistics about permissions
|
|
9220
|
-
*/
|
|
9221
|
-
getStats() {
|
|
9222
|
-
return {
|
|
9223
|
-
trustedCount: this.trustedTools.size,
|
|
9224
|
-
autoApproveCount: 0,
|
|
9225
|
-
// TODO: Would need full tool list
|
|
9226
|
-
promptAlwaysCount: 0,
|
|
9227
|
-
promptDefaultCount: 0
|
|
9228
|
-
};
|
|
9229
|
-
}
|
|
9230
|
-
};
|
|
9231
|
-
|
|
9232
|
-
// src/utils/apiUrl.ts
|
|
9233
|
-
var BIKE4MIND_URL = "https://app.bike4mind.com";
|
|
9234
|
-
function getApiUrl(configApiConfig) {
|
|
9235
|
-
if (configApiConfig?.customUrl) {
|
|
9236
|
-
return configApiConfig.customUrl;
|
|
9237
|
-
}
|
|
9238
|
-
return BIKE4MIND_URL;
|
|
9239
|
-
}
|
|
9240
|
-
function getEnvironmentName(configApiConfig) {
|
|
9241
|
-
if (configApiConfig?.customUrl) {
|
|
9242
|
-
return "Self-Hosted";
|
|
9243
|
-
}
|
|
9244
|
-
return "Bike4Mind";
|
|
9245
|
-
}
|
|
9246
|
-
|
|
9247
|
-
// src/utils/Logger.ts
|
|
9248
|
-
import fs10 from "fs/promises";
|
|
9249
|
-
import path11 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;
|
|
9257
|
-
}
|
|
9258
|
-
static {
|
|
9259
|
-
this.instance = null;
|
|
9260
|
-
}
|
|
9261
|
-
static getInstance() {
|
|
9262
|
-
if (!_Logger.instance) {
|
|
9263
|
-
_Logger.instance = new _Logger();
|
|
9264
|
-
}
|
|
9265
|
-
return _Logger.instance;
|
|
9266
|
-
}
|
|
9267
|
-
/**
|
|
9268
|
-
* Initialize the logger with a session ID
|
|
9269
|
-
*/
|
|
9270
|
-
async initialize(sessionId) {
|
|
9271
|
-
this.sessionId = sessionId;
|
|
9272
|
-
const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
|
|
9273
|
-
await fs10.mkdir(debugDir, { recursive: true });
|
|
9274
|
-
this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
|
|
9275
|
-
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
9276
|
-
}
|
|
9277
|
-
/**
|
|
9278
|
-
* Set whether verbose console logging is enabled
|
|
9279
|
-
*/
|
|
9280
|
-
setVerbose(enabled) {
|
|
9281
|
-
this.consoleVerbose = enabled;
|
|
9282
|
-
}
|
|
9283
|
-
/**
|
|
9284
|
-
* Set whether file logging is enabled
|
|
9285
|
-
*/
|
|
9286
|
-
setFileLoggingEnabled(enabled) {
|
|
9287
|
-
this.fileLoggingEnabled = enabled;
|
|
9288
|
-
}
|
|
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
|
-
}
|
|
9298
|
-
}
|
|
9299
|
-
/**
|
|
9300
|
-
* INFO level - always shown to user
|
|
9301
|
-
*/
|
|
9302
|
-
info(message) {
|
|
9303
|
-
this.writeToFile("INFO", message).catch(() => {
|
|
9304
|
-
});
|
|
9305
|
-
console.log(message);
|
|
9306
|
-
}
|
|
9307
|
-
/**
|
|
9308
|
-
* WARN level - always shown to user
|
|
9309
|
-
*/
|
|
9310
|
-
warn(message) {
|
|
9311
|
-
this.writeToFile("WARN", message).catch(() => {
|
|
9312
|
-
});
|
|
9313
|
-
console.warn(message);
|
|
9397
|
+
this.trustedTools.add(toolName);
|
|
9398
|
+
return true;
|
|
9314
9399
|
}
|
|
9315
9400
|
/**
|
|
9316
|
-
*
|
|
9401
|
+
* Remove a tool from the trusted list
|
|
9317
9402
|
*/
|
|
9318
|
-
|
|
9319
|
-
this.
|
|
9320
|
-
});
|
|
9321
|
-
if (err) {
|
|
9322
|
-
this.logErrorDetailsToFile(err).catch(() => {
|
|
9323
|
-
});
|
|
9324
|
-
}
|
|
9325
|
-
console.error(message);
|
|
9403
|
+
untrustTool(toolName) {
|
|
9404
|
+
this.trustedTools.delete(toolName);
|
|
9326
9405
|
}
|
|
9327
9406
|
/**
|
|
9328
|
-
*
|
|
9407
|
+
* Get list of all trusted tools
|
|
9329
9408
|
*/
|
|
9330
|
-
|
|
9331
|
-
|
|
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
|
-
}
|
|
9409
|
+
getTrustedTools() {
|
|
9410
|
+
return Array.from(this.trustedTools).sort();
|
|
9342
9411
|
}
|
|
9343
9412
|
/**
|
|
9344
|
-
*
|
|
9413
|
+
* Check if a tool is currently trusted
|
|
9345
9414
|
*/
|
|
9346
|
-
|
|
9347
|
-
|
|
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)`);
|
|
9372
|
-
}
|
|
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
|
-
}
|
|
9415
|
+
isTrusted(toolName) {
|
|
9416
|
+
return this.trustedTools.has(toolName);
|
|
9382
9417
|
}
|
|
9383
9418
|
/**
|
|
9384
|
-
*
|
|
9419
|
+
* Get the category for a tool
|
|
9385
9420
|
*/
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
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
|
-
}
|
|
9421
|
+
getCategory(toolName) {
|
|
9422
|
+
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9423
|
+
return getToolCategory(toolName, categoryMap);
|
|
9408
9424
|
}
|
|
9409
9425
|
/**
|
|
9410
|
-
*
|
|
9426
|
+
* Check if a tool can be trusted (not in prompt_always category or denied by project)
|
|
9411
9427
|
*/
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
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
|
-
}
|
|
9428
|
+
canBeTrusted(toolName) {
|
|
9429
|
+
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9430
|
+
if (this.deniedTools.has(toolName)) {
|
|
9431
|
+
return false;
|
|
9437
9432
|
}
|
|
9438
|
-
return
|
|
9433
|
+
return canTrustTool(toolName, categoryMap);
|
|
9439
9434
|
}
|
|
9440
9435
|
/**
|
|
9441
|
-
*
|
|
9436
|
+
* Check if a tool is denied by project config
|
|
9442
9437
|
*/
|
|
9443
|
-
|
|
9444
|
-
|
|
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;
|
|
9438
|
+
isDenied(toolName) {
|
|
9439
|
+
return this.deniedTools.has(toolName);
|
|
9458
9440
|
}
|
|
9459
9441
|
/**
|
|
9460
|
-
*
|
|
9442
|
+
* Get list of denied tools
|
|
9461
9443
|
*/
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
return str;
|
|
9465
|
-
}
|
|
9466
|
-
return str.substring(0, maxLength) + "... [truncated]";
|
|
9444
|
+
getDeniedTools() {
|
|
9445
|
+
return Array.from(this.deniedTools).sort();
|
|
9467
9446
|
}
|
|
9468
9447
|
/**
|
|
9469
|
-
*
|
|
9448
|
+
* Clear all trusted tools
|
|
9470
9449
|
*/
|
|
9471
|
-
|
|
9472
|
-
|
|
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];
|
|
9450
|
+
clearTrustedTools() {
|
|
9451
|
+
this.trustedTools.clear();
|
|
9477
9452
|
}
|
|
9478
9453
|
/**
|
|
9479
|
-
*
|
|
9454
|
+
* Get statistics about permissions
|
|
9480
9455
|
*/
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
const filePath = path11.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
|
-
}
|
|
9456
|
+
getStats() {
|
|
9457
|
+
return {
|
|
9458
|
+
trustedCount: this.trustedTools.size,
|
|
9459
|
+
autoApproveCount: 0,
|
|
9460
|
+
// TODO: Would need full tool list
|
|
9461
|
+
promptAlwaysCount: 0,
|
|
9462
|
+
promptDefaultCount: 0
|
|
9463
|
+
};
|
|
9498
9464
|
}
|
|
9499
9465
|
};
|
|
9500
|
-
|
|
9466
|
+
|
|
9467
|
+
// src/utils/apiUrl.ts
|
|
9468
|
+
var BIKE4MIND_URL = "https://app.bike4mind.com";
|
|
9469
|
+
function getApiUrl(configApiConfig) {
|
|
9470
|
+
if (configApiConfig?.customUrl) {
|
|
9471
|
+
return configApiConfig.customUrl;
|
|
9472
|
+
}
|
|
9473
|
+
return BIKE4MIND_URL;
|
|
9474
|
+
}
|
|
9475
|
+
function getEnvironmentName(configApiConfig) {
|
|
9476
|
+
if (configApiConfig?.customUrl) {
|
|
9477
|
+
return "Self-Hosted";
|
|
9478
|
+
}
|
|
9479
|
+
return "Bike4Mind";
|
|
9480
|
+
}
|
|
9501
9481
|
|
|
9502
9482
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
9503
9483
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -10302,7 +10282,10 @@ var ApiClient = class {
|
|
|
10302
10282
|
* Make a POST request
|
|
10303
10283
|
*/
|
|
10304
10284
|
async post(url, data, config) {
|
|
10285
|
+
logger.debug(`[ApiClient] POST ${this.client.defaults.baseURL}${url}`);
|
|
10286
|
+
logger.debug(`[ApiClient] Request body: ${JSON.stringify(data)}`);
|
|
10305
10287
|
const response = await this.client.post(url, data, config);
|
|
10288
|
+
logger.debug(`[ApiClient] Response status: ${response.status}`);
|
|
10306
10289
|
return response.data;
|
|
10307
10290
|
}
|
|
10308
10291
|
/**
|
|
@@ -10353,7 +10336,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
10353
10336
|
// package.json
|
|
10354
10337
|
var package_default = {
|
|
10355
10338
|
name: "@bike4mind/cli",
|
|
10356
|
-
version: "0.2.7-feat-cli-
|
|
10339
|
+
version: "0.2.7-feat-cli-tool-lambda-v2.17131+a38465a4f",
|
|
10357
10340
|
type: "module",
|
|
10358
10341
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
10359
10342
|
license: "UNLICENSED",
|
|
@@ -10456,10 +10439,10 @@ var package_default = {
|
|
|
10456
10439
|
},
|
|
10457
10440
|
devDependencies: {
|
|
10458
10441
|
"@bike4mind/agents": "0.1.0",
|
|
10459
|
-
"@bike4mind/common": "2.38.1-feat-cli-
|
|
10460
|
-
"@bike4mind/mcp": "1.20.1-feat-cli-
|
|
10461
|
-
"@bike4mind/services": "2.33.1-feat-cli-
|
|
10462
|
-
"@bike4mind/utils": "2.1.2-feat-cli-
|
|
10442
|
+
"@bike4mind/common": "2.38.1-feat-cli-tool-lambda-v2.17131+a38465a4f",
|
|
10443
|
+
"@bike4mind/mcp": "1.20.1-feat-cli-tool-lambda-v2.17131+a38465a4f",
|
|
10444
|
+
"@bike4mind/services": "2.33.1-feat-cli-tool-lambda-v2.17131+a38465a4f",
|
|
10445
|
+
"@bike4mind/utils": "2.1.2-feat-cli-tool-lambda-v2.17131+a38465a4f",
|
|
10463
10446
|
"@types/better-sqlite3": "^7.6.12",
|
|
10464
10447
|
"@types/diff": "^5.0.9",
|
|
10465
10448
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -10472,7 +10455,7 @@ var package_default = {
|
|
|
10472
10455
|
typescript: "^5.9.3",
|
|
10473
10456
|
vitest: "^3.2.4"
|
|
10474
10457
|
},
|
|
10475
|
-
gitHead: "
|
|
10458
|
+
gitHead: "a38465a4f20beebef3d8bab525586f0826eaa06b"
|
|
10476
10459
|
};
|
|
10477
10460
|
|
|
10478
10461
|
// src/config/constants.ts
|
|
@@ -10494,7 +10477,7 @@ var usageCache = null;
|
|
|
10494
10477
|
function CliApp() {
|
|
10495
10478
|
const { exit } = useApp();
|
|
10496
10479
|
const imageRenderer = new ImageRenderer();
|
|
10497
|
-
const [state, setState] =
|
|
10480
|
+
const [state, setState] = useState4({
|
|
10498
10481
|
session: null,
|
|
10499
10482
|
sessionStore: new SessionStore(),
|
|
10500
10483
|
configStore: new ConfigStore(),
|
|
@@ -10507,12 +10490,11 @@ function CliApp() {
|
|
|
10507
10490
|
mcpManager: null,
|
|
10508
10491
|
permissionManager: null,
|
|
10509
10492
|
permissionPrompt: null,
|
|
10510
|
-
trustLocationSelector: null
|
|
10511
|
-
rewindSelector: null
|
|
10493
|
+
trustLocationSelector: null
|
|
10512
10494
|
});
|
|
10513
|
-
const [isInitialized, setIsInitialized] =
|
|
10514
|
-
const [initError, setInitError] =
|
|
10515
|
-
const [commandHistory, setCommandHistory] =
|
|
10495
|
+
const [isInitialized, setIsInitialized] = useState4(false);
|
|
10496
|
+
const [initError, setInitError] = useState4(null);
|
|
10497
|
+
const [commandHistory, setCommandHistory] = useState4([]);
|
|
10516
10498
|
const imageStoreInitPromise = useRef(null);
|
|
10517
10499
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
10518
10500
|
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
@@ -10553,7 +10535,7 @@ function CliApp() {
|
|
|
10553
10535
|
process.exit(0);
|
|
10554
10536
|
}, 100);
|
|
10555
10537
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
10556
|
-
|
|
10538
|
+
useInput3((input, key) => {
|
|
10557
10539
|
if (key.ctrl && input === "c") {
|
|
10558
10540
|
const now = Date.now();
|
|
10559
10541
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
@@ -10697,10 +10679,6 @@ function CliApp() {
|
|
|
10697
10679
|
setPermissionPrompt(prompt);
|
|
10698
10680
|
});
|
|
10699
10681
|
};
|
|
10700
|
-
console.log("\u{1F511} Tool API Keys:", {
|
|
10701
|
-
openweather: config.toolApiKeys?.openweather ? "\u2713 configured" : "\u2717 missing",
|
|
10702
|
-
serper: config.toolApiKeys?.serper ? "\u2713 configured" : "\u2717 missing"
|
|
10703
|
-
});
|
|
10704
10682
|
const agentContext = {
|
|
10705
10683
|
currentAgent: null,
|
|
10706
10684
|
observationQueue: []
|
|
@@ -10713,7 +10691,7 @@ function CliApp() {
|
|
|
10713
10691
|
promptFn,
|
|
10714
10692
|
agentContext,
|
|
10715
10693
|
state.configStore,
|
|
10716
|
-
|
|
10694
|
+
apiClient
|
|
10717
10695
|
);
|
|
10718
10696
|
console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
|
|
10719
10697
|
const mcpManager = new McpManager(config);
|
|
@@ -10907,30 +10885,6 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10907
10885
|
throw error;
|
|
10908
10886
|
}
|
|
10909
10887
|
};
|
|
10910
|
-
const recalculateSessionMetadata = (messages) => {
|
|
10911
|
-
let totalTokens = 0;
|
|
10912
|
-
let totalCost = 0;
|
|
10913
|
-
let toolCallCount = 0;
|
|
10914
|
-
for (const msg of messages) {
|
|
10915
|
-
if (msg.metadata) {
|
|
10916
|
-
if (msg.metadata.tokenUsage) {
|
|
10917
|
-
totalTokens += msg.metadata.tokenUsage.total || 0;
|
|
10918
|
-
}
|
|
10919
|
-
if (msg.metadata.cost) {
|
|
10920
|
-
totalCost += msg.metadata.cost;
|
|
10921
|
-
}
|
|
10922
|
-
if (msg.metadata.steps) {
|
|
10923
|
-
const observations = msg.metadata.steps.filter((s) => s.type === "observation");
|
|
10924
|
-
toolCallCount += observations.length;
|
|
10925
|
-
}
|
|
10926
|
-
}
|
|
10927
|
-
}
|
|
10928
|
-
return {
|
|
10929
|
-
totalTokens,
|
|
10930
|
-
totalCost,
|
|
10931
|
-
toolCallCount
|
|
10932
|
-
};
|
|
10933
|
-
};
|
|
10934
10888
|
const handleCommand = async (command, args) => {
|
|
10935
10889
|
switch (command) {
|
|
10936
10890
|
case "help":
|
|
@@ -10939,7 +10893,6 @@ Available commands:
|
|
|
10939
10893
|
/help - Show this help message
|
|
10940
10894
|
/exit - Exit the CLI
|
|
10941
10895
|
/clear - Start a new session
|
|
10942
|
-
/rewind - Rewind conversation to a previous point
|
|
10943
10896
|
/login - Authenticate with your B4M account
|
|
10944
10897
|
/logout - Clear authentication and sign out
|
|
10945
10898
|
/whoami - Show current authenticated user
|
|
@@ -11215,65 +11168,6 @@ Project Configuration:
|
|
|
11215
11168
|
`);
|
|
11216
11169
|
break;
|
|
11217
11170
|
}
|
|
11218
|
-
case "rewind":
|
|
11219
|
-
case "undo": {
|
|
11220
|
-
if (!state.session) {
|
|
11221
|
-
console.log("No active session to rewind");
|
|
11222
|
-
return;
|
|
11223
|
-
}
|
|
11224
|
-
if (state.session.messages.length === 0) {
|
|
11225
|
-
console.log("\u26A0\uFE0F Conversation is empty. Nothing to rewind.");
|
|
11226
|
-
return;
|
|
11227
|
-
}
|
|
11228
|
-
const userMessages = state.session.messages.filter((msg) => msg.role === "user");
|
|
11229
|
-
if (userMessages.length === 0) {
|
|
11230
|
-
console.log("\u26A0\uFE0F No user messages found. Nothing to rewind.");
|
|
11231
|
-
return;
|
|
11232
|
-
}
|
|
11233
|
-
if (userMessages.length === 1) {
|
|
11234
|
-
console.log("\u26A0\uFE0F Only one exchange in the conversation. Use /clear to start a new session instead.");
|
|
11235
|
-
return;
|
|
11236
|
-
}
|
|
11237
|
-
const handleRewindSelect = async (messageIndex) => {
|
|
11238
|
-
setState((prev) => ({ ...prev, rewindSelector: null }));
|
|
11239
|
-
if (messageIndex === null) {
|
|
11240
|
-
console.log("\u274C Rewind operation cancelled");
|
|
11241
|
-
return;
|
|
11242
|
-
}
|
|
11243
|
-
if (!state.session) {
|
|
11244
|
-
console.log("\u274C No active session");
|
|
11245
|
-
return;
|
|
11246
|
-
}
|
|
11247
|
-
const selectedMessage = state.session.messages[messageIndex];
|
|
11248
|
-
const prefillContent = selectedMessage?.content || "";
|
|
11249
|
-
const rewindedMessages = state.session.messages.slice(0, messageIndex);
|
|
11250
|
-
const newMetadata = recalculateSessionMetadata(rewindedMessages);
|
|
11251
|
-
const rewindedSession = {
|
|
11252
|
-
...state.session,
|
|
11253
|
-
messages: rewindedMessages,
|
|
11254
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11255
|
-
metadata: newMetadata
|
|
11256
|
-
};
|
|
11257
|
-
setState((prev) => ({
|
|
11258
|
-
...prev,
|
|
11259
|
-
session: rewindedSession,
|
|
11260
|
-
prefillInput: prefillContent
|
|
11261
|
-
}));
|
|
11262
|
-
setStoreSession(rewindedSession);
|
|
11263
|
-
await state.sessionStore.save(rewindedSession);
|
|
11264
|
-
console.log("\u2705 Conversation rewound successfully");
|
|
11265
|
-
console.log(`\u{1F4CA} Current state: ${rewindedMessages.length} messages, ${newMetadata.totalTokens} tokens`);
|
|
11266
|
-
console.log(`\u{1F4DD} Your message has been placed in the input. Edit and send when ready.
|
|
11267
|
-
`);
|
|
11268
|
-
};
|
|
11269
|
-
setState((prev) => ({
|
|
11270
|
-
...prev,
|
|
11271
|
-
rewindSelector: {
|
|
11272
|
-
resolve: handleRewindSelect
|
|
11273
|
-
}
|
|
11274
|
-
}));
|
|
11275
|
-
break;
|
|
11276
|
-
}
|
|
11277
11171
|
case "usage": {
|
|
11278
11172
|
const usageAuthTokens = await state.configStore.getAuthTokens();
|
|
11279
11173
|
if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
@@ -11427,12 +11321,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11427
11321
|
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
11428
11322
|
};
|
|
11429
11323
|
if (initError) {
|
|
11430
|
-
return /* @__PURE__ */
|
|
11324
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React13.createElement(Text12, null, initError), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
|
|
11431
11325
|
}
|
|
11432
11326
|
if (state.trustLocationSelector) {
|
|
11433
11327
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
11434
11328
|
const inProject = projectDir !== null;
|
|
11435
|
-
return /* @__PURE__ */
|
|
11329
|
+
return /* @__PURE__ */ React13.createElement(
|
|
11436
11330
|
TrustLocationSelector,
|
|
11437
11331
|
{
|
|
11438
11332
|
inProject,
|
|
@@ -11449,27 +11343,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11449
11343
|
}
|
|
11450
11344
|
);
|
|
11451
11345
|
}
|
|
11452
|
-
if (state.rewindSelector && state.session) {
|
|
11453
|
-
return /* @__PURE__ */ React14.createElement(
|
|
11454
|
-
RewindSelector,
|
|
11455
|
-
{
|
|
11456
|
-
messages: state.session.messages,
|
|
11457
|
-
onSelect: (messageIndex) => {
|
|
11458
|
-
if (state.rewindSelector) {
|
|
11459
|
-
state.rewindSelector.resolve(messageIndex);
|
|
11460
|
-
}
|
|
11461
|
-
},
|
|
11462
|
-
onCancel: () => {
|
|
11463
|
-
if (state.rewindSelector) {
|
|
11464
|
-
state.rewindSelector.resolve(null);
|
|
11465
|
-
}
|
|
11466
|
-
}
|
|
11467
|
-
}
|
|
11468
|
-
);
|
|
11469
|
-
}
|
|
11470
11346
|
if (state.showLoginFlow) {
|
|
11471
11347
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
11472
|
-
return /* @__PURE__ */
|
|
11348
|
+
return /* @__PURE__ */ React13.createElement(
|
|
11473
11349
|
LoginFlow,
|
|
11474
11350
|
{
|
|
11475
11351
|
apiUrl: loginApiUrl,
|
|
@@ -11492,9 +11368,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11492
11368
|
);
|
|
11493
11369
|
}
|
|
11494
11370
|
if (!isInitialized) {
|
|
11495
|
-
return /* @__PURE__ */
|
|
11371
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "\u{1F680} Initializing..."));
|
|
11496
11372
|
}
|
|
11497
|
-
return /* @__PURE__ */
|
|
11373
|
+
return /* @__PURE__ */ React13.createElement(
|
|
11498
11374
|
App,
|
|
11499
11375
|
{
|
|
11500
11376
|
onMessage: handleMessage,
|
|
@@ -11504,10 +11380,6 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11504
11380
|
config: state.config,
|
|
11505
11381
|
availableModels: state.availableModels,
|
|
11506
11382
|
onSaveConfig: handleSaveConfig,
|
|
11507
|
-
prefillInput: state.prefillInput,
|
|
11508
|
-
onPrefillConsumed: () => {
|
|
11509
|
-
setState((prev) => ({ ...prev, prefillInput: void 0 }));
|
|
11510
|
-
},
|
|
11511
11383
|
onPermissionResponse: (response) => {
|
|
11512
11384
|
if (state.permissionPrompt) {
|
|
11513
11385
|
state.permissionPrompt.resolve({ action: response });
|
|
@@ -11533,6 +11405,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
11533
11405
|
if (isDevMode) {
|
|
11534
11406
|
console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
11535
11407
|
}
|
|
11536
|
-
render(/* @__PURE__ */
|
|
11408
|
+
render(/* @__PURE__ */ React13.createElement(CliApp, null), {
|
|
11537
11409
|
exitOnCtrlC: false
|
|
11538
11410
|
});
|