@bike4mind/cli 0.2.1-quest-file-uploads.17007 → 0.2.2
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/artifactExtractor-GXQ5YZF7.js +0 -0
- package/dist/{chunk-F3ETXO5N.js → chunk-5N4PG5EH.js} +1 -1
- package/dist/chunk-AMDXHL6S.js +0 -0
- package/dist/chunk-BDQBOLYG.js +0 -0
- package/dist/chunk-BPFEGDC7.js +0 -0
- package/dist/{chunk-QXGU6KYI.js → chunk-GZTILMGE.js} +1 -1
- package/dist/{chunk-U3GPYBTD.js → chunk-NZADYDFG.js} +12 -7
- package/dist/chunk-PDX44BCA.js +0 -0
- package/dist/chunk-WR6YZRQV.js +0 -0
- package/dist/{chunk-LZMRYJ3B.js → chunk-Y4HMTEDH.js} +13 -5
- package/dist/{create-YUYXTEIS.js → create-ZTK4723N.js} +2 -2
- package/dist/formatConverter-I7EIUVDY.js +0 -0
- package/dist/index.js +740 -217
- package/dist/llmMarkdownGenerator-4ZR3FLEQ.js +0 -0
- package/dist/markdownGenerator-7WE7WKME.js +0 -0
- package/dist/{mementoService-2BDIT6PH.js → mementoService-5XQYECNM.js} +2 -2
- package/dist/notificationDeduplicator-UW7D75QS.js +0 -0
- package/dist/src-6J6LFPZ6.js +0 -0
- package/dist/{src-VLP2EHKE.js → src-DOO3QAO2.js} +1 -1
- package/dist/{subtractCredits-NNYIS5XZ.js → subtractCredits-3QOJA6XF.js} +2 -2
- package/dist/utils-JPMDGUBL.js +0 -0
- package/package.json +20 -16
package/dist/index.js
CHANGED
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
getEffectiveApiKey,
|
|
5
5
|
getOpenWeatherKey,
|
|
6
6
|
getSerperKey
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-GZTILMGE.js";
|
|
8
|
+
import "./chunk-Y4HMTEDH.js";
|
|
9
|
+
import "./chunk-5N4PG5EH.js";
|
|
10
10
|
import {
|
|
11
11
|
BFLImageService,
|
|
12
12
|
BaseStorage,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
OpenAIBackend,
|
|
16
16
|
OpenAIImageService,
|
|
17
17
|
XAIImageService
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-NZADYDFG.js";
|
|
19
19
|
import {
|
|
20
20
|
Logger
|
|
21
21
|
} from "./chunk-AMDXHL6S.js";
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
AuthEvents,
|
|
30
30
|
BFL_IMAGE_MODELS,
|
|
31
31
|
BFL_SAFETY_TOLERANCE,
|
|
32
|
+
CREDIT_DEDUCT_TRANSACTION_TYPES,
|
|
32
33
|
ChatCompletionCreateInputSchema,
|
|
33
34
|
ChatModels,
|
|
34
35
|
CollectionType,
|
|
@@ -78,13 +79,13 @@ import {
|
|
|
78
79
|
} from "./chunk-PDX44BCA.js";
|
|
79
80
|
|
|
80
81
|
// src/index.tsx
|
|
81
|
-
import
|
|
82
|
-
import { render, Box as
|
|
82
|
+
import React11, { useState as useState3, useEffect as useEffect3, useCallback } from "react";
|
|
83
|
+
import { render, Box as Box11, Text as Text10, useApp, useInput as useInput2 } from "ink";
|
|
83
84
|
import { v4 as uuidv49 } from "uuid";
|
|
84
85
|
|
|
85
86
|
// src/components/App.tsx
|
|
86
|
-
import
|
|
87
|
-
import { Box as
|
|
87
|
+
import React8 from "react";
|
|
88
|
+
import { Box as Box8, Text as Text7, Static } from "ink";
|
|
88
89
|
|
|
89
90
|
// src/components/StatusBar.tsx
|
|
90
91
|
import React from "react";
|
|
@@ -94,8 +95,8 @@ var StatusBar = React.memo(function StatusBar2({ sessionName, model, tokenUsage
|
|
|
94
95
|
});
|
|
95
96
|
|
|
96
97
|
// src/components/InputPrompt.tsx
|
|
97
|
-
import
|
|
98
|
-
import { Box as
|
|
98
|
+
import React4, { useState, useMemo, useEffect } from "react";
|
|
99
|
+
import { Box as Box4, Text as Text4, useInput } from "ink";
|
|
99
100
|
import TextInput from "ink-text-input";
|
|
100
101
|
|
|
101
102
|
// src/components/CommandAutocomplete.tsx
|
|
@@ -112,8 +113,210 @@ function CommandAutocomplete({ commands, selectedIndex }) {
|
|
|
112
113
|
}));
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
// src/
|
|
116
|
+
// src/components/FileAutocomplete.tsx
|
|
117
|
+
import React3 from "react";
|
|
118
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
119
|
+
|
|
120
|
+
// src/utils/fileSearch.ts
|
|
121
|
+
import * as fs from "fs";
|
|
122
|
+
import * as path from "path";
|
|
116
123
|
import Fuse from "fuse.js";
|
|
124
|
+
var DEFAULT_IGNORE_PATTERNS = [
|
|
125
|
+
"node_modules",
|
|
126
|
+
".git",
|
|
127
|
+
".next",
|
|
128
|
+
".turbo",
|
|
129
|
+
"dist",
|
|
130
|
+
"build",
|
|
131
|
+
".cache",
|
|
132
|
+
"coverage",
|
|
133
|
+
".DS_Store",
|
|
134
|
+
".env",
|
|
135
|
+
".env.local",
|
|
136
|
+
".env.secrets"
|
|
137
|
+
];
|
|
138
|
+
function walkDirectory(basePath = process.cwd(), maxDepth = 5, ignorePatterns = DEFAULT_IGNORE_PATTERNS) {
|
|
139
|
+
const results = [];
|
|
140
|
+
const normalizedBase = path.resolve(basePath);
|
|
141
|
+
function walk(currentPath, depth) {
|
|
142
|
+
if (depth > maxDepth) return;
|
|
143
|
+
try {
|
|
144
|
+
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
145
|
+
for (const entry of entries) {
|
|
146
|
+
if (ignorePatterns.some((pattern) => entry.name === pattern || entry.name.startsWith(pattern))) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const fullPath = path.join(currentPath, entry.name);
|
|
150
|
+
const relativePath = path.relative(normalizedBase, fullPath);
|
|
151
|
+
if (entry.isDirectory()) {
|
|
152
|
+
results.push({
|
|
153
|
+
path: relativePath,
|
|
154
|
+
isDirectory: true
|
|
155
|
+
});
|
|
156
|
+
walk(fullPath, depth + 1);
|
|
157
|
+
} else if (entry.isFile()) {
|
|
158
|
+
try {
|
|
159
|
+
const stats = fs.statSync(fullPath);
|
|
160
|
+
results.push({
|
|
161
|
+
path: relativePath,
|
|
162
|
+
isDirectory: false,
|
|
163
|
+
size: stats.size
|
|
164
|
+
});
|
|
165
|
+
} catch {
|
|
166
|
+
results.push({
|
|
167
|
+
path: relativePath,
|
|
168
|
+
isDirectory: false
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
walk(normalizedBase, 0);
|
|
177
|
+
return results;
|
|
178
|
+
}
|
|
179
|
+
function formatFileSize(bytes) {
|
|
180
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
181
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
182
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
183
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
184
|
+
}
|
|
185
|
+
var cachedFiles = null;
|
|
186
|
+
var cacheTimestamp = 0;
|
|
187
|
+
var CACHE_TTL_MS = 3e4;
|
|
188
|
+
function getCachedFiles() {
|
|
189
|
+
const now = Date.now();
|
|
190
|
+
if (!cachedFiles || now - cacheTimestamp > CACHE_TTL_MS) {
|
|
191
|
+
cachedFiles = walkDirectory();
|
|
192
|
+
cacheTimestamp = now;
|
|
193
|
+
}
|
|
194
|
+
return cachedFiles;
|
|
195
|
+
}
|
|
196
|
+
function searchFiles(query) {
|
|
197
|
+
const files = getCachedFiles();
|
|
198
|
+
if (!query || query.trim() === "") {
|
|
199
|
+
return files.filter((f) => !f.path.includes(path.sep)).slice(0, 20);
|
|
200
|
+
}
|
|
201
|
+
const queryDir = path.dirname(query);
|
|
202
|
+
let searchPool = files;
|
|
203
|
+
if (queryDir && queryDir !== ".") {
|
|
204
|
+
const normalizedDir = queryDir.replace(/\\/g, "/");
|
|
205
|
+
searchPool = files.filter((f) => {
|
|
206
|
+
const normalizedPath = f.path.replace(/\\/g, "/");
|
|
207
|
+
return normalizedPath.startsWith(normalizedDir + "/") || normalizedPath === normalizedDir;
|
|
208
|
+
});
|
|
209
|
+
if (searchPool.length === 0) {
|
|
210
|
+
searchPool = files;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const fuse2 = new Fuse(searchPool, {
|
|
214
|
+
keys: [{ name: "path", weight: 1 }],
|
|
215
|
+
threshold: 0.4,
|
|
216
|
+
// Allow some fuzzy matching
|
|
217
|
+
includeScore: true,
|
|
218
|
+
minMatchCharLength: 1,
|
|
219
|
+
// Use extended search for better path matching
|
|
220
|
+
useExtendedSearch: false
|
|
221
|
+
});
|
|
222
|
+
const results = fuse2.search(query);
|
|
223
|
+
return results.slice(0, 20).map((result) => result.item);
|
|
224
|
+
}
|
|
225
|
+
function isPathWithinCwd(filePath) {
|
|
226
|
+
const cwd = path.resolve(process.cwd());
|
|
227
|
+
const absolutePath = path.resolve(cwd, filePath);
|
|
228
|
+
let resolvedCwd = cwd;
|
|
229
|
+
let resolvedPath = absolutePath;
|
|
230
|
+
try {
|
|
231
|
+
const realCwd = fs.realpathSync.native(cwd);
|
|
232
|
+
if (realCwd) {
|
|
233
|
+
resolvedCwd = realCwd;
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const realPath = fs.realpathSync.native(absolutePath);
|
|
239
|
+
if (realPath) {
|
|
240
|
+
resolvedPath = realPath;
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
const relativePath2 = path.relative(cwd, absolutePath);
|
|
244
|
+
resolvedPath = path.resolve(resolvedCwd, relativePath2);
|
|
245
|
+
}
|
|
246
|
+
const normalizedCwd = resolvedCwd.toLowerCase();
|
|
247
|
+
const normalizedPath = resolvedPath.toLowerCase();
|
|
248
|
+
const isWithin = normalizedPath === normalizedCwd || normalizedPath.startsWith(normalizedCwd + path.sep);
|
|
249
|
+
if (!isWithin) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
const relativePath = path.relative(resolvedCwd, resolvedPath);
|
|
253
|
+
return !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
|
|
254
|
+
}
|
|
255
|
+
function isBinaryFile(filePath) {
|
|
256
|
+
const binaryExtensions = [
|
|
257
|
+
".png",
|
|
258
|
+
".jpg",
|
|
259
|
+
".jpeg",
|
|
260
|
+
".gif",
|
|
261
|
+
".bmp",
|
|
262
|
+
".ico",
|
|
263
|
+
".webp",
|
|
264
|
+
// Note: .svg is NOT included because SVG is XML text format that can be safely read
|
|
265
|
+
".pdf",
|
|
266
|
+
".doc",
|
|
267
|
+
".docx",
|
|
268
|
+
".xls",
|
|
269
|
+
".xlsx",
|
|
270
|
+
".ppt",
|
|
271
|
+
".pptx",
|
|
272
|
+
".zip",
|
|
273
|
+
".tar",
|
|
274
|
+
".gz",
|
|
275
|
+
".rar",
|
|
276
|
+
".7z",
|
|
277
|
+
".exe",
|
|
278
|
+
".dll",
|
|
279
|
+
".so",
|
|
280
|
+
".dylib",
|
|
281
|
+
".bin",
|
|
282
|
+
".dat",
|
|
283
|
+
".mp3",
|
|
284
|
+
".mp4",
|
|
285
|
+
".wav",
|
|
286
|
+
".avi",
|
|
287
|
+
".mov",
|
|
288
|
+
".mkv",
|
|
289
|
+
".ttf",
|
|
290
|
+
".otf",
|
|
291
|
+
".woff",
|
|
292
|
+
".woff2",
|
|
293
|
+
".eot",
|
|
294
|
+
".class",
|
|
295
|
+
".pyc",
|
|
296
|
+
".o",
|
|
297
|
+
".obj"
|
|
298
|
+
];
|
|
299
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
300
|
+
return binaryExtensions.includes(ext);
|
|
301
|
+
}
|
|
302
|
+
var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
303
|
+
|
|
304
|
+
// src/components/FileAutocomplete.tsx
|
|
305
|
+
function FileAutocomplete({ files, selectedIndex, query }) {
|
|
306
|
+
if (files.length === 0) {
|
|
307
|
+
return /* @__PURE__ */ React3.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No matching files", query ? ` for "${query}"` : ""));
|
|
308
|
+
}
|
|
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) => {
|
|
310
|
+
const isSelected = index === selectedIndex;
|
|
311
|
+
const icon = file.isDirectory ? "[folder]" : "[file] ";
|
|
312
|
+
const sizeDisplay = file.isDirectory ? "" : file.size !== void 0 ? ` (${formatFileSize(file.size)})` : "";
|
|
313
|
+
const pathDisplay = file.isDirectory ? `${file.path}/` : file.path;
|
|
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));
|
|
315
|
+
}));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/utils/fuzzySearch.ts
|
|
319
|
+
import Fuse2 from "fuse.js";
|
|
117
320
|
|
|
118
321
|
// src/config/commands.ts
|
|
119
322
|
var COMMANDS = [
|
|
@@ -177,11 +380,15 @@ var COMMANDS = [
|
|
|
177
380
|
{
|
|
178
381
|
name: "trusted",
|
|
179
382
|
description: "List all trusted tools"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: "usage",
|
|
386
|
+
description: "Show credit usage and balance"
|
|
180
387
|
}
|
|
181
388
|
];
|
|
182
389
|
|
|
183
390
|
// src/utils/fuzzySearch.ts
|
|
184
|
-
var fuse = new
|
|
391
|
+
var fuse = new Fuse2(COMMANDS, {
|
|
185
392
|
keys: [
|
|
186
393
|
{ name: "name", weight: 0.7 },
|
|
187
394
|
// Prioritize command name
|
|
@@ -202,23 +409,65 @@ function searchCommands(query) {
|
|
|
202
409
|
}
|
|
203
410
|
|
|
204
411
|
// src/components/InputPrompt.tsx
|
|
412
|
+
function findAtTrigger(value) {
|
|
413
|
+
for (let i = value.length - 1; i >= 0; i--) {
|
|
414
|
+
if (value[i] === "@") {
|
|
415
|
+
const isValid = i === 0 || value[i - 1] === " ";
|
|
416
|
+
if (isValid) {
|
|
417
|
+
const query = value.slice(i + 1);
|
|
418
|
+
if (query.includes(" ")) {
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
return { startIndex: i, query };
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
205
427
|
function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
206
428
|
const [value, setValue] = useState("");
|
|
207
429
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
208
430
|
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
209
431
|
const [tempInput, setTempInput] = useState("");
|
|
210
432
|
const [inputKey, setInputKey] = useState(0);
|
|
211
|
-
const
|
|
212
|
-
const
|
|
433
|
+
const [fileAutocomplete, setFileAutocomplete] = useState(null);
|
|
434
|
+
const [fileSelectedIndex, setFileSelectedIndex] = useState(0);
|
|
435
|
+
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
|
|
436
|
+
const commandQuery = shouldShowCommandAutocomplete ? value.slice(1) : "";
|
|
213
437
|
const filteredCommands = useMemo(() => {
|
|
214
|
-
if (!
|
|
215
|
-
return searchCommands(
|
|
216
|
-
}, [
|
|
438
|
+
if (!shouldShowCommandAutocomplete) return [];
|
|
439
|
+
return searchCommands(commandQuery);
|
|
440
|
+
}, [shouldShowCommandAutocomplete, commandQuery]);
|
|
441
|
+
const filteredFiles = useMemo(() => {
|
|
442
|
+
if (!fileAutocomplete?.active) return [];
|
|
443
|
+
return searchFiles(fileAutocomplete.query);
|
|
444
|
+
}, [fileAutocomplete?.active, fileAutocomplete?.query]);
|
|
217
445
|
useEffect(() => {
|
|
218
446
|
setSelectedIndex(0);
|
|
219
447
|
}, [filteredCommands]);
|
|
448
|
+
useEffect(() => {
|
|
449
|
+
setFileSelectedIndex(0);
|
|
450
|
+
}, [filteredFiles]);
|
|
220
451
|
useInput((input, key) => {
|
|
221
|
-
if (
|
|
452
|
+
if (fileAutocomplete?.active && filteredFiles.length > 0) {
|
|
453
|
+
if (key.upArrow) {
|
|
454
|
+
setFileSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredFiles.length - 1);
|
|
455
|
+
return;
|
|
456
|
+
} else if (key.downArrow) {
|
|
457
|
+
setFileSelectedIndex((prev) => prev < filteredFiles.length - 1 ? prev + 1 : 0);
|
|
458
|
+
return;
|
|
459
|
+
} else if (key.tab) {
|
|
460
|
+
const selectedFile = filteredFiles[fileSelectedIndex];
|
|
461
|
+
if (selectedFile) {
|
|
462
|
+
insertSelectedFile(selectedFile);
|
|
463
|
+
}
|
|
464
|
+
return;
|
|
465
|
+
} else if (key.escape) {
|
|
466
|
+
setFileAutocomplete(null);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (shouldShowCommandAutocomplete && filteredCommands.length > 0) {
|
|
222
471
|
if (key.upArrow) {
|
|
223
472
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredCommands.length - 1);
|
|
224
473
|
} else if (key.downArrow) {
|
|
@@ -226,7 +475,7 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
|
226
475
|
}
|
|
227
476
|
return;
|
|
228
477
|
}
|
|
229
|
-
if (!
|
|
478
|
+
if (!shouldShowCommandAutocomplete && !fileAutocomplete?.active && history.length > 0) {
|
|
230
479
|
if (key.upArrow) {
|
|
231
480
|
if (historyIndex === -1) {
|
|
232
481
|
setTempInput(value);
|
|
@@ -249,9 +498,35 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
|
249
498
|
}
|
|
250
499
|
}
|
|
251
500
|
});
|
|
501
|
+
const insertSelectedFile = (file) => {
|
|
502
|
+
if (!fileAutocomplete) return;
|
|
503
|
+
const beforeAt = value.slice(0, fileAutocomplete.startIndex);
|
|
504
|
+
const afterQuery = value.slice(fileAutocomplete.startIndex + 1 + fileAutocomplete.query.length);
|
|
505
|
+
const pathToInsert = file.isDirectory ? `@${file.path}/` : `@${file.path}`;
|
|
506
|
+
const newValue = beforeAt + pathToInsert + afterQuery;
|
|
507
|
+
setValue(newValue);
|
|
508
|
+
setInputKey((prev) => prev + 1);
|
|
509
|
+
if (file.isDirectory) {
|
|
510
|
+
setFileAutocomplete({
|
|
511
|
+
active: true,
|
|
512
|
+
startIndex: fileAutocomplete.startIndex,
|
|
513
|
+
query: file.path + "/"
|
|
514
|
+
});
|
|
515
|
+
setFileSelectedIndex(0);
|
|
516
|
+
} else {
|
|
517
|
+
setFileAutocomplete(null);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
252
520
|
const handleSubmit = (input) => {
|
|
253
521
|
if (disabled || !input.trim()) return;
|
|
254
|
-
if (
|
|
522
|
+
if (fileAutocomplete?.active && filteredFiles.length > 0) {
|
|
523
|
+
const selectedFile = filteredFiles[fileSelectedIndex];
|
|
524
|
+
if (selectedFile) {
|
|
525
|
+
insertSelectedFile(selectedFile);
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (shouldShowCommandAutocomplete && filteredCommands.length > 0) {
|
|
255
530
|
const selectedCommand = filteredCommands[selectedIndex];
|
|
256
531
|
const commandText = `/${selectedCommand.name}`;
|
|
257
532
|
if (selectedCommand.args) {
|
|
@@ -268,6 +543,7 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
|
268
543
|
onSubmit(input);
|
|
269
544
|
setValue("");
|
|
270
545
|
setHistoryIndex(-1);
|
|
546
|
+
setFileAutocomplete(null);
|
|
271
547
|
};
|
|
272
548
|
const handleChange = (newValue) => {
|
|
273
549
|
setValue(newValue);
|
|
@@ -275,30 +551,40 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
|
275
551
|
setHistoryIndex(-1);
|
|
276
552
|
setTempInput("");
|
|
277
553
|
}
|
|
554
|
+
const atTrigger = findAtTrigger(newValue);
|
|
555
|
+
if (atTrigger) {
|
|
556
|
+
setFileAutocomplete({
|
|
557
|
+
active: true,
|
|
558
|
+
startIndex: atTrigger.startIndex,
|
|
559
|
+
query: atTrigger.query
|
|
560
|
+
});
|
|
561
|
+
} else {
|
|
562
|
+
setFileAutocomplete(null);
|
|
563
|
+
}
|
|
278
564
|
};
|
|
279
|
-
return /* @__PURE__ */
|
|
565
|
+
return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React4.createElement(
|
|
280
566
|
TextInput,
|
|
281
567
|
{
|
|
282
568
|
key: inputKey,
|
|
283
569
|
value,
|
|
284
570
|
onChange: handleChange,
|
|
285
571
|
onSubmit: handleSubmit,
|
|
286
|
-
placeholder: disabled ? "Thinking..." : "Type your message
|
|
572
|
+
placeholder: disabled ? "Thinking..." : "Type your message, /help for commands, or @file to reference",
|
|
287
573
|
showCursor: !disabled
|
|
288
574
|
}
|
|
289
|
-
)),
|
|
575
|
+
)), shouldShowCommandAutocomplete && /* @__PURE__ */ React4.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React4.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
|
|
290
576
|
}
|
|
291
577
|
|
|
292
578
|
// src/components/AgentThinking.tsx
|
|
293
|
-
import
|
|
294
|
-
import { Box as
|
|
579
|
+
import React6 from "react";
|
|
580
|
+
import { Box as Box6 } from "ink";
|
|
295
581
|
|
|
296
582
|
// src/components/ThoughtStream.tsx
|
|
297
|
-
import
|
|
298
|
-
import { Box as
|
|
583
|
+
import React5 from "react";
|
|
584
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
299
585
|
import Spinner from "ink-spinner";
|
|
300
|
-
var ThoughtStream =
|
|
301
|
-
return /* @__PURE__ */
|
|
586
|
+
var ThoughtStream = React5.memo(function ThoughtStream2({ steps }) {
|
|
587
|
+
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...")));
|
|
302
588
|
});
|
|
303
589
|
|
|
304
590
|
// src/store/index.ts
|
|
@@ -324,32 +610,32 @@ var useCliStore = create((set) => ({
|
|
|
324
610
|
}));
|
|
325
611
|
|
|
326
612
|
// src/components/AgentThinking.tsx
|
|
327
|
-
var AgentThinking =
|
|
613
|
+
var AgentThinking = React6.memo(function AgentThinking2() {
|
|
328
614
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
329
615
|
const agentSteps = useCliStore((state) => state.agentSteps);
|
|
330
616
|
if (!isThinking) {
|
|
331
617
|
return null;
|
|
332
618
|
}
|
|
333
|
-
return /* @__PURE__ */
|
|
619
|
+
return /* @__PURE__ */ React6.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React6.createElement(ThoughtStream, { steps: agentSteps }));
|
|
334
620
|
});
|
|
335
621
|
|
|
336
622
|
// src/components/PermissionPrompt.tsx
|
|
337
|
-
import
|
|
338
|
-
import { Box as
|
|
623
|
+
import React7 from "react";
|
|
624
|
+
import { Box as Box7, Text as Text6 } from "ink";
|
|
339
625
|
import SelectInput from "ink-select-input";
|
|
340
626
|
function renderDiffPreview(preview) {
|
|
341
627
|
const lines = preview.split("\n");
|
|
342
628
|
return lines.map((line, index) => {
|
|
343
629
|
if (line.startsWith("+")) {
|
|
344
|
-
return /* @__PURE__ */
|
|
630
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "green" }, line);
|
|
345
631
|
}
|
|
346
632
|
if (line.startsWith("-")) {
|
|
347
|
-
return /* @__PURE__ */
|
|
633
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "red" }, line);
|
|
348
634
|
}
|
|
349
635
|
if (line.startsWith("@@")) {
|
|
350
|
-
return /* @__PURE__ */
|
|
636
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "cyan" }, line);
|
|
351
637
|
}
|
|
352
|
-
return /* @__PURE__ */
|
|
638
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, dimColor: true }, line);
|
|
353
639
|
});
|
|
354
640
|
}
|
|
355
641
|
function PermissionPrompt({
|
|
@@ -369,7 +655,121 @@ function PermissionPrompt({
|
|
|
369
655
|
{ label: "\u2717 Deny", value: "deny" }
|
|
370
656
|
];
|
|
371
657
|
const argsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
|
|
372
|
-
return /* @__PURE__ */
|
|
658
|
+
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) })));
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/utils/processFileReferences.ts
|
|
662
|
+
import * as fs2 from "fs";
|
|
663
|
+
import * as path2 from "path";
|
|
664
|
+
|
|
665
|
+
// src/utils/constants.ts
|
|
666
|
+
var NAME_SUFFIXES = ["jr", "sr", "ii", "iii", "iv", "v", "phd", "md", "esq"];
|
|
667
|
+
function isNameSuffix(value) {
|
|
668
|
+
return NAME_SUFFIXES.includes(value);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/utils/processFileReferences.ts
|
|
672
|
+
var FILE_REFERENCE_REGEX = /(?:^|\s)@([^\s@]+)/g;
|
|
673
|
+
function looksLikeFilePath(ref) {
|
|
674
|
+
if (ref.includes("/") || ref.includes(path2.sep)) {
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
const extensionMatch = /\.(\w+)$/.exec(ref);
|
|
678
|
+
if (extensionMatch) {
|
|
679
|
+
const ext = extensionMatch[1].toLowerCase();
|
|
680
|
+
if (isNameSuffix(ext)) {
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
if (ext.length > 10) {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
function extractFileReferences(message) {
|
|
691
|
+
const references = [];
|
|
692
|
+
const regex = /(?:^|\s)@([^\s@]+)/g;
|
|
693
|
+
let match;
|
|
694
|
+
while ((match = regex.exec(message)) !== null) {
|
|
695
|
+
const ref = match[1];
|
|
696
|
+
if (looksLikeFilePath(ref)) {
|
|
697
|
+
references.push(ref);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return references;
|
|
701
|
+
}
|
|
702
|
+
function readFileContents(filePath) {
|
|
703
|
+
const cwd = process.cwd();
|
|
704
|
+
const absolutePath = path2.resolve(cwd, filePath);
|
|
705
|
+
if (!isPathWithinCwd(filePath)) {
|
|
706
|
+
return { error: `Security: Path "${filePath}" is outside the current working directory` };
|
|
707
|
+
}
|
|
708
|
+
if (!fs2.existsSync(absolutePath)) {
|
|
709
|
+
return { error: `File not found: "${filePath}"` };
|
|
710
|
+
}
|
|
711
|
+
const stats = fs2.statSync(absolutePath);
|
|
712
|
+
if (stats.isDirectory()) {
|
|
713
|
+
try {
|
|
714
|
+
const entries = fs2.readdirSync(absolutePath);
|
|
715
|
+
const fileCount = entries.length;
|
|
716
|
+
return {
|
|
717
|
+
content: `(Directory with ${fileCount} items. Use file tools to explore if needed.)`,
|
|
718
|
+
size: 0
|
|
719
|
+
};
|
|
720
|
+
} catch (err) {
|
|
721
|
+
return { error: `Cannot read directory "${filePath}": ${err instanceof Error ? err.message : "Unknown error"}` };
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
725
|
+
return {
|
|
726
|
+
error: `File too large: "${filePath}" is ${formatFileSize(stats.size)} (max ${formatFileSize(MAX_FILE_SIZE)})`
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
if (isBinaryFile(filePath)) {
|
|
730
|
+
return { error: `Binary file: "${filePath}" cannot be included as text content` };
|
|
731
|
+
}
|
|
732
|
+
try {
|
|
733
|
+
const content = fs2.readFileSync(absolutePath, "utf-8");
|
|
734
|
+
return { content, size: stats.size };
|
|
735
|
+
} catch (err) {
|
|
736
|
+
return { error: `Cannot read file "${filePath}": ${err instanceof Error ? err.message : "Unknown error"}` };
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
function formatFileBlock(filePath, content, size, isDirectory) {
|
|
740
|
+
if (isDirectory) {
|
|
741
|
+
return `
|
|
742
|
+
--- Directory Reference: ${filePath} ---
|
|
743
|
+
${content}
|
|
744
|
+
--- End of ${filePath} ---`;
|
|
745
|
+
}
|
|
746
|
+
return `
|
|
747
|
+
--- Referenced File: ${filePath} (${formatFileSize(size)}) ---
|
|
748
|
+
${content}
|
|
749
|
+
--- End of ${filePath} ---`;
|
|
750
|
+
}
|
|
751
|
+
async function processFileReferences(message) {
|
|
752
|
+
const references = extractFileReferences(message);
|
|
753
|
+
const errors = [];
|
|
754
|
+
const fileBlocks = [];
|
|
755
|
+
for (const ref of references) {
|
|
756
|
+
const result = readFileContents(ref);
|
|
757
|
+
if ("error" in result) {
|
|
758
|
+
errors.push(result.error);
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
const isDirectory = result.size === 0 && result.content.startsWith("(Directory");
|
|
762
|
+
fileBlocks.push(formatFileBlock(ref, result.content, result.size, isDirectory));
|
|
763
|
+
}
|
|
764
|
+
if (fileBlocks.length === 0) {
|
|
765
|
+
return { content: message, errors };
|
|
766
|
+
}
|
|
767
|
+
const processedContent = message + "\n" + fileBlocks.join("\n");
|
|
768
|
+
return { content: processedContent, errors };
|
|
769
|
+
}
|
|
770
|
+
function hasFileReferences(message) {
|
|
771
|
+
FILE_REFERENCE_REGEX.lastIndex = 0;
|
|
772
|
+
return FILE_REFERENCE_REGEX.test(message);
|
|
373
773
|
}
|
|
374
774
|
|
|
375
775
|
// src/components/App.tsx
|
|
@@ -382,7 +782,7 @@ function App({ onMessage, onCommand, onPermissionResponse, commandHistory = [] }
|
|
|
382
782
|
const permissionPrompt = useCliStore((state) => state.permissionPrompt);
|
|
383
783
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
384
784
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
385
|
-
const handleSubmit =
|
|
785
|
+
const handleSubmit = React8.useCallback(
|
|
386
786
|
async (input) => {
|
|
387
787
|
const trimmed = input.trim();
|
|
388
788
|
if (!trimmed) return;
|
|
@@ -393,14 +793,25 @@ function App({ onMessage, onCommand, onPermissionResponse, commandHistory = [] }
|
|
|
393
793
|
}
|
|
394
794
|
setIsThinking(true);
|
|
395
795
|
try {
|
|
396
|
-
|
|
796
|
+
let messageToSend = trimmed;
|
|
797
|
+
if (hasFileReferences(trimmed)) {
|
|
798
|
+
const processed = await processFileReferences(trimmed);
|
|
799
|
+
messageToSend = processed.content;
|
|
800
|
+
if (processed.errors.length > 0) {
|
|
801
|
+
const errorBlock = processed.errors.map((e) => `[Warning: ${e}]`).join("\n");
|
|
802
|
+
messageToSend = `${messageToSend}
|
|
803
|
+
|
|
804
|
+
${errorBlock}`;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
await onMessage(messageToSend);
|
|
397
808
|
} finally {
|
|
398
809
|
setIsThinking(false);
|
|
399
810
|
}
|
|
400
811
|
},
|
|
401
812
|
[onMessage, onCommand, setIsThinking]
|
|
402
813
|
);
|
|
403
|
-
return /* @__PURE__ */
|
|
814
|
+
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), permissionPrompt ? /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React8.createElement(
|
|
404
815
|
PermissionPrompt,
|
|
405
816
|
{
|
|
406
817
|
toolName: permissionPrompt.toolName,
|
|
@@ -409,39 +820,39 @@ function App({ onMessage, onCommand, onPermissionResponse, commandHistory = [] }
|
|
|
409
820
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
410
821
|
onResponse: onPermissionResponse
|
|
411
822
|
}
|
|
412
|
-
)) : /* @__PURE__ */
|
|
823
|
+
)) : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React8.createElement(Box8, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React8.createElement(MessageItem, { message }))), /* @__PURE__ */ React8.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "yellow", bold: true }, "\u26A0\uFE0F Press Ctrl+C again to exit")), /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React8.createElement(InputPrompt, { onSubmit: handleSubmit, disabled: isThinking, history: commandHistory }))));
|
|
413
824
|
}
|
|
414
825
|
function MessageItem({ message }) {
|
|
415
|
-
return /* @__PURE__ */
|
|
826
|
+
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React8.createElement(Box8, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React8.createElement(Text7, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React8.createElement(Box8, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { 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__ */ React8.createElement(Text7, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
416
827
|
if (step.type === "thought") {
|
|
417
|
-
return /* @__PURE__ */
|
|
828
|
+
return /* @__PURE__ */ React8.createElement(Box8, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
|
|
418
829
|
}
|
|
419
830
|
if (step.type === "action") {
|
|
420
831
|
const toolName = step.metadata?.toolName || "unknown";
|
|
421
832
|
const toolInput = step.metadata?.toolInput;
|
|
422
833
|
const observationStep = message.metadata.steps[idx + 1];
|
|
423
834
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
424
|
-
return /* @__PURE__ */
|
|
835
|
+
return /* @__PURE__ */ React8.createElement(Box8, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React8.createElement(Text7, { 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 ? "..." : ""}`));
|
|
425
836
|
}
|
|
426
837
|
return null;
|
|
427
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
838
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React8.createElement(Box8, { paddingLeft: 2 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
428
839
|
}
|
|
429
840
|
|
|
430
841
|
// src/components/MessageList.tsx
|
|
431
|
-
import
|
|
432
|
-
import { Box as
|
|
842
|
+
import React9 from "react";
|
|
843
|
+
import { Box as Box9, Text as Text8 } from "ink";
|
|
433
844
|
function stripThinkingTags(content) {
|
|
434
845
|
return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
435
846
|
}
|
|
436
|
-
var MessageList =
|
|
847
|
+
var MessageList = React9.memo(
|
|
437
848
|
function MessageList2({ messages }) {
|
|
438
849
|
if (messages.length === 0) {
|
|
439
|
-
return /* @__PURE__ */
|
|
850
|
+
return /* @__PURE__ */ React9.createElement(Box9, { paddingY: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "No messages yet. Type a message to start!"));
|
|
440
851
|
}
|
|
441
|
-
return /* @__PURE__ */
|
|
852
|
+
return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React9.createElement(Box9, { key: index, 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 ", stripThinkingTags(message.content)) : /* @__PURE__ */ React9.createElement(Text8, null, stripThinkingTags(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) => {
|
|
442
853
|
if (step.type === "thought") {
|
|
443
|
-
return /* @__PURE__ */
|
|
444
|
-
|
|
854
|
+
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(
|
|
855
|
+
Text8,
|
|
445
856
|
{
|
|
446
857
|
dimColor: true
|
|
447
858
|
},
|
|
@@ -453,10 +864,10 @@ var MessageList = React8.memo(
|
|
|
453
864
|
const toolInput = step.metadata?.toolInput;
|
|
454
865
|
const observationStep = message.metadata.steps[idx + 1];
|
|
455
866
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
456
|
-
return /* @__PURE__ */
|
|
867
|
+
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 ? "..." : ""}`));
|
|
457
868
|
}
|
|
458
869
|
return null;
|
|
459
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
870
|
+
}).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`)))));
|
|
460
871
|
},
|
|
461
872
|
(prevProps, nextProps) => {
|
|
462
873
|
if (prevProps.messages.length !== nextProps.messages.length) {
|
|
@@ -467,8 +878,8 @@ var MessageList = React8.memo(
|
|
|
467
878
|
);
|
|
468
879
|
|
|
469
880
|
// src/components/LoginFlow.tsx
|
|
470
|
-
import
|
|
471
|
-
import { Box as
|
|
881
|
+
import React10, { useState as useState2, useEffect as useEffect2 } from "react";
|
|
882
|
+
import { Box as Box10, Text as Text9 } from "ink";
|
|
472
883
|
import Spinner2 from "ink-spinner";
|
|
473
884
|
import jwt from "jsonwebtoken";
|
|
474
885
|
import open from "open";
|
|
@@ -578,7 +989,7 @@ var OAuthClient = class {
|
|
|
578
989
|
* Sleep for specified milliseconds
|
|
579
990
|
*/
|
|
580
991
|
sleep(ms) {
|
|
581
|
-
return new Promise((
|
|
992
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
582
993
|
}
|
|
583
994
|
};
|
|
584
995
|
|
|
@@ -631,31 +1042,31 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
631
1042
|
}
|
|
632
1043
|
}, [deviceFlow, status]);
|
|
633
1044
|
if (status === "initiating") {
|
|
634
|
-
return /* @__PURE__ */
|
|
1045
|
+
return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan" }, /* @__PURE__ */ React10.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
|
|
635
1046
|
}
|
|
636
1047
|
if (status === "error") {
|
|
637
|
-
return /* @__PURE__ */
|
|
1048
|
+
return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Text9, { color: "red" }, error)));
|
|
638
1049
|
}
|
|
639
1050
|
if (status === "success") {
|
|
640
|
-
return /* @__PURE__ */
|
|
1051
|
+
return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "You can now use B4M CLI with your account.")));
|
|
641
1052
|
}
|
|
642
|
-
return /* @__PURE__ */
|
|
1053
|
+
return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, null, "And enter this code when prompted:")), /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan" }, /* @__PURE__ */ React10.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
|
|
643
1054
|
}
|
|
644
1055
|
|
|
645
1056
|
// src/storage/SessionStore.ts
|
|
646
|
-
import { promises as
|
|
647
|
-
import
|
|
1057
|
+
import { promises as fs3 } from "fs";
|
|
1058
|
+
import path3 from "path";
|
|
648
1059
|
import { homedir } from "os";
|
|
649
1060
|
var SessionStore = class {
|
|
650
1061
|
constructor(basePath) {
|
|
651
|
-
this.basePath = basePath ||
|
|
1062
|
+
this.basePath = basePath || path3.join(homedir(), ".bike4mind", "sessions");
|
|
652
1063
|
}
|
|
653
1064
|
/**
|
|
654
1065
|
* Initialize storage directory
|
|
655
1066
|
*/
|
|
656
1067
|
async init() {
|
|
657
1068
|
try {
|
|
658
|
-
await
|
|
1069
|
+
await fs3.mkdir(this.basePath, { recursive: true });
|
|
659
1070
|
} catch (error) {
|
|
660
1071
|
console.error("Failed to initialize session storage:", error);
|
|
661
1072
|
throw error;
|
|
@@ -666,9 +1077,9 @@ var SessionStore = class {
|
|
|
666
1077
|
*/
|
|
667
1078
|
async save(session) {
|
|
668
1079
|
await this.init();
|
|
669
|
-
const filePath =
|
|
1080
|
+
const filePath = path3.join(this.basePath, `${session.id}.json`);
|
|
670
1081
|
try {
|
|
671
|
-
await
|
|
1082
|
+
await fs3.writeFile(filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
672
1083
|
} catch (error) {
|
|
673
1084
|
console.error("Failed to save session:", error);
|
|
674
1085
|
throw error;
|
|
@@ -678,9 +1089,9 @@ var SessionStore = class {
|
|
|
678
1089
|
* Load a session from disk by ID
|
|
679
1090
|
*/
|
|
680
1091
|
async load(id) {
|
|
681
|
-
const filePath =
|
|
1092
|
+
const filePath = path3.join(this.basePath, `${id}.json`);
|
|
682
1093
|
try {
|
|
683
|
-
const data = await
|
|
1094
|
+
const data = await fs3.readFile(filePath, "utf-8");
|
|
684
1095
|
return JSON.parse(data);
|
|
685
1096
|
} catch (error) {
|
|
686
1097
|
if (error.code === "ENOENT") {
|
|
@@ -704,12 +1115,12 @@ var SessionStore = class {
|
|
|
704
1115
|
async list() {
|
|
705
1116
|
await this.init();
|
|
706
1117
|
try {
|
|
707
|
-
const files = await
|
|
1118
|
+
const files = await fs3.readdir(this.basePath);
|
|
708
1119
|
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
709
1120
|
const sessions = await Promise.all(
|
|
710
1121
|
jsonFiles.map(async (file) => {
|
|
711
|
-
const filePath =
|
|
712
|
-
const data = await
|
|
1122
|
+
const filePath = path3.join(this.basePath, file);
|
|
1123
|
+
const data = await fs3.readFile(filePath, "utf-8");
|
|
713
1124
|
return JSON.parse(data);
|
|
714
1125
|
})
|
|
715
1126
|
);
|
|
@@ -723,9 +1134,9 @@ var SessionStore = class {
|
|
|
723
1134
|
* Delete a session
|
|
724
1135
|
*/
|
|
725
1136
|
async delete(id) {
|
|
726
|
-
const filePath =
|
|
1137
|
+
const filePath = path3.join(this.basePath, `${id}.json`);
|
|
727
1138
|
try {
|
|
728
|
-
await
|
|
1139
|
+
await fs3.unlink(filePath);
|
|
729
1140
|
return true;
|
|
730
1141
|
} catch (error) {
|
|
731
1142
|
if (error.code === "ENOENT") {
|
|
@@ -761,8 +1172,8 @@ var SessionStore = class {
|
|
|
761
1172
|
};
|
|
762
1173
|
|
|
763
1174
|
// src/storage/ConfigStore.ts
|
|
764
|
-
import { promises as
|
|
765
|
-
import
|
|
1175
|
+
import { promises as fs4 } from "fs";
|
|
1176
|
+
import path4 from "path";
|
|
766
1177
|
import { homedir as homedir2 } from "os";
|
|
767
1178
|
import { v4 as uuidv4 } from "uuid";
|
|
768
1179
|
import { z } from "zod";
|
|
@@ -836,15 +1247,15 @@ var DEFAULT_CONFIG = {
|
|
|
836
1247
|
var ConfigStore = class {
|
|
837
1248
|
constructor(configPath) {
|
|
838
1249
|
this.config = null;
|
|
839
|
-
this.configPath = configPath ||
|
|
1250
|
+
this.configPath = configPath || path4.join(homedir2(), ".bike4mind", "config.json");
|
|
840
1251
|
}
|
|
841
1252
|
/**
|
|
842
1253
|
* Initialize config directory
|
|
843
1254
|
*/
|
|
844
1255
|
async init() {
|
|
845
|
-
const dir =
|
|
1256
|
+
const dir = path4.dirname(this.configPath);
|
|
846
1257
|
try {
|
|
847
|
-
await
|
|
1258
|
+
await fs4.mkdir(dir, { recursive: true });
|
|
848
1259
|
} catch (error) {
|
|
849
1260
|
console.error("Failed to initialize config directory:", error);
|
|
850
1261
|
throw error;
|
|
@@ -859,15 +1270,15 @@ var ConfigStore = class {
|
|
|
859
1270
|
}
|
|
860
1271
|
try {
|
|
861
1272
|
try {
|
|
862
|
-
const stats = await
|
|
1273
|
+
const stats = await fs4.stat(this.configPath);
|
|
863
1274
|
const mode = stats.mode & 511;
|
|
864
1275
|
if (mode !== 384) {
|
|
865
1276
|
console.warn(`\u26A0\uFE0F Config file has insecure permissions (${mode.toString(8)}). Setting to 0600...`);
|
|
866
|
-
await
|
|
1277
|
+
await fs4.chmod(this.configPath, 384);
|
|
867
1278
|
}
|
|
868
1279
|
} catch (statError) {
|
|
869
1280
|
}
|
|
870
|
-
const data = await
|
|
1281
|
+
const data = await fs4.readFile(this.configPath, "utf-8");
|
|
871
1282
|
const rawConfig = JSON.parse(data);
|
|
872
1283
|
if (rawConfig.apiConfig && "environment" in rawConfig.apiConfig) {
|
|
873
1284
|
const oldApiConfig = rawConfig.apiConfig;
|
|
@@ -940,8 +1351,8 @@ var ConfigStore = class {
|
|
|
940
1351
|
throw new Error("No configuration to save");
|
|
941
1352
|
}
|
|
942
1353
|
try {
|
|
943
|
-
await
|
|
944
|
-
await
|
|
1354
|
+
await fs4.writeFile(this.configPath, JSON.stringify(this.config, null, 2), "utf-8");
|
|
1355
|
+
await fs4.chmod(this.configPath, 384);
|
|
945
1356
|
} catch (error) {
|
|
946
1357
|
console.error("Failed to save config:", error);
|
|
947
1358
|
throw error;
|
|
@@ -1088,22 +1499,22 @@ var ConfigStore = class {
|
|
|
1088
1499
|
};
|
|
1089
1500
|
|
|
1090
1501
|
// src/storage/CommandHistoryStore.ts
|
|
1091
|
-
import { promises as
|
|
1092
|
-
import
|
|
1502
|
+
import { promises as fs5 } from "fs";
|
|
1503
|
+
import path5 from "path";
|
|
1093
1504
|
import { homedir as homedir3 } from "os";
|
|
1094
1505
|
var MAX_HISTORY_ENTRIES = 1e3;
|
|
1095
1506
|
var CommandHistoryStore = class {
|
|
1096
1507
|
constructor(historyPath) {
|
|
1097
1508
|
this.history = null;
|
|
1098
|
-
this.historyPath = historyPath ||
|
|
1509
|
+
this.historyPath = historyPath || path5.join(homedir3(), ".bike4mind", "history.jsonl");
|
|
1099
1510
|
}
|
|
1100
1511
|
/**
|
|
1101
1512
|
* Initialize history directory
|
|
1102
1513
|
*/
|
|
1103
1514
|
async init() {
|
|
1104
|
-
const dir =
|
|
1515
|
+
const dir = path5.dirname(this.historyPath);
|
|
1105
1516
|
try {
|
|
1106
|
-
await
|
|
1517
|
+
await fs5.mkdir(dir, { recursive: true });
|
|
1107
1518
|
} catch (error) {
|
|
1108
1519
|
console.error("Failed to initialize history directory:", error);
|
|
1109
1520
|
throw error;
|
|
@@ -1118,7 +1529,7 @@ var CommandHistoryStore = class {
|
|
|
1118
1529
|
return this.history;
|
|
1119
1530
|
}
|
|
1120
1531
|
try {
|
|
1121
|
-
const data = await
|
|
1532
|
+
const data = await fs5.readFile(this.historyPath, "utf-8");
|
|
1122
1533
|
const lines = data.trim().split("\n").filter((line) => line.length > 0);
|
|
1123
1534
|
const entries = lines.map((line) => {
|
|
1124
1535
|
try {
|
|
@@ -1155,7 +1566,7 @@ var CommandHistoryStore = class {
|
|
|
1155
1566
|
};
|
|
1156
1567
|
await this.init();
|
|
1157
1568
|
const line = JSON.stringify(entry) + "\n";
|
|
1158
|
-
await
|
|
1569
|
+
await fs5.appendFile(this.historyPath, line, "utf-8");
|
|
1159
1570
|
this.history = [command, ...this.history || []];
|
|
1160
1571
|
if (this.history.length > MAX_HISTORY_ENTRIES) {
|
|
1161
1572
|
await this.trim();
|
|
@@ -1178,7 +1589,7 @@ var CommandHistoryStore = class {
|
|
|
1178
1589
|
};
|
|
1179
1590
|
return JSON.stringify(entry);
|
|
1180
1591
|
}).join("\n");
|
|
1181
|
-
await
|
|
1592
|
+
await fs5.writeFile(this.historyPath, lines + "\n", "utf-8");
|
|
1182
1593
|
this.history = trimmedCommands;
|
|
1183
1594
|
}
|
|
1184
1595
|
/**
|
|
@@ -1186,7 +1597,7 @@ var CommandHistoryStore = class {
|
|
|
1186
1597
|
*/
|
|
1187
1598
|
async clear() {
|
|
1188
1599
|
try {
|
|
1189
|
-
await
|
|
1600
|
+
await fs5.unlink(this.historyPath);
|
|
1190
1601
|
} catch (error) {
|
|
1191
1602
|
if (error.code !== "ENOENT") {
|
|
1192
1603
|
throw error;
|
|
@@ -1960,7 +2371,7 @@ import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
|
1960
2371
|
import OpenAI2 from "openai";
|
|
1961
2372
|
|
|
1962
2373
|
// ../../b4m-core/packages/services/dist/src/importHistoryService/index.js
|
|
1963
|
-
import
|
|
2374
|
+
import fs6, { unlinkSync } from "fs";
|
|
1964
2375
|
import yauzl from "yauzl";
|
|
1965
2376
|
import axios3 from "axios";
|
|
1966
2377
|
|
|
@@ -3646,8 +4057,8 @@ async function processAndStoreImages(images, context) {
|
|
|
3646
4057
|
const buffer = await downloadImage(image);
|
|
3647
4058
|
const fileType = await fileTypeFromBuffer2(buffer);
|
|
3648
4059
|
const filename = `${uuidv45()}.${fileType?.ext}`;
|
|
3649
|
-
const
|
|
3650
|
-
return
|
|
4060
|
+
const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
4061
|
+
return path13;
|
|
3651
4062
|
}));
|
|
3652
4063
|
}
|
|
3653
4064
|
async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
|
|
@@ -4831,8 +5242,8 @@ async function processAndStoreImage(imageUrl, context) {
|
|
|
4831
5242
|
const buffer = await downloadImage2(imageUrl);
|
|
4832
5243
|
const fileType = await fileTypeFromBuffer3(buffer);
|
|
4833
5244
|
const filename = `${uuidv46()}.${fileType?.ext}`;
|
|
4834
|
-
const
|
|
4835
|
-
return
|
|
5245
|
+
const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
5246
|
+
return path13;
|
|
4836
5247
|
}
|
|
4837
5248
|
async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
|
|
4838
5249
|
await context.onFinish?.("edit_image", storedImagePath);
|
|
@@ -6669,33 +7080,33 @@ var planetVisibilityTool = {
|
|
|
6669
7080
|
};
|
|
6670
7081
|
|
|
6671
7082
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
|
|
6672
|
-
import { promises as
|
|
6673
|
-
import { existsSync, statSync } from "fs";
|
|
6674
|
-
import
|
|
6675
|
-
var
|
|
7083
|
+
import { promises as fs7 } from "fs";
|
|
7084
|
+
import { existsSync as existsSync2, statSync as statSync3 } from "fs";
|
|
7085
|
+
import path6 from "path";
|
|
7086
|
+
var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
|
|
6676
7087
|
async function readFileContent(params) {
|
|
6677
7088
|
const { path: filePath, encoding = "utf-8", maxLines } = params;
|
|
6678
|
-
const normalizedPath =
|
|
6679
|
-
const resolvedPath =
|
|
6680
|
-
const cwd =
|
|
7089
|
+
const normalizedPath = path6.normalize(filePath);
|
|
7090
|
+
const resolvedPath = path6.resolve(process.cwd(), normalizedPath);
|
|
7091
|
+
const cwd = path6.resolve(process.cwd());
|
|
6681
7092
|
if (!resolvedPath.startsWith(cwd)) {
|
|
6682
7093
|
throw new Error(`Access denied: Cannot read files outside of current working directory`);
|
|
6683
7094
|
}
|
|
6684
|
-
if (!
|
|
7095
|
+
if (!existsSync2(resolvedPath)) {
|
|
6685
7096
|
throw new Error(`File not found: ${filePath}`);
|
|
6686
7097
|
}
|
|
6687
|
-
const stats =
|
|
7098
|
+
const stats = statSync3(resolvedPath);
|
|
6688
7099
|
if (stats.isDirectory()) {
|
|
6689
7100
|
throw new Error(`Path is a directory, not a file: ${filePath}`);
|
|
6690
7101
|
}
|
|
6691
|
-
if (stats.size >
|
|
6692
|
-
throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${
|
|
7102
|
+
if (stats.size > MAX_FILE_SIZE2) {
|
|
7103
|
+
throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${MAX_FILE_SIZE2 / 1024 / 1024}MB)`);
|
|
6693
7104
|
}
|
|
6694
7105
|
const isBinary = await checkIfBinary(resolvedPath);
|
|
6695
7106
|
if (isBinary && encoding === "utf-8") {
|
|
6696
7107
|
throw new Error(`File appears to be binary. Use encoding 'base64' to read binary files, or specify a different encoding.`);
|
|
6697
7108
|
}
|
|
6698
|
-
const content = await
|
|
7109
|
+
const content = await fs7.readFile(resolvedPath, encoding);
|
|
6699
7110
|
if (maxLines && typeof content === "string") {
|
|
6700
7111
|
const lines = content.split("\n");
|
|
6701
7112
|
if (lines.length > maxLines) {
|
|
@@ -6710,7 +7121,7 @@ ${content}`;
|
|
|
6710
7121
|
}
|
|
6711
7122
|
async function checkIfBinary(filePath) {
|
|
6712
7123
|
const buffer = Buffer.alloc(8192);
|
|
6713
|
-
const fd = await
|
|
7124
|
+
const fd = await fs7.open(filePath, "r");
|
|
6714
7125
|
try {
|
|
6715
7126
|
const { bytesRead } = await fd.read(buffer, 0, 8192, 0);
|
|
6716
7127
|
const chunk = buffer.slice(0, bytesRead);
|
|
@@ -6727,7 +7138,7 @@ var fileReadTool = {
|
|
|
6727
7138
|
context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
|
|
6728
7139
|
try {
|
|
6729
7140
|
const content = await readFileContent(params);
|
|
6730
|
-
const stats =
|
|
7141
|
+
const stats = statSync3(path6.resolve(process.cwd(), path6.normalize(params.path)));
|
|
6731
7142
|
context.logger.info("\u2705 FileRead: Success", {
|
|
6732
7143
|
path: params.path,
|
|
6733
7144
|
size: stats.size,
|
|
@@ -6766,25 +7177,25 @@ var fileReadTool = {
|
|
|
6766
7177
|
};
|
|
6767
7178
|
|
|
6768
7179
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
|
|
6769
|
-
import { promises as
|
|
6770
|
-
import { existsSync as
|
|
6771
|
-
import
|
|
7180
|
+
import { promises as fs8 } from "fs";
|
|
7181
|
+
import { existsSync as existsSync3 } from "fs";
|
|
7182
|
+
import path7 from "path";
|
|
6772
7183
|
async function createFile(params) {
|
|
6773
7184
|
const { path: filePath, content, createDirectories = true } = params;
|
|
6774
|
-
const normalizedPath =
|
|
6775
|
-
const resolvedPath =
|
|
6776
|
-
const cwd =
|
|
7185
|
+
const normalizedPath = path7.normalize(filePath);
|
|
7186
|
+
const resolvedPath = path7.resolve(process.cwd(), normalizedPath);
|
|
7187
|
+
const cwd = path7.resolve(process.cwd());
|
|
6777
7188
|
if (!resolvedPath.startsWith(cwd)) {
|
|
6778
7189
|
throw new Error(`Access denied: Cannot create files outside of current working directory`);
|
|
6779
7190
|
}
|
|
6780
|
-
const fileExists =
|
|
7191
|
+
const fileExists = existsSync3(resolvedPath);
|
|
6781
7192
|
const action = fileExists ? "overwritten" : "created";
|
|
6782
7193
|
if (createDirectories) {
|
|
6783
|
-
const dir =
|
|
6784
|
-
await
|
|
7194
|
+
const dir = path7.dirname(resolvedPath);
|
|
7195
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
6785
7196
|
}
|
|
6786
|
-
await
|
|
6787
|
-
const stats = await
|
|
7197
|
+
await fs8.writeFile(resolvedPath, content, "utf-8");
|
|
7198
|
+
const stats = await fs8.stat(resolvedPath);
|
|
6788
7199
|
const lines = content.split("\n").length;
|
|
6789
7200
|
return `File ${action} successfully: ${filePath}
|
|
6790
7201
|
Size: ${stats.size} bytes
|
|
@@ -6795,7 +7206,7 @@ var createFileTool = {
|
|
|
6795
7206
|
implementation: (context) => ({
|
|
6796
7207
|
toolFn: async (value) => {
|
|
6797
7208
|
const params = value;
|
|
6798
|
-
const fileExists =
|
|
7209
|
+
const fileExists = existsSync3(path7.resolve(process.cwd(), path7.normalize(params.path)));
|
|
6799
7210
|
context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
|
|
6800
7211
|
path: params.path,
|
|
6801
7212
|
size: params.content.length
|
|
@@ -6836,8 +7247,8 @@ var createFileTool = {
|
|
|
6836
7247
|
|
|
6837
7248
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
|
|
6838
7249
|
import { readdir } from "fs/promises";
|
|
6839
|
-
import { statSync as
|
|
6840
|
-
import
|
|
7250
|
+
import { statSync as statSync4 } from "fs";
|
|
7251
|
+
import path8 from "path";
|
|
6841
7252
|
function matchGlob(filePath, pattern) {
|
|
6842
7253
|
let regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
|
|
6843
7254
|
regexPattern = regexPattern.replace(/\{\{GLOBSTAR\}\}\//g, "(.*/)?").replace(/\/\{\{GLOBSTAR\}\}/g, "(/.*)?").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
@@ -6855,8 +7266,8 @@ async function walkDir(dir, pattern, includeHidden, maxResults, basePath = "") {
|
|
|
6855
7266
|
if (!includeHidden && entry.name.startsWith(".")) {
|
|
6856
7267
|
continue;
|
|
6857
7268
|
}
|
|
6858
|
-
const relativePath = basePath ?
|
|
6859
|
-
const fullPath =
|
|
7269
|
+
const relativePath = basePath ? path8.join(basePath, entry.name) : entry.name;
|
|
7270
|
+
const fullPath = path8.join(dir, entry.name);
|
|
6860
7271
|
if (matchGlob(relativePath, pattern)) {
|
|
6861
7272
|
matches.push(relativePath);
|
|
6862
7273
|
}
|
|
@@ -6872,7 +7283,7 @@ async function walkDir(dir, pattern, includeHidden, maxResults, basePath = "") {
|
|
|
6872
7283
|
async function listFiles(params) {
|
|
6873
7284
|
const { pattern, cwd: relativeCwd, includeHidden = false, maxResults = 1e3 } = params;
|
|
6874
7285
|
const baseCwd = process.cwd();
|
|
6875
|
-
const targetCwd = relativeCwd ?
|
|
7286
|
+
const targetCwd = relativeCwd ? path8.resolve(baseCwd, path8.normalize(relativeCwd)) : baseCwd;
|
|
6876
7287
|
if (!targetCwd.startsWith(baseCwd)) {
|
|
6877
7288
|
throw new Error(`Access denied: Cannot list files outside of current working directory`);
|
|
6878
7289
|
}
|
|
@@ -6883,8 +7294,8 @@ async function listFiles(params) {
|
|
|
6883
7294
|
const filesInfo = [];
|
|
6884
7295
|
for (const match of matches) {
|
|
6885
7296
|
try {
|
|
6886
|
-
const fullPath =
|
|
6887
|
-
const stats =
|
|
7297
|
+
const fullPath = path8.join(targetCwd, match);
|
|
7298
|
+
const stats = statSync4(fullPath);
|
|
6888
7299
|
filesInfo.push({
|
|
6889
7300
|
path: match,
|
|
6890
7301
|
size: stats.size,
|
|
@@ -6965,9 +7376,9 @@ var globFilesTool = {
|
|
|
6965
7376
|
|
|
6966
7377
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
|
|
6967
7378
|
import { readdir as readdir2 } from "fs/promises";
|
|
6968
|
-
import { readFileSync, statSync as
|
|
6969
|
-
import
|
|
6970
|
-
var
|
|
7379
|
+
import { readFileSync as readFileSync2, statSync as statSync5 } from "fs";
|
|
7380
|
+
import path9 from "path";
|
|
7381
|
+
var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
|
|
6971
7382
|
function matchGlob2(filePath, pattern) {
|
|
6972
7383
|
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
|
|
6973
7384
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
@@ -6984,8 +7395,8 @@ async function walkDir2(dir, pattern, maxResults, basePath = "") {
|
|
|
6984
7395
|
if (entry.name.startsWith(".")) {
|
|
6985
7396
|
continue;
|
|
6986
7397
|
}
|
|
6987
|
-
const relativePath = basePath ?
|
|
6988
|
-
const fullPath =
|
|
7398
|
+
const relativePath = basePath ? path9.join(basePath, entry.name) : entry.name;
|
|
7399
|
+
const fullPath = path9.join(dir, entry.name);
|
|
6989
7400
|
if (matchGlob2(relativePath, pattern)) {
|
|
6990
7401
|
matches.push(relativePath);
|
|
6991
7402
|
}
|
|
@@ -6998,10 +7409,10 @@ async function walkDir2(dir, pattern, maxResults, basePath = "") {
|
|
|
6998
7409
|
}
|
|
6999
7410
|
return matches;
|
|
7000
7411
|
}
|
|
7001
|
-
async function
|
|
7412
|
+
async function searchFiles2(params) {
|
|
7002
7413
|
const { pattern, filePattern = "**/*", cwd: relativeCwd, caseSensitive = false, maxResults = 100, contextLines = 0 } = params;
|
|
7003
7414
|
const baseCwd = process.cwd();
|
|
7004
|
-
const targetCwd = relativeCwd ?
|
|
7415
|
+
const targetCwd = relativeCwd ? path9.resolve(baseCwd, path9.normalize(relativeCwd)) : baseCwd;
|
|
7005
7416
|
if (!targetCwd.startsWith(baseCwd)) {
|
|
7006
7417
|
throw new Error(`Access denied: Cannot search files outside of current working directory`);
|
|
7007
7418
|
}
|
|
@@ -7015,13 +7426,13 @@ async function searchFiles(params) {
|
|
|
7015
7426
|
const filesToSearch = await walkDir2(targetCwd, filePattern, 1e4);
|
|
7016
7427
|
const filteredFiles = [];
|
|
7017
7428
|
for (const match of filesToSearch) {
|
|
7018
|
-
const fullPath =
|
|
7429
|
+
const fullPath = path9.join(targetCwd, match);
|
|
7019
7430
|
try {
|
|
7020
|
-
const stats =
|
|
7021
|
-
if (stats.isDirectory() || stats.size >
|
|
7431
|
+
const stats = statSync5(fullPath);
|
|
7432
|
+
if (stats.isDirectory() || stats.size > MAX_FILE_SIZE3) {
|
|
7022
7433
|
continue;
|
|
7023
7434
|
}
|
|
7024
|
-
if (
|
|
7435
|
+
if (isBinaryFile2(fullPath)) {
|
|
7025
7436
|
continue;
|
|
7026
7437
|
}
|
|
7027
7438
|
filteredFiles.push(match);
|
|
@@ -7039,9 +7450,9 @@ async function searchFiles(params) {
|
|
|
7039
7450
|
break;
|
|
7040
7451
|
}
|
|
7041
7452
|
filesSearched++;
|
|
7042
|
-
const fullPath =
|
|
7453
|
+
const fullPath = path9.join(targetCwd, file);
|
|
7043
7454
|
try {
|
|
7044
|
-
const content =
|
|
7455
|
+
const content = readFileSync2(fullPath, "utf-8");
|
|
7045
7456
|
const lines = content.split("\n");
|
|
7046
7457
|
for (let i = 0; i < lines.length; i++) {
|
|
7047
7458
|
if (matches.length >= maxResults) {
|
|
@@ -7105,7 +7516,7 @@ ${match.context.after.map((l) => ` ${l}`).join("\n")}`;
|
|
|
7105
7516
|
}).join("\n");
|
|
7106
7517
|
return `${summary}${patternInfo}${cwdInfo}${matchesList}`;
|
|
7107
7518
|
}
|
|
7108
|
-
function
|
|
7519
|
+
function isBinaryFile2(filePath) {
|
|
7109
7520
|
try {
|
|
7110
7521
|
const buffer = Buffer.alloc(8192);
|
|
7111
7522
|
const fd = __require("fs").openSync(filePath, "r");
|
|
@@ -7128,7 +7539,7 @@ var grepSearchTool = {
|
|
|
7128
7539
|
cwd: params.cwd || "."
|
|
7129
7540
|
});
|
|
7130
7541
|
try {
|
|
7131
|
-
const result = await
|
|
7542
|
+
const result = await searchFiles2(params);
|
|
7132
7543
|
context.logger.info("\u2705 GrepSearch: Success");
|
|
7133
7544
|
return result;
|
|
7134
7545
|
} catch (error) {
|
|
@@ -7174,31 +7585,31 @@ var grepSearchTool = {
|
|
|
7174
7585
|
};
|
|
7175
7586
|
|
|
7176
7587
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
|
|
7177
|
-
import { promises as
|
|
7178
|
-
import { existsSync as
|
|
7179
|
-
import
|
|
7588
|
+
import { promises as fs9 } from "fs";
|
|
7589
|
+
import { existsSync as existsSync4, statSync as statSync6 } from "fs";
|
|
7590
|
+
import path10 from "path";
|
|
7180
7591
|
async function deleteFile(params) {
|
|
7181
7592
|
const { path: filePath, recursive = false } = params;
|
|
7182
|
-
const normalizedPath =
|
|
7183
|
-
const resolvedPath =
|
|
7184
|
-
const cwd =
|
|
7593
|
+
const normalizedPath = path10.normalize(filePath);
|
|
7594
|
+
const resolvedPath = path10.resolve(process.cwd(), normalizedPath);
|
|
7595
|
+
const cwd = path10.resolve(process.cwd());
|
|
7185
7596
|
if (!resolvedPath.startsWith(cwd)) {
|
|
7186
7597
|
throw new Error(`Access denied: Cannot delete files outside of current working directory`);
|
|
7187
7598
|
}
|
|
7188
|
-
if (!
|
|
7599
|
+
if (!existsSync4(resolvedPath)) {
|
|
7189
7600
|
throw new Error(`File or directory not found: ${filePath}`);
|
|
7190
7601
|
}
|
|
7191
|
-
const stats =
|
|
7602
|
+
const stats = statSync6(resolvedPath);
|
|
7192
7603
|
const isDirectory = stats.isDirectory();
|
|
7193
7604
|
const size = stats.size;
|
|
7194
7605
|
if (isDirectory && !recursive) {
|
|
7195
7606
|
throw new Error(`Path is a directory: ${filePath}. Use recursive=true to delete directories and their contents.`);
|
|
7196
7607
|
}
|
|
7197
7608
|
if (isDirectory) {
|
|
7198
|
-
await
|
|
7609
|
+
await fs9.rm(resolvedPath, { recursive: true, force: true });
|
|
7199
7610
|
return `Directory deleted successfully: ${filePath}`;
|
|
7200
7611
|
} else {
|
|
7201
|
-
await
|
|
7612
|
+
await fs9.unlink(resolvedPath);
|
|
7202
7613
|
return `File deleted successfully: ${filePath}
|
|
7203
7614
|
Size: ${size} bytes`;
|
|
7204
7615
|
}
|
|
@@ -7208,8 +7619,8 @@ var deleteFileTool = {
|
|
|
7208
7619
|
implementation: (context) => ({
|
|
7209
7620
|
toolFn: async (value) => {
|
|
7210
7621
|
const params = value;
|
|
7211
|
-
const resolvedPath =
|
|
7212
|
-
const isDirectory =
|
|
7622
|
+
const resolvedPath = path10.resolve(process.cwd(), path10.normalize(params.path));
|
|
7623
|
+
const isDirectory = existsSync4(resolvedPath) && statSync6(resolvedPath).isDirectory();
|
|
7213
7624
|
context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
|
|
7214
7625
|
path: params.path,
|
|
7215
7626
|
recursive: params.recursive
|
|
@@ -7499,10 +7910,10 @@ import jwt2 from "jsonwebtoken";
|
|
|
7499
7910
|
// src/utils/diffPreview.ts
|
|
7500
7911
|
import * as Diff from "diff";
|
|
7501
7912
|
import { readFile } from "fs/promises";
|
|
7502
|
-
import { existsSync as
|
|
7913
|
+
import { existsSync as existsSync5 } from "fs";
|
|
7503
7914
|
async function generateFileDiffPreview(args) {
|
|
7504
7915
|
try {
|
|
7505
|
-
if (!
|
|
7916
|
+
if (!existsSync5(args.path)) {
|
|
7506
7917
|
const lines2 = args.content.split("\n");
|
|
7507
7918
|
const preview = lines2.slice(0, 20).join("\n");
|
|
7508
7919
|
const hasMore = lines2.length > 20;
|
|
@@ -7532,10 +7943,10 @@ ${preview}${hasMore ? `
|
|
|
7532
7943
|
}
|
|
7533
7944
|
async function generateFileDeletePreview(args) {
|
|
7534
7945
|
try {
|
|
7535
|
-
if (!
|
|
7946
|
+
if (!existsSync5(args.path)) {
|
|
7536
7947
|
return `[File does not exist: ${args.path}]`;
|
|
7537
7948
|
}
|
|
7538
|
-
const stats = await import("fs/promises").then((
|
|
7949
|
+
const stats = await import("fs/promises").then((fs11) => fs11.stat(args.path));
|
|
7539
7950
|
return `[File will be deleted]
|
|
7540
7951
|
|
|
7541
7952
|
Path: ${args.path}
|
|
@@ -7551,21 +7962,21 @@ var NoOpStorage = class extends BaseStorage {
|
|
|
7551
7962
|
async upload(input, destination, options) {
|
|
7552
7963
|
return `/tmp/${destination}`;
|
|
7553
7964
|
}
|
|
7554
|
-
async download(
|
|
7965
|
+
async download(path13) {
|
|
7555
7966
|
throw new Error("Download not supported in CLI");
|
|
7556
7967
|
}
|
|
7557
|
-
async delete(
|
|
7968
|
+
async delete(path13) {
|
|
7558
7969
|
}
|
|
7559
|
-
async getSignedUrl(
|
|
7560
|
-
return `/tmp/${
|
|
7970
|
+
async getSignedUrl(path13) {
|
|
7971
|
+
return `/tmp/${path13}`;
|
|
7561
7972
|
}
|
|
7562
|
-
getPublicUrl(
|
|
7563
|
-
return `/tmp/${
|
|
7973
|
+
getPublicUrl(path13) {
|
|
7974
|
+
return `/tmp/${path13}`;
|
|
7564
7975
|
}
|
|
7565
|
-
async getPreview(
|
|
7566
|
-
return `/tmp/${
|
|
7976
|
+
async getPreview(path13) {
|
|
7977
|
+
return `/tmp/${path13}`;
|
|
7567
7978
|
}
|
|
7568
|
-
async getMetadata(
|
|
7979
|
+
async getMetadata(path13) {
|
|
7569
7980
|
return { size: 0, contentType: "application/octet-stream" };
|
|
7570
7981
|
}
|
|
7571
7982
|
};
|
|
@@ -7891,8 +8302,8 @@ function getEnvironmentName(configApiConfig) {
|
|
|
7891
8302
|
}
|
|
7892
8303
|
|
|
7893
8304
|
// src/utils/Logger.ts
|
|
7894
|
-
import
|
|
7895
|
-
import
|
|
8305
|
+
import fs10 from "fs/promises";
|
|
8306
|
+
import path11 from "path";
|
|
7896
8307
|
import os from "os";
|
|
7897
8308
|
var Logger2 = class _Logger {
|
|
7898
8309
|
constructor() {
|
|
@@ -7915,9 +8326,9 @@ var Logger2 = class _Logger {
|
|
|
7915
8326
|
*/
|
|
7916
8327
|
async initialize(sessionId) {
|
|
7917
8328
|
this.sessionId = sessionId;
|
|
7918
|
-
const debugDir =
|
|
7919
|
-
await
|
|
7920
|
-
this.logFilePath =
|
|
8329
|
+
const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
|
|
8330
|
+
await fs10.mkdir(debugDir, { recursive: true });
|
|
8331
|
+
this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
|
|
7921
8332
|
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
7922
8333
|
}
|
|
7923
8334
|
/**
|
|
@@ -7981,7 +8392,7 @@ var Logger2 = class _Logger {
|
|
|
7981
8392
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
7982
8393
|
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
7983
8394
|
`;
|
|
7984
|
-
await
|
|
8395
|
+
await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
7985
8396
|
} catch (error) {
|
|
7986
8397
|
console.error("File logging failed:", error);
|
|
7987
8398
|
}
|
|
@@ -8127,15 +8538,15 @@ var Logger2 = class _Logger {
|
|
|
8127
8538
|
async cleanupOldLogs() {
|
|
8128
8539
|
if (!this.fileLoggingEnabled) return;
|
|
8129
8540
|
try {
|
|
8130
|
-
const debugDir =
|
|
8131
|
-
const files = await
|
|
8541
|
+
const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
|
|
8542
|
+
const files = await fs10.readdir(debugDir);
|
|
8132
8543
|
const now = Date.now();
|
|
8133
8544
|
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
8134
8545
|
for (const file of files) {
|
|
8135
|
-
const filePath =
|
|
8136
|
-
const stats = await
|
|
8546
|
+
const filePath = path11.join(debugDir, file);
|
|
8547
|
+
const stats = await fs10.stat(filePath);
|
|
8137
8548
|
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
8138
|
-
await
|
|
8549
|
+
await fs10.unlink(filePath);
|
|
8139
8550
|
}
|
|
8140
8551
|
}
|
|
8141
8552
|
} catch (error) {
|
|
@@ -8148,8 +8559,8 @@ var logger = Logger2.getInstance();
|
|
|
8148
8559
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
8149
8560
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
8150
8561
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
8151
|
-
import
|
|
8152
|
-
import { existsSync as
|
|
8562
|
+
import path12 from "path";
|
|
8563
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
|
|
8153
8564
|
var MCPClient = class {
|
|
8154
8565
|
// Note: This class handles MCP server communication with repository filtering
|
|
8155
8566
|
mcp;
|
|
@@ -8174,20 +8585,20 @@ var MCPClient = class {
|
|
|
8174
8585
|
const root = process.env.INIT_CWD || process.cwd();
|
|
8175
8586
|
const candidatePaths = [
|
|
8176
8587
|
// When running from SST Lambda with node_modules structure (copyFiles)
|
|
8177
|
-
|
|
8588
|
+
path12.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
|
|
8178
8589
|
// When running from SST Lambda deployed environment (/var/task)
|
|
8179
|
-
|
|
8590
|
+
path12.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
8180
8591
|
// When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
|
|
8181
|
-
|
|
8592
|
+
path12.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
8182
8593
|
// When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
|
|
8183
|
-
|
|
8594
|
+
path12.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
8184
8595
|
// Original paths (backward compatibility)
|
|
8185
|
-
|
|
8186
|
-
|
|
8596
|
+
path12.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
8597
|
+
path12.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
8187
8598
|
];
|
|
8188
|
-
const serverScriptPath = candidatePaths.find((p) =>
|
|
8599
|
+
const serverScriptPath = candidatePaths.find((p) => existsSync6(p));
|
|
8189
8600
|
if (!serverScriptPath) {
|
|
8190
|
-
const getDirectories = (source) =>
|
|
8601
|
+
const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
8191
8602
|
console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
|
|
8192
8603
|
console.error(`[MCP] Available directories in ${root}:`, getDirectories(root));
|
|
8193
8604
|
throw new Error(`MCP Server script path not found for ${this.serverName}. Root: ${root}, Available dirs: ${getDirectories(root).join(", ")}`);
|
|
@@ -8216,7 +8627,7 @@ var MCPClient = class {
|
|
|
8216
8627
|
}
|
|
8217
8628
|
};
|
|
8218
8629
|
await this.mcp.connect(this.transport);
|
|
8219
|
-
await new Promise((
|
|
8630
|
+
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
8220
8631
|
const toolsResult = await this.mcp.listTools();
|
|
8221
8632
|
this.tools = toolsResult.tools.map((tool) => {
|
|
8222
8633
|
return {
|
|
@@ -8399,7 +8810,7 @@ var ServerLlmBackend = class {
|
|
|
8399
8810
|
*/
|
|
8400
8811
|
async complete(model, messages, options, callback) {
|
|
8401
8812
|
logger.debug(`[ServerLlmBackend] Starting complete() with model: ${model}`);
|
|
8402
|
-
return new Promise(async (
|
|
8813
|
+
return new Promise(async (resolve3, reject) => {
|
|
8403
8814
|
try {
|
|
8404
8815
|
logger.debug("[ServerLlmBackend] Making streaming request...");
|
|
8405
8816
|
const response = await this.makeStreamingRequest(model, messages, options);
|
|
@@ -8429,15 +8840,15 @@ var ServerLlmBackend = class {
|
|
|
8429
8840
|
reject(err);
|
|
8430
8841
|
}).then(() => {
|
|
8431
8842
|
logger.debug("[ServerLlmBackend] Callback completed, resolving");
|
|
8432
|
-
|
|
8843
|
+
resolve3();
|
|
8433
8844
|
});
|
|
8434
8845
|
} else if (cleanedText) {
|
|
8435
8846
|
callback([cleanedText], lastUsageInfo).catch((err) => {
|
|
8436
8847
|
logger.error("[ServerLlmBackend] Callback error:", err);
|
|
8437
8848
|
reject(err);
|
|
8438
|
-
}).then(() =>
|
|
8849
|
+
}).then(() => resolve3());
|
|
8439
8850
|
} else {
|
|
8440
|
-
|
|
8851
|
+
resolve3();
|
|
8441
8852
|
}
|
|
8442
8853
|
return;
|
|
8443
8854
|
}
|
|
@@ -8497,7 +8908,7 @@ var ServerLlmBackend = class {
|
|
|
8497
8908
|
response.data.on("end", () => {
|
|
8498
8909
|
if (!receivedDone) {
|
|
8499
8910
|
logger.debug("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
|
|
8500
|
-
|
|
8911
|
+
resolve3();
|
|
8501
8912
|
} else {
|
|
8502
8913
|
logger.debug("[ServerLlmBackend] Stream ended, [DONE] handler will resolve");
|
|
8503
8914
|
}
|
|
@@ -8761,10 +9172,13 @@ var ApiClient = class {
|
|
|
8761
9172
|
}
|
|
8762
9173
|
};
|
|
8763
9174
|
|
|
9175
|
+
// src/index.tsx
|
|
9176
|
+
import { isAxiosError as isAxiosError2 } from "axios";
|
|
9177
|
+
|
|
8764
9178
|
// package.json
|
|
8765
9179
|
var package_default = {
|
|
8766
9180
|
name: "@bike4mind/cli",
|
|
8767
|
-
version: "0.2.
|
|
9181
|
+
version: "0.2.2",
|
|
8768
9182
|
type: "module",
|
|
8769
9183
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
8770
9184
|
license: "UNLICENSED",
|
|
@@ -8775,6 +9189,9 @@ var package_default = {
|
|
|
8775
9189
|
},
|
|
8776
9190
|
homepage: "https://github.com/bike4mind/lumina5#readme",
|
|
8777
9191
|
bugs: "https://github.com/bike4mind/lumina5/issues",
|
|
9192
|
+
publishConfig: {
|
|
9193
|
+
access: "public"
|
|
9194
|
+
},
|
|
8778
9195
|
keywords: [
|
|
8779
9196
|
"cli",
|
|
8780
9197
|
"ai",
|
|
@@ -8795,6 +9212,8 @@ var package_default = {
|
|
|
8795
9212
|
dev: "tsx src/index.tsx",
|
|
8796
9213
|
build: "tsup",
|
|
8797
9214
|
typecheck: "tsc --noEmit",
|
|
9215
|
+
test: "vitest run",
|
|
9216
|
+
"test:watch": "vitest",
|
|
8798
9217
|
start: "node dist/index.js",
|
|
8799
9218
|
prepublishOnly: "pnpm build"
|
|
8800
9219
|
},
|
|
@@ -8860,11 +9279,11 @@ var package_default = {
|
|
|
8860
9279
|
zustand: "^4.5.4"
|
|
8861
9280
|
},
|
|
8862
9281
|
devDependencies: {
|
|
8863
|
-
"@bike4mind/agents": "
|
|
8864
|
-
"@bike4mind/common": "
|
|
8865
|
-
"@bike4mind/mcp": "
|
|
8866
|
-
"@bike4mind/services": "
|
|
8867
|
-
"@bike4mind/utils": "
|
|
9282
|
+
"@bike4mind/agents": "workspace:*",
|
|
9283
|
+
"@bike4mind/common": "workspace:*",
|
|
9284
|
+
"@bike4mind/mcp": "workspace:*",
|
|
9285
|
+
"@bike4mind/services": "workspace:*",
|
|
9286
|
+
"@bike4mind/utils": "workspace:*",
|
|
8868
9287
|
"@types/diff": "^5.0.9",
|
|
8869
9288
|
"@types/jsonwebtoken": "^9.0.4",
|
|
8870
9289
|
"@types/node": "^22.9.0",
|
|
@@ -8873,11 +9292,16 @@ var package_default = {
|
|
|
8873
9292
|
"@types/yargs": "^17.0.32",
|
|
8874
9293
|
tsup: "^8.5.1",
|
|
8875
9294
|
tsx: "^4.21.0",
|
|
8876
|
-
typescript: "^5.9.3"
|
|
8877
|
-
|
|
8878
|
-
|
|
9295
|
+
typescript: "^5.9.3",
|
|
9296
|
+
vitest: "^3.2.4"
|
|
9297
|
+
}
|
|
8879
9298
|
};
|
|
8880
9299
|
|
|
9300
|
+
// src/config/constants.ts
|
|
9301
|
+
var USAGE_DAYS = 30;
|
|
9302
|
+
var MODEL_NAME_COLUMN_WIDTH = 18;
|
|
9303
|
+
var USAGE_CACHE_TTL = 5 * 60 * 1e3;
|
|
9304
|
+
|
|
8881
9305
|
// src/index.tsx
|
|
8882
9306
|
process.removeAllListeners("warning");
|
|
8883
9307
|
process.on("warning", (warning) => {
|
|
@@ -8888,6 +9312,7 @@ process.on("warning", (warning) => {
|
|
|
8888
9312
|
});
|
|
8889
9313
|
var exitTimestamp = null;
|
|
8890
9314
|
var EXIT_TIMEOUT_MS = 2e3;
|
|
9315
|
+
var usageCache = null;
|
|
8891
9316
|
function CliApp() {
|
|
8892
9317
|
const { exit } = useApp();
|
|
8893
9318
|
const [state, setState] = useState3({
|
|
@@ -8927,7 +9352,7 @@ function CliApp() {
|
|
|
8927
9352
|
}
|
|
8928
9353
|
await Promise.race([
|
|
8929
9354
|
Promise.all(cleanupTasks),
|
|
8930
|
-
new Promise((
|
|
9355
|
+
new Promise((resolve3) => setTimeout(resolve3, 500))
|
|
8931
9356
|
// 500ms timeout
|
|
8932
9357
|
]);
|
|
8933
9358
|
setTimeout(() => {
|
|
@@ -9061,9 +9486,9 @@ function CliApp() {
|
|
|
9061
9486
|
};
|
|
9062
9487
|
const permissionManager = new PermissionManager(config.trustedTools || []);
|
|
9063
9488
|
const promptFn = (toolName, args, preview) => {
|
|
9064
|
-
return new Promise((
|
|
9489
|
+
return new Promise((resolve3) => {
|
|
9065
9490
|
const canBeTrusted = permissionManager.canBeTrusted(toolName);
|
|
9066
|
-
const prompt = { toolName, args, preview, canBeTrusted, resolve };
|
|
9491
|
+
const prompt = { toolName, args, preview, canBeTrusted, resolve: resolve3 };
|
|
9067
9492
|
setState((prev) => ({
|
|
9068
9493
|
...prev,
|
|
9069
9494
|
permissionManager,
|
|
@@ -9137,7 +9562,7 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
9137
9562
|
llm,
|
|
9138
9563
|
model: modelInfo.id,
|
|
9139
9564
|
tools: allTools,
|
|
9140
|
-
maxIterations:
|
|
9565
|
+
maxIterations: 10,
|
|
9141
9566
|
maxTokens: config.preferences.maxTokens,
|
|
9142
9567
|
temperature: config.preferences.temperature,
|
|
9143
9568
|
systemPrompt: cliSystemPrompt
|
|
@@ -9252,6 +9677,7 @@ Available commands:
|
|
|
9252
9677
|
/login - Authenticate with your B4M account
|
|
9253
9678
|
/logout - Clear authentication and sign out
|
|
9254
9679
|
/whoami - Show current authenticated user
|
|
9680
|
+
/usage - Show credit usage and balance
|
|
9255
9681
|
/save <name> - Save current session
|
|
9256
9682
|
/sessions - List saved sessions
|
|
9257
9683
|
/config - Show configuration
|
|
@@ -9422,17 +9848,114 @@ Tool Permissions:
|
|
|
9422
9848
|
console.log(`Expires: ${new Date(authTokens.expiresAt).toLocaleString()}`);
|
|
9423
9849
|
console.log("");
|
|
9424
9850
|
break;
|
|
9851
|
+
case "usage": {
|
|
9852
|
+
const usageAuthTokens = await state.configStore.getAuthTokens();
|
|
9853
|
+
if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
9854
|
+
console.log("\nNot authenticated. Run /login to authenticate.\n");
|
|
9855
|
+
return;
|
|
9856
|
+
}
|
|
9857
|
+
console.log("\nFetching usage data...");
|
|
9858
|
+
const now = Date.now();
|
|
9859
|
+
let currentCredits;
|
|
9860
|
+
let transactions;
|
|
9861
|
+
if (usageCache && now - usageCache.timestamp < USAGE_CACHE_TTL) {
|
|
9862
|
+
currentCredits = usageCache.data.currentCredits;
|
|
9863
|
+
transactions = usageCache.data.transactions;
|
|
9864
|
+
console.log("(using cached data)\n");
|
|
9865
|
+
} else {
|
|
9866
|
+
try {
|
|
9867
|
+
const config = await state.configStore.get();
|
|
9868
|
+
const apiBaseURL = getApiUrl(config.apiConfig);
|
|
9869
|
+
const apiClient = new ApiClient(apiBaseURL, state.configStore);
|
|
9870
|
+
const [identifyResponse, transactionsResponse] = await Promise.all([
|
|
9871
|
+
apiClient.get("/api/identify"),
|
|
9872
|
+
apiClient.get(`/api/credits/transactions?days=${USAGE_DAYS}`)
|
|
9873
|
+
]);
|
|
9874
|
+
currentCredits = identifyResponse.user.currentCredits;
|
|
9875
|
+
transactions = transactionsResponse || [];
|
|
9876
|
+
usageCache = {
|
|
9877
|
+
data: { currentCredits, transactions },
|
|
9878
|
+
timestamp: now
|
|
9879
|
+
};
|
|
9880
|
+
} catch (error) {
|
|
9881
|
+
if (isAxiosError2(error)) {
|
|
9882
|
+
if (error.response?.status === 401) {
|
|
9883
|
+
console.log("\n\u274C Authentication failed. Run /login to re-authenticate.\n");
|
|
9884
|
+
} else if (error.response?.status === 403) {
|
|
9885
|
+
console.log("\n\u274C Access denied. You may not have permission to view usage data.\n");
|
|
9886
|
+
} else if (error.response?.status === 404) {
|
|
9887
|
+
console.log("\n\u274C Usage endpoint not available on this server.\n");
|
|
9888
|
+
} else if (error.code === "ECONNREFUSED" || error.code === "ENOTFOUND") {
|
|
9889
|
+
console.log("\n\u274C Cannot connect to server. Check your API configuration with /api-info\n");
|
|
9890
|
+
} else {
|
|
9891
|
+
console.log(`
|
|
9892
|
+
\u274C Error fetching usage data: ${error.message}
|
|
9893
|
+
`);
|
|
9894
|
+
}
|
|
9895
|
+
} else if (error instanceof Error) {
|
|
9896
|
+
console.log(`
|
|
9897
|
+
\u274C Error: ${error.message}
|
|
9898
|
+
`);
|
|
9899
|
+
} else {
|
|
9900
|
+
console.log("\n\u274C Unexpected error fetching usage data. Please try again.\n");
|
|
9901
|
+
}
|
|
9902
|
+
break;
|
|
9903
|
+
}
|
|
9904
|
+
}
|
|
9905
|
+
const deductTransactions = transactions.filter(
|
|
9906
|
+
(t) => CREDIT_DEDUCT_TRANSACTION_TYPES.includes(t.type)
|
|
9907
|
+
);
|
|
9908
|
+
const totalCreditsUsed = deductTransactions.reduce((sum2, t) => sum2 + Math.abs(t.credits), 0);
|
|
9909
|
+
const dailyBurnRate = Math.round(totalCreditsUsed / USAGE_DAYS);
|
|
9910
|
+
let daysRemainingStr;
|
|
9911
|
+
if (dailyBurnRate === 0) {
|
|
9912
|
+
daysRemainingStr = "N/A (no usage)";
|
|
9913
|
+
} else {
|
|
9914
|
+
const daysRemaining = Math.floor(currentCredits / dailyBurnRate);
|
|
9915
|
+
daysRemainingStr = `~${daysRemaining.toLocaleString()} days`;
|
|
9916
|
+
}
|
|
9917
|
+
const usageByModel = {};
|
|
9918
|
+
for (const t of deductTransactions) {
|
|
9919
|
+
const modelName = t.metadata?.modelName || ("model" in t ? t.model : null) || "Unknown";
|
|
9920
|
+
usageByModel[modelName] = (usageByModel[modelName] || 0) + Math.abs(t.credits);
|
|
9921
|
+
}
|
|
9922
|
+
const sortedModels = Object.entries(usageByModel).sort((a, b) => b[1] - a[1]);
|
|
9923
|
+
console.log("\n Credit Usage Summary");
|
|
9924
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n");
|
|
9925
|
+
console.log(`Credits Balance: ${currentCredits.toLocaleString()}`);
|
|
9926
|
+
console.log(`Daily Burn Rate: ${dailyBurnRate.toLocaleString()} credits/day`);
|
|
9927
|
+
console.log(`Days Remaining: ${daysRemainingStr}`);
|
|
9928
|
+
if (currentCredits === 0) {
|
|
9929
|
+
console.log("\n\u26A0\uFE0F You have no credits remaining.");
|
|
9930
|
+
console.log("\u{1F4A1} Visit bike4mind.io to purchase more credits.\n");
|
|
9931
|
+
break;
|
|
9932
|
+
}
|
|
9933
|
+
if (sortedModels.length > 0) {
|
|
9934
|
+
console.log(`
|
|
9935
|
+
Usage by Model (Last ${USAGE_DAYS} days):`);
|
|
9936
|
+
for (const [model, credits] of sortedModels) {
|
|
9937
|
+
const percentage = totalCreditsUsed > 0 ? Math.round(credits / totalCreditsUsed * 100) : 0;
|
|
9938
|
+
const paddedModel = model.padEnd(MODEL_NAME_COLUMN_WIDTH);
|
|
9939
|
+
console.log(` ${paddedModel}${credits.toLocaleString()} credits (${percentage}%)`);
|
|
9940
|
+
}
|
|
9941
|
+
} else {
|
|
9942
|
+
console.log(`
|
|
9943
|
+
No usage data available for the last ${USAGE_DAYS} days.`);
|
|
9944
|
+
}
|
|
9945
|
+
console.log("");
|
|
9946
|
+
break;
|
|
9947
|
+
}
|
|
9425
9948
|
default:
|
|
9426
9949
|
console.log(`Unknown command: /${command}`);
|
|
9427
9950
|
console.log("Type /help for available commands");
|
|
9428
9951
|
}
|
|
9429
9952
|
};
|
|
9430
9953
|
if (initError) {
|
|
9431
|
-
return /* @__PURE__ */
|
|
9954
|
+
return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React11.createElement(Text10, null, initError), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
|
|
9432
9955
|
}
|
|
9433
9956
|
if (state.showLoginFlow) {
|
|
9434
9957
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
9435
|
-
return /* @__PURE__ */
|
|
9958
|
+
return /* @__PURE__ */ React11.createElement(
|
|
9436
9959
|
LoginFlow,
|
|
9437
9960
|
{
|
|
9438
9961
|
apiUrl: loginApiUrl,
|
|
@@ -9455,9 +9978,9 @@ Tool Permissions:
|
|
|
9455
9978
|
);
|
|
9456
9979
|
}
|
|
9457
9980
|
if (!isInitialized) {
|
|
9458
|
-
return /* @__PURE__ */
|
|
9981
|
+
return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React11.createElement(Text10, null, "\u{1F680} Initializing..."));
|
|
9459
9982
|
}
|
|
9460
|
-
return /* @__PURE__ */
|
|
9983
|
+
return /* @__PURE__ */ React11.createElement(
|
|
9461
9984
|
App,
|
|
9462
9985
|
{
|
|
9463
9986
|
onMessage: handleMessage,
|
|
@@ -9488,6 +10011,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
9488
10011
|
if (isDevMode) {
|
|
9489
10012
|
console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
9490
10013
|
}
|
|
9491
|
-
render(/* @__PURE__ */
|
|
10014
|
+
render(/* @__PURE__ */ React11.createElement(CliApp, null), {
|
|
9492
10015
|
exitOnCtrlC: false
|
|
9493
10016
|
});
|