@bike4mind/cli 0.2.8-cli-bash-tools.17140 → 0.2.8-cli-bash-tools.17156
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 +667 -501
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -79,13 +79,13 @@ import {
|
|
|
79
79
|
} from "./chunk-PDX44BCA.js";
|
|
80
80
|
|
|
81
81
|
// src/index.tsx
|
|
82
|
-
import
|
|
83
|
-
import { render, Box as Box14, Text as
|
|
82
|
+
import React15, { useState as useState6, useEffect as useEffect3, useCallback, useRef } from "react";
|
|
83
|
+
import { render, Box as Box14, Text as Text14, useApp, useInput as useInput5 } from "ink";
|
|
84
84
|
import { v4 as uuidv49 } from "uuid";
|
|
85
85
|
|
|
86
86
|
// src/components/App.tsx
|
|
87
|
-
import
|
|
88
|
-
import { Box as Box9, Text as
|
|
87
|
+
import React10 from "react";
|
|
88
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
89
89
|
|
|
90
90
|
// src/components/StatusBar.tsx
|
|
91
91
|
import React from "react";
|
|
@@ -95,27 +95,127 @@ var StatusBar = React.memo(function StatusBar2({ sessionName, model, tokenUsage
|
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
// src/components/InputPrompt.tsx
|
|
98
|
-
import
|
|
99
|
-
import { Box as Box4, Text as
|
|
100
|
-
|
|
98
|
+
import React5, { useState as useState2, useMemo, useEffect } from "react";
|
|
99
|
+
import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
|
|
100
|
+
|
|
101
|
+
// src/components/CustomTextInput.tsx
|
|
102
|
+
import React2, { useState } from "react";
|
|
103
|
+
import { Text as Text2, useInput } from "ink";
|
|
104
|
+
function CustomTextInput({
|
|
105
|
+
value,
|
|
106
|
+
onChange,
|
|
107
|
+
onSubmit,
|
|
108
|
+
placeholder = "",
|
|
109
|
+
showCursor = true
|
|
110
|
+
}) {
|
|
111
|
+
const [cursorOffset, setCursorOffset] = useState(value.length);
|
|
112
|
+
useInput(
|
|
113
|
+
(input, key) => {
|
|
114
|
+
if (key.return && !key.meta && !key.shift) {
|
|
115
|
+
onSubmit(value);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (key.ctrl) {
|
|
119
|
+
switch (input) {
|
|
120
|
+
case "u":
|
|
121
|
+
{
|
|
122
|
+
const lines = value.split("\n");
|
|
123
|
+
let currentLineStart = 0;
|
|
124
|
+
let currentLineIndex = 0;
|
|
125
|
+
let charCount = 0;
|
|
126
|
+
for (let i = 0; i < lines.length; i++) {
|
|
127
|
+
const lineLength = lines[i].length;
|
|
128
|
+
if (charCount + lineLength >= cursorOffset) {
|
|
129
|
+
currentLineIndex = i;
|
|
130
|
+
currentLineStart = charCount;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
charCount += lineLength + 1;
|
|
134
|
+
}
|
|
135
|
+
lines.splice(currentLineIndex, 1);
|
|
136
|
+
const newValue = lines.join("\n");
|
|
137
|
+
onChange(newValue);
|
|
138
|
+
setCursorOffset(Math.min(currentLineStart, newValue.length));
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
case "k":
|
|
142
|
+
onChange(value.slice(0, cursorOffset));
|
|
143
|
+
return;
|
|
144
|
+
case "w":
|
|
145
|
+
{
|
|
146
|
+
const beforeCursor2 = value.slice(0, cursorOffset);
|
|
147
|
+
const afterCursor2 = value.slice(cursorOffset);
|
|
148
|
+
const trimmed = beforeCursor2.trimEnd();
|
|
149
|
+
const lastSpace = trimmed.lastIndexOf(" ");
|
|
150
|
+
const newBefore = lastSpace === -1 ? "" : trimmed.slice(0, lastSpace + 1);
|
|
151
|
+
const newValue = newBefore + afterCursor2;
|
|
152
|
+
onChange(newValue);
|
|
153
|
+
setCursorOffset(newBefore.length);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
case "a":
|
|
157
|
+
setCursorOffset(0);
|
|
158
|
+
return;
|
|
159
|
+
case "e":
|
|
160
|
+
setCursorOffset(value.length);
|
|
161
|
+
return;
|
|
162
|
+
case "l":
|
|
163
|
+
onChange("");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (key.backspace || key.delete) {
|
|
168
|
+
if (cursorOffset > 0) {
|
|
169
|
+
const newValue = value.slice(0, cursorOffset - 1) + value.slice(cursorOffset);
|
|
170
|
+
onChange(newValue);
|
|
171
|
+
setCursorOffset(cursorOffset - 1);
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (key.leftArrow) {
|
|
176
|
+
setCursorOffset(Math.max(0, cursorOffset - 1));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (key.rightArrow) {
|
|
180
|
+
setCursorOffset(Math.min(value.length, cursorOffset + 1));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!key.ctrl && !key.meta && input.length === 1) {
|
|
184
|
+
const sanitizedInput = input === "\r" ? "\n" : input;
|
|
185
|
+
const newValue = value.slice(0, cursorOffset) + sanitizedInput + value.slice(cursorOffset);
|
|
186
|
+
onChange(newValue);
|
|
187
|
+
setCursorOffset(cursorOffset + 1);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{ isActive: true }
|
|
191
|
+
);
|
|
192
|
+
const hasValue = value.length > 0;
|
|
193
|
+
if (!hasValue) {
|
|
194
|
+
return /* @__PURE__ */ React2.createElement(Text2, null, showCursor ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, { inverse: true }, " "), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, placeholder)) : /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, placeholder));
|
|
195
|
+
}
|
|
196
|
+
const beforeCursor = value.slice(0, cursorOffset);
|
|
197
|
+
const cursorChar = value[cursorOffset] || " ";
|
|
198
|
+
const afterCursor = value.slice(cursorOffset + 1);
|
|
199
|
+
return /* @__PURE__ */ React2.createElement(Text2, null, beforeCursor, showCursor && /* @__PURE__ */ React2.createElement(Text2, { inverse: true }, cursorChar), !showCursor && cursorChar, afterCursor);
|
|
200
|
+
}
|
|
101
201
|
|
|
102
202
|
// src/components/CommandAutocomplete.tsx
|
|
103
|
-
import
|
|
104
|
-
import { Box as Box2, Text as
|
|
203
|
+
import React3 from "react";
|
|
204
|
+
import { Box as Box2, Text as Text3 } from "ink";
|
|
105
205
|
function CommandAutocomplete({ commands, selectedIndex }) {
|
|
106
206
|
if (commands.length === 0) {
|
|
107
|
-
return /* @__PURE__ */
|
|
207
|
+
return /* @__PURE__ */ React3.createElement(Box2, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No matching commands"));
|
|
108
208
|
}
|
|
109
|
-
return /* @__PURE__ */
|
|
209
|
+
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, commands.length === 1 ? "1 command" : `${commands.length} commands`, " \u2022 Use \u2191\u2193 to navigate, Enter to select")), commands.map((cmd, index) => {
|
|
110
210
|
const args = cmd.args ? ` ${cmd.args}` : "";
|
|
111
211
|
const isSelected = index === selectedIndex;
|
|
112
|
-
return /* @__PURE__ */
|
|
212
|
+
return /* @__PURE__ */ React3.createElement(Box2, { key: cmd.name, marginLeft: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "\u25B8 " : " ", "/", cmd.name, args, " - ", cmd.description));
|
|
113
213
|
}));
|
|
114
214
|
}
|
|
115
215
|
|
|
116
216
|
// src/components/FileAutocomplete.tsx
|
|
117
|
-
import
|
|
118
|
-
import { Box as Box3, Text as
|
|
217
|
+
import React4 from "react";
|
|
218
|
+
import { Box as Box3, Text as Text4 } from "ink";
|
|
119
219
|
|
|
120
220
|
// src/utils/fileSearch.ts
|
|
121
221
|
import * as fs from "fs";
|
|
@@ -304,14 +404,14 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
|
304
404
|
// src/components/FileAutocomplete.tsx
|
|
305
405
|
function FileAutocomplete({ files, selectedIndex, query }) {
|
|
306
406
|
if (files.length === 0) {
|
|
307
|
-
return /* @__PURE__ */
|
|
407
|
+
return /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No matching files", query ? ` for "${query}"` : ""));
|
|
308
408
|
}
|
|
309
|
-
return /* @__PURE__ */
|
|
409
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, files.length === 1 ? "1 match" : `${files.length} matches`, " - Use up/down to navigate, Tab to select")), files.map((file, index) => {
|
|
310
410
|
const isSelected = index === selectedIndex;
|
|
311
411
|
const icon = file.isDirectory ? "[folder]" : "[file] ";
|
|
312
412
|
const sizeDisplay = file.isDirectory ? "" : file.size !== void 0 ? ` (${formatFileSize(file.size)})` : "";
|
|
313
413
|
const pathDisplay = file.isDirectory ? `${file.path}/` : file.path;
|
|
314
|
-
return /* @__PURE__ */
|
|
414
|
+
return /* @__PURE__ */ React4.createElement(Box3, { key: file.path, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "> " : " "), /* @__PURE__ */ React4.createElement(Text4, { color: file.isDirectory ? "yellow" : isSelected ? "cyan" : void 0, bold: isSelected }, icon), /* @__PURE__ */ React4.createElement(Text4, { color: isSelected ? "cyan" : void 0, bold: isSelected }, " ", pathDisplay), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, sizeDisplay));
|
|
315
415
|
}));
|
|
316
416
|
}
|
|
317
417
|
|
|
@@ -638,20 +738,18 @@ function InputPrompt({
|
|
|
638
738
|
prefillInput,
|
|
639
739
|
onPrefillConsumed
|
|
640
740
|
}) {
|
|
641
|
-
const [value, setValue] =
|
|
642
|
-
const [selectedIndex, setSelectedIndex] =
|
|
643
|
-
const [historyIndex, setHistoryIndex] =
|
|
644
|
-
const [tempInput, setTempInput] =
|
|
645
|
-
const [inputKey, setInputKey] = useState(0);
|
|
741
|
+
const [value, setValue] = useState2("");
|
|
742
|
+
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
743
|
+
const [historyIndex, setHistoryIndex] = useState2(-1);
|
|
744
|
+
const [tempInput, setTempInput] = useState2("");
|
|
646
745
|
useEffect(() => {
|
|
647
746
|
if (prefillInput) {
|
|
648
747
|
setValue(prefillInput);
|
|
649
|
-
setInputKey((prev) => prev + 1);
|
|
650
748
|
onPrefillConsumed?.();
|
|
651
749
|
}
|
|
652
750
|
}, [prefillInput, onPrefillConsumed]);
|
|
653
|
-
const [fileAutocomplete, setFileAutocomplete] =
|
|
654
|
-
const [fileSelectedIndex, setFileSelectedIndex] =
|
|
751
|
+
const [fileAutocomplete, setFileAutocomplete] = useState2(null);
|
|
752
|
+
const [fileSelectedIndex, setFileSelectedIndex] = useState2(0);
|
|
655
753
|
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
|
|
656
754
|
const commandQuery = shouldShowCommandAutocomplete ? value.slice(1) : "";
|
|
657
755
|
const filteredCommands = useMemo(() => {
|
|
@@ -668,7 +766,7 @@ function InputPrompt({
|
|
|
668
766
|
useEffect(() => {
|
|
669
767
|
setFileSelectedIndex(0);
|
|
670
768
|
}, [filteredFiles]);
|
|
671
|
-
|
|
769
|
+
useInput2((_input, key) => {
|
|
672
770
|
if (fileAutocomplete?.active && filteredFiles.length > 0) {
|
|
673
771
|
if (key.upArrow) {
|
|
674
772
|
setFileSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredFiles.length - 1);
|
|
@@ -725,7 +823,6 @@ function InputPrompt({
|
|
|
725
823
|
const pathToInsert = file.isDirectory ? `@${file.path}/` : `@${file.path}`;
|
|
726
824
|
const newValue = beforeAt + pathToInsert + afterQuery;
|
|
727
825
|
setValue(newValue);
|
|
728
|
-
setInputKey((prev) => prev + 1);
|
|
729
826
|
if (file.isDirectory) {
|
|
730
827
|
setFileAutocomplete({
|
|
731
828
|
active: true,
|
|
@@ -752,7 +849,6 @@ function InputPrompt({
|
|
|
752
849
|
if (selectedCommand.args) {
|
|
753
850
|
setValue(commandText + " ");
|
|
754
851
|
setHistoryIndex(-1);
|
|
755
|
-
setInputKey((prev) => prev + 1);
|
|
756
852
|
return;
|
|
757
853
|
}
|
|
758
854
|
setValue("");
|
|
@@ -771,7 +867,6 @@ function InputPrompt({
|
|
|
771
867
|
if (imageEvent && onImageDetected) {
|
|
772
868
|
const placeholder = await onImageDetected(imageEvent.data);
|
|
773
869
|
setValue(`${placeholder} `);
|
|
774
|
-
setInputKey((prev) => prev + 1);
|
|
775
870
|
return;
|
|
776
871
|
}
|
|
777
872
|
}
|
|
@@ -791,29 +886,28 @@ function InputPrompt({
|
|
|
791
886
|
setFileAutocomplete(null);
|
|
792
887
|
}
|
|
793
888
|
};
|
|
794
|
-
return /* @__PURE__ */
|
|
795
|
-
|
|
889
|
+
return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(
|
|
890
|
+
CustomTextInput,
|
|
796
891
|
{
|
|
797
|
-
key: inputKey,
|
|
798
892
|
value,
|
|
799
893
|
onChange: handleChange,
|
|
800
894
|
onSubmit: handleSubmit,
|
|
801
895
|
placeholder: disabled ? "" : "Type your message, /help for commands, or @file to reference",
|
|
802
896
|
showCursor: !disabled
|
|
803
897
|
}
|
|
804
|
-
)), shouldShowCommandAutocomplete && /* @__PURE__ */
|
|
898
|
+
)), shouldShowCommandAutocomplete && /* @__PURE__ */ React5.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React5.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
|
|
805
899
|
}
|
|
806
900
|
|
|
807
901
|
// src/components/AgentThinking.tsx
|
|
808
|
-
import
|
|
902
|
+
import React7 from "react";
|
|
809
903
|
import { Box as Box6 } from "ink";
|
|
810
904
|
|
|
811
905
|
// src/components/ThoughtStream.tsx
|
|
812
|
-
import
|
|
813
|
-
import { Box as Box5, Text as
|
|
906
|
+
import React6 from "react";
|
|
907
|
+
import { Box as Box5, Text as Text6 } from "ink";
|
|
814
908
|
import Spinner from "ink-spinner";
|
|
815
|
-
var ThoughtStream =
|
|
816
|
-
return /* @__PURE__ */
|
|
909
|
+
var ThoughtStream = React6.memo(function ThoughtStream2({ steps }) {
|
|
910
|
+
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column", gap: 1 }, steps.map((step, index) => /* @__PURE__ */ React6.createElement(Box5, { key: index, flexDirection: "column" }, step.type === "thought" && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, "\u{1F914} Thought: "), /* @__PURE__ */ React6.createElement(Text6, null, step.content)), step.type === "action" && /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, "\u26A1 Action: "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, step.metadata?.toolName || "unknown")), step.metadata?.toolInput && /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, JSON.stringify(step.metadata.toolInput, null, 2)))), step.type === "observation" && /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, "\u{1F4CA} Observation: ")), /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, null, step.content))), step.type === "final_answer" && /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green", bold: true }, "\u2705 Final Answer:", " ")), /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, null, step.content)), step.metadata?.tokenUsage && /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "(", step.metadata.tokenUsage.total, " tokens)"))))), !steps.some((s) => s.type === "final_answer") && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " ", steps.length === 0 ? "Thinking..." : "Processing...")));
|
|
817
911
|
});
|
|
818
912
|
|
|
819
913
|
// src/store/index.ts
|
|
@@ -843,32 +937,32 @@ var useCliStore = create((set) => ({
|
|
|
843
937
|
}));
|
|
844
938
|
|
|
845
939
|
// src/components/AgentThinking.tsx
|
|
846
|
-
var AgentThinking =
|
|
940
|
+
var AgentThinking = React7.memo(function AgentThinking2() {
|
|
847
941
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
848
942
|
const agentSteps = useCliStore((state) => state.agentSteps);
|
|
849
943
|
if (!isThinking) {
|
|
850
944
|
return null;
|
|
851
945
|
}
|
|
852
|
-
return /* @__PURE__ */
|
|
946
|
+
return /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
|
|
853
947
|
});
|
|
854
948
|
|
|
855
949
|
// src/components/PermissionPrompt.tsx
|
|
856
|
-
import
|
|
857
|
-
import { Box as Box7, Text as
|
|
950
|
+
import React8 from "react";
|
|
951
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
858
952
|
import SelectInput from "ink-select-input";
|
|
859
953
|
function renderDiffPreview(preview) {
|
|
860
954
|
const lines = preview.split("\n");
|
|
861
955
|
return lines.map((line, index) => {
|
|
862
956
|
if (line.startsWith("+")) {
|
|
863
|
-
return /* @__PURE__ */
|
|
957
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "green" }, line);
|
|
864
958
|
}
|
|
865
959
|
if (line.startsWith("-")) {
|
|
866
|
-
return /* @__PURE__ */
|
|
960
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "red" }, line);
|
|
867
961
|
}
|
|
868
962
|
if (line.startsWith("@@")) {
|
|
869
|
-
return /* @__PURE__ */
|
|
963
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "cyan" }, line);
|
|
870
964
|
}
|
|
871
|
-
return /* @__PURE__ */
|
|
965
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, dimColor: true }, line);
|
|
872
966
|
});
|
|
873
967
|
}
|
|
874
968
|
function PermissionPrompt({
|
|
@@ -888,12 +982,12 @@ function PermissionPrompt({
|
|
|
888
982
|
{ label: "\u2717 Deny", value: "deny" }
|
|
889
983
|
];
|
|
890
984
|
const argsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
|
|
891
|
-
return /* @__PURE__ */
|
|
985
|
+
return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Tool: "), /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Action: "), /* @__PURE__ */ React8.createElement(Text7, null, toolDescription)), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Arguments:"), /* @__PURE__ */ React8.createElement(Box7, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Preview:"), /* @__PURE__ */ React8.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(SelectInput, { items, onSelect: (item) => onResponse(item.value) })));
|
|
892
986
|
}
|
|
893
987
|
|
|
894
988
|
// src/components/ConfigEditor.tsx
|
|
895
|
-
import
|
|
896
|
-
import { Box as Box8, Text as
|
|
989
|
+
import React9, { useState as useState3, useMemo as useMemo2 } from "react";
|
|
990
|
+
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
897
991
|
var MAX_ITERATIONS_OPTIONS = [
|
|
898
992
|
{ label: "10", value: 10 },
|
|
899
993
|
{ label: "20", value: 20 },
|
|
@@ -1024,10 +1118,10 @@ function buildConfigItems(availableModels) {
|
|
|
1024
1118
|
return items;
|
|
1025
1119
|
}
|
|
1026
1120
|
function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
1027
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1028
|
-
const [editedConfig, setEditedConfig] =
|
|
1029
|
-
const [saveError, setSaveError] =
|
|
1030
|
-
const [isSaving, setIsSaving] =
|
|
1121
|
+
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
1122
|
+
const [editedConfig, setEditedConfig] = useState3(config);
|
|
1123
|
+
const [saveError, setSaveError] = useState3(null);
|
|
1124
|
+
const [isSaving, setIsSaving] = useState3(false);
|
|
1031
1125
|
const configItems = useMemo2(() => buildConfigItems(availableModels), [availableModels]);
|
|
1032
1126
|
const hasChanges = useMemo2(() => {
|
|
1033
1127
|
return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences) || config.defaultModel !== editedConfig.defaultModel;
|
|
@@ -1047,7 +1141,7 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1047
1141
|
}
|
|
1048
1142
|
onClose();
|
|
1049
1143
|
};
|
|
1050
|
-
|
|
1144
|
+
useInput3((input, key) => {
|
|
1051
1145
|
const currentItem = configItems[selectedIndex];
|
|
1052
1146
|
if (key.upArrow) {
|
|
1053
1147
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : configItems.length - 1);
|
|
@@ -1112,13 +1206,13 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1112
1206
|
}
|
|
1113
1207
|
return String(value);
|
|
1114
1208
|
};
|
|
1115
|
-
return /* @__PURE__ */
|
|
1209
|
+
return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, marginY: 1 }, /* @__PURE__ */ React9.createElement(Box8, { justifyContent: "space-between", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "Settings"), isSaving ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "Saving...") : hasChanges ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, color: "yellow" }, "(unsaved changes)") : null), saveError && /* @__PURE__ */ React9.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, "Error: ", saveError)), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, configItems.map((item, index) => {
|
|
1116
1210
|
const isSelected = index === selectedIndex;
|
|
1117
1211
|
const value = item.getValue(editedConfig);
|
|
1118
1212
|
const displayValue = formatValue(item, value);
|
|
1119
1213
|
const hasOptions = item.type === "select" || item.type === "boolean" || item.type === "number";
|
|
1120
|
-
return /* @__PURE__ */
|
|
1121
|
-
})), /* @__PURE__ */
|
|
1214
|
+
return /* @__PURE__ */ React9.createElement(Box8, { key: item.key }, /* @__PURE__ */ React9.createElement(Text8, { color: isSelected ? "cyan" : void 0 }, isSelected ? "> " : " "), /* @__PURE__ */ React9.createElement(Box8, { width: 18 }, /* @__PURE__ */ React9.createElement(Text8, { bold: isSelected, color: isSelected ? "white" : "gray" }, item.label, ":")), /* @__PURE__ */ React9.createElement(Box8, null, hasOptions && isSelected && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "< "), /* @__PURE__ */ React9.createElement(Text8, { bold: isSelected, color: isSelected ? "green" : void 0 }, displayValue), hasOptions && isSelected && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " >")));
|
|
1215
|
+
})), /* @__PURE__ */ React9.createElement(Box8, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u2191/\u2193: Navigate | Space/\u2190/\u2192: Change | q/Esc: Save & Exit")));
|
|
1122
1216
|
}
|
|
1123
1217
|
|
|
1124
1218
|
// src/utils/processFileReferences.ts
|
|
@@ -1258,7 +1352,7 @@ function App({
|
|
|
1258
1352
|
const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
|
|
1259
1353
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
1260
1354
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
1261
|
-
const handleSubmit =
|
|
1355
|
+
const handleSubmit = React10.useCallback(
|
|
1262
1356
|
async (input) => {
|
|
1263
1357
|
const trimmed = input.trim();
|
|
1264
1358
|
if (!trimmed) return;
|
|
@@ -1287,7 +1381,7 @@ ${errorBlock}`;
|
|
|
1287
1381
|
},
|
|
1288
1382
|
[onMessage, onCommand, setIsThinking]
|
|
1289
1383
|
);
|
|
1290
|
-
return /* @__PURE__ */
|
|
1384
|
+
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), showConfigEditor && config && onSaveConfig ? /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
|
|
1291
1385
|
ConfigEditor,
|
|
1292
1386
|
{
|
|
1293
1387
|
config,
|
|
@@ -1295,7 +1389,7 @@ ${errorBlock}`;
|
|
|
1295
1389
|
onSave: onSaveConfig,
|
|
1296
1390
|
onClose: () => setShowConfigEditor(false)
|
|
1297
1391
|
}
|
|
1298
|
-
)) : permissionPrompt ? /* @__PURE__ */
|
|
1392
|
+
)) : permissionPrompt ? /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
|
|
1299
1393
|
PermissionPrompt,
|
|
1300
1394
|
{
|
|
1301
1395
|
toolName: permissionPrompt.toolName,
|
|
@@ -1304,7 +1398,7 @@ ${errorBlock}`;
|
|
|
1304
1398
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
1305
1399
|
onResponse: onPermissionResponse
|
|
1306
1400
|
}
|
|
1307
|
-
)) : /* @__PURE__ */
|
|
1401
|
+
)) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column" }, messages.map((message, index) => /* @__PURE__ */ React10.createElement(Box9, { key: `${message.timestamp}-${message.role}-${index}`, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(MessageItem, { message })))), /* @__PURE__ */ React10.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React10.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
|
|
1308
1402
|
InputPrompt,
|
|
1309
1403
|
{
|
|
1310
1404
|
onSubmit: handleSubmit,
|
|
@@ -1318,36 +1412,36 @@ ${errorBlock}`;
|
|
|
1318
1412
|
}
|
|
1319
1413
|
function MessageItem({ message }) {
|
|
1320
1414
|
const isUser = message.role === "user";
|
|
1321
|
-
return /* @__PURE__ */
|
|
1415
|
+
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: isUser ? "cyan" : "green" }, isUser ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1322
1416
|
if (step.type === "thought") {
|
|
1323
|
-
return /* @__PURE__ */
|
|
1417
|
+
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
|
|
1324
1418
|
}
|
|
1325
1419
|
if (step.type === "action") {
|
|
1326
1420
|
const toolName = step.metadata?.toolName || "unknown";
|
|
1327
1421
|
const toolInput = step.metadata?.toolInput;
|
|
1328
1422
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1329
1423
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1330
|
-
return /* @__PURE__ */
|
|
1424
|
+
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1331
1425
|
}
|
|
1332
1426
|
return null;
|
|
1333
|
-
}).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */
|
|
1427
|
+
}).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: message.metadata?.steps?.length ? 1 : 0 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React10.createElement(Text9, null, message.content)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
1334
1428
|
}
|
|
1335
1429
|
|
|
1336
1430
|
// src/components/MessageList.tsx
|
|
1337
|
-
import
|
|
1338
|
-
import { Box as Box10, Text as
|
|
1431
|
+
import React11 from "react";
|
|
1432
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1339
1433
|
function stripThinkingTags(content) {
|
|
1340
1434
|
return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
1341
1435
|
}
|
|
1342
|
-
var MessageList =
|
|
1436
|
+
var MessageList = React11.memo(
|
|
1343
1437
|
function MessageList2({ messages }) {
|
|
1344
1438
|
if (messages.length === 0) {
|
|
1345
|
-
return /* @__PURE__ */
|
|
1439
|
+
return /* @__PURE__ */ React11.createElement(Box10, { paddingY: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "No messages yet. Type a message to start!"));
|
|
1346
1440
|
}
|
|
1347
|
-
return /* @__PURE__ */
|
|
1441
|
+
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React11.createElement(Box10, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React11.createElement(Box10, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u26A0\uFE0F ", stripThinkingTags(message.content)) : /* @__PURE__ */ React11.createElement(Text10, null, stripThinkingTags(message.content))), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React11.createElement(Box10, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text10, { 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__ */ React11.createElement(Text10, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1348
1442
|
if (step.type === "thought") {
|
|
1349
|
-
return /* @__PURE__ */
|
|
1350
|
-
|
|
1443
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text10, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React11.createElement(
|
|
1444
|
+
Text10,
|
|
1351
1445
|
{
|
|
1352
1446
|
dimColor: true
|
|
1353
1447
|
},
|
|
@@ -1359,10 +1453,10 @@ var MessageList = React10.memo(
|
|
|
1359
1453
|
const toolInput = step.metadata?.toolInput;
|
|
1360
1454
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1361
1455
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1362
|
-
return /* @__PURE__ */
|
|
1456
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React11.createElement(Text10, { 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 ? "..." : ""}`));
|
|
1363
1457
|
}
|
|
1364
1458
|
return null;
|
|
1365
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1459
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React11.createElement(Box10, { paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
|
|
1366
1460
|
},
|
|
1367
1461
|
(prevProps, nextProps) => {
|
|
1368
1462
|
if (prevProps.messages.length !== nextProps.messages.length) {
|
|
@@ -1373,8 +1467,8 @@ var MessageList = React10.memo(
|
|
|
1373
1467
|
);
|
|
1374
1468
|
|
|
1375
1469
|
// src/components/TrustLocationSelector.tsx
|
|
1376
|
-
import
|
|
1377
|
-
import { Box as Box11, Text as
|
|
1470
|
+
import React12 from "react";
|
|
1471
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1378
1472
|
import SelectInput2 from "ink-select-input";
|
|
1379
1473
|
function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
1380
1474
|
const items = [];
|
|
@@ -1395,24 +1489,24 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
1395
1489
|
const handleSelect = (item) => {
|
|
1396
1490
|
onSelect(item.value);
|
|
1397
1491
|
};
|
|
1398
|
-
return /* @__PURE__ */
|
|
1492
|
+
return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React12.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Where should this rule be saved?")), /* @__PURE__ */ React12.createElement(
|
|
1399
1493
|
SelectInput2,
|
|
1400
1494
|
{
|
|
1401
1495
|
items,
|
|
1402
1496
|
onSelect: handleSelect,
|
|
1403
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1497
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1404
1498
|
}
|
|
1405
|
-
), /* @__PURE__ */
|
|
1499
|
+
), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Ctrl+C to cancel")));
|
|
1406
1500
|
}
|
|
1407
1501
|
|
|
1408
1502
|
// src/components/RewindSelector.tsx
|
|
1409
|
-
import
|
|
1410
|
-
import { Box as Box12, Text as
|
|
1503
|
+
import React13, { useState as useState4 } from "react";
|
|
1504
|
+
import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
|
|
1411
1505
|
import SelectInput3 from "ink-select-input";
|
|
1412
1506
|
function RewindSelector({ messages, onSelect, onCancel }) {
|
|
1413
|
-
const [step, setStep] =
|
|
1414
|
-
const [selectedMessageIndex, setSelectedMessageIndex] =
|
|
1415
|
-
|
|
1507
|
+
const [step, setStep] = useState4("selection");
|
|
1508
|
+
const [selectedMessageIndex, setSelectedMessageIndex] = useState4(null);
|
|
1509
|
+
useInput4((input, key) => {
|
|
1416
1510
|
if (key.escape) {
|
|
1417
1511
|
if (step === "confirmation") {
|
|
1418
1512
|
setStep("selection");
|
|
@@ -1449,14 +1543,14 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
1449
1543
|
return sum2 + (msg.metadata?.tokenUsage?.total || 0);
|
|
1450
1544
|
}, 0);
|
|
1451
1545
|
if (step === "selection") {
|
|
1452
|
-
return /* @__PURE__ */
|
|
1546
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { bold: true }, "Select a point to rewind to:")), /* @__PURE__ */ React13.createElement(
|
|
1453
1547
|
SelectInput3,
|
|
1454
1548
|
{
|
|
1455
1549
|
items,
|
|
1456
1550
|
onSelect: handleSelectionSelect,
|
|
1457
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1551
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1458
1552
|
}
|
|
1459
|
-
), /* @__PURE__ */
|
|
1553
|
+
), /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
1460
1554
|
}
|
|
1461
1555
|
const confirmationItems = [
|
|
1462
1556
|
{ label: "Yes, rewind to this point", value: "confirm" },
|
|
@@ -1464,19 +1558,19 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
1464
1558
|
];
|
|
1465
1559
|
const selectedMessage = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages[selectedMessageIndex] : null;
|
|
1466
1560
|
const selectedPreview = selectedMessage ? selectedMessage.content.length > 50 ? selectedMessage.content.substring(0, 50) + "..." : selectedMessage.content : "";
|
|
1467
|
-
return /* @__PURE__ */
|
|
1561
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Text12, { bold: true }, "Confirm rewind operation:"), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Message: ", selectedPreview), /* @__PURE__ */ React13.createElement(Text12, { color: "yellow" }, "This will remove ", messagesToRemove.length, " message(s) (", tokensToRemove.toLocaleString(), " tokens)"), /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, "The selected message will be placed in your input for editing.")), /* @__PURE__ */ React13.createElement(
|
|
1468
1562
|
SelectInput3,
|
|
1469
1563
|
{
|
|
1470
1564
|
items: confirmationItems,
|
|
1471
1565
|
onSelect: handleConfirmationSelect,
|
|
1472
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1566
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1473
1567
|
}
|
|
1474
|
-
), /* @__PURE__ */
|
|
1568
|
+
), /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
|
|
1475
1569
|
}
|
|
1476
1570
|
|
|
1477
1571
|
// src/components/LoginFlow.tsx
|
|
1478
|
-
import
|
|
1479
|
-
import { Box as Box13, Text as
|
|
1572
|
+
import React14, { useState as useState5, useEffect as useEffect2 } from "react";
|
|
1573
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
1480
1574
|
import Spinner2 from "ink-spinner";
|
|
1481
1575
|
import jwt from "jsonwebtoken";
|
|
1482
1576
|
import open from "open";
|
|
@@ -1592,10 +1686,10 @@ var OAuthClient = class {
|
|
|
1592
1686
|
|
|
1593
1687
|
// src/components/LoginFlow.tsx
|
|
1594
1688
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
1595
|
-
const [status, setStatus] =
|
|
1596
|
-
const [deviceFlow, setDeviceFlow] =
|
|
1597
|
-
const [statusMessage, setStatusMessage] =
|
|
1598
|
-
const [error, setError] =
|
|
1689
|
+
const [status, setStatus] = useState5("initiating");
|
|
1690
|
+
const [deviceFlow, setDeviceFlow] = useState5(null);
|
|
1691
|
+
const [statusMessage, setStatusMessage] = useState5("Initiating device authorization...");
|
|
1692
|
+
const [error, setError] = useState5(null);
|
|
1599
1693
|
useEffect2(() => {
|
|
1600
1694
|
const runLoginFlow = async () => {
|
|
1601
1695
|
const oauth = new OAuthClient(apiUrl);
|
|
@@ -1639,15 +1733,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
1639
1733
|
}
|
|
1640
1734
|
}, [deviceFlow, status]);
|
|
1641
1735
|
if (status === "initiating") {
|
|
1642
|
-
return /* @__PURE__ */
|
|
1736
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { color: "cyan" }, /* @__PURE__ */ React14.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
|
|
1643
1737
|
}
|
|
1644
1738
|
if (status === "error") {
|
|
1645
|
-
return /* @__PURE__ */
|
|
1739
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { color: "red" }, error)));
|
|
1646
1740
|
}
|
|
1647
1741
|
if (status === "success") {
|
|
1648
|
-
return /* @__PURE__ */
|
|
1742
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "You can now use B4M CLI with your account.")));
|
|
1649
1743
|
}
|
|
1650
|
-
return /* @__PURE__ */
|
|
1744
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, "And enter this code when prompted:")), /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "cyan" }, /* @__PURE__ */ React14.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
|
|
1651
1745
|
}
|
|
1652
1746
|
|
|
1653
1747
|
// src/storage/SessionStore.ts
|
|
@@ -9183,6 +9277,17 @@ var MementoEvalResponseSchema = z142.object({
|
|
|
9183
9277
|
// ../../b4m-core/packages/services/dist/src/auth/AccessTokenGeneratorService.js
|
|
9184
9278
|
import jwt2 from "jsonwebtoken";
|
|
9185
9279
|
|
|
9280
|
+
// ../../b4m-core/packages/services/dist/src/cliTools/types.js
|
|
9281
|
+
var ToolErrorType;
|
|
9282
|
+
(function(ToolErrorType2) {
|
|
9283
|
+
ToolErrorType2["INVALID_INPUT"] = "INVALID_INPUT";
|
|
9284
|
+
ToolErrorType2["API_KEY_MISSING"] = "API_KEY_MISSING";
|
|
9285
|
+
ToolErrorType2["EXTERNAL_API_ERROR"] = "EXTERNAL_API_ERROR";
|
|
9286
|
+
ToolErrorType2["RATE_LIMIT"] = "RATE_LIMIT";
|
|
9287
|
+
ToolErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
9288
|
+
ToolErrorType2["UNKNOWN"] = "UNKNOWN";
|
|
9289
|
+
})(ToolErrorType || (ToolErrorType = {}));
|
|
9290
|
+
|
|
9186
9291
|
// src/utils/diffPreview.ts
|
|
9187
9292
|
import * as Diff from "diff";
|
|
9188
9293
|
import { readFile } from "fs/promises";
|
|
@@ -9233,122 +9338,455 @@ Last modified: ${stats.mtime.toLocaleString()}`;
|
|
|
9233
9338
|
}
|
|
9234
9339
|
}
|
|
9235
9340
|
|
|
9236
|
-
// src/utils/
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9341
|
+
// src/utils/Logger.ts
|
|
9342
|
+
import fs10 from "fs/promises";
|
|
9343
|
+
import path12 from "path";
|
|
9344
|
+
import os from "os";
|
|
9345
|
+
var Logger2 = class _Logger {
|
|
9346
|
+
constructor() {
|
|
9347
|
+
this.logFilePath = null;
|
|
9348
|
+
this.sessionId = null;
|
|
9349
|
+
this.fileLoggingEnabled = true;
|
|
9350
|
+
this.consoleVerbose = false;
|
|
9245
9351
|
}
|
|
9246
|
-
|
|
9247
|
-
|
|
9352
|
+
static {
|
|
9353
|
+
this.instance = null;
|
|
9248
9354
|
}
|
|
9249
|
-
|
|
9250
|
-
|
|
9355
|
+
static getInstance() {
|
|
9356
|
+
if (!_Logger.instance) {
|
|
9357
|
+
_Logger.instance = new _Logger();
|
|
9358
|
+
}
|
|
9359
|
+
return _Logger.instance;
|
|
9251
9360
|
}
|
|
9252
|
-
|
|
9253
|
-
|
|
9361
|
+
/**
|
|
9362
|
+
* Initialize the logger with a session ID
|
|
9363
|
+
*/
|
|
9364
|
+
async initialize(sessionId) {
|
|
9365
|
+
this.sessionId = sessionId;
|
|
9366
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9367
|
+
await fs10.mkdir(debugDir, { recursive: true });
|
|
9368
|
+
this.logFilePath = path12.join(debugDir, `${sessionId}.txt`);
|
|
9369
|
+
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
9254
9370
|
}
|
|
9255
|
-
|
|
9256
|
-
|
|
9371
|
+
/**
|
|
9372
|
+
* Set whether verbose console logging is enabled
|
|
9373
|
+
*/
|
|
9374
|
+
setVerbose(enabled) {
|
|
9375
|
+
this.consoleVerbose = enabled;
|
|
9257
9376
|
}
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9377
|
+
/**
|
|
9378
|
+
* Set whether file logging is enabled
|
|
9379
|
+
*/
|
|
9380
|
+
setFileLoggingEnabled(enabled) {
|
|
9381
|
+
this.fileLoggingEnabled = enabled;
|
|
9262
9382
|
}
|
|
9263
|
-
|
|
9264
|
-
|
|
9383
|
+
/**
|
|
9384
|
+
* DEBUG level - verbose-only console, always file
|
|
9385
|
+
*/
|
|
9386
|
+
debug(message) {
|
|
9387
|
+
this.writeToFile("DEBUG", message).catch(() => {
|
|
9388
|
+
});
|
|
9389
|
+
if (this.consoleVerbose) {
|
|
9390
|
+
console.log(message);
|
|
9391
|
+
}
|
|
9265
9392
|
}
|
|
9266
|
-
|
|
9393
|
+
/**
|
|
9394
|
+
* INFO level - always shown to user
|
|
9395
|
+
*/
|
|
9396
|
+
info(message) {
|
|
9397
|
+
this.writeToFile("INFO", message).catch(() => {
|
|
9398
|
+
});
|
|
9399
|
+
console.log(message);
|
|
9267
9400
|
}
|
|
9268
|
-
|
|
9401
|
+
/**
|
|
9402
|
+
* WARN level - always shown to user
|
|
9403
|
+
*/
|
|
9404
|
+
warn(message) {
|
|
9405
|
+
this.writeToFile("WARN", message).catch(() => {
|
|
9406
|
+
});
|
|
9407
|
+
console.warn(message);
|
|
9269
9408
|
}
|
|
9270
|
-
|
|
9409
|
+
/**
|
|
9410
|
+
* ERROR level - always shown to user
|
|
9411
|
+
*/
|
|
9412
|
+
error(message, err) {
|
|
9413
|
+
this.writeToFile("ERROR", message).catch(() => {
|
|
9414
|
+
});
|
|
9415
|
+
if (err) {
|
|
9416
|
+
this.logErrorDetailsToFile(err).catch(() => {
|
|
9417
|
+
});
|
|
9418
|
+
}
|
|
9419
|
+
console.error(message);
|
|
9271
9420
|
}
|
|
9272
|
-
|
|
9421
|
+
/**
|
|
9422
|
+
* Write log entry to file
|
|
9423
|
+
*/
|
|
9424
|
+
async writeToFile(level, message) {
|
|
9425
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
9426
|
+
return;
|
|
9427
|
+
}
|
|
9428
|
+
try {
|
|
9429
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
9430
|
+
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
9431
|
+
`;
|
|
9432
|
+
await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
9433
|
+
} catch (error) {
|
|
9434
|
+
console.error("File logging failed:", error);
|
|
9435
|
+
}
|
|
9273
9436
|
}
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
}
|
|
9296
|
-
} else if (toolName === "create_file" && args?.path && args?.content) {
|
|
9297
|
-
try {
|
|
9298
|
-
preview = await generateFileDiffPreview({
|
|
9299
|
-
path: args.path,
|
|
9300
|
-
content: args.content
|
|
9301
|
-
});
|
|
9302
|
-
} catch (error) {
|
|
9303
|
-
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9304
|
-
}
|
|
9305
|
-
} else if (toolName === "delete_file" && args?.path) {
|
|
9306
|
-
try {
|
|
9307
|
-
preview = await generateFileDeletePreview({
|
|
9308
|
-
path: args.path
|
|
9309
|
-
});
|
|
9310
|
-
} catch (error) {
|
|
9311
|
-
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
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}`;
|
|
9317
|
-
}
|
|
9318
|
-
const response = await showPermissionPrompt(toolName, args, preview);
|
|
9319
|
-
if (response.action === "deny") {
|
|
9320
|
-
throw new PermissionDeniedError(toolName, args);
|
|
9321
|
-
}
|
|
9322
|
-
if (response.action === "allow-always") {
|
|
9323
|
-
const canTrust = permissionManager.trustTool(toolName);
|
|
9324
|
-
if (canTrust) {
|
|
9325
|
-
const projectDir = configStore.getProjectConfigDir();
|
|
9326
|
-
if (projectDir) {
|
|
9327
|
-
try {
|
|
9328
|
-
await configStore.initProjectConfig();
|
|
9329
|
-
const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
|
|
9330
|
-
const updatedLocal = {
|
|
9331
|
-
...existingLocal,
|
|
9332
|
-
trustedTools: [...existingLocal.trustedTools || [], toolName]
|
|
9333
|
-
};
|
|
9334
|
-
await configStore.saveProjectLocalConfig(updatedLocal);
|
|
9335
|
-
} catch (error) {
|
|
9336
|
-
await configStore.trustTool(toolName);
|
|
9437
|
+
/**
|
|
9438
|
+
* Log error details to file
|
|
9439
|
+
*/
|
|
9440
|
+
async logErrorDetailsToFile(err) {
|
|
9441
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
9442
|
+
return;
|
|
9443
|
+
}
|
|
9444
|
+
try {
|
|
9445
|
+
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
9446
|
+
const response = err.response;
|
|
9447
|
+
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
9448
|
+
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
9449
|
+
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
9450
|
+
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
9451
|
+
if (response.data) {
|
|
9452
|
+
const errorText = this.extractErrorMessage(response.data);
|
|
9453
|
+
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
9454
|
+
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
9455
|
+
const parsedError = this.parseHtmlError(errorText);
|
|
9456
|
+
if (parsedError) {
|
|
9457
|
+
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
9337
9458
|
}
|
|
9459
|
+
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
9338
9460
|
} else {
|
|
9339
|
-
|
|
9461
|
+
const preview = this.truncate(errorText, 500);
|
|
9462
|
+
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
9340
9463
|
}
|
|
9464
|
+
} else {
|
|
9465
|
+
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
9341
9466
|
}
|
|
9342
9467
|
}
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9468
|
+
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
9469
|
+
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
9470
|
+
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
9471
|
+
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
9472
|
+
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
9473
|
+
}
|
|
9474
|
+
} catch (error) {
|
|
9346
9475
|
}
|
|
9347
|
-
}
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9476
|
+
}
|
|
9477
|
+
/**
|
|
9478
|
+
* Safely stringify object, handling circular references
|
|
9479
|
+
*/
|
|
9480
|
+
safeStringify(obj) {
|
|
9481
|
+
try {
|
|
9482
|
+
return JSON.stringify(obj);
|
|
9483
|
+
} catch (error) {
|
|
9484
|
+
if (error instanceof Error && error.message.includes("circular")) {
|
|
9485
|
+
try {
|
|
9486
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
9487
|
+
return JSON.stringify(obj, (key, value) => {
|
|
9488
|
+
if (typeof value === "object" && value !== null) {
|
|
9489
|
+
if (seen.has(value)) {
|
|
9490
|
+
return "[Circular]";
|
|
9491
|
+
}
|
|
9492
|
+
seen.add(value);
|
|
9493
|
+
}
|
|
9494
|
+
return value;
|
|
9495
|
+
});
|
|
9496
|
+
} catch {
|
|
9497
|
+
return "[Unable to stringify]";
|
|
9498
|
+
}
|
|
9499
|
+
}
|
|
9500
|
+
return "[Stringify error]";
|
|
9501
|
+
}
|
|
9502
|
+
}
|
|
9503
|
+
/**
|
|
9504
|
+
* Extract readable text from error response data
|
|
9505
|
+
*/
|
|
9506
|
+
extractErrorMessage(data) {
|
|
9507
|
+
if (Buffer.isBuffer(data)) {
|
|
9508
|
+
return data.toString("utf-8");
|
|
9509
|
+
}
|
|
9510
|
+
if (typeof data === "string") {
|
|
9511
|
+
return data;
|
|
9512
|
+
}
|
|
9513
|
+
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) {
|
|
9514
|
+
const chunks = [];
|
|
9515
|
+
for (const chunk of data._readableState.buffer) {
|
|
9516
|
+
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
9517
|
+
chunks.push(Buffer.from(chunk.data));
|
|
9518
|
+
} else if (Buffer.isBuffer(chunk)) {
|
|
9519
|
+
chunks.push(chunk);
|
|
9520
|
+
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
9521
|
+
if (Buffer.isBuffer(chunk.data)) {
|
|
9522
|
+
chunks.push(chunk.data);
|
|
9523
|
+
} else if (Array.isArray(chunk.data)) {
|
|
9524
|
+
chunks.push(Buffer.from(chunk.data));
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9527
|
+
}
|
|
9528
|
+
if (chunks.length > 0) {
|
|
9529
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
9530
|
+
}
|
|
9531
|
+
}
|
|
9532
|
+
return this.safeStringify(data);
|
|
9533
|
+
}
|
|
9534
|
+
/**
|
|
9535
|
+
* Parse HTML error page to extract error message
|
|
9536
|
+
*/
|
|
9537
|
+
parseHtmlError(html) {
|
|
9538
|
+
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
|
|
9539
|
+
const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
|
|
9540
|
+
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
9541
|
+
if (titleMatch && titleMatch[1] !== "Error") {
|
|
9542
|
+
return titleMatch[1].trim();
|
|
9543
|
+
}
|
|
9544
|
+
if (h1Match) {
|
|
9545
|
+
return h1Match[1].trim();
|
|
9546
|
+
}
|
|
9547
|
+
if (bodyMatch) {
|
|
9548
|
+
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
9549
|
+
return text.substring(0, 200);
|
|
9550
|
+
}
|
|
9551
|
+
return null;
|
|
9552
|
+
}
|
|
9553
|
+
/**
|
|
9554
|
+
* Truncate string to max length with ellipsis
|
|
9555
|
+
*/
|
|
9556
|
+
truncate(str, maxLength) {
|
|
9557
|
+
if (str.length <= maxLength) {
|
|
9558
|
+
return str;
|
|
9559
|
+
}
|
|
9560
|
+
return str.substring(0, maxLength) + "... [truncated]";
|
|
9561
|
+
}
|
|
9562
|
+
/**
|
|
9563
|
+
* Format bytes to human-readable size
|
|
9564
|
+
*/
|
|
9565
|
+
formatBytes(bytes) {
|
|
9566
|
+
if (bytes === 0) return "0 B";
|
|
9567
|
+
const k = 1024;
|
|
9568
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
9569
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
9570
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
9571
|
+
}
|
|
9572
|
+
/**
|
|
9573
|
+
* Clean up old debug logs (older than 30 days)
|
|
9574
|
+
*/
|
|
9575
|
+
async cleanupOldLogs() {
|
|
9576
|
+
if (!this.fileLoggingEnabled) return;
|
|
9577
|
+
try {
|
|
9578
|
+
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9579
|
+
const files = await fs10.readdir(debugDir);
|
|
9580
|
+
const now = Date.now();
|
|
9581
|
+
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
9582
|
+
for (const file of files) {
|
|
9583
|
+
const filePath = path12.join(debugDir, file);
|
|
9584
|
+
const stats = await fs10.stat(filePath);
|
|
9585
|
+
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
9586
|
+
await fs10.unlink(filePath);
|
|
9587
|
+
}
|
|
9588
|
+
}
|
|
9589
|
+
} catch (error) {
|
|
9590
|
+
console.error("Failed to cleanup old logs:", error);
|
|
9591
|
+
}
|
|
9592
|
+
}
|
|
9593
|
+
};
|
|
9594
|
+
var logger = Logger2.getInstance();
|
|
9595
|
+
|
|
9596
|
+
// src/llm/ServerToolExecutor.ts
|
|
9597
|
+
var ServerToolExecutor = class {
|
|
9598
|
+
constructor(apiClient) {
|
|
9599
|
+
this.toolsEndpoint = "/api/ai/v1/tools";
|
|
9600
|
+
this.apiClient = apiClient;
|
|
9601
|
+
}
|
|
9602
|
+
/**
|
|
9603
|
+
* Execute a tool server-side
|
|
9604
|
+
*
|
|
9605
|
+
* @param toolName - Name of the tool to execute (e.g., 'weather_info', 'web_search')
|
|
9606
|
+
* @param input - Tool input parameters
|
|
9607
|
+
* @returns Tool execution result (content or error)
|
|
9608
|
+
*/
|
|
9609
|
+
async executeTool(toolName, input) {
|
|
9610
|
+
logger.debug(`[ServerToolExecutor] Executing tool: ${toolName}`);
|
|
9611
|
+
logger.debug(`[ServerToolExecutor] Endpoint: ${this.toolsEndpoint}`);
|
|
9612
|
+
logger.debug(`[ServerToolExecutor] Input: ${JSON.stringify(input)}`);
|
|
9613
|
+
try {
|
|
9614
|
+
const result = await this.apiClient.post(this.toolsEndpoint, {
|
|
9615
|
+
toolName,
|
|
9616
|
+
input
|
|
9617
|
+
});
|
|
9618
|
+
logger.debug(`[ServerToolExecutor] Response: ${JSON.stringify(result)}`);
|
|
9619
|
+
if (!result.success) {
|
|
9620
|
+
const errorMessage = result.error || "Tool execution failed";
|
|
9621
|
+
logger.error(`[ServerToolExecutor] Tool ${toolName} failed: ${errorMessage}`);
|
|
9622
|
+
return `Error executing ${toolName}: ${errorMessage}`;
|
|
9623
|
+
}
|
|
9624
|
+
logger.debug(`[ServerToolExecutor] Tool ${toolName} succeeded in ${result.executionTimeMs}ms`);
|
|
9625
|
+
return result.content || "";
|
|
9626
|
+
} catch (error) {
|
|
9627
|
+
const err = error;
|
|
9628
|
+
const errorMessage = err?.response?.data?.error || err?.message || "Unknown error";
|
|
9629
|
+
const status = err?.response?.status || "unknown";
|
|
9630
|
+
logger.error(`[ServerToolExecutor] Request failed for ${toolName}: ${errorMessage} (status: ${status})`);
|
|
9631
|
+
logger.error(`[ServerToolExecutor] Full error: ${JSON.stringify(err, null, 2)}`);
|
|
9632
|
+
return `Error executing ${toolName}: ${errorMessage}`;
|
|
9633
|
+
}
|
|
9634
|
+
}
|
|
9635
|
+
};
|
|
9636
|
+
|
|
9637
|
+
// src/llm/ToolRouter.ts
|
|
9638
|
+
var SERVER_TOOLS = ["weather_info", "web_search"];
|
|
9639
|
+
var LOCAL_TOOLS = [
|
|
9640
|
+
"file_read",
|
|
9641
|
+
"create_file",
|
|
9642
|
+
"glob_files",
|
|
9643
|
+
"grep_search",
|
|
9644
|
+
"delete_file",
|
|
9645
|
+
"edit_file",
|
|
9646
|
+
"dice_roll",
|
|
9647
|
+
"math_evaluate",
|
|
9648
|
+
"current_datetime",
|
|
9649
|
+
"bash_execute"
|
|
9650
|
+
];
|
|
9651
|
+
function isServerTool(toolName) {
|
|
9652
|
+
return SERVER_TOOLS.includes(toolName);
|
|
9653
|
+
}
|
|
9654
|
+
function isLocalTool(toolName) {
|
|
9655
|
+
return LOCAL_TOOLS.includes(toolName);
|
|
9656
|
+
}
|
|
9657
|
+
async function executeTool(toolName, input, apiClient, localToolFn) {
|
|
9658
|
+
if (isServerTool(toolName)) {
|
|
9659
|
+
logger.debug(`[ToolRouter] Routing ${toolName} to server`);
|
|
9660
|
+
const executor = new ServerToolExecutor(apiClient);
|
|
9661
|
+
return await executor.executeTool(toolName, input);
|
|
9662
|
+
} else if (isLocalTool(toolName)) {
|
|
9663
|
+
logger.debug(`[ToolRouter] Executing ${toolName} locally`);
|
|
9664
|
+
if (!localToolFn) {
|
|
9665
|
+
throw new Error(`Local tool ${toolName} has no implementation`);
|
|
9666
|
+
}
|
|
9667
|
+
const result = await localToolFn(input);
|
|
9668
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
9669
|
+
} else {
|
|
9670
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
9671
|
+
}
|
|
9672
|
+
}
|
|
9673
|
+
|
|
9674
|
+
// src/utils/toolsAdapter.ts
|
|
9675
|
+
var NoOpStorage = class extends BaseStorage {
|
|
9676
|
+
async upload(input, destination, options) {
|
|
9677
|
+
return `/tmp/${destination}`;
|
|
9678
|
+
}
|
|
9679
|
+
async download(path14) {
|
|
9680
|
+
throw new Error("Download not supported in CLI");
|
|
9681
|
+
}
|
|
9682
|
+
async delete(path14) {
|
|
9683
|
+
}
|
|
9684
|
+
async getSignedUrl(path14) {
|
|
9685
|
+
return `/tmp/${path14}`;
|
|
9686
|
+
}
|
|
9687
|
+
getPublicUrl(path14) {
|
|
9688
|
+
return `/tmp/${path14}`;
|
|
9689
|
+
}
|
|
9690
|
+
async getPreview(path14) {
|
|
9691
|
+
return `/tmp/${path14}`;
|
|
9692
|
+
}
|
|
9693
|
+
async getMetadata(path14) {
|
|
9694
|
+
return { size: 0, contentType: "application/octet-stream" };
|
|
9695
|
+
}
|
|
9696
|
+
};
|
|
9697
|
+
var CliLogger = class extends Logger {
|
|
9698
|
+
constructor() {
|
|
9699
|
+
super({ logInJson: false });
|
|
9700
|
+
}
|
|
9701
|
+
// Override all logging methods to be silent
|
|
9702
|
+
info(...args) {
|
|
9703
|
+
}
|
|
9704
|
+
warn(...args) {
|
|
9705
|
+
}
|
|
9706
|
+
error(...args) {
|
|
9707
|
+
}
|
|
9708
|
+
debug(...args) {
|
|
9709
|
+
}
|
|
9710
|
+
log(...args) {
|
|
9711
|
+
}
|
|
9712
|
+
};
|
|
9713
|
+
function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
|
|
9714
|
+
const originalFn = tool.toolFn;
|
|
9715
|
+
const toolName = tool.toolSchema.name;
|
|
9716
|
+
return {
|
|
9717
|
+
...tool,
|
|
9718
|
+
toolFn: async (args) => {
|
|
9719
|
+
if (!permissionManager.needsPermission(toolName)) {
|
|
9720
|
+
const result2 = await executeTool(toolName, args, apiClient, originalFn);
|
|
9721
|
+
agentContext.observationQueue.push({ toolName, result: result2 });
|
|
9722
|
+
return result2;
|
|
9723
|
+
}
|
|
9724
|
+
let preview;
|
|
9725
|
+
if (toolName === "edit_file" && args?.path && args?.content) {
|
|
9726
|
+
try {
|
|
9727
|
+
preview = await generateFileDiffPreview({
|
|
9728
|
+
path: args.path,
|
|
9729
|
+
content: args.content
|
|
9730
|
+
});
|
|
9731
|
+
} catch (error) {
|
|
9732
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9733
|
+
}
|
|
9734
|
+
} else if (toolName === "create_file" && args?.path && args?.content) {
|
|
9735
|
+
try {
|
|
9736
|
+
preview = await generateFileDiffPreview({
|
|
9737
|
+
path: args.path,
|
|
9738
|
+
content: args.content
|
|
9739
|
+
});
|
|
9740
|
+
} catch (error) {
|
|
9741
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9742
|
+
}
|
|
9743
|
+
} else if (toolName === "delete_file" && args?.path) {
|
|
9744
|
+
try {
|
|
9745
|
+
preview = await generateFileDeletePreview({
|
|
9746
|
+
path: args.path
|
|
9747
|
+
});
|
|
9748
|
+
} catch (error) {
|
|
9749
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9750
|
+
}
|
|
9751
|
+
} else if (toolName === "bash_execute" && args?.command) {
|
|
9752
|
+
const cwd = args.cwd ? ` (in ${args.cwd})` : "";
|
|
9753
|
+
const timeout = args.timeout ? ` [timeout: ${args.timeout}ms]` : "";
|
|
9754
|
+
preview = `$ ${args.command}${cwd}${timeout}`;
|
|
9755
|
+
}
|
|
9756
|
+
const response = await showPermissionPrompt(toolName, args, preview);
|
|
9757
|
+
if (response.action === "deny") {
|
|
9758
|
+
throw new PermissionDeniedError(toolName, args);
|
|
9759
|
+
}
|
|
9760
|
+
if (response.action === "allow-always") {
|
|
9761
|
+
const canTrust = permissionManager.trustTool(toolName);
|
|
9762
|
+
if (canTrust) {
|
|
9763
|
+
const projectDir = configStore.getProjectConfigDir();
|
|
9764
|
+
if (projectDir) {
|
|
9765
|
+
try {
|
|
9766
|
+
await configStore.initProjectConfig();
|
|
9767
|
+
const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
|
|
9768
|
+
const updatedLocal = {
|
|
9769
|
+
...existingLocal,
|
|
9770
|
+
trustedTools: [...existingLocal.trustedTools || [], toolName]
|
|
9771
|
+
};
|
|
9772
|
+
await configStore.saveProjectLocalConfig(updatedLocal);
|
|
9773
|
+
} catch (error) {
|
|
9774
|
+
await configStore.trustTool(toolName);
|
|
9775
|
+
}
|
|
9776
|
+
} else {
|
|
9777
|
+
await configStore.trustTool(toolName);
|
|
9778
|
+
}
|
|
9779
|
+
}
|
|
9780
|
+
}
|
|
9781
|
+
const result = await executeTool(toolName, args, apiClient, originalFn);
|
|
9782
|
+
agentContext.observationQueue.push({ toolName, result });
|
|
9783
|
+
return result;
|
|
9784
|
+
}
|
|
9785
|
+
};
|
|
9786
|
+
}
|
|
9787
|
+
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
|
|
9788
|
+
const logger2 = new CliLogger();
|
|
9789
|
+
const storage = new NoOpStorage();
|
|
9352
9790
|
const user = {
|
|
9353
9791
|
_id: userId,
|
|
9354
9792
|
email: "cli-user@bike4mind.local",
|
|
@@ -9363,18 +9801,6 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9363
9801
|
};
|
|
9364
9802
|
const mockAdminSettings = {
|
|
9365
9803
|
findBySettingName: async (settingName) => {
|
|
9366
|
-
if (settingName === "OpenWeatherKey") {
|
|
9367
|
-
const key = toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY;
|
|
9368
|
-
if (key) {
|
|
9369
|
-
return { settingValue: key };
|
|
9370
|
-
}
|
|
9371
|
-
}
|
|
9372
|
-
if (settingName === "SerperKey") {
|
|
9373
|
-
const key = toolApiKeys?.serper || process.env.SERPER_API_KEY;
|
|
9374
|
-
if (key) {
|
|
9375
|
-
return { settingValue: key };
|
|
9376
|
-
}
|
|
9377
|
-
}
|
|
9378
9804
|
return null;
|
|
9379
9805
|
}
|
|
9380
9806
|
};
|
|
@@ -9384,15 +9810,13 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9384
9810
|
adminSettings: mockAdminSettings
|
|
9385
9811
|
}
|
|
9386
9812
|
};
|
|
9387
|
-
const hasSerperKey = !!(toolApiKeys?.serper || process.env.SERPER_API_KEY);
|
|
9388
|
-
const hasOpenWeatherKey = !!(toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY);
|
|
9389
9813
|
const toolConfig = {
|
|
9390
|
-
//
|
|
9814
|
+
// Local-only tools (no external API keys needed)
|
|
9391
9815
|
dice_roll: {},
|
|
9392
9816
|
math_evaluate: {},
|
|
9393
9817
|
current_datetime: {},
|
|
9394
9818
|
prompt_enhancement: {},
|
|
9395
|
-
// File operation tools (CLI-specific)
|
|
9819
|
+
// File operation tools (CLI-specific, local execution)
|
|
9396
9820
|
file_read: {},
|
|
9397
9821
|
create_file: {},
|
|
9398
9822
|
glob_files: {},
|
|
@@ -9400,13 +9824,10 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9400
9824
|
delete_file: {},
|
|
9401
9825
|
// Shell execution (always requires permission)
|
|
9402
9826
|
bash_execute: {},
|
|
9403
|
-
//
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
// Requires OPENWEATHER_API_KEY
|
|
9408
|
-
...hasSerperKey && { deep_research: {} }
|
|
9409
|
-
// Requires SERPER_API_KEY
|
|
9827
|
+
// Server-side tools (executed via /api/ai/v1/tools Lambda)
|
|
9828
|
+
// Routed by ToolRouter to use B4M company API keys
|
|
9829
|
+
weather_info: {},
|
|
9830
|
+
web_search: {}
|
|
9410
9831
|
// Disable web-only tools
|
|
9411
9832
|
// blog_publish: undefined,
|
|
9412
9833
|
// blog_edit: undefined,
|
|
@@ -9416,6 +9837,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9416
9837
|
// mermaid_chart: undefined,
|
|
9417
9838
|
// recharts: undefined,
|
|
9418
9839
|
// edit_file: undefined,
|
|
9840
|
+
// deep_research: undefined,
|
|
9419
9841
|
};
|
|
9420
9842
|
const toolsMap = generateTools(
|
|
9421
9843
|
userId,
|
|
@@ -9433,7 +9855,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9433
9855
|
model
|
|
9434
9856
|
);
|
|
9435
9857
|
const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
|
|
9436
|
-
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore)
|
|
9858
|
+
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
|
|
9437
9859
|
);
|
|
9438
9860
|
return { tools: tools2, agentContext };
|
|
9439
9861
|
}
|
|
@@ -9629,261 +10051,6 @@ function getEnvironmentName(configApiConfig) {
|
|
|
9629
10051
|
return "Bike4Mind";
|
|
9630
10052
|
}
|
|
9631
10053
|
|
|
9632
|
-
// src/utils/Logger.ts
|
|
9633
|
-
import fs10 from "fs/promises";
|
|
9634
|
-
import path12 from "path";
|
|
9635
|
-
import os from "os";
|
|
9636
|
-
var Logger2 = class _Logger {
|
|
9637
|
-
constructor() {
|
|
9638
|
-
this.logFilePath = null;
|
|
9639
|
-
this.sessionId = null;
|
|
9640
|
-
this.fileLoggingEnabled = true;
|
|
9641
|
-
this.consoleVerbose = false;
|
|
9642
|
-
}
|
|
9643
|
-
static {
|
|
9644
|
-
this.instance = null;
|
|
9645
|
-
}
|
|
9646
|
-
static getInstance() {
|
|
9647
|
-
if (!_Logger.instance) {
|
|
9648
|
-
_Logger.instance = new _Logger();
|
|
9649
|
-
}
|
|
9650
|
-
return _Logger.instance;
|
|
9651
|
-
}
|
|
9652
|
-
/**
|
|
9653
|
-
* Initialize the logger with a session ID
|
|
9654
|
-
*/
|
|
9655
|
-
async initialize(sessionId) {
|
|
9656
|
-
this.sessionId = sessionId;
|
|
9657
|
-
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9658
|
-
await fs10.mkdir(debugDir, { recursive: true });
|
|
9659
|
-
this.logFilePath = path12.join(debugDir, `${sessionId}.txt`);
|
|
9660
|
-
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
9661
|
-
}
|
|
9662
|
-
/**
|
|
9663
|
-
* Set whether verbose console logging is enabled
|
|
9664
|
-
*/
|
|
9665
|
-
setVerbose(enabled) {
|
|
9666
|
-
this.consoleVerbose = enabled;
|
|
9667
|
-
}
|
|
9668
|
-
/**
|
|
9669
|
-
* Set whether file logging is enabled
|
|
9670
|
-
*/
|
|
9671
|
-
setFileLoggingEnabled(enabled) {
|
|
9672
|
-
this.fileLoggingEnabled = enabled;
|
|
9673
|
-
}
|
|
9674
|
-
/**
|
|
9675
|
-
* DEBUG level - verbose-only console, always file
|
|
9676
|
-
*/
|
|
9677
|
-
debug(message) {
|
|
9678
|
-
this.writeToFile("DEBUG", message).catch(() => {
|
|
9679
|
-
});
|
|
9680
|
-
if (this.consoleVerbose) {
|
|
9681
|
-
console.log(message);
|
|
9682
|
-
}
|
|
9683
|
-
}
|
|
9684
|
-
/**
|
|
9685
|
-
* INFO level - always shown to user
|
|
9686
|
-
*/
|
|
9687
|
-
info(message) {
|
|
9688
|
-
this.writeToFile("INFO", message).catch(() => {
|
|
9689
|
-
});
|
|
9690
|
-
console.log(message);
|
|
9691
|
-
}
|
|
9692
|
-
/**
|
|
9693
|
-
* WARN level - always shown to user
|
|
9694
|
-
*/
|
|
9695
|
-
warn(message) {
|
|
9696
|
-
this.writeToFile("WARN", message).catch(() => {
|
|
9697
|
-
});
|
|
9698
|
-
console.warn(message);
|
|
9699
|
-
}
|
|
9700
|
-
/**
|
|
9701
|
-
* ERROR level - always shown to user
|
|
9702
|
-
*/
|
|
9703
|
-
error(message, err) {
|
|
9704
|
-
this.writeToFile("ERROR", message).catch(() => {
|
|
9705
|
-
});
|
|
9706
|
-
if (err) {
|
|
9707
|
-
this.logErrorDetailsToFile(err).catch(() => {
|
|
9708
|
-
});
|
|
9709
|
-
}
|
|
9710
|
-
console.error(message);
|
|
9711
|
-
}
|
|
9712
|
-
/**
|
|
9713
|
-
* Write log entry to file
|
|
9714
|
-
*/
|
|
9715
|
-
async writeToFile(level, message) {
|
|
9716
|
-
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
9717
|
-
return;
|
|
9718
|
-
}
|
|
9719
|
-
try {
|
|
9720
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
9721
|
-
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
9722
|
-
`;
|
|
9723
|
-
await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
9724
|
-
} catch (error) {
|
|
9725
|
-
console.error("File logging failed:", error);
|
|
9726
|
-
}
|
|
9727
|
-
}
|
|
9728
|
-
/**
|
|
9729
|
-
* Log error details to file
|
|
9730
|
-
*/
|
|
9731
|
-
async logErrorDetailsToFile(err) {
|
|
9732
|
-
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
9733
|
-
return;
|
|
9734
|
-
}
|
|
9735
|
-
try {
|
|
9736
|
-
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
9737
|
-
const response = err.response;
|
|
9738
|
-
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
9739
|
-
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
9740
|
-
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
9741
|
-
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
9742
|
-
if (response.data) {
|
|
9743
|
-
const errorText = this.extractErrorMessage(response.data);
|
|
9744
|
-
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
9745
|
-
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
9746
|
-
const parsedError = this.parseHtmlError(errorText);
|
|
9747
|
-
if (parsedError) {
|
|
9748
|
-
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
9749
|
-
}
|
|
9750
|
-
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
9751
|
-
} else {
|
|
9752
|
-
const preview = this.truncate(errorText, 500);
|
|
9753
|
-
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
9754
|
-
}
|
|
9755
|
-
} else {
|
|
9756
|
-
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
9757
|
-
}
|
|
9758
|
-
}
|
|
9759
|
-
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
9760
|
-
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
9761
|
-
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
9762
|
-
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
9763
|
-
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
9764
|
-
}
|
|
9765
|
-
} catch (error) {
|
|
9766
|
-
}
|
|
9767
|
-
}
|
|
9768
|
-
/**
|
|
9769
|
-
* Safely stringify object, handling circular references
|
|
9770
|
-
*/
|
|
9771
|
-
safeStringify(obj) {
|
|
9772
|
-
try {
|
|
9773
|
-
return JSON.stringify(obj);
|
|
9774
|
-
} catch (error) {
|
|
9775
|
-
if (error instanceof Error && error.message.includes("circular")) {
|
|
9776
|
-
try {
|
|
9777
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
9778
|
-
return JSON.stringify(obj, (key, value) => {
|
|
9779
|
-
if (typeof value === "object" && value !== null) {
|
|
9780
|
-
if (seen.has(value)) {
|
|
9781
|
-
return "[Circular]";
|
|
9782
|
-
}
|
|
9783
|
-
seen.add(value);
|
|
9784
|
-
}
|
|
9785
|
-
return value;
|
|
9786
|
-
});
|
|
9787
|
-
} catch {
|
|
9788
|
-
return "[Unable to stringify]";
|
|
9789
|
-
}
|
|
9790
|
-
}
|
|
9791
|
-
return "[Stringify error]";
|
|
9792
|
-
}
|
|
9793
|
-
}
|
|
9794
|
-
/**
|
|
9795
|
-
* Extract readable text from error response data
|
|
9796
|
-
*/
|
|
9797
|
-
extractErrorMessage(data) {
|
|
9798
|
-
if (Buffer.isBuffer(data)) {
|
|
9799
|
-
return data.toString("utf-8");
|
|
9800
|
-
}
|
|
9801
|
-
if (typeof data === "string") {
|
|
9802
|
-
return data;
|
|
9803
|
-
}
|
|
9804
|
-
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) {
|
|
9805
|
-
const chunks = [];
|
|
9806
|
-
for (const chunk of data._readableState.buffer) {
|
|
9807
|
-
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
9808
|
-
chunks.push(Buffer.from(chunk.data));
|
|
9809
|
-
} else if (Buffer.isBuffer(chunk)) {
|
|
9810
|
-
chunks.push(chunk);
|
|
9811
|
-
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
9812
|
-
if (Buffer.isBuffer(chunk.data)) {
|
|
9813
|
-
chunks.push(chunk.data);
|
|
9814
|
-
} else if (Array.isArray(chunk.data)) {
|
|
9815
|
-
chunks.push(Buffer.from(chunk.data));
|
|
9816
|
-
}
|
|
9817
|
-
}
|
|
9818
|
-
}
|
|
9819
|
-
if (chunks.length > 0) {
|
|
9820
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
9821
|
-
}
|
|
9822
|
-
}
|
|
9823
|
-
return this.safeStringify(data);
|
|
9824
|
-
}
|
|
9825
|
-
/**
|
|
9826
|
-
* Parse HTML error page to extract error message
|
|
9827
|
-
*/
|
|
9828
|
-
parseHtmlError(html) {
|
|
9829
|
-
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
|
|
9830
|
-
const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
|
|
9831
|
-
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
9832
|
-
if (titleMatch && titleMatch[1] !== "Error") {
|
|
9833
|
-
return titleMatch[1].trim();
|
|
9834
|
-
}
|
|
9835
|
-
if (h1Match) {
|
|
9836
|
-
return h1Match[1].trim();
|
|
9837
|
-
}
|
|
9838
|
-
if (bodyMatch) {
|
|
9839
|
-
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
9840
|
-
return text.substring(0, 200);
|
|
9841
|
-
}
|
|
9842
|
-
return null;
|
|
9843
|
-
}
|
|
9844
|
-
/**
|
|
9845
|
-
* Truncate string to max length with ellipsis
|
|
9846
|
-
*/
|
|
9847
|
-
truncate(str, maxLength) {
|
|
9848
|
-
if (str.length <= maxLength) {
|
|
9849
|
-
return str;
|
|
9850
|
-
}
|
|
9851
|
-
return str.substring(0, maxLength) + "... [truncated]";
|
|
9852
|
-
}
|
|
9853
|
-
/**
|
|
9854
|
-
* Format bytes to human-readable size
|
|
9855
|
-
*/
|
|
9856
|
-
formatBytes(bytes) {
|
|
9857
|
-
if (bytes === 0) return "0 B";
|
|
9858
|
-
const k = 1024;
|
|
9859
|
-
const sizes = ["B", "KB", "MB", "GB"];
|
|
9860
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
9861
|
-
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
9862
|
-
}
|
|
9863
|
-
/**
|
|
9864
|
-
* Clean up old debug logs (older than 30 days)
|
|
9865
|
-
*/
|
|
9866
|
-
async cleanupOldLogs() {
|
|
9867
|
-
if (!this.fileLoggingEnabled) return;
|
|
9868
|
-
try {
|
|
9869
|
-
const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
|
|
9870
|
-
const files = await fs10.readdir(debugDir);
|
|
9871
|
-
const now = Date.now();
|
|
9872
|
-
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
9873
|
-
for (const file of files) {
|
|
9874
|
-
const filePath = path12.join(debugDir, file);
|
|
9875
|
-
const stats = await fs10.stat(filePath);
|
|
9876
|
-
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
9877
|
-
await fs10.unlink(filePath);
|
|
9878
|
-
}
|
|
9879
|
-
}
|
|
9880
|
-
} catch (error) {
|
|
9881
|
-
console.error("Failed to cleanup old logs:", error);
|
|
9882
|
-
}
|
|
9883
|
-
}
|
|
9884
|
-
};
|
|
9885
|
-
var logger = Logger2.getInstance();
|
|
9886
|
-
|
|
9887
10054
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
9888
10055
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
9889
10056
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -10687,7 +10854,10 @@ var ApiClient = class {
|
|
|
10687
10854
|
* Make a POST request
|
|
10688
10855
|
*/
|
|
10689
10856
|
async post(url, data, config) {
|
|
10857
|
+
logger.debug(`[ApiClient] POST ${this.client.defaults.baseURL}${url}`);
|
|
10858
|
+
logger.debug(`[ApiClient] Request body: ${JSON.stringify(data)}`);
|
|
10690
10859
|
const response = await this.client.post(url, data, config);
|
|
10860
|
+
logger.debug(`[ApiClient] Response status: ${response.status}`);
|
|
10691
10861
|
return response.data;
|
|
10692
10862
|
}
|
|
10693
10863
|
/**
|
|
@@ -10738,7 +10908,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
10738
10908
|
// package.json
|
|
10739
10909
|
var package_default = {
|
|
10740
10910
|
name: "@bike4mind/cli",
|
|
10741
|
-
version: "0.2.8-cli-bash-tools.
|
|
10911
|
+
version: "0.2.8-cli-bash-tools.17156+ced3f15ab",
|
|
10742
10912
|
type: "module",
|
|
10743
10913
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
10744
10914
|
license: "UNLICENSED",
|
|
@@ -10841,10 +11011,10 @@ var package_default = {
|
|
|
10841
11011
|
},
|
|
10842
11012
|
devDependencies: {
|
|
10843
11013
|
"@bike4mind/agents": "0.1.0",
|
|
10844
|
-
"@bike4mind/common": "2.38.2-cli-bash-tools.
|
|
10845
|
-
"@bike4mind/mcp": "1.20.2-cli-bash-tools.
|
|
10846
|
-
"@bike4mind/services": "2.33.2-cli-bash-tools.
|
|
10847
|
-
"@bike4mind/utils": "2.1.3-cli-bash-tools.
|
|
11014
|
+
"@bike4mind/common": "2.38.2-cli-bash-tools.17156+ced3f15ab",
|
|
11015
|
+
"@bike4mind/mcp": "1.20.2-cli-bash-tools.17156+ced3f15ab",
|
|
11016
|
+
"@bike4mind/services": "2.33.2-cli-bash-tools.17156+ced3f15ab",
|
|
11017
|
+
"@bike4mind/utils": "2.1.3-cli-bash-tools.17156+ced3f15ab",
|
|
10848
11018
|
"@types/better-sqlite3": "^7.6.12",
|
|
10849
11019
|
"@types/diff": "^5.0.9",
|
|
10850
11020
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -10857,7 +11027,7 @@ var package_default = {
|
|
|
10857
11027
|
typescript: "^5.9.3",
|
|
10858
11028
|
vitest: "^3.2.4"
|
|
10859
11029
|
},
|
|
10860
|
-
gitHead: "
|
|
11030
|
+
gitHead: "ced3f15ab25eb7967bc31832769ab00be9965ff3"
|
|
10861
11031
|
};
|
|
10862
11032
|
|
|
10863
11033
|
// src/config/constants.ts
|
|
@@ -10879,7 +11049,7 @@ var usageCache = null;
|
|
|
10879
11049
|
function CliApp() {
|
|
10880
11050
|
const { exit } = useApp();
|
|
10881
11051
|
const imageRenderer = new ImageRenderer();
|
|
10882
|
-
const [state, setState] =
|
|
11052
|
+
const [state, setState] = useState6({
|
|
10883
11053
|
session: null,
|
|
10884
11054
|
sessionStore: new SessionStore(),
|
|
10885
11055
|
configStore: new ConfigStore(),
|
|
@@ -10895,9 +11065,9 @@ function CliApp() {
|
|
|
10895
11065
|
trustLocationSelector: null,
|
|
10896
11066
|
rewindSelector: null
|
|
10897
11067
|
});
|
|
10898
|
-
const [isInitialized, setIsInitialized] =
|
|
10899
|
-
const [initError, setInitError] =
|
|
10900
|
-
const [commandHistory, setCommandHistory] =
|
|
11068
|
+
const [isInitialized, setIsInitialized] = useState6(false);
|
|
11069
|
+
const [initError, setInitError] = useState6(null);
|
|
11070
|
+
const [commandHistory, setCommandHistory] = useState6([]);
|
|
10901
11071
|
const imageStoreInitPromise = useRef(null);
|
|
10902
11072
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
10903
11073
|
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
@@ -10938,7 +11108,7 @@ function CliApp() {
|
|
|
10938
11108
|
process.exit(0);
|
|
10939
11109
|
}, 100);
|
|
10940
11110
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
10941
|
-
|
|
11111
|
+
useInput5((input, key) => {
|
|
10942
11112
|
if (key.ctrl && input === "c") {
|
|
10943
11113
|
const now = Date.now();
|
|
10944
11114
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
@@ -11082,10 +11252,6 @@ function CliApp() {
|
|
|
11082
11252
|
setPermissionPrompt(prompt);
|
|
11083
11253
|
});
|
|
11084
11254
|
};
|
|
11085
|
-
console.log("\u{1F511} Tool API Keys:", {
|
|
11086
|
-
openweather: config.toolApiKeys?.openweather ? "\u2713 configured" : "\u2717 missing",
|
|
11087
|
-
serper: config.toolApiKeys?.serper ? "\u2713 configured" : "\u2717 missing"
|
|
11088
|
-
});
|
|
11089
11255
|
const agentContext = {
|
|
11090
11256
|
currentAgent: null,
|
|
11091
11257
|
observationQueue: []
|
|
@@ -11098,7 +11264,7 @@ function CliApp() {
|
|
|
11098
11264
|
promptFn,
|
|
11099
11265
|
agentContext,
|
|
11100
11266
|
state.configStore,
|
|
11101
|
-
|
|
11267
|
+
apiClient
|
|
11102
11268
|
);
|
|
11103
11269
|
console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
|
|
11104
11270
|
const mcpManager = new McpManager(config);
|
|
@@ -11854,12 +12020,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11854
12020
|
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
11855
12021
|
};
|
|
11856
12022
|
if (initError) {
|
|
11857
|
-
return /* @__PURE__ */
|
|
12023
|
+
return /* @__PURE__ */ React15.createElement(Box14, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React15.createElement(Text14, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React15.createElement(Text14, null, initError), /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
|
|
11858
12024
|
}
|
|
11859
12025
|
if (state.trustLocationSelector) {
|
|
11860
12026
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
11861
12027
|
const inProject = projectDir !== null;
|
|
11862
|
-
return /* @__PURE__ */
|
|
12028
|
+
return /* @__PURE__ */ React15.createElement(
|
|
11863
12029
|
TrustLocationSelector,
|
|
11864
12030
|
{
|
|
11865
12031
|
inProject,
|
|
@@ -11877,7 +12043,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11877
12043
|
);
|
|
11878
12044
|
}
|
|
11879
12045
|
if (state.rewindSelector && state.session) {
|
|
11880
|
-
return /* @__PURE__ */
|
|
12046
|
+
return /* @__PURE__ */ React15.createElement(
|
|
11881
12047
|
RewindSelector,
|
|
11882
12048
|
{
|
|
11883
12049
|
messages: state.session.messages,
|
|
@@ -11896,7 +12062,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11896
12062
|
}
|
|
11897
12063
|
if (state.showLoginFlow) {
|
|
11898
12064
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
11899
|
-
return /* @__PURE__ */
|
|
12065
|
+
return /* @__PURE__ */ React15.createElement(
|
|
11900
12066
|
LoginFlow,
|
|
11901
12067
|
{
|
|
11902
12068
|
apiUrl: loginApiUrl,
|
|
@@ -11919,9 +12085,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11919
12085
|
);
|
|
11920
12086
|
}
|
|
11921
12087
|
if (!isInitialized) {
|
|
11922
|
-
return /* @__PURE__ */
|
|
12088
|
+
return /* @__PURE__ */ React15.createElement(Box14, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React15.createElement(Text14, null, "\u{1F680} Initializing..."));
|
|
11923
12089
|
}
|
|
11924
|
-
return /* @__PURE__ */
|
|
12090
|
+
return /* @__PURE__ */ React15.createElement(
|
|
11925
12091
|
App,
|
|
11926
12092
|
{
|
|
11927
12093
|
onMessage: handleMessage,
|
|
@@ -11960,6 +12126,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
11960
12126
|
if (isDevMode) {
|
|
11961
12127
|
console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
11962
12128
|
}
|
|
11963
|
-
render(/* @__PURE__ */
|
|
12129
|
+
render(/* @__PURE__ */ React15.createElement(CliApp, null), {
|
|
11964
12130
|
exitOnCtrlC: false
|
|
11965
12131
|
});
|