@bike4mind/cli 0.2.7-basic-cli-shortcuts.17131 → 0.2.7-feat-cli-tool-lambda-v2.17129
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 +639 -674
- 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 Box13, Text 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
|
|
87
|
-
import
|
|
88
|
-
import { Box as Box9, Text as
|
|
87
|
+
import React9 from "react";
|
|
88
|
+
import { Box as Box9, Text as Text8, Static } from "ink";
|
|
89
89
|
|
|
90
90
|
// src/components/StatusBar.tsx
|
|
91
91
|
import React from "react";
|
|
@@ -95,127 +95,27 @@ 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
|
-
|
|
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
|
-
}
|
|
98
|
+
import React4, { useState, useMemo, useEffect } from "react";
|
|
99
|
+
import { Box as Box4, Text as Text4, useInput } from "ink";
|
|
100
|
+
import TextInput from "ink-text-input";
|
|
201
101
|
|
|
202
102
|
// src/components/CommandAutocomplete.tsx
|
|
203
|
-
import
|
|
204
|
-
import { Box as Box2, Text as
|
|
103
|
+
import React2 from "react";
|
|
104
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
205
105
|
function CommandAutocomplete({ commands, selectedIndex }) {
|
|
206
106
|
if (commands.length === 0) {
|
|
207
|
-
return /* @__PURE__ */
|
|
107
|
+
return /* @__PURE__ */ React2.createElement(Box2, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "No matching commands"));
|
|
208
108
|
}
|
|
209
|
-
return /* @__PURE__ */
|
|
109
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, commands.length === 1 ? "1 command" : `${commands.length} commands`, " \u2022 Use \u2191\u2193 to navigate, Enter to select")), commands.map((cmd, index) => {
|
|
210
110
|
const args = cmd.args ? ` ${cmd.args}` : "";
|
|
211
111
|
const isSelected = index === selectedIndex;
|
|
212
|
-
return /* @__PURE__ */
|
|
112
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: cmd.name, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "\u25B8 " : " ", "/", cmd.name, args, " - ", cmd.description));
|
|
213
113
|
}));
|
|
214
114
|
}
|
|
215
115
|
|
|
216
116
|
// src/components/FileAutocomplete.tsx
|
|
217
|
-
import
|
|
218
|
-
import { Box as Box3, Text as
|
|
117
|
+
import React3 from "react";
|
|
118
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
219
119
|
|
|
220
120
|
// src/utils/fileSearch.ts
|
|
221
121
|
import * as fs from "fs";
|
|
@@ -404,14 +304,14 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
|
404
304
|
// src/components/FileAutocomplete.tsx
|
|
405
305
|
function FileAutocomplete({ files, selectedIndex, query }) {
|
|
406
306
|
if (files.length === 0) {
|
|
407
|
-
return /* @__PURE__ */
|
|
307
|
+
return /* @__PURE__ */ React3.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No matching files", query ? ` for "${query}"` : ""));
|
|
408
308
|
}
|
|
409
|
-
return /* @__PURE__ */
|
|
309
|
+
return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, files.length === 1 ? "1 match" : `${files.length} matches`, " - Use up/down to navigate, Tab to select")), files.map((file, index) => {
|
|
410
310
|
const isSelected = index === selectedIndex;
|
|
411
311
|
const icon = file.isDirectory ? "[folder]" : "[file] ";
|
|
412
312
|
const sizeDisplay = file.isDirectory ? "" : file.size !== void 0 ? ` (${formatFileSize(file.size)})` : "";
|
|
413
313
|
const pathDisplay = file.isDirectory ? `${file.path}/` : file.path;
|
|
414
|
-
return /* @__PURE__ */
|
|
314
|
+
return /* @__PURE__ */ React3.createElement(Box3, { key: file.path, marginLeft: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "> " : " "), /* @__PURE__ */ React3.createElement(Text3, { color: file.isDirectory ? "yellow" : isSelected ? "cyan" : void 0, bold: isSelected }, icon), /* @__PURE__ */ React3.createElement(Text3, { color: isSelected ? "cyan" : void 0, bold: isSelected }, " ", pathDisplay), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, sizeDisplay));
|
|
415
315
|
}));
|
|
416
316
|
}
|
|
417
317
|
|
|
@@ -726,12 +626,13 @@ function findAtTrigger(value) {
|
|
|
726
626
|
return null;
|
|
727
627
|
}
|
|
728
628
|
function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = [] }) {
|
|
729
|
-
const [value, setValue] =
|
|
730
|
-
const [selectedIndex, setSelectedIndex] =
|
|
731
|
-
const [historyIndex, setHistoryIndex] =
|
|
732
|
-
const [tempInput, setTempInput] =
|
|
733
|
-
const [
|
|
734
|
-
const [
|
|
629
|
+
const [value, setValue] = useState("");
|
|
630
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
631
|
+
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
632
|
+
const [tempInput, setTempInput] = useState("");
|
|
633
|
+
const [inputKey, setInputKey] = useState(0);
|
|
634
|
+
const [fileAutocomplete, setFileAutocomplete] = useState(null);
|
|
635
|
+
const [fileSelectedIndex, setFileSelectedIndex] = useState(0);
|
|
735
636
|
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
|
|
736
637
|
const commandQuery = shouldShowCommandAutocomplete ? value.slice(1) : "";
|
|
737
638
|
const filteredCommands = useMemo(() => {
|
|
@@ -748,7 +649,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
748
649
|
useEffect(() => {
|
|
749
650
|
setFileSelectedIndex(0);
|
|
750
651
|
}, [filteredFiles]);
|
|
751
|
-
|
|
652
|
+
useInput((input, key) => {
|
|
752
653
|
if (fileAutocomplete?.active && filteredFiles.length > 0) {
|
|
753
654
|
if (key.upArrow) {
|
|
754
655
|
setFileSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredFiles.length - 1);
|
|
@@ -805,6 +706,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
805
706
|
const pathToInsert = file.isDirectory ? `@${file.path}/` : `@${file.path}`;
|
|
806
707
|
const newValue = beforeAt + pathToInsert + afterQuery;
|
|
807
708
|
setValue(newValue);
|
|
709
|
+
setInputKey((prev) => prev + 1);
|
|
808
710
|
if (file.isDirectory) {
|
|
809
711
|
setFileAutocomplete({
|
|
810
712
|
active: true,
|
|
@@ -831,6 +733,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
831
733
|
if (selectedCommand.args) {
|
|
832
734
|
setValue(commandText + " ");
|
|
833
735
|
setHistoryIndex(-1);
|
|
736
|
+
setInputKey((prev) => prev + 1);
|
|
834
737
|
return;
|
|
835
738
|
}
|
|
836
739
|
setValue("");
|
|
@@ -849,6 +752,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
849
752
|
if (imageEvent && onImageDetected) {
|
|
850
753
|
const placeholder = await onImageDetected(imageEvent.data);
|
|
851
754
|
setValue(`${placeholder} `);
|
|
755
|
+
setInputKey((prev) => prev + 1);
|
|
852
756
|
return;
|
|
853
757
|
}
|
|
854
758
|
}
|
|
@@ -868,28 +772,29 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
868
772
|
setFileAutocomplete(null);
|
|
869
773
|
}
|
|
870
774
|
};
|
|
871
|
-
return /* @__PURE__ */
|
|
872
|
-
|
|
775
|
+
return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React4.createElement(
|
|
776
|
+
TextInput,
|
|
873
777
|
{
|
|
778
|
+
key: inputKey,
|
|
874
779
|
value,
|
|
875
780
|
onChange: handleChange,
|
|
876
781
|
onSubmit: handleSubmit,
|
|
877
782
|
placeholder: disabled ? "Thinking..." : "Type your message, /help for commands, or @file to reference",
|
|
878
783
|
showCursor: !disabled
|
|
879
784
|
}
|
|
880
|
-
)), shouldShowCommandAutocomplete && /* @__PURE__ */
|
|
785
|
+
)), shouldShowCommandAutocomplete && /* @__PURE__ */ React4.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React4.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
|
|
881
786
|
}
|
|
882
787
|
|
|
883
788
|
// src/components/AgentThinking.tsx
|
|
884
|
-
import
|
|
789
|
+
import React6 from "react";
|
|
885
790
|
import { Box as Box6 } from "ink";
|
|
886
791
|
|
|
887
792
|
// src/components/ThoughtStream.tsx
|
|
888
|
-
import
|
|
889
|
-
import { Box as Box5, Text as
|
|
793
|
+
import React5 from "react";
|
|
794
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
890
795
|
import Spinner from "ink-spinner";
|
|
891
|
-
var ThoughtStream =
|
|
892
|
-
return /* @__PURE__ */
|
|
796
|
+
var ThoughtStream = React5.memo(function ThoughtStream2({ steps }) {
|
|
797
|
+
return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", gap: 1 }, steps.map((step, index) => /* @__PURE__ */ React5.createElement(Box5, { key: index, flexDirection: "column" }, step.type === "thought" && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, "\u{1F914} Thought: "), /* @__PURE__ */ React5.createElement(Text5, null, step.content)), step.type === "action" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, "\u26A1 Action: "), /* @__PURE__ */ React5.createElement(Text5, { bold: true }, step.metadata?.toolName || "unknown")), step.metadata?.toolInput && /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, JSON.stringify(step.metadata.toolInput, null, 2)))), step.type === "observation" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, "\u{1F4CA} Observation: ")), /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, null, step.content))), step.type === "final_answer" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "green", bold: true }, "\u2705 Final Answer:", " ")), /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, null, step.content)), step.metadata?.tokenUsage && /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(", step.metadata.tokenUsage.total, " tokens)"))))), !steps.some((s) => s.type === "final_answer") && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, /* @__PURE__ */ React5.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, null, " ", steps.length === 0 ? "Thinking..." : "Processing...")));
|
|
893
798
|
});
|
|
894
799
|
|
|
895
800
|
// src/store/index.ts
|
|
@@ -918,32 +823,32 @@ var useCliStore = create((set) => ({
|
|
|
918
823
|
}));
|
|
919
824
|
|
|
920
825
|
// src/components/AgentThinking.tsx
|
|
921
|
-
var AgentThinking =
|
|
826
|
+
var AgentThinking = React6.memo(function AgentThinking2() {
|
|
922
827
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
923
828
|
const agentSteps = useCliStore((state) => state.agentSteps);
|
|
924
829
|
if (!isThinking) {
|
|
925
830
|
return null;
|
|
926
831
|
}
|
|
927
|
-
return /* @__PURE__ */
|
|
832
|
+
return /* @__PURE__ */ React6.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React6.createElement(ThoughtStream, { steps: agentSteps }));
|
|
928
833
|
});
|
|
929
834
|
|
|
930
835
|
// src/components/PermissionPrompt.tsx
|
|
931
|
-
import
|
|
932
|
-
import { Box as Box7, Text as
|
|
836
|
+
import React7 from "react";
|
|
837
|
+
import { Box as Box7, Text as Text6 } from "ink";
|
|
933
838
|
import SelectInput from "ink-select-input";
|
|
934
839
|
function renderDiffPreview(preview) {
|
|
935
840
|
const lines = preview.split("\n");
|
|
936
841
|
return lines.map((line, index) => {
|
|
937
842
|
if (line.startsWith("+")) {
|
|
938
|
-
return /* @__PURE__ */
|
|
843
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "green" }, line);
|
|
939
844
|
}
|
|
940
845
|
if (line.startsWith("-")) {
|
|
941
|
-
return /* @__PURE__ */
|
|
846
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "red" }, line);
|
|
942
847
|
}
|
|
943
848
|
if (line.startsWith("@@")) {
|
|
944
|
-
return /* @__PURE__ */
|
|
849
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "cyan" }, line);
|
|
945
850
|
}
|
|
946
|
-
return /* @__PURE__ */
|
|
851
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, dimColor: true }, line);
|
|
947
852
|
});
|
|
948
853
|
}
|
|
949
854
|
function PermissionPrompt({
|
|
@@ -963,12 +868,12 @@ function PermissionPrompt({
|
|
|
963
868
|
{ label: "\u2717 Deny", value: "deny" }
|
|
964
869
|
];
|
|
965
870
|
const argsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
|
|
966
|
-
return /* @__PURE__ */
|
|
871
|
+
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "Tool: "), /* @__PURE__ */ React7.createElement(Text6, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "Action: "), /* @__PURE__ */ React7.createElement(Text6, null, toolDescription)), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Arguments:"), /* @__PURE__ */ React7.createElement(Box7, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Preview:"), /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text6, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(SelectInput, { items, onSelect: (item) => onResponse(item.value) })));
|
|
967
872
|
}
|
|
968
873
|
|
|
969
874
|
// src/components/ConfigEditor.tsx
|
|
970
|
-
import
|
|
971
|
-
import { Box as Box8, Text as
|
|
875
|
+
import React8, { useState as useState2, useMemo as useMemo2 } from "react";
|
|
876
|
+
import { Box as Box8, Text as Text7, useInput as useInput2 } from "ink";
|
|
972
877
|
var MAX_ITERATIONS_OPTIONS = [
|
|
973
878
|
{ label: "10", value: 10 },
|
|
974
879
|
{ label: "20", value: 20 },
|
|
@@ -1099,10 +1004,10 @@ function buildConfigItems(availableModels) {
|
|
|
1099
1004
|
return items;
|
|
1100
1005
|
}
|
|
1101
1006
|
function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
1102
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1103
|
-
const [editedConfig, setEditedConfig] =
|
|
1104
|
-
const [saveError, setSaveError] =
|
|
1105
|
-
const [isSaving, setIsSaving] =
|
|
1007
|
+
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
1008
|
+
const [editedConfig, setEditedConfig] = useState2(config);
|
|
1009
|
+
const [saveError, setSaveError] = useState2(null);
|
|
1010
|
+
const [isSaving, setIsSaving] = useState2(false);
|
|
1106
1011
|
const configItems = useMemo2(() => buildConfigItems(availableModels), [availableModels]);
|
|
1107
1012
|
const hasChanges = useMemo2(() => {
|
|
1108
1013
|
return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences) || config.defaultModel !== editedConfig.defaultModel;
|
|
@@ -1122,7 +1027,7 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1122
1027
|
}
|
|
1123
1028
|
onClose();
|
|
1124
1029
|
};
|
|
1125
|
-
|
|
1030
|
+
useInput2((input, key) => {
|
|
1126
1031
|
const currentItem = configItems[selectedIndex];
|
|
1127
1032
|
if (key.upArrow) {
|
|
1128
1033
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : configItems.length - 1);
|
|
@@ -1187,13 +1092,13 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1187
1092
|
}
|
|
1188
1093
|
return String(value);
|
|
1189
1094
|
};
|
|
1190
|
-
return /* @__PURE__ */
|
|
1095
|
+
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, marginY: 1 }, /* @__PURE__ */ React8.createElement(Box8, { justifyContent: "space-between", marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, "Settings"), isSaving ? /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, "Saving...") : hasChanges ? /* @__PURE__ */ React8.createElement(Text7, { dimColor: true, color: "yellow" }, "(unsaved changes)") : null), saveError && /* @__PURE__ */ React8.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "red" }, "Error: ", saveError)), /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, configItems.map((item, index) => {
|
|
1191
1096
|
const isSelected = index === selectedIndex;
|
|
1192
1097
|
const value = item.getValue(editedConfig);
|
|
1193
1098
|
const displayValue = formatValue(item, value);
|
|
1194
1099
|
const hasOptions = item.type === "select" || item.type === "boolean" || item.type === "number";
|
|
1195
|
-
return /* @__PURE__ */
|
|
1196
|
-
})), /* @__PURE__ */
|
|
1100
|
+
return /* @__PURE__ */ React8.createElement(Box8, { key: item.key }, /* @__PURE__ */ React8.createElement(Text7, { color: isSelected ? "cyan" : void 0 }, isSelected ? "> " : " "), /* @__PURE__ */ React8.createElement(Box8, { width: 18 }, /* @__PURE__ */ React8.createElement(Text7, { bold: isSelected, color: isSelected ? "white" : "gray" }, item.label, ":")), /* @__PURE__ */ React8.createElement(Box8, null, hasOptions && isSelected && /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "< "), /* @__PURE__ */ React8.createElement(Text7, { bold: isSelected, color: isSelected ? "green" : void 0 }, displayValue), hasOptions && isSelected && /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " >")));
|
|
1101
|
+
})), /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "\u2191/\u2193: Navigate | Space/\u2190/\u2192: Change | q/Esc: Save & Exit")));
|
|
1197
1102
|
}
|
|
1198
1103
|
|
|
1199
1104
|
// src/utils/processFileReferences.ts
|
|
@@ -1331,7 +1236,7 @@ function App({
|
|
|
1331
1236
|
const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
|
|
1332
1237
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
1333
1238
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
1334
|
-
const handleSubmit =
|
|
1239
|
+
const handleSubmit = React9.useCallback(
|
|
1335
1240
|
async (input) => {
|
|
1336
1241
|
const trimmed = input.trim();
|
|
1337
1242
|
if (!trimmed) return;
|
|
@@ -1360,7 +1265,7 @@ ${errorBlock}`;
|
|
|
1360
1265
|
},
|
|
1361
1266
|
[onMessage, onCommand, setIsThinking]
|
|
1362
1267
|
);
|
|
1363
|
-
return /* @__PURE__ */
|
|
1268
|
+
return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), showConfigEditor && config && onSaveConfig ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
|
|
1364
1269
|
ConfigEditor,
|
|
1365
1270
|
{
|
|
1366
1271
|
config,
|
|
@@ -1368,7 +1273,7 @@ ${errorBlock}`;
|
|
|
1368
1273
|
onSave: onSaveConfig,
|
|
1369
1274
|
onClose: () => setShowConfigEditor(false)
|
|
1370
1275
|
}
|
|
1371
|
-
)) : permissionPrompt ? /* @__PURE__ */
|
|
1276
|
+
)) : permissionPrompt ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
|
|
1372
1277
|
PermissionPrompt,
|
|
1373
1278
|
{
|
|
1374
1279
|
toolName: permissionPrompt.toolName,
|
|
@@ -1377,7 +1282,7 @@ ${errorBlock}`;
|
|
|
1377
1282
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
1378
1283
|
onResponse: onPermissionResponse
|
|
1379
1284
|
}
|
|
1380
|
-
)) : /* @__PURE__ */
|
|
1285
|
+
)) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React9.createElement(Box9, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(MessageItem, { message }))), /* @__PURE__ */ React9.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React9.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React9.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
|
|
1381
1286
|
InputPrompt,
|
|
1382
1287
|
{
|
|
1383
1288
|
onSubmit: handleSubmit,
|
|
@@ -1388,36 +1293,36 @@ ${errorBlock}`;
|
|
|
1388
1293
|
))));
|
|
1389
1294
|
}
|
|
1390
1295
|
function MessageItem({ message }) {
|
|
1391
|
-
return /* @__PURE__ */
|
|
1296
|
+
return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React9.createElement(Text8, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1392
1297
|
if (step.type === "thought") {
|
|
1393
|
-
return /* @__PURE__ */
|
|
1298
|
+
return /* @__PURE__ */ React9.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
|
|
1394
1299
|
}
|
|
1395
1300
|
if (step.type === "action") {
|
|
1396
1301
|
const toolName = step.metadata?.toolName || "unknown";
|
|
1397
1302
|
const toolInput = step.metadata?.toolInput;
|
|
1398
1303
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1399
1304
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1400
|
-
return /* @__PURE__ */
|
|
1305
|
+
return /* @__PURE__ */ React9.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1401
1306
|
}
|
|
1402
1307
|
return null;
|
|
1403
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1308
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
1404
1309
|
}
|
|
1405
1310
|
|
|
1406
1311
|
// src/components/MessageList.tsx
|
|
1407
|
-
import
|
|
1408
|
-
import { Box as Box10, Text as
|
|
1312
|
+
import React10 from "react";
|
|
1313
|
+
import { Box as Box10, Text as Text9 } from "ink";
|
|
1409
1314
|
function stripThinkingTags(content) {
|
|
1410
1315
|
return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
1411
1316
|
}
|
|
1412
|
-
var MessageList =
|
|
1317
|
+
var MessageList = React10.memo(
|
|
1413
1318
|
function MessageList2({ messages }) {
|
|
1414
1319
|
if (messages.length === 0) {
|
|
1415
|
-
return /* @__PURE__ */
|
|
1320
|
+
return /* @__PURE__ */ React10.createElement(Box10, { paddingY: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "No messages yet. Type a message to start!"));
|
|
1416
1321
|
}
|
|
1417
|
-
return /* @__PURE__ */
|
|
1322
|
+
return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React10.createElement(Box10, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React10.createElement(Box10, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", stripThinkingTags(message.content)) : /* @__PURE__ */ React10.createElement(Text9, null, stripThinkingTags(message.content))), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box10, { 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) => {
|
|
1418
1323
|
if (step.type === "thought") {
|
|
1419
|
-
return /* @__PURE__ */
|
|
1420
|
-
|
|
1324
|
+
return /* @__PURE__ */ React10.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(
|
|
1325
|
+
Text9,
|
|
1421
1326
|
{
|
|
1422
1327
|
dimColor: true
|
|
1423
1328
|
},
|
|
@@ -1429,10 +1334,10 @@ var MessageList = React11.memo(
|
|
|
1429
1334
|
const toolInput = step.metadata?.toolInput;
|
|
1430
1335
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1431
1336
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1432
|
-
return /* @__PURE__ */
|
|
1337
|
+
return /* @__PURE__ */ React10.createElement(Box10, { 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 ? "..." : ""}`));
|
|
1433
1338
|
}
|
|
1434
1339
|
return null;
|
|
1435
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1340
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box10, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
|
|
1436
1341
|
},
|
|
1437
1342
|
(prevProps, nextProps) => {
|
|
1438
1343
|
if (prevProps.messages.length !== nextProps.messages.length) {
|
|
@@ -1443,8 +1348,8 @@ var MessageList = React11.memo(
|
|
|
1443
1348
|
);
|
|
1444
1349
|
|
|
1445
1350
|
// src/components/TrustLocationSelector.tsx
|
|
1446
|
-
import
|
|
1447
|
-
import { Box as Box11, Text as
|
|
1351
|
+
import React11 from "react";
|
|
1352
|
+
import { Box as Box11, Text as Text10 } from "ink";
|
|
1448
1353
|
import SelectInput2 from "ink-select-input";
|
|
1449
1354
|
function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
1450
1355
|
const items = [];
|
|
@@ -1465,19 +1370,19 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
1465
1370
|
const handleSelect = (item) => {
|
|
1466
1371
|
onSelect(item.value);
|
|
1467
1372
|
};
|
|
1468
|
-
return /* @__PURE__ */
|
|
1373
|
+
return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "Where should this rule be saved?")), /* @__PURE__ */ React11.createElement(
|
|
1469
1374
|
SelectInput2,
|
|
1470
1375
|
{
|
|
1471
1376
|
items,
|
|
1472
1377
|
onSelect: handleSelect,
|
|
1473
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1378
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text10, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1474
1379
|
}
|
|
1475
|
-
), /* @__PURE__ */
|
|
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")));
|
|
1476
1381
|
}
|
|
1477
1382
|
|
|
1478
1383
|
// src/components/LoginFlow.tsx
|
|
1479
|
-
import
|
|
1480
|
-
import { Box as Box12, Text as
|
|
1384
|
+
import React12, { useState as useState3, useEffect as useEffect2 } from "react";
|
|
1385
|
+
import { Box as Box12, Text as Text11 } from "ink";
|
|
1481
1386
|
import Spinner2 from "ink-spinner";
|
|
1482
1387
|
import jwt from "jsonwebtoken";
|
|
1483
1388
|
import open from "open";
|
|
@@ -1593,10 +1498,10 @@ var OAuthClient = class {
|
|
|
1593
1498
|
|
|
1594
1499
|
// src/components/LoginFlow.tsx
|
|
1595
1500
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
1596
|
-
const [status, setStatus] =
|
|
1597
|
-
const [deviceFlow, setDeviceFlow] =
|
|
1598
|
-
const [statusMessage, setStatusMessage] =
|
|
1599
|
-
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);
|
|
1600
1505
|
useEffect2(() => {
|
|
1601
1506
|
const runLoginFlow = async () => {
|
|
1602
1507
|
const oauth = new OAuthClient(apiUrl);
|
|
@@ -1640,15 +1545,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
1640
1545
|
}
|
|
1641
1546
|
}, [deviceFlow, status]);
|
|
1642
1547
|
if (status === "initiating") {
|
|
1643
|
-
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...")));
|
|
1644
1549
|
}
|
|
1645
1550
|
if (status === "error") {
|
|
1646
|
-
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)));
|
|
1647
1552
|
}
|
|
1648
1553
|
if (status === "success") {
|
|
1649
|
-
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.")));
|
|
1650
1555
|
}
|
|
1651
|
-
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")));
|
|
1652
1557
|
}
|
|
1653
1558
|
|
|
1654
1559
|
// src/storage/SessionStore.ts
|
|
@@ -8858,144 +8763,464 @@ Last modified: ${stats.mtime.toLocaleString()}`;
|
|
|
8858
8763
|
}
|
|
8859
8764
|
}
|
|
8860
8765
|
|
|
8861
|
-
// src/utils/
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8766
|
+
// src/utils/Logger.ts
|
|
8767
|
+
import fs10 from "fs/promises";
|
|
8768
|
+
import path11 from "path";
|
|
8769
|
+
import os from "os";
|
|
8770
|
+
var Logger2 = class _Logger {
|
|
8771
|
+
constructor() {
|
|
8772
|
+
this.logFilePath = null;
|
|
8773
|
+
this.sessionId = null;
|
|
8774
|
+
this.fileLoggingEnabled = true;
|
|
8775
|
+
this.consoleVerbose = false;
|
|
8870
8776
|
}
|
|
8871
|
-
|
|
8872
|
-
|
|
8777
|
+
static {
|
|
8778
|
+
this.instance = null;
|
|
8873
8779
|
}
|
|
8874
|
-
|
|
8875
|
-
|
|
8780
|
+
static getInstance() {
|
|
8781
|
+
if (!_Logger.instance) {
|
|
8782
|
+
_Logger.instance = new _Logger();
|
|
8783
|
+
}
|
|
8784
|
+
return _Logger.instance;
|
|
8876
8785
|
}
|
|
8877
|
-
|
|
8878
|
-
|
|
8786
|
+
/**
|
|
8787
|
+
* Initialize the logger with a session ID
|
|
8788
|
+
*/
|
|
8789
|
+
async initialize(sessionId) {
|
|
8790
|
+
this.sessionId = sessionId;
|
|
8791
|
+
const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
|
|
8792
|
+
await fs10.mkdir(debugDir, { recursive: true });
|
|
8793
|
+
this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
|
|
8794
|
+
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
8879
8795
|
}
|
|
8880
|
-
|
|
8881
|
-
|
|
8796
|
+
/**
|
|
8797
|
+
* Set whether verbose console logging is enabled
|
|
8798
|
+
*/
|
|
8799
|
+
setVerbose(enabled) {
|
|
8800
|
+
this.consoleVerbose = enabled;
|
|
8882
8801
|
}
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8802
|
+
/**
|
|
8803
|
+
* Set whether file logging is enabled
|
|
8804
|
+
*/
|
|
8805
|
+
setFileLoggingEnabled(enabled) {
|
|
8806
|
+
this.fileLoggingEnabled = enabled;
|
|
8887
8807
|
}
|
|
8888
|
-
|
|
8889
|
-
|
|
8808
|
+
/**
|
|
8809
|
+
* DEBUG level - verbose-only console, always file
|
|
8810
|
+
*/
|
|
8811
|
+
debug(message) {
|
|
8812
|
+
this.writeToFile("DEBUG", message).catch(() => {
|
|
8813
|
+
});
|
|
8814
|
+
if (this.consoleVerbose) {
|
|
8815
|
+
console.log(message);
|
|
8816
|
+
}
|
|
8890
8817
|
}
|
|
8891
|
-
|
|
8818
|
+
/**
|
|
8819
|
+
* INFO level - always shown to user
|
|
8820
|
+
*/
|
|
8821
|
+
info(message) {
|
|
8822
|
+
this.writeToFile("INFO", message).catch(() => {
|
|
8823
|
+
});
|
|
8824
|
+
console.log(message);
|
|
8892
8825
|
}
|
|
8893
|
-
|
|
8826
|
+
/**
|
|
8827
|
+
* WARN level - always shown to user
|
|
8828
|
+
*/
|
|
8829
|
+
warn(message) {
|
|
8830
|
+
this.writeToFile("WARN", message).catch(() => {
|
|
8831
|
+
});
|
|
8832
|
+
console.warn(message);
|
|
8894
8833
|
}
|
|
8895
|
-
|
|
8834
|
+
/**
|
|
8835
|
+
* ERROR level - always shown to user
|
|
8836
|
+
*/
|
|
8837
|
+
error(message, err) {
|
|
8838
|
+
this.writeToFile("ERROR", message).catch(() => {
|
|
8839
|
+
});
|
|
8840
|
+
if (err) {
|
|
8841
|
+
this.logErrorDetailsToFile(err).catch(() => {
|
|
8842
|
+
});
|
|
8843
|
+
}
|
|
8844
|
+
console.error(message);
|
|
8896
8845
|
}
|
|
8897
|
-
|
|
8846
|
+
/**
|
|
8847
|
+
* Write log entry to file
|
|
8848
|
+
*/
|
|
8849
|
+
async writeToFile(level, message) {
|
|
8850
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
8851
|
+
return;
|
|
8852
|
+
}
|
|
8853
|
+
try {
|
|
8854
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
8855
|
+
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
8856
|
+
`;
|
|
8857
|
+
await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
8858
|
+
} catch (error) {
|
|
8859
|
+
console.error("File logging failed:", error);
|
|
8860
|
+
}
|
|
8898
8861
|
}
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
}
|
|
8921
|
-
} else if (toolName === "create_file" && args?.path && args?.content) {
|
|
8922
|
-
try {
|
|
8923
|
-
preview = await generateFileDiffPreview({
|
|
8924
|
-
path: args.path,
|
|
8925
|
-
content: args.content
|
|
8926
|
-
});
|
|
8927
|
-
} catch (error) {
|
|
8928
|
-
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
8929
|
-
}
|
|
8930
|
-
} else if (toolName === "delete_file" && args?.path) {
|
|
8931
|
-
try {
|
|
8932
|
-
preview = await generateFileDeletePreview({
|
|
8933
|
-
path: args.path
|
|
8934
|
-
});
|
|
8935
|
-
} catch (error) {
|
|
8936
|
-
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
8937
|
-
}
|
|
8938
|
-
}
|
|
8939
|
-
const response = await showPermissionPrompt(toolName, args, preview);
|
|
8940
|
-
if (response.action === "deny") {
|
|
8941
|
-
throw new PermissionDeniedError(toolName, args);
|
|
8942
|
-
}
|
|
8943
|
-
if (response.action === "allow-always") {
|
|
8944
|
-
const canTrust = permissionManager.trustTool(toolName);
|
|
8945
|
-
if (canTrust) {
|
|
8946
|
-
const projectDir = configStore.getProjectConfigDir();
|
|
8947
|
-
if (projectDir) {
|
|
8948
|
-
try {
|
|
8949
|
-
await configStore.initProjectConfig();
|
|
8950
|
-
const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
|
|
8951
|
-
const updatedLocal = {
|
|
8952
|
-
...existingLocal,
|
|
8953
|
-
trustedTools: [...existingLocal.trustedTools || [], toolName]
|
|
8954
|
-
};
|
|
8955
|
-
await configStore.saveProjectLocalConfig(updatedLocal);
|
|
8956
|
-
} catch (error) {
|
|
8957
|
-
await configStore.trustTool(toolName);
|
|
8862
|
+
/**
|
|
8863
|
+
* Log error details to file
|
|
8864
|
+
*/
|
|
8865
|
+
async logErrorDetailsToFile(err) {
|
|
8866
|
+
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
8867
|
+
return;
|
|
8868
|
+
}
|
|
8869
|
+
try {
|
|
8870
|
+
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
8871
|
+
const response = err.response;
|
|
8872
|
+
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
8873
|
+
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
8874
|
+
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
8875
|
+
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
8876
|
+
if (response.data) {
|
|
8877
|
+
const errorText = this.extractErrorMessage(response.data);
|
|
8878
|
+
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
8879
|
+
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
8880
|
+
const parsedError = this.parseHtmlError(errorText);
|
|
8881
|
+
if (parsedError) {
|
|
8882
|
+
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
8958
8883
|
}
|
|
8884
|
+
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
8959
8885
|
} else {
|
|
8960
|
-
|
|
8886
|
+
const preview = this.truncate(errorText, 500);
|
|
8887
|
+
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
8961
8888
|
}
|
|
8889
|
+
} else {
|
|
8890
|
+
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
8962
8891
|
}
|
|
8963
8892
|
}
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8893
|
+
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
8894
|
+
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
8895
|
+
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
8896
|
+
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
8897
|
+
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
8898
|
+
}
|
|
8899
|
+
} catch (error) {
|
|
8967
8900
|
}
|
|
8968
|
-
}
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8901
|
+
}
|
|
8902
|
+
/**
|
|
8903
|
+
* Safely stringify object, handling circular references
|
|
8904
|
+
*/
|
|
8905
|
+
safeStringify(obj) {
|
|
8906
|
+
try {
|
|
8907
|
+
return JSON.stringify(obj);
|
|
8908
|
+
} catch (error) {
|
|
8909
|
+
if (error instanceof Error && error.message.includes("circular")) {
|
|
8910
|
+
try {
|
|
8911
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
8912
|
+
return JSON.stringify(obj, (key, value) => {
|
|
8913
|
+
if (typeof value === "object" && value !== null) {
|
|
8914
|
+
if (seen.has(value)) {
|
|
8915
|
+
return "[Circular]";
|
|
8916
|
+
}
|
|
8917
|
+
seen.add(value);
|
|
8918
|
+
}
|
|
8919
|
+
return value;
|
|
8920
|
+
});
|
|
8921
|
+
} catch {
|
|
8922
|
+
return "[Unable to stringify]";
|
|
8923
|
+
}
|
|
8924
|
+
}
|
|
8925
|
+
return "[Stringify error]";
|
|
8926
|
+
}
|
|
8927
|
+
}
|
|
8928
|
+
/**
|
|
8929
|
+
* Extract readable text from error response data
|
|
8930
|
+
*/
|
|
8931
|
+
extractErrorMessage(data) {
|
|
8932
|
+
if (Buffer.isBuffer(data)) {
|
|
8933
|
+
return data.toString("utf-8");
|
|
8934
|
+
}
|
|
8935
|
+
if (typeof data === "string") {
|
|
8936
|
+
return data;
|
|
8937
|
+
}
|
|
8938
|
+
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) {
|
|
8939
|
+
const chunks = [];
|
|
8940
|
+
for (const chunk of data._readableState.buffer) {
|
|
8941
|
+
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
8942
|
+
chunks.push(Buffer.from(chunk.data));
|
|
8943
|
+
} else if (Buffer.isBuffer(chunk)) {
|
|
8944
|
+
chunks.push(chunk);
|
|
8945
|
+
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
8946
|
+
if (Buffer.isBuffer(chunk.data)) {
|
|
8947
|
+
chunks.push(chunk.data);
|
|
8948
|
+
} else if (Array.isArray(chunk.data)) {
|
|
8949
|
+
chunks.push(Buffer.from(chunk.data));
|
|
8950
|
+
}
|
|
8951
|
+
}
|
|
8952
|
+
}
|
|
8953
|
+
if (chunks.length > 0) {
|
|
8954
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
8955
|
+
}
|
|
8956
|
+
}
|
|
8957
|
+
return this.safeStringify(data);
|
|
8958
|
+
}
|
|
8959
|
+
/**
|
|
8960
|
+
* Parse HTML error page to extract error message
|
|
8961
|
+
*/
|
|
8962
|
+
parseHtmlError(html) {
|
|
8963
|
+
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
|
|
8964
|
+
const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
|
|
8965
|
+
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
8966
|
+
if (titleMatch && titleMatch[1] !== "Error") {
|
|
8967
|
+
return titleMatch[1].trim();
|
|
8968
|
+
}
|
|
8969
|
+
if (h1Match) {
|
|
8970
|
+
return h1Match[1].trim();
|
|
8971
|
+
}
|
|
8972
|
+
if (bodyMatch) {
|
|
8973
|
+
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
8974
|
+
return text.substring(0, 200);
|
|
8975
|
+
}
|
|
8976
|
+
return null;
|
|
8977
|
+
}
|
|
8978
|
+
/**
|
|
8979
|
+
* Truncate string to max length with ellipsis
|
|
8980
|
+
*/
|
|
8981
|
+
truncate(str, maxLength) {
|
|
8982
|
+
if (str.length <= maxLength) {
|
|
8983
|
+
return str;
|
|
8984
|
+
}
|
|
8985
|
+
return str.substring(0, maxLength) + "... [truncated]";
|
|
8986
|
+
}
|
|
8987
|
+
/**
|
|
8988
|
+
* Format bytes to human-readable size
|
|
8989
|
+
*/
|
|
8990
|
+
formatBytes(bytes) {
|
|
8991
|
+
if (bytes === 0) return "0 B";
|
|
8992
|
+
const k = 1024;
|
|
8993
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
8994
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
8995
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
8996
|
+
}
|
|
8997
|
+
/**
|
|
8998
|
+
* Clean up old debug logs (older than 30 days)
|
|
8999
|
+
*/
|
|
9000
|
+
async cleanupOldLogs() {
|
|
9001
|
+
if (!this.fileLoggingEnabled) return;
|
|
9002
|
+
try {
|
|
9003
|
+
const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
|
|
9004
|
+
const files = await fs10.readdir(debugDir);
|
|
9005
|
+
const now = Date.now();
|
|
9006
|
+
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
9007
|
+
for (const file of files) {
|
|
9008
|
+
const filePath = path11.join(debugDir, file);
|
|
9009
|
+
const stats = await fs10.stat(filePath);
|
|
9010
|
+
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
9011
|
+
await fs10.unlink(filePath);
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
9014
|
+
} catch (error) {
|
|
9015
|
+
console.error("Failed to cleanup old logs:", error);
|
|
9016
|
+
}
|
|
9017
|
+
}
|
|
9018
|
+
};
|
|
9019
|
+
var logger = Logger2.getInstance();
|
|
9020
|
+
|
|
9021
|
+
// src/llm/ServerToolExecutor.ts
|
|
9022
|
+
var ServerToolExecutor = class {
|
|
9023
|
+
constructor(apiClient) {
|
|
9024
|
+
this.toolsEndpoint = "/api/ai/v1/tools";
|
|
9025
|
+
this.apiClient = apiClient;
|
|
9026
|
+
}
|
|
9027
|
+
/**
|
|
9028
|
+
* Execute a tool server-side
|
|
9029
|
+
*
|
|
9030
|
+
* @param toolName - Name of the tool to execute (e.g., 'weather_info', 'web_search')
|
|
9031
|
+
* @param input - Tool input parameters
|
|
9032
|
+
* @returns Tool execution result (content or error)
|
|
9033
|
+
*/
|
|
9034
|
+
async executeTool(toolName, input) {
|
|
9035
|
+
logger.debug(`[ServerToolExecutor] Executing tool: ${toolName}`);
|
|
9036
|
+
logger.debug(`[ServerToolExecutor] Endpoint: ${this.toolsEndpoint}`);
|
|
9037
|
+
logger.debug(`[ServerToolExecutor] Input: ${JSON.stringify(input)}`);
|
|
9038
|
+
try {
|
|
9039
|
+
const result = await this.apiClient.post(this.toolsEndpoint, {
|
|
9040
|
+
toolName,
|
|
9041
|
+
input
|
|
9042
|
+
});
|
|
9043
|
+
logger.debug(`[ServerToolExecutor] Response: ${JSON.stringify(result)}`);
|
|
9044
|
+
if (!result.success) {
|
|
9045
|
+
const errorMessage = result.error || "Tool execution failed";
|
|
9046
|
+
logger.error(`[ServerToolExecutor] Tool ${toolName} failed: ${errorMessage}`);
|
|
9047
|
+
return `Error executing ${toolName}: ${errorMessage}`;
|
|
9048
|
+
}
|
|
9049
|
+
logger.debug(`[ServerToolExecutor] Tool ${toolName} succeeded in ${result.executionTimeMs}ms`);
|
|
9050
|
+
return result.content || "";
|
|
9051
|
+
} catch (error) {
|
|
9052
|
+
const err = error;
|
|
9053
|
+
const errorMessage = err?.response?.data?.error || err?.message || "Unknown error";
|
|
9054
|
+
const status = err?.response?.status || "unknown";
|
|
9055
|
+
logger.error(`[ServerToolExecutor] Request failed for ${toolName}: ${errorMessage} (status: ${status})`);
|
|
9056
|
+
logger.error(`[ServerToolExecutor] Full error: ${JSON.stringify(err, null, 2)}`);
|
|
9057
|
+
return `Error executing ${toolName}: ${errorMessage}`;
|
|
9058
|
+
}
|
|
9059
|
+
}
|
|
9060
|
+
};
|
|
9061
|
+
|
|
9062
|
+
// src/llm/ToolRouter.ts
|
|
9063
|
+
var SERVER_TOOLS = ["weather_info", "web_search"];
|
|
9064
|
+
var LOCAL_TOOLS = [
|
|
9065
|
+
"file_read",
|
|
9066
|
+
"create_file",
|
|
9067
|
+
"glob_files",
|
|
9068
|
+
"grep_search",
|
|
9069
|
+
"delete_file",
|
|
9070
|
+
"edit_file",
|
|
9071
|
+
"dice_roll",
|
|
9072
|
+
"math_evaluate",
|
|
9073
|
+
"current_datetime"
|
|
9074
|
+
];
|
|
9075
|
+
function isServerTool(toolName) {
|
|
9076
|
+
return SERVER_TOOLS.includes(toolName);
|
|
9077
|
+
}
|
|
9078
|
+
function isLocalTool(toolName) {
|
|
9079
|
+
return LOCAL_TOOLS.includes(toolName);
|
|
9080
|
+
}
|
|
9081
|
+
async function executeTool(toolName, input, apiClient, localToolFn) {
|
|
9082
|
+
if (isServerTool(toolName)) {
|
|
9083
|
+
logger.debug(`[ToolRouter] Routing ${toolName} to server`);
|
|
9084
|
+
const executor = new ServerToolExecutor(apiClient);
|
|
9085
|
+
return await executor.executeTool(toolName, input);
|
|
9086
|
+
} else if (isLocalTool(toolName)) {
|
|
9087
|
+
logger.debug(`[ToolRouter] Executing ${toolName} locally`);
|
|
9088
|
+
if (!localToolFn) {
|
|
9089
|
+
throw new Error(`Local tool ${toolName} has no implementation`);
|
|
9090
|
+
}
|
|
9091
|
+
const result = await localToolFn(input);
|
|
9092
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
9093
|
+
} else {
|
|
9094
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
9095
|
+
}
|
|
9096
|
+
}
|
|
9097
|
+
|
|
9098
|
+
// src/utils/toolsAdapter.ts
|
|
9099
|
+
var NoOpStorage = class extends BaseStorage {
|
|
9100
|
+
async upload(input, destination, options) {
|
|
9101
|
+
return `/tmp/${destination}`;
|
|
9102
|
+
}
|
|
9103
|
+
async download(path13) {
|
|
9104
|
+
throw new Error("Download not supported in CLI");
|
|
9105
|
+
}
|
|
9106
|
+
async delete(path13) {
|
|
9107
|
+
}
|
|
9108
|
+
async getSignedUrl(path13) {
|
|
9109
|
+
return `/tmp/${path13}`;
|
|
9110
|
+
}
|
|
9111
|
+
getPublicUrl(path13) {
|
|
9112
|
+
return `/tmp/${path13}`;
|
|
9113
|
+
}
|
|
9114
|
+
async getPreview(path13) {
|
|
9115
|
+
return `/tmp/${path13}`;
|
|
9116
|
+
}
|
|
9117
|
+
async getMetadata(path13) {
|
|
9118
|
+
return { size: 0, contentType: "application/octet-stream" };
|
|
9119
|
+
}
|
|
9120
|
+
};
|
|
9121
|
+
var CliLogger = class extends Logger {
|
|
9122
|
+
constructor() {
|
|
9123
|
+
super({ logInJson: false });
|
|
9124
|
+
}
|
|
9125
|
+
// Override all logging methods to be silent
|
|
9126
|
+
info(...args) {
|
|
9127
|
+
}
|
|
9128
|
+
warn(...args) {
|
|
9129
|
+
}
|
|
9130
|
+
error(...args) {
|
|
9131
|
+
}
|
|
9132
|
+
debug(...args) {
|
|
9133
|
+
}
|
|
9134
|
+
log(...args) {
|
|
9135
|
+
}
|
|
9136
|
+
};
|
|
9137
|
+
function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
|
|
9138
|
+
const originalFn = tool.toolFn;
|
|
9139
|
+
const toolName = tool.toolSchema.name;
|
|
9140
|
+
return {
|
|
9141
|
+
...tool,
|
|
9142
|
+
toolFn: async (args) => {
|
|
9143
|
+
if (!permissionManager.needsPermission(toolName)) {
|
|
9144
|
+
const result2 = await executeTool(toolName, args, apiClient, originalFn);
|
|
9145
|
+
agentContext.observationQueue.push({ toolName, result: result2 });
|
|
9146
|
+
return result2;
|
|
9147
|
+
}
|
|
9148
|
+
let preview;
|
|
9149
|
+
if (toolName === "edit_file" && args?.path && args?.content) {
|
|
9150
|
+
try {
|
|
9151
|
+
preview = await generateFileDiffPreview({
|
|
9152
|
+
path: args.path,
|
|
9153
|
+
content: args.content
|
|
9154
|
+
});
|
|
9155
|
+
} catch (error) {
|
|
9156
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9157
|
+
}
|
|
9158
|
+
} else if (toolName === "create_file" && args?.path && args?.content) {
|
|
9159
|
+
try {
|
|
9160
|
+
preview = await generateFileDiffPreview({
|
|
9161
|
+
path: args.path,
|
|
9162
|
+
content: args.content
|
|
9163
|
+
});
|
|
9164
|
+
} catch (error) {
|
|
9165
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9166
|
+
}
|
|
9167
|
+
} else if (toolName === "delete_file" && args?.path) {
|
|
9168
|
+
try {
|
|
9169
|
+
preview = await generateFileDeletePreview({
|
|
9170
|
+
path: args.path
|
|
9171
|
+
});
|
|
9172
|
+
} catch (error) {
|
|
9173
|
+
preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
|
9174
|
+
}
|
|
9175
|
+
}
|
|
9176
|
+
const response = await showPermissionPrompt(toolName, args, preview);
|
|
9177
|
+
if (response.action === "deny") {
|
|
9178
|
+
throw new PermissionDeniedError(toolName, args);
|
|
9179
|
+
}
|
|
9180
|
+
if (response.action === "allow-always") {
|
|
9181
|
+
const canTrust = permissionManager.trustTool(toolName);
|
|
9182
|
+
if (canTrust) {
|
|
9183
|
+
const projectDir = configStore.getProjectConfigDir();
|
|
9184
|
+
if (projectDir) {
|
|
9185
|
+
try {
|
|
9186
|
+
await configStore.initProjectConfig();
|
|
9187
|
+
const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
|
|
9188
|
+
const updatedLocal = {
|
|
9189
|
+
...existingLocal,
|
|
9190
|
+
trustedTools: [...existingLocal.trustedTools || [], toolName]
|
|
9191
|
+
};
|
|
9192
|
+
await configStore.saveProjectLocalConfig(updatedLocal);
|
|
9193
|
+
} catch (error) {
|
|
9194
|
+
await configStore.trustTool(toolName);
|
|
9195
|
+
}
|
|
9196
|
+
} else {
|
|
9197
|
+
await configStore.trustTool(toolName);
|
|
9198
|
+
}
|
|
9199
|
+
}
|
|
9200
|
+
}
|
|
9201
|
+
const result = await executeTool(toolName, args, apiClient, originalFn);
|
|
9202
|
+
agentContext.observationQueue.push({ toolName, result });
|
|
9203
|
+
return result;
|
|
9204
|
+
}
|
|
9205
|
+
};
|
|
9206
|
+
}
|
|
9207
|
+
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
|
|
9208
|
+
const logger2 = new CliLogger();
|
|
9209
|
+
const storage = new NoOpStorage();
|
|
9210
|
+
const user = {
|
|
9211
|
+
_id: userId,
|
|
9212
|
+
email: "cli-user@bike4mind.local",
|
|
9213
|
+
firstName: "CLI",
|
|
9214
|
+
lastName: "User"
|
|
9215
|
+
};
|
|
9216
|
+
const statusUpdate = async (q, status) => {
|
|
9217
|
+
};
|
|
8981
9218
|
const onStart = async (toolName, data) => {
|
|
8982
9219
|
};
|
|
8983
9220
|
const onFinish = async (toolName, data) => {
|
|
8984
9221
|
};
|
|
8985
9222
|
const mockAdminSettings = {
|
|
8986
9223
|
findBySettingName: async (settingName) => {
|
|
8987
|
-
if (settingName === "OpenWeatherKey") {
|
|
8988
|
-
const key = toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY;
|
|
8989
|
-
if (key) {
|
|
8990
|
-
return { settingValue: key };
|
|
8991
|
-
}
|
|
8992
|
-
}
|
|
8993
|
-
if (settingName === "SerperKey") {
|
|
8994
|
-
const key = toolApiKeys?.serper || process.env.SERPER_API_KEY;
|
|
8995
|
-
if (key) {
|
|
8996
|
-
return { settingValue: key };
|
|
8997
|
-
}
|
|
8998
|
-
}
|
|
8999
9224
|
return null;
|
|
9000
9225
|
}
|
|
9001
9226
|
};
|
|
@@ -9005,27 +9230,22 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9005
9230
|
adminSettings: mockAdminSettings
|
|
9006
9231
|
}
|
|
9007
9232
|
};
|
|
9008
|
-
const hasSerperKey = !!(toolApiKeys?.serper || process.env.SERPER_API_KEY);
|
|
9009
|
-
const hasOpenWeatherKey = !!(toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY);
|
|
9010
9233
|
const toolConfig = {
|
|
9011
|
-
//
|
|
9234
|
+
// Local-only tools (no external API keys needed)
|
|
9012
9235
|
dice_roll: {},
|
|
9013
9236
|
math_evaluate: {},
|
|
9014
9237
|
current_datetime: {},
|
|
9015
9238
|
prompt_enhancement: {},
|
|
9016
|
-
// File operation tools (CLI-specific)
|
|
9239
|
+
// File operation tools (CLI-specific, local execution)
|
|
9017
9240
|
file_read: {},
|
|
9018
9241
|
create_file: {},
|
|
9019
9242
|
glob_files: {},
|
|
9020
9243
|
grep_search: {},
|
|
9021
9244
|
delete_file: {},
|
|
9022
|
-
//
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
// Requires OPENWEATHER_API_KEY
|
|
9027
|
-
...hasSerperKey && { deep_research: {} }
|
|
9028
|
-
// Requires SERPER_API_KEY
|
|
9245
|
+
// Server-side tools (executed via /api/ai/v1/tools Lambda)
|
|
9246
|
+
// Routed by ToolRouter to use B4M company API keys
|
|
9247
|
+
weather_info: {},
|
|
9248
|
+
web_search: {}
|
|
9029
9249
|
// Disable web-only tools
|
|
9030
9250
|
// blog_publish: undefined,
|
|
9031
9251
|
// blog_edit: undefined,
|
|
@@ -9035,6 +9255,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9035
9255
|
// mermaid_chart: undefined,
|
|
9036
9256
|
// recharts: undefined,
|
|
9037
9257
|
// edit_file: undefined,
|
|
9258
|
+
// deep_research: undefined,
|
|
9038
9259
|
};
|
|
9039
9260
|
const toolsMap = generateTools(
|
|
9040
9261
|
userId,
|
|
@@ -9052,7 +9273,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
9052
9273
|
model
|
|
9053
9274
|
);
|
|
9054
9275
|
const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
|
|
9055
|
-
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore)
|
|
9276
|
+
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
|
|
9056
9277
|
);
|
|
9057
9278
|
return { tools: tools2, agentContext };
|
|
9058
9279
|
}
|
|
@@ -9123,384 +9344,129 @@ var PermissionManager = class {
|
|
|
9123
9344
|
this.deniedTools = /* @__PURE__ */ new Set();
|
|
9124
9345
|
this.trustedTools = new Set(trustedTools);
|
|
9125
9346
|
this.customCategories = new Map(Object.entries(customCategories || {}));
|
|
9126
|
-
this.deniedTools = new Set(deniedTools || []);
|
|
9127
|
-
}
|
|
9128
|
-
/**
|
|
9129
|
-
* Check if a tool needs permission before execution
|
|
9130
|
-
*
|
|
9131
|
-
* A tool needs permission if:
|
|
9132
|
-
* 1. It's in the denied tools list (from project config), OR
|
|
9133
|
-
* 2. It's not in the auto_approve category, AND
|
|
9134
|
-
* 3. It's not in the trusted tools set
|
|
9135
|
-
*
|
|
9136
|
-
* Note: prompt_always tools CANNOT be trusted, so they always need permission
|
|
9137
|
-
* Note: denied tools from project config ALWAYS need permission (cannot be overridden locally)
|
|
9138
|
-
*/
|
|
9139
|
-
needsPermission(toolName) {
|
|
9140
|
-
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9141
|
-
const category = getToolCategory(toolName, categoryMap);
|
|
9142
|
-
if (this.deniedTools.has(toolName)) {
|
|
9143
|
-
return true;
|
|
9144
|
-
}
|
|
9145
|
-
if (category === "auto_approve") {
|
|
9146
|
-
return false;
|
|
9147
|
-
}
|
|
9148
|
-
if (category === "prompt_always") {
|
|
9149
|
-
return true;
|
|
9150
|
-
}
|
|
9151
|
-
return !this.trustedTools.has(toolName);
|
|
9152
|
-
}
|
|
9153
|
-
/**
|
|
9154
|
-
* Add a tool to the trusted list
|
|
9155
|
-
* Returns false if the tool cannot be trusted (prompt_always category or denied by project)
|
|
9156
|
-
*/
|
|
9157
|
-
trustTool(toolName) {
|
|
9158
|
-
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9159
|
-
if (this.deniedTools.has(toolName)) {
|
|
9160
|
-
return false;
|
|
9161
|
-
}
|
|
9162
|
-
if (!canTrustTool(toolName, categoryMap)) {
|
|
9163
|
-
return false;
|
|
9164
|
-
}
|
|
9165
|
-
this.trustedTools.add(toolName);
|
|
9166
|
-
return true;
|
|
9167
|
-
}
|
|
9168
|
-
/**
|
|
9169
|
-
* Remove a tool from the trusted list
|
|
9170
|
-
*/
|
|
9171
|
-
untrustTool(toolName) {
|
|
9172
|
-
this.trustedTools.delete(toolName);
|
|
9173
|
-
}
|
|
9174
|
-
/**
|
|
9175
|
-
* Get list of all trusted tools
|
|
9176
|
-
*/
|
|
9177
|
-
getTrustedTools() {
|
|
9178
|
-
return Array.from(this.trustedTools).sort();
|
|
9179
|
-
}
|
|
9180
|
-
/**
|
|
9181
|
-
* Check if a tool is currently trusted
|
|
9182
|
-
*/
|
|
9183
|
-
isTrusted(toolName) {
|
|
9184
|
-
return this.trustedTools.has(toolName);
|
|
9185
|
-
}
|
|
9186
|
-
/**
|
|
9187
|
-
* Get the category for a tool
|
|
9188
|
-
*/
|
|
9189
|
-
getCategory(toolName) {
|
|
9190
|
-
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9191
|
-
return getToolCategory(toolName, categoryMap);
|
|
9192
|
-
}
|
|
9193
|
-
/**
|
|
9194
|
-
* Check if a tool can be trusted (not in prompt_always category or denied by project)
|
|
9195
|
-
*/
|
|
9196
|
-
canBeTrusted(toolName) {
|
|
9197
|
-
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9198
|
-
if (this.deniedTools.has(toolName)) {
|
|
9199
|
-
return false;
|
|
9200
|
-
}
|
|
9201
|
-
return canTrustTool(toolName, categoryMap);
|
|
9202
|
-
}
|
|
9203
|
-
/**
|
|
9204
|
-
* Check if a tool is denied by project config
|
|
9205
|
-
*/
|
|
9206
|
-
isDenied(toolName) {
|
|
9207
|
-
return this.deniedTools.has(toolName);
|
|
9208
|
-
}
|
|
9209
|
-
/**
|
|
9210
|
-
* Get list of denied tools
|
|
9211
|
-
*/
|
|
9212
|
-
getDeniedTools() {
|
|
9213
|
-
return Array.from(this.deniedTools).sort();
|
|
9214
|
-
}
|
|
9215
|
-
/**
|
|
9216
|
-
* Clear all trusted tools
|
|
9217
|
-
*/
|
|
9218
|
-
clearTrustedTools() {
|
|
9219
|
-
this.trustedTools.clear();
|
|
9220
|
-
}
|
|
9221
|
-
/**
|
|
9222
|
-
* Get statistics about permissions
|
|
9223
|
-
*/
|
|
9224
|
-
getStats() {
|
|
9225
|
-
return {
|
|
9226
|
-
trustedCount: this.trustedTools.size,
|
|
9227
|
-
autoApproveCount: 0,
|
|
9228
|
-
// TODO: Would need full tool list
|
|
9229
|
-
promptAlwaysCount: 0,
|
|
9230
|
-
promptDefaultCount: 0
|
|
9231
|
-
};
|
|
9232
|
-
}
|
|
9233
|
-
};
|
|
9234
|
-
|
|
9235
|
-
// src/utils/apiUrl.ts
|
|
9236
|
-
var BIKE4MIND_URL = "https://app.bike4mind.com";
|
|
9237
|
-
function getApiUrl(configApiConfig) {
|
|
9238
|
-
if (configApiConfig?.customUrl) {
|
|
9239
|
-
return configApiConfig.customUrl;
|
|
9240
|
-
}
|
|
9241
|
-
return BIKE4MIND_URL;
|
|
9242
|
-
}
|
|
9243
|
-
function getEnvironmentName(configApiConfig) {
|
|
9244
|
-
if (configApiConfig?.customUrl) {
|
|
9245
|
-
return "Self-Hosted";
|
|
9246
|
-
}
|
|
9247
|
-
return "Bike4Mind";
|
|
9248
|
-
}
|
|
9249
|
-
|
|
9250
|
-
// src/utils/Logger.ts
|
|
9251
|
-
import fs10 from "fs/promises";
|
|
9252
|
-
import path11 from "path";
|
|
9253
|
-
import os from "os";
|
|
9254
|
-
var Logger2 = class _Logger {
|
|
9255
|
-
constructor() {
|
|
9256
|
-
this.logFilePath = null;
|
|
9257
|
-
this.sessionId = null;
|
|
9258
|
-
this.fileLoggingEnabled = true;
|
|
9259
|
-
this.consoleVerbose = false;
|
|
9260
|
-
}
|
|
9261
|
-
static {
|
|
9262
|
-
this.instance = null;
|
|
9263
|
-
}
|
|
9264
|
-
static getInstance() {
|
|
9265
|
-
if (!_Logger.instance) {
|
|
9266
|
-
_Logger.instance = new _Logger();
|
|
9267
|
-
}
|
|
9268
|
-
return _Logger.instance;
|
|
9269
|
-
}
|
|
9270
|
-
/**
|
|
9271
|
-
* Initialize the logger with a session ID
|
|
9272
|
-
*/
|
|
9273
|
-
async initialize(sessionId) {
|
|
9274
|
-
this.sessionId = sessionId;
|
|
9275
|
-
const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
|
|
9276
|
-
await fs10.mkdir(debugDir, { recursive: true });
|
|
9277
|
-
this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
|
|
9278
|
-
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
9279
|
-
}
|
|
9280
|
-
/**
|
|
9281
|
-
* Set whether verbose console logging is enabled
|
|
9282
|
-
*/
|
|
9283
|
-
setVerbose(enabled) {
|
|
9284
|
-
this.consoleVerbose = enabled;
|
|
9285
|
-
}
|
|
9286
|
-
/**
|
|
9287
|
-
* Set whether file logging is enabled
|
|
9288
|
-
*/
|
|
9289
|
-
setFileLoggingEnabled(enabled) {
|
|
9290
|
-
this.fileLoggingEnabled = enabled;
|
|
9291
|
-
}
|
|
9292
|
-
/**
|
|
9293
|
-
* DEBUG level - verbose-only console, always file
|
|
9294
|
-
*/
|
|
9295
|
-
debug(message) {
|
|
9296
|
-
this.writeToFile("DEBUG", message).catch(() => {
|
|
9297
|
-
});
|
|
9298
|
-
if (this.consoleVerbose) {
|
|
9299
|
-
console.log(message);
|
|
9300
|
-
}
|
|
9347
|
+
this.deniedTools = new Set(deniedTools || []);
|
|
9301
9348
|
}
|
|
9302
9349
|
/**
|
|
9303
|
-
*
|
|
9350
|
+
* Check if a tool needs permission before execution
|
|
9351
|
+
*
|
|
9352
|
+
* A tool needs permission if:
|
|
9353
|
+
* 1. It's in the denied tools list (from project config), OR
|
|
9354
|
+
* 2. It's not in the auto_approve category, AND
|
|
9355
|
+
* 3. It's not in the trusted tools set
|
|
9356
|
+
*
|
|
9357
|
+
* Note: prompt_always tools CANNOT be trusted, so they always need permission
|
|
9358
|
+
* Note: denied tools from project config ALWAYS need permission (cannot be overridden locally)
|
|
9304
9359
|
*/
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9360
|
+
needsPermission(toolName) {
|
|
9361
|
+
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9362
|
+
const category = getToolCategory(toolName, categoryMap);
|
|
9363
|
+
if (this.deniedTools.has(toolName)) {
|
|
9364
|
+
return true;
|
|
9365
|
+
}
|
|
9366
|
+
if (category === "auto_approve") {
|
|
9367
|
+
return false;
|
|
9368
|
+
}
|
|
9369
|
+
if (category === "prompt_always") {
|
|
9370
|
+
return true;
|
|
9371
|
+
}
|
|
9372
|
+
return !this.trustedTools.has(toolName);
|
|
9309
9373
|
}
|
|
9310
9374
|
/**
|
|
9311
|
-
*
|
|
9375
|
+
* Add a tool to the trusted list
|
|
9376
|
+
* Returns false if the tool cannot be trusted (prompt_always category or denied by project)
|
|
9312
9377
|
*/
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9378
|
+
trustTool(toolName) {
|
|
9379
|
+
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9380
|
+
if (this.deniedTools.has(toolName)) {
|
|
9381
|
+
return false;
|
|
9382
|
+
}
|
|
9383
|
+
if (!canTrustTool(toolName, categoryMap)) {
|
|
9384
|
+
return false;
|
|
9385
|
+
}
|
|
9386
|
+
this.trustedTools.add(toolName);
|
|
9387
|
+
return true;
|
|
9317
9388
|
}
|
|
9318
9389
|
/**
|
|
9319
|
-
*
|
|
9390
|
+
* Remove a tool from the trusted list
|
|
9320
9391
|
*/
|
|
9321
|
-
|
|
9322
|
-
this.
|
|
9323
|
-
});
|
|
9324
|
-
if (err) {
|
|
9325
|
-
this.logErrorDetailsToFile(err).catch(() => {
|
|
9326
|
-
});
|
|
9327
|
-
}
|
|
9328
|
-
console.error(message);
|
|
9392
|
+
untrustTool(toolName) {
|
|
9393
|
+
this.trustedTools.delete(toolName);
|
|
9329
9394
|
}
|
|
9330
9395
|
/**
|
|
9331
|
-
*
|
|
9396
|
+
* Get list of all trusted tools
|
|
9332
9397
|
*/
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
return;
|
|
9336
|
-
}
|
|
9337
|
-
try {
|
|
9338
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
9339
|
-
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
9340
|
-
`;
|
|
9341
|
-
await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
9342
|
-
} catch (error) {
|
|
9343
|
-
console.error("File logging failed:", error);
|
|
9344
|
-
}
|
|
9398
|
+
getTrustedTools() {
|
|
9399
|
+
return Array.from(this.trustedTools).sort();
|
|
9345
9400
|
}
|
|
9346
9401
|
/**
|
|
9347
|
-
*
|
|
9402
|
+
* Check if a tool is currently trusted
|
|
9348
9403
|
*/
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
return;
|
|
9352
|
-
}
|
|
9353
|
-
try {
|
|
9354
|
-
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
9355
|
-
const response = err.response;
|
|
9356
|
-
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
9357
|
-
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
9358
|
-
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
9359
|
-
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
9360
|
-
if (response.data) {
|
|
9361
|
-
const errorText = this.extractErrorMessage(response.data);
|
|
9362
|
-
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
9363
|
-
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
9364
|
-
const parsedError = this.parseHtmlError(errorText);
|
|
9365
|
-
if (parsedError) {
|
|
9366
|
-
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
9367
|
-
}
|
|
9368
|
-
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
9369
|
-
} else {
|
|
9370
|
-
const preview = this.truncate(errorText, 500);
|
|
9371
|
-
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
9372
|
-
}
|
|
9373
|
-
} else {
|
|
9374
|
-
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
9375
|
-
}
|
|
9376
|
-
}
|
|
9377
|
-
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
9378
|
-
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
9379
|
-
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
9380
|
-
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
9381
|
-
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
9382
|
-
}
|
|
9383
|
-
} catch (error) {
|
|
9384
|
-
}
|
|
9404
|
+
isTrusted(toolName) {
|
|
9405
|
+
return this.trustedTools.has(toolName);
|
|
9385
9406
|
}
|
|
9386
9407
|
/**
|
|
9387
|
-
*
|
|
9408
|
+
* Get the category for a tool
|
|
9388
9409
|
*/
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
} catch (error) {
|
|
9393
|
-
if (error instanceof Error && error.message.includes("circular")) {
|
|
9394
|
-
try {
|
|
9395
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
9396
|
-
return JSON.stringify(obj, (key, value) => {
|
|
9397
|
-
if (typeof value === "object" && value !== null) {
|
|
9398
|
-
if (seen.has(value)) {
|
|
9399
|
-
return "[Circular]";
|
|
9400
|
-
}
|
|
9401
|
-
seen.add(value);
|
|
9402
|
-
}
|
|
9403
|
-
return value;
|
|
9404
|
-
});
|
|
9405
|
-
} catch {
|
|
9406
|
-
return "[Unable to stringify]";
|
|
9407
|
-
}
|
|
9408
|
-
}
|
|
9409
|
-
return "[Stringify error]";
|
|
9410
|
-
}
|
|
9410
|
+
getCategory(toolName) {
|
|
9411
|
+
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9412
|
+
return getToolCategory(toolName, categoryMap);
|
|
9411
9413
|
}
|
|
9412
9414
|
/**
|
|
9413
|
-
*
|
|
9415
|
+
* Check if a tool can be trusted (not in prompt_always category or denied by project)
|
|
9414
9416
|
*/
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
if (typeof data === "string") {
|
|
9420
|
-
return data;
|
|
9421
|
-
}
|
|
9422
|
-
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) {
|
|
9423
|
-
const chunks = [];
|
|
9424
|
-
for (const chunk of data._readableState.buffer) {
|
|
9425
|
-
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
9426
|
-
chunks.push(Buffer.from(chunk.data));
|
|
9427
|
-
} else if (Buffer.isBuffer(chunk)) {
|
|
9428
|
-
chunks.push(chunk);
|
|
9429
|
-
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
9430
|
-
if (Buffer.isBuffer(chunk.data)) {
|
|
9431
|
-
chunks.push(chunk.data);
|
|
9432
|
-
} else if (Array.isArray(chunk.data)) {
|
|
9433
|
-
chunks.push(Buffer.from(chunk.data));
|
|
9434
|
-
}
|
|
9435
|
-
}
|
|
9436
|
-
}
|
|
9437
|
-
if (chunks.length > 0) {
|
|
9438
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
9439
|
-
}
|
|
9417
|
+
canBeTrusted(toolName) {
|
|
9418
|
+
const categoryMap = Object.fromEntries(this.customCategories);
|
|
9419
|
+
if (this.deniedTools.has(toolName)) {
|
|
9420
|
+
return false;
|
|
9440
9421
|
}
|
|
9441
|
-
return
|
|
9422
|
+
return canTrustTool(toolName, categoryMap);
|
|
9442
9423
|
}
|
|
9443
9424
|
/**
|
|
9444
|
-
*
|
|
9425
|
+
* Check if a tool is denied by project config
|
|
9445
9426
|
*/
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
|
|
9449
|
-
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
9450
|
-
if (titleMatch && titleMatch[1] !== "Error") {
|
|
9451
|
-
return titleMatch[1].trim();
|
|
9452
|
-
}
|
|
9453
|
-
if (h1Match) {
|
|
9454
|
-
return h1Match[1].trim();
|
|
9455
|
-
}
|
|
9456
|
-
if (bodyMatch) {
|
|
9457
|
-
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
9458
|
-
return text.substring(0, 200);
|
|
9459
|
-
}
|
|
9460
|
-
return null;
|
|
9427
|
+
isDenied(toolName) {
|
|
9428
|
+
return this.deniedTools.has(toolName);
|
|
9461
9429
|
}
|
|
9462
9430
|
/**
|
|
9463
|
-
*
|
|
9431
|
+
* Get list of denied tools
|
|
9464
9432
|
*/
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
return str;
|
|
9468
|
-
}
|
|
9469
|
-
return str.substring(0, maxLength) + "... [truncated]";
|
|
9433
|
+
getDeniedTools() {
|
|
9434
|
+
return Array.from(this.deniedTools).sort();
|
|
9470
9435
|
}
|
|
9471
9436
|
/**
|
|
9472
|
-
*
|
|
9437
|
+
* Clear all trusted tools
|
|
9473
9438
|
*/
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
const k = 1024;
|
|
9477
|
-
const sizes = ["B", "KB", "MB", "GB"];
|
|
9478
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
9479
|
-
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
9439
|
+
clearTrustedTools() {
|
|
9440
|
+
this.trustedTools.clear();
|
|
9480
9441
|
}
|
|
9481
9442
|
/**
|
|
9482
|
-
*
|
|
9443
|
+
* Get statistics about permissions
|
|
9483
9444
|
*/
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
const filePath = path11.join(debugDir, file);
|
|
9493
|
-
const stats = await fs10.stat(filePath);
|
|
9494
|
-
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
9495
|
-
await fs10.unlink(filePath);
|
|
9496
|
-
}
|
|
9497
|
-
}
|
|
9498
|
-
} catch (error) {
|
|
9499
|
-
console.error("Failed to cleanup old logs:", error);
|
|
9500
|
-
}
|
|
9445
|
+
getStats() {
|
|
9446
|
+
return {
|
|
9447
|
+
trustedCount: this.trustedTools.size,
|
|
9448
|
+
autoApproveCount: 0,
|
|
9449
|
+
// TODO: Would need full tool list
|
|
9450
|
+
promptAlwaysCount: 0,
|
|
9451
|
+
promptDefaultCount: 0
|
|
9452
|
+
};
|
|
9501
9453
|
}
|
|
9502
9454
|
};
|
|
9503
|
-
|
|
9455
|
+
|
|
9456
|
+
// src/utils/apiUrl.ts
|
|
9457
|
+
var BIKE4MIND_URL = "https://app.bike4mind.com";
|
|
9458
|
+
function getApiUrl(configApiConfig) {
|
|
9459
|
+
if (configApiConfig?.customUrl) {
|
|
9460
|
+
return configApiConfig.customUrl;
|
|
9461
|
+
}
|
|
9462
|
+
return BIKE4MIND_URL;
|
|
9463
|
+
}
|
|
9464
|
+
function getEnvironmentName(configApiConfig) {
|
|
9465
|
+
if (configApiConfig?.customUrl) {
|
|
9466
|
+
return "Self-Hosted";
|
|
9467
|
+
}
|
|
9468
|
+
return "Bike4Mind";
|
|
9469
|
+
}
|
|
9504
9470
|
|
|
9505
9471
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
9506
9472
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -10305,7 +10271,10 @@ var ApiClient = class {
|
|
|
10305
10271
|
* Make a POST request
|
|
10306
10272
|
*/
|
|
10307
10273
|
async post(url, data, config) {
|
|
10274
|
+
logger.debug(`[ApiClient] POST ${this.client.defaults.baseURL}${url}`);
|
|
10275
|
+
logger.debug(`[ApiClient] Request body: ${JSON.stringify(data)}`);
|
|
10308
10276
|
const response = await this.client.post(url, data, config);
|
|
10277
|
+
logger.debug(`[ApiClient] Response status: ${response.status}`);
|
|
10309
10278
|
return response.data;
|
|
10310
10279
|
}
|
|
10311
10280
|
/**
|
|
@@ -10356,7 +10325,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
10356
10325
|
// package.json
|
|
10357
10326
|
var package_default = {
|
|
10358
10327
|
name: "@bike4mind/cli",
|
|
10359
|
-
version: "0.2.7-
|
|
10328
|
+
version: "0.2.7-feat-cli-tool-lambda-v2.17129+8e46f37a5",
|
|
10360
10329
|
type: "module",
|
|
10361
10330
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
10362
10331
|
license: "UNLICENSED",
|
|
@@ -10459,10 +10428,10 @@ var package_default = {
|
|
|
10459
10428
|
},
|
|
10460
10429
|
devDependencies: {
|
|
10461
10430
|
"@bike4mind/agents": "0.1.0",
|
|
10462
|
-
"@bike4mind/common": "2.38.1-
|
|
10463
|
-
"@bike4mind/mcp": "1.20.1-
|
|
10464
|
-
"@bike4mind/services": "2.33.1-
|
|
10465
|
-
"@bike4mind/utils": "2.1.2-
|
|
10431
|
+
"@bike4mind/common": "2.38.1-feat-cli-tool-lambda-v2.17129+8e46f37a5",
|
|
10432
|
+
"@bike4mind/mcp": "1.20.1-feat-cli-tool-lambda-v2.17129+8e46f37a5",
|
|
10433
|
+
"@bike4mind/services": "2.33.1-feat-cli-tool-lambda-v2.17129+8e46f37a5",
|
|
10434
|
+
"@bike4mind/utils": "2.1.2-feat-cli-tool-lambda-v2.17129+8e46f37a5",
|
|
10466
10435
|
"@types/better-sqlite3": "^7.6.12",
|
|
10467
10436
|
"@types/diff": "^5.0.9",
|
|
10468
10437
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -10475,7 +10444,7 @@ var package_default = {
|
|
|
10475
10444
|
typescript: "^5.9.3",
|
|
10476
10445
|
vitest: "^3.2.4"
|
|
10477
10446
|
},
|
|
10478
|
-
gitHead: "
|
|
10447
|
+
gitHead: "8e46f37a5b295f66cc4da97b2134509dc23cd237"
|
|
10479
10448
|
};
|
|
10480
10449
|
|
|
10481
10450
|
// src/config/constants.ts
|
|
@@ -10497,7 +10466,7 @@ var usageCache = null;
|
|
|
10497
10466
|
function CliApp() {
|
|
10498
10467
|
const { exit } = useApp();
|
|
10499
10468
|
const imageRenderer = new ImageRenderer();
|
|
10500
|
-
const [state, setState] =
|
|
10469
|
+
const [state, setState] = useState4({
|
|
10501
10470
|
session: null,
|
|
10502
10471
|
sessionStore: new SessionStore(),
|
|
10503
10472
|
configStore: new ConfigStore(),
|
|
@@ -10512,9 +10481,9 @@ function CliApp() {
|
|
|
10512
10481
|
permissionPrompt: null,
|
|
10513
10482
|
trustLocationSelector: null
|
|
10514
10483
|
});
|
|
10515
|
-
const [isInitialized, setIsInitialized] =
|
|
10516
|
-
const [initError, setInitError] =
|
|
10517
|
-
const [commandHistory, setCommandHistory] =
|
|
10484
|
+
const [isInitialized, setIsInitialized] = useState4(false);
|
|
10485
|
+
const [initError, setInitError] = useState4(null);
|
|
10486
|
+
const [commandHistory, setCommandHistory] = useState4([]);
|
|
10518
10487
|
const imageStoreInitPromise = useRef(null);
|
|
10519
10488
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
10520
10489
|
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
@@ -10555,7 +10524,7 @@ function CliApp() {
|
|
|
10555
10524
|
process.exit(0);
|
|
10556
10525
|
}, 100);
|
|
10557
10526
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
10558
|
-
|
|
10527
|
+
useInput3((input, key) => {
|
|
10559
10528
|
if (key.ctrl && input === "c") {
|
|
10560
10529
|
const now = Date.now();
|
|
10561
10530
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
@@ -10699,10 +10668,6 @@ function CliApp() {
|
|
|
10699
10668
|
setPermissionPrompt(prompt);
|
|
10700
10669
|
});
|
|
10701
10670
|
};
|
|
10702
|
-
console.log("\u{1F511} Tool API Keys:", {
|
|
10703
|
-
openweather: config.toolApiKeys?.openweather ? "\u2713 configured" : "\u2717 missing",
|
|
10704
|
-
serper: config.toolApiKeys?.serper ? "\u2713 configured" : "\u2717 missing"
|
|
10705
|
-
});
|
|
10706
10671
|
const agentContext = {
|
|
10707
10672
|
currentAgent: null,
|
|
10708
10673
|
observationQueue: []
|
|
@@ -10715,7 +10680,7 @@ function CliApp() {
|
|
|
10715
10680
|
promptFn,
|
|
10716
10681
|
agentContext,
|
|
10717
10682
|
state.configStore,
|
|
10718
|
-
|
|
10683
|
+
apiClient
|
|
10719
10684
|
);
|
|
10720
10685
|
console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
|
|
10721
10686
|
const mcpManager = new McpManager(config);
|
|
@@ -11345,12 +11310,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11345
11310
|
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
11346
11311
|
};
|
|
11347
11312
|
if (initError) {
|
|
11348
|
-
return /* @__PURE__ */
|
|
11313
|
+
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"));
|
|
11349
11314
|
}
|
|
11350
11315
|
if (state.trustLocationSelector) {
|
|
11351
11316
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
11352
11317
|
const inProject = projectDir !== null;
|
|
11353
|
-
return /* @__PURE__ */
|
|
11318
|
+
return /* @__PURE__ */ React13.createElement(
|
|
11354
11319
|
TrustLocationSelector,
|
|
11355
11320
|
{
|
|
11356
11321
|
inProject,
|
|
@@ -11369,7 +11334,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11369
11334
|
}
|
|
11370
11335
|
if (state.showLoginFlow) {
|
|
11371
11336
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
11372
|
-
return /* @__PURE__ */
|
|
11337
|
+
return /* @__PURE__ */ React13.createElement(
|
|
11373
11338
|
LoginFlow,
|
|
11374
11339
|
{
|
|
11375
11340
|
apiUrl: loginApiUrl,
|
|
@@ -11392,9 +11357,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11392
11357
|
);
|
|
11393
11358
|
}
|
|
11394
11359
|
if (!isInitialized) {
|
|
11395
|
-
return /* @__PURE__ */
|
|
11360
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "\u{1F680} Initializing..."));
|
|
11396
11361
|
}
|
|
11397
|
-
return /* @__PURE__ */
|
|
11362
|
+
return /* @__PURE__ */ React13.createElement(
|
|
11398
11363
|
App,
|
|
11399
11364
|
{
|
|
11400
11365
|
onMessage: handleMessage,
|
|
@@ -11429,6 +11394,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
11429
11394
|
if (isDevMode) {
|
|
11430
11395
|
console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
11431
11396
|
}
|
|
11432
|
-
render(/* @__PURE__ */
|
|
11397
|
+
render(/* @__PURE__ */ React13.createElement(CliApp, null), {
|
|
11433
11398
|
exitOnCtrlC: false
|
|
11434
11399
|
});
|