@bike4mind/cli 0.2.6-fix-cli-mcp-type-mismatch.17119 → 0.2.7-basic-cli-shortcuts.17131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ImageStore-RQZ7OF7P.js +225 -0
- package/dist/index.js +680 -135
- package/package.json +9 -7
package/dist/index.js
CHANGED
|
@@ -79,13 +79,13 @@ import {
|
|
|
79
79
|
} from "./chunk-PDX44BCA.js";
|
|
80
80
|
|
|
81
81
|
// src/index.tsx
|
|
82
|
-
import
|
|
83
|
-
import { render, Box as Box13, Text as
|
|
82
|
+
import React14, { useState as useState5, useEffect as useEffect3, useCallback, useRef } from "react";
|
|
83
|
+
import { render, Box as Box13, Text as Text13, useApp, useInput as useInput4 } from "ink";
|
|
84
84
|
import { v4 as uuidv49 } from "uuid";
|
|
85
85
|
|
|
86
86
|
// src/components/App.tsx
|
|
87
|
-
import
|
|
88
|
-
import { Box as Box9, Text as
|
|
87
|
+
import React10 from "react";
|
|
88
|
+
import { Box as Box9, Text as Text9, Static } from "ink";
|
|
89
89
|
|
|
90
90
|
// src/components/StatusBar.tsx
|
|
91
91
|
import React from "react";
|
|
@@ -95,27 +95,127 @@ var StatusBar = React.memo(function StatusBar2({ sessionName, model, tokenUsage
|
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
// src/components/InputPrompt.tsx
|
|
98
|
-
import
|
|
99
|
-
import { Box as Box4, Text as
|
|
100
|
-
|
|
98
|
+
import React5, { useState as useState2, useMemo, useEffect } from "react";
|
|
99
|
+
import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
|
|
100
|
+
|
|
101
|
+
// src/components/CustomTextInput.tsx
|
|
102
|
+
import React2, { useState } from "react";
|
|
103
|
+
import { Text as Text2, useInput } from "ink";
|
|
104
|
+
function CustomTextInput({
|
|
105
|
+
value,
|
|
106
|
+
onChange,
|
|
107
|
+
onSubmit,
|
|
108
|
+
placeholder = "",
|
|
109
|
+
showCursor = true
|
|
110
|
+
}) {
|
|
111
|
+
const [cursorOffset, setCursorOffset] = useState(value.length);
|
|
112
|
+
useInput(
|
|
113
|
+
(input, key) => {
|
|
114
|
+
if (key.return && !key.meta && !key.shift) {
|
|
115
|
+
onSubmit(value);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (key.ctrl) {
|
|
119
|
+
switch (input) {
|
|
120
|
+
case "u":
|
|
121
|
+
{
|
|
122
|
+
const lines = value.split("\n");
|
|
123
|
+
let currentLineStart = 0;
|
|
124
|
+
let currentLineIndex = 0;
|
|
125
|
+
let charCount = 0;
|
|
126
|
+
for (let i = 0; i < lines.length; i++) {
|
|
127
|
+
const lineLength = lines[i].length;
|
|
128
|
+
if (charCount + lineLength >= cursorOffset) {
|
|
129
|
+
currentLineIndex = i;
|
|
130
|
+
currentLineStart = charCount;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
charCount += lineLength + 1;
|
|
134
|
+
}
|
|
135
|
+
lines.splice(currentLineIndex, 1);
|
|
136
|
+
const newValue = lines.join("\n");
|
|
137
|
+
onChange(newValue);
|
|
138
|
+
setCursorOffset(Math.min(currentLineStart, newValue.length));
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
case "k":
|
|
142
|
+
onChange(value.slice(0, cursorOffset));
|
|
143
|
+
return;
|
|
144
|
+
case "w":
|
|
145
|
+
{
|
|
146
|
+
const beforeCursor2 = value.slice(0, cursorOffset);
|
|
147
|
+
const afterCursor2 = value.slice(cursorOffset);
|
|
148
|
+
const trimmed = beforeCursor2.trimEnd();
|
|
149
|
+
const lastSpace = trimmed.lastIndexOf(" ");
|
|
150
|
+
const newBefore = lastSpace === -1 ? "" : trimmed.slice(0, lastSpace + 1);
|
|
151
|
+
const newValue = newBefore + afterCursor2;
|
|
152
|
+
onChange(newValue);
|
|
153
|
+
setCursorOffset(newBefore.length);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
case "a":
|
|
157
|
+
setCursorOffset(0);
|
|
158
|
+
return;
|
|
159
|
+
case "e":
|
|
160
|
+
setCursorOffset(value.length);
|
|
161
|
+
return;
|
|
162
|
+
case "l":
|
|
163
|
+
onChange("");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (key.backspace || key.delete) {
|
|
168
|
+
if (cursorOffset > 0) {
|
|
169
|
+
const newValue = value.slice(0, cursorOffset - 1) + value.slice(cursorOffset);
|
|
170
|
+
onChange(newValue);
|
|
171
|
+
setCursorOffset(cursorOffset - 1);
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (key.leftArrow) {
|
|
176
|
+
setCursorOffset(Math.max(0, cursorOffset - 1));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (key.rightArrow) {
|
|
180
|
+
setCursorOffset(Math.min(value.length, cursorOffset + 1));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!key.ctrl && !key.meta && input.length === 1) {
|
|
184
|
+
const sanitizedInput = input === "\r" ? "\n" : input;
|
|
185
|
+
const newValue = value.slice(0, cursorOffset) + sanitizedInput + value.slice(cursorOffset);
|
|
186
|
+
onChange(newValue);
|
|
187
|
+
setCursorOffset(cursorOffset + 1);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{ isActive: true }
|
|
191
|
+
);
|
|
192
|
+
const hasValue = value.length > 0;
|
|
193
|
+
if (!hasValue) {
|
|
194
|
+
return /* @__PURE__ */ React2.createElement(Text2, null, showCursor ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, { inverse: true }, " "), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, placeholder)) : /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, placeholder));
|
|
195
|
+
}
|
|
196
|
+
const beforeCursor = value.slice(0, cursorOffset);
|
|
197
|
+
const cursorChar = value[cursorOffset] || " ";
|
|
198
|
+
const afterCursor = value.slice(cursorOffset + 1);
|
|
199
|
+
return /* @__PURE__ */ React2.createElement(Text2, null, beforeCursor, showCursor && /* @__PURE__ */ React2.createElement(Text2, { inverse: true }, cursorChar), !showCursor && cursorChar, afterCursor);
|
|
200
|
+
}
|
|
101
201
|
|
|
102
202
|
// src/components/CommandAutocomplete.tsx
|
|
103
|
-
import
|
|
104
|
-
import { Box as Box2, Text as
|
|
203
|
+
import React3 from "react";
|
|
204
|
+
import { Box as Box2, Text as Text3 } from "ink";
|
|
105
205
|
function CommandAutocomplete({ commands, selectedIndex }) {
|
|
106
206
|
if (commands.length === 0) {
|
|
107
|
-
return /* @__PURE__ */
|
|
207
|
+
return /* @__PURE__ */ React3.createElement(Box2, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No matching commands"));
|
|
108
208
|
}
|
|
109
|
-
return /* @__PURE__ */
|
|
209
|
+
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, commands.length === 1 ? "1 command" : `${commands.length} commands`, " \u2022 Use \u2191\u2193 to navigate, Enter to select")), commands.map((cmd, index) => {
|
|
110
210
|
const args = cmd.args ? ` ${cmd.args}` : "";
|
|
111
211
|
const isSelected = index === selectedIndex;
|
|
112
|
-
return /* @__PURE__ */
|
|
212
|
+
return /* @__PURE__ */ React3.createElement(Box2, { key: cmd.name, marginLeft: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "\u25B8 " : " ", "/", cmd.name, args, " - ", cmd.description));
|
|
113
213
|
}));
|
|
114
214
|
}
|
|
115
215
|
|
|
116
216
|
// src/components/FileAutocomplete.tsx
|
|
117
|
-
import
|
|
118
|
-
import { Box as Box3, Text as
|
|
217
|
+
import React4 from "react";
|
|
218
|
+
import { Box as Box3, Text as Text4 } from "ink";
|
|
119
219
|
|
|
120
220
|
// src/utils/fileSearch.ts
|
|
121
221
|
import * as fs from "fs";
|
|
@@ -304,14 +404,14 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
|
304
404
|
// src/components/FileAutocomplete.tsx
|
|
305
405
|
function FileAutocomplete({ files, selectedIndex, query }) {
|
|
306
406
|
if (files.length === 0) {
|
|
307
|
-
return /* @__PURE__ */
|
|
407
|
+
return /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No matching files", query ? ` for "${query}"` : ""));
|
|
308
408
|
}
|
|
309
|
-
return /* @__PURE__ */
|
|
409
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, files.length === 1 ? "1 match" : `${files.length} matches`, " - Use up/down to navigate, Tab to select")), files.map((file, index) => {
|
|
310
410
|
const isSelected = index === selectedIndex;
|
|
311
411
|
const icon = file.isDirectory ? "[folder]" : "[file] ";
|
|
312
412
|
const sizeDisplay = file.isDirectory ? "" : file.size !== void 0 ? ` (${formatFileSize(file.size)})` : "";
|
|
313
413
|
const pathDisplay = file.isDirectory ? `${file.path}/` : file.path;
|
|
314
|
-
return /* @__PURE__ */
|
|
414
|
+
return /* @__PURE__ */ React4.createElement(Box3, { key: file.path, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "> " : " "), /* @__PURE__ */ React4.createElement(Text4, { color: file.isDirectory ? "yellow" : isSelected ? "cyan" : void 0, bold: isSelected }, icon), /* @__PURE__ */ React4.createElement(Text4, { color: isSelected ? "cyan" : void 0, bold: isSelected }, " ", pathDisplay), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, sizeDisplay));
|
|
315
415
|
}));
|
|
316
416
|
}
|
|
317
417
|
|
|
@@ -413,6 +513,202 @@ function searchCommands(query) {
|
|
|
413
513
|
return results.map((result) => result.item);
|
|
414
514
|
}
|
|
415
515
|
|
|
516
|
+
// src/utils/imageDetector.ts
|
|
517
|
+
import { readFileSync, existsSync, statSync as statSync2 } from "fs";
|
|
518
|
+
import { extname as extname2 } from "path";
|
|
519
|
+
var ImageInputDetector = class {
|
|
520
|
+
static {
|
|
521
|
+
this.MAX_IMAGE_SIZE = 10 * 1024 * 1024;
|
|
522
|
+
}
|
|
523
|
+
static {
|
|
524
|
+
// 10MB
|
|
525
|
+
this.SUPPORTED_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".webp"];
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Check if input contains image data
|
|
529
|
+
*/
|
|
530
|
+
static containsImageData(input) {
|
|
531
|
+
if (input.includes("\x1B]1337;File=")) {
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
if (input.includes("\x1B_G")) {
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
537
|
+
if (input.includes("\x1BP")) {
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
if (input.match(/data:image\/(png|jpeg|jpg|gif|webp);base64,/)) {
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
if (this.looksLikeFilePath(input)) {
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Extract image data from terminal input
|
|
550
|
+
*/
|
|
551
|
+
static extractImageData(input) {
|
|
552
|
+
const iterm2 = this.extractITerm2Image(input);
|
|
553
|
+
if (iterm2) return iterm2;
|
|
554
|
+
const kitty = this.extractKittyImage(input);
|
|
555
|
+
if (kitty) return kitty;
|
|
556
|
+
const sixel = this.extractSixelImage(input);
|
|
557
|
+
if (sixel) return sixel;
|
|
558
|
+
const base64 = this.extractBase64Image(input);
|
|
559
|
+
if (base64) return base64;
|
|
560
|
+
const fileDrop = this.extractDroppedFile(input);
|
|
561
|
+
if (fileDrop) return fileDrop;
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Extract iTerm2 inline image
|
|
566
|
+
* Format: ESC]1337;File=[args]:base64data^G
|
|
567
|
+
*/
|
|
568
|
+
static extractITerm2Image(input) {
|
|
569
|
+
const match = input.match(/\x1b\]1337;File=([^:]*):([^\x07]+)\x07/);
|
|
570
|
+
if (!match) return null;
|
|
571
|
+
try {
|
|
572
|
+
const args = match[1];
|
|
573
|
+
const base64Data = match[2];
|
|
574
|
+
const data = Buffer.from(base64Data, "base64");
|
|
575
|
+
let format = "png";
|
|
576
|
+
const nameMatch = args.match(/name=([^;]+)/);
|
|
577
|
+
if (nameMatch) {
|
|
578
|
+
const ext = extname2(nameMatch[1]).substring(1).toLowerCase();
|
|
579
|
+
if (ext && this.SUPPORTED_EXTENSIONS.includes(`.${ext}`)) {
|
|
580
|
+
format = ext === "jpeg" ? "jpg" : ext;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return {
|
|
584
|
+
type: "paste",
|
|
585
|
+
data,
|
|
586
|
+
format,
|
|
587
|
+
protocol: "iterm2"
|
|
588
|
+
};
|
|
589
|
+
} catch (error) {
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Extract Kitty graphics protocol image
|
|
595
|
+
* Format: ESC_Ga=T,f=100;base64data ESC\
|
|
596
|
+
*/
|
|
597
|
+
static extractKittyImage(input) {
|
|
598
|
+
const match = input.match(/\x1b_G([^;]*);([^\x1b]+)\x1b\\/);
|
|
599
|
+
if (!match) return null;
|
|
600
|
+
try {
|
|
601
|
+
const params = match[1];
|
|
602
|
+
const base64Data = match[2];
|
|
603
|
+
const data = Buffer.from(base64Data, "base64");
|
|
604
|
+
let format = "png";
|
|
605
|
+
const formatMatch = params.match(/f=(\d+)/);
|
|
606
|
+
if (formatMatch) {
|
|
607
|
+
const formatCode = parseInt(formatMatch[1], 10);
|
|
608
|
+
if (formatCode === 100) format = "jpg";
|
|
609
|
+
}
|
|
610
|
+
return {
|
|
611
|
+
type: "paste",
|
|
612
|
+
data,
|
|
613
|
+
format,
|
|
614
|
+
protocol: "kitty"
|
|
615
|
+
};
|
|
616
|
+
} catch (error) {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Extract Sixel image
|
|
622
|
+
* Format: ESC P q ... ESC \
|
|
623
|
+
*/
|
|
624
|
+
static extractSixelImage(input) {
|
|
625
|
+
const match = input.match(/\x1bP([^q]*q[^\x1b]+)\x1b\\/);
|
|
626
|
+
if (!match) return null;
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Extract base64 data URL
|
|
631
|
+
* Format: data:image/png;base64,iVBORw0KGgo...
|
|
632
|
+
*/
|
|
633
|
+
static extractBase64Image(input) {
|
|
634
|
+
const match = input.match(/data:image\/(png|jpeg|jpg|gif|webp);base64,([A-Za-z0-9+/=]+)/);
|
|
635
|
+
if (!match) return null;
|
|
636
|
+
try {
|
|
637
|
+
const format = match[1] === "jpeg" ? "jpg" : match[1];
|
|
638
|
+
const base64Data = match[2];
|
|
639
|
+
const data = Buffer.from(base64Data, "base64");
|
|
640
|
+
return {
|
|
641
|
+
type: "paste",
|
|
642
|
+
data,
|
|
643
|
+
format,
|
|
644
|
+
protocol: "base64"
|
|
645
|
+
};
|
|
646
|
+
} catch (error) {
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Extract dropped file path and read image
|
|
652
|
+
*/
|
|
653
|
+
static extractDroppedFile(input) {
|
|
654
|
+
if (!this.looksLikeFilePath(input)) return null;
|
|
655
|
+
let filepath = input.trim();
|
|
656
|
+
filepath = filepath.replace(/^["']|["']$/g, "");
|
|
657
|
+
filepath = filepath.replace(/\\\s/g, " ");
|
|
658
|
+
if (!existsSync(filepath)) return null;
|
|
659
|
+
const stats = statSync2(filepath);
|
|
660
|
+
if (!stats.isFile()) return null;
|
|
661
|
+
const ext = extname2(filepath).toLowerCase();
|
|
662
|
+
if (!this.SUPPORTED_EXTENSIONS.includes(ext)) return null;
|
|
663
|
+
if (stats.size > this.MAX_IMAGE_SIZE) return null;
|
|
664
|
+
try {
|
|
665
|
+
const data = readFileSync(filepath);
|
|
666
|
+
const format = ext.substring(1) === "jpeg" ? "jpg" : ext.substring(1);
|
|
667
|
+
const filename = filepath.split("/").pop() || "image";
|
|
668
|
+
return {
|
|
669
|
+
type: "drop",
|
|
670
|
+
data,
|
|
671
|
+
format,
|
|
672
|
+
filename,
|
|
673
|
+
filepath
|
|
674
|
+
};
|
|
675
|
+
} catch (error) {
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Check if string looks like a file path
|
|
681
|
+
*/
|
|
682
|
+
static looksLikeFilePath(input) {
|
|
683
|
+
const trimmed = input.trim();
|
|
684
|
+
if (trimmed.startsWith("/") || trimmed.match(/^[a-zA-Z]:\\/)) {
|
|
685
|
+
return true;
|
|
686
|
+
}
|
|
687
|
+
if (trimmed.startsWith("~")) {
|
|
688
|
+
return true;
|
|
689
|
+
}
|
|
690
|
+
if (this.SUPPORTED_EXTENSIONS.some((ext) => trimmed.toLowerCase().endsWith(ext))) {
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Remove image data from input string, leaving only text
|
|
697
|
+
*/
|
|
698
|
+
static stripImageData(input) {
|
|
699
|
+
let cleaned = input;
|
|
700
|
+
cleaned = cleaned.replace(/\x1b\]1337;File=[^:]*:[^\x07]+\x07/g, "");
|
|
701
|
+
cleaned = cleaned.replace(/\x1b_G[^;]*;[^\x1b]+\x1b\\/g, "");
|
|
702
|
+
cleaned = cleaned.replace(/\x1bP[^q]*q[^\x1b]+\x1b\\/g, "");
|
|
703
|
+
cleaned = cleaned.replace(/data:image\/(png|jpeg|jpg|gif|webp);base64,[A-Za-z0-9+/=]+/g, "");
|
|
704
|
+
const filepath = this.extractDroppedFile(cleaned);
|
|
705
|
+
if (filepath) {
|
|
706
|
+
cleaned = cleaned.replace(filepath.filepath, "");
|
|
707
|
+
}
|
|
708
|
+
return cleaned.trim();
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
416
712
|
// src/components/InputPrompt.tsx
|
|
417
713
|
function findAtTrigger(value) {
|
|
418
714
|
for (let i = value.length - 1; i >= 0; i--) {
|
|
@@ -429,14 +725,13 @@ function findAtTrigger(value) {
|
|
|
429
725
|
}
|
|
430
726
|
return null;
|
|
431
727
|
}
|
|
432
|
-
function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
433
|
-
const [value, setValue] =
|
|
434
|
-
const [selectedIndex, setSelectedIndex] =
|
|
435
|
-
const [historyIndex, setHistoryIndex] =
|
|
436
|
-
const [tempInput, setTempInput] =
|
|
437
|
-
const [
|
|
438
|
-
const [
|
|
439
|
-
const [fileSelectedIndex, setFileSelectedIndex] = useState(0);
|
|
728
|
+
function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = [] }) {
|
|
729
|
+
const [value, setValue] = useState2("");
|
|
730
|
+
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
731
|
+
const [historyIndex, setHistoryIndex] = useState2(-1);
|
|
732
|
+
const [tempInput, setTempInput] = useState2("");
|
|
733
|
+
const [fileAutocomplete, setFileAutocomplete] = useState2(null);
|
|
734
|
+
const [fileSelectedIndex, setFileSelectedIndex] = useState2(0);
|
|
440
735
|
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
|
|
441
736
|
const commandQuery = shouldShowCommandAutocomplete ? value.slice(1) : "";
|
|
442
737
|
const filteredCommands = useMemo(() => {
|
|
@@ -453,7 +748,7 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
|
453
748
|
useEffect(() => {
|
|
454
749
|
setFileSelectedIndex(0);
|
|
455
750
|
}, [filteredFiles]);
|
|
456
|
-
|
|
751
|
+
useInput2((_input, key) => {
|
|
457
752
|
if (fileAutocomplete?.active && filteredFiles.length > 0) {
|
|
458
753
|
if (key.upArrow) {
|
|
459
754
|
setFileSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredFiles.length - 1);
|
|
@@ -510,7 +805,6 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
|
510
805
|
const pathToInsert = file.isDirectory ? `@${file.path}/` : `@${file.path}`;
|
|
511
806
|
const newValue = beforeAt + pathToInsert + afterQuery;
|
|
512
807
|
setValue(newValue);
|
|
513
|
-
setInputKey((prev) => prev + 1);
|
|
514
808
|
if (file.isDirectory) {
|
|
515
809
|
setFileAutocomplete({
|
|
516
810
|
active: true,
|
|
@@ -537,7 +831,6 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
|
537
831
|
if (selectedCommand.args) {
|
|
538
832
|
setValue(commandText + " ");
|
|
539
833
|
setHistoryIndex(-1);
|
|
540
|
-
setInputKey((prev) => prev + 1);
|
|
541
834
|
return;
|
|
542
835
|
}
|
|
543
836
|
setValue("");
|
|
@@ -550,7 +843,15 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
|
550
843
|
setHistoryIndex(-1);
|
|
551
844
|
setFileAutocomplete(null);
|
|
552
845
|
};
|
|
553
|
-
const handleChange = (newValue) => {
|
|
846
|
+
const handleChange = async (newValue) => {
|
|
847
|
+
if (ImageInputDetector.containsImageData(newValue)) {
|
|
848
|
+
const imageEvent = ImageInputDetector.extractImageData(newValue);
|
|
849
|
+
if (imageEvent && onImageDetected) {
|
|
850
|
+
const placeholder = await onImageDetected(imageEvent.data);
|
|
851
|
+
setValue(`${placeholder} `);
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
554
855
|
setValue(newValue);
|
|
555
856
|
if (historyIndex !== -1) {
|
|
556
857
|
setHistoryIndex(-1);
|
|
@@ -567,29 +868,28 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
|
|
|
567
868
|
setFileAutocomplete(null);
|
|
568
869
|
}
|
|
569
870
|
};
|
|
570
|
-
return /* @__PURE__ */
|
|
571
|
-
|
|
871
|
+
return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(
|
|
872
|
+
CustomTextInput,
|
|
572
873
|
{
|
|
573
|
-
key: inputKey,
|
|
574
874
|
value,
|
|
575
875
|
onChange: handleChange,
|
|
576
876
|
onSubmit: handleSubmit,
|
|
577
877
|
placeholder: disabled ? "Thinking..." : "Type your message, /help for commands, or @file to reference",
|
|
578
878
|
showCursor: !disabled
|
|
579
879
|
}
|
|
580
|
-
)), shouldShowCommandAutocomplete && /* @__PURE__ */
|
|
880
|
+
)), shouldShowCommandAutocomplete && /* @__PURE__ */ React5.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React5.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
|
|
581
881
|
}
|
|
582
882
|
|
|
583
883
|
// src/components/AgentThinking.tsx
|
|
584
|
-
import
|
|
884
|
+
import React7 from "react";
|
|
585
885
|
import { Box as Box6 } from "ink";
|
|
586
886
|
|
|
587
887
|
// src/components/ThoughtStream.tsx
|
|
588
|
-
import
|
|
589
|
-
import { Box as Box5, Text as
|
|
888
|
+
import React6 from "react";
|
|
889
|
+
import { Box as Box5, Text as Text6 } from "ink";
|
|
590
890
|
import Spinner from "ink-spinner";
|
|
591
|
-
var ThoughtStream =
|
|
592
|
-
return /* @__PURE__ */
|
|
891
|
+
var ThoughtStream = React6.memo(function ThoughtStream2({ steps }) {
|
|
892
|
+
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column", gap: 1 }, steps.map((step, index) => /* @__PURE__ */ React6.createElement(Box5, { key: index, flexDirection: "column" }, step.type === "thought" && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, "\u{1F914} Thought: "), /* @__PURE__ */ React6.createElement(Text6, null, step.content)), step.type === "action" && /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, "\u26A1 Action: "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, step.metadata?.toolName || "unknown")), step.metadata?.toolInput && /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, JSON.stringify(step.metadata.toolInput, null, 2)))), step.type === "observation" && /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, "\u{1F4CA} Observation: ")), /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, null, step.content))), step.type === "final_answer" && /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green", bold: true }, "\u2705 Final Answer:", " ")), /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, null, step.content)), step.metadata?.tokenUsage && /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "(", step.metadata.tokenUsage.total, " tokens)"))))), !steps.some((s) => s.type === "final_answer") && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " ", steps.length === 0 ? "Thinking..." : "Processing...")));
|
|
593
893
|
});
|
|
594
894
|
|
|
595
895
|
// src/store/index.ts
|
|
@@ -618,32 +918,32 @@ var useCliStore = create((set) => ({
|
|
|
618
918
|
}));
|
|
619
919
|
|
|
620
920
|
// src/components/AgentThinking.tsx
|
|
621
|
-
var AgentThinking =
|
|
921
|
+
var AgentThinking = React7.memo(function AgentThinking2() {
|
|
622
922
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
623
923
|
const agentSteps = useCliStore((state) => state.agentSteps);
|
|
624
924
|
if (!isThinking) {
|
|
625
925
|
return null;
|
|
626
926
|
}
|
|
627
|
-
return /* @__PURE__ */
|
|
927
|
+
return /* @__PURE__ */ React7.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
|
|
628
928
|
});
|
|
629
929
|
|
|
630
930
|
// src/components/PermissionPrompt.tsx
|
|
631
|
-
import
|
|
632
|
-
import { Box as Box7, Text as
|
|
931
|
+
import React8 from "react";
|
|
932
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
633
933
|
import SelectInput from "ink-select-input";
|
|
634
934
|
function renderDiffPreview(preview) {
|
|
635
935
|
const lines = preview.split("\n");
|
|
636
936
|
return lines.map((line, index) => {
|
|
637
937
|
if (line.startsWith("+")) {
|
|
638
|
-
return /* @__PURE__ */
|
|
938
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "green" }, line);
|
|
639
939
|
}
|
|
640
940
|
if (line.startsWith("-")) {
|
|
641
|
-
return /* @__PURE__ */
|
|
941
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "red" }, line);
|
|
642
942
|
}
|
|
643
943
|
if (line.startsWith("@@")) {
|
|
644
|
-
return /* @__PURE__ */
|
|
944
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "cyan" }, line);
|
|
645
945
|
}
|
|
646
|
-
return /* @__PURE__ */
|
|
946
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, dimColor: true }, line);
|
|
647
947
|
});
|
|
648
948
|
}
|
|
649
949
|
function PermissionPrompt({
|
|
@@ -663,12 +963,12 @@ function PermissionPrompt({
|
|
|
663
963
|
{ label: "\u2717 Deny", value: "deny" }
|
|
664
964
|
];
|
|
665
965
|
const argsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
|
|
666
|
-
return /* @__PURE__ */
|
|
966
|
+
return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Tool: "), /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Action: "), /* @__PURE__ */ React8.createElement(Text7, null, toolDescription)), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Arguments:"), /* @__PURE__ */ React8.createElement(Box7, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Preview:"), /* @__PURE__ */ React8.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(SelectInput, { items, onSelect: (item) => onResponse(item.value) })));
|
|
667
967
|
}
|
|
668
968
|
|
|
669
969
|
// src/components/ConfigEditor.tsx
|
|
670
|
-
import
|
|
671
|
-
import { Box as Box8, Text as
|
|
970
|
+
import React9, { useState as useState3, useMemo as useMemo2 } from "react";
|
|
971
|
+
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
672
972
|
var MAX_ITERATIONS_OPTIONS = [
|
|
673
973
|
{ label: "10", value: 10 },
|
|
674
974
|
{ label: "20", value: 20 },
|
|
@@ -799,10 +1099,10 @@ function buildConfigItems(availableModels) {
|
|
|
799
1099
|
return items;
|
|
800
1100
|
}
|
|
801
1101
|
function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
802
|
-
const [selectedIndex, setSelectedIndex] =
|
|
803
|
-
const [editedConfig, setEditedConfig] =
|
|
804
|
-
const [saveError, setSaveError] =
|
|
805
|
-
const [isSaving, setIsSaving] =
|
|
1102
|
+
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
1103
|
+
const [editedConfig, setEditedConfig] = useState3(config);
|
|
1104
|
+
const [saveError, setSaveError] = useState3(null);
|
|
1105
|
+
const [isSaving, setIsSaving] = useState3(false);
|
|
806
1106
|
const configItems = useMemo2(() => buildConfigItems(availableModels), [availableModels]);
|
|
807
1107
|
const hasChanges = useMemo2(() => {
|
|
808
1108
|
return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences) || config.defaultModel !== editedConfig.defaultModel;
|
|
@@ -822,7 +1122,7 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
822
1122
|
}
|
|
823
1123
|
onClose();
|
|
824
1124
|
};
|
|
825
|
-
|
|
1125
|
+
useInput3((input, key) => {
|
|
826
1126
|
const currentItem = configItems[selectedIndex];
|
|
827
1127
|
if (key.upArrow) {
|
|
828
1128
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : configItems.length - 1);
|
|
@@ -887,13 +1187,13 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
887
1187
|
}
|
|
888
1188
|
return String(value);
|
|
889
1189
|
};
|
|
890
|
-
return /* @__PURE__ */
|
|
1190
|
+
return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, marginY: 1 }, /* @__PURE__ */ React9.createElement(Box8, { justifyContent: "space-between", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "Settings"), isSaving ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "Saving...") : hasChanges ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, color: "yellow" }, "(unsaved changes)") : null), saveError && /* @__PURE__ */ React9.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, "Error: ", saveError)), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, configItems.map((item, index) => {
|
|
891
1191
|
const isSelected = index === selectedIndex;
|
|
892
1192
|
const value = item.getValue(editedConfig);
|
|
893
1193
|
const displayValue = formatValue(item, value);
|
|
894
1194
|
const hasOptions = item.type === "select" || item.type === "boolean" || item.type === "number";
|
|
895
|
-
return /* @__PURE__ */
|
|
896
|
-
})), /* @__PURE__ */
|
|
1195
|
+
return /* @__PURE__ */ React9.createElement(Box8, { key: item.key }, /* @__PURE__ */ React9.createElement(Text8, { color: isSelected ? "cyan" : void 0 }, isSelected ? "> " : " "), /* @__PURE__ */ React9.createElement(Box8, { width: 18 }, /* @__PURE__ */ React9.createElement(Text8, { bold: isSelected, color: isSelected ? "white" : "gray" }, item.label, ":")), /* @__PURE__ */ React9.createElement(Box8, null, hasOptions && isSelected && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "< "), /* @__PURE__ */ React9.createElement(Text8, { bold: isSelected, color: isSelected ? "green" : void 0 }, displayValue), hasOptions && isSelected && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " >")));
|
|
1196
|
+
})), /* @__PURE__ */ React9.createElement(Box8, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u2191/\u2193: Navigate | Space/\u2190/\u2192: Change | q/Esc: Save & Exit")));
|
|
897
1197
|
}
|
|
898
1198
|
|
|
899
1199
|
// src/utils/processFileReferences.ts
|
|
@@ -1015,6 +1315,7 @@ function App({
|
|
|
1015
1315
|
onMessage,
|
|
1016
1316
|
onCommand,
|
|
1017
1317
|
onPermissionResponse,
|
|
1318
|
+
onImageDetected,
|
|
1018
1319
|
commandHistory = [],
|
|
1019
1320
|
config,
|
|
1020
1321
|
availableModels = [],
|
|
@@ -1030,11 +1331,11 @@ function App({
|
|
|
1030
1331
|
const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
|
|
1031
1332
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
1032
1333
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
1033
|
-
const handleSubmit =
|
|
1334
|
+
const handleSubmit = React10.useCallback(
|
|
1034
1335
|
async (input) => {
|
|
1035
1336
|
const trimmed = input.trim();
|
|
1036
1337
|
if (!trimmed) return;
|
|
1037
|
-
if (trimmed.startsWith("/")) {
|
|
1338
|
+
if (trimmed.startsWith("/") && !trimmed.match(/^\/[A-Z]/)) {
|
|
1038
1339
|
const [command, ...args] = trimmed.slice(1).split(" ");
|
|
1039
1340
|
await onCommand(command, args);
|
|
1040
1341
|
return;
|
|
@@ -1059,7 +1360,7 @@ ${errorBlock}`;
|
|
|
1059
1360
|
},
|
|
1060
1361
|
[onMessage, onCommand, setIsThinking]
|
|
1061
1362
|
);
|
|
1062
|
-
return /* @__PURE__ */
|
|
1363
|
+
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), showConfigEditor && config && onSaveConfig ? /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
|
|
1063
1364
|
ConfigEditor,
|
|
1064
1365
|
{
|
|
1065
1366
|
config,
|
|
@@ -1067,7 +1368,7 @@ ${errorBlock}`;
|
|
|
1067
1368
|
onSave: onSaveConfig,
|
|
1068
1369
|
onClose: () => setShowConfigEditor(false)
|
|
1069
1370
|
}
|
|
1070
|
-
)) : permissionPrompt ? /* @__PURE__ */
|
|
1371
|
+
)) : permissionPrompt ? /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
|
|
1071
1372
|
PermissionPrompt,
|
|
1072
1373
|
{
|
|
1073
1374
|
toolName: permissionPrompt.toolName,
|
|
@@ -1076,39 +1377,47 @@ ${errorBlock}`;
|
|
|
1076
1377
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
1077
1378
|
onResponse: onPermissionResponse
|
|
1078
1379
|
}
|
|
1079
|
-
)) : /* @__PURE__ */
|
|
1380
|
+
)) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React10.createElement(Box9, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(MessageItem, { message }))), /* @__PURE__ */ React10.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React10.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
|
|
1381
|
+
InputPrompt,
|
|
1382
|
+
{
|
|
1383
|
+
onSubmit: handleSubmit,
|
|
1384
|
+
onImageDetected,
|
|
1385
|
+
disabled: isThinking,
|
|
1386
|
+
history: commandHistory
|
|
1387
|
+
}
|
|
1388
|
+
))));
|
|
1080
1389
|
}
|
|
1081
1390
|
function MessageItem({ message }) {
|
|
1082
|
-
return /* @__PURE__ */
|
|
1391
|
+
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React10.createElement(Text9, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1083
1392
|
if (step.type === "thought") {
|
|
1084
|
-
return /* @__PURE__ */
|
|
1393
|
+
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
|
|
1085
1394
|
}
|
|
1086
1395
|
if (step.type === "action") {
|
|
1087
1396
|
const toolName = step.metadata?.toolName || "unknown";
|
|
1088
1397
|
const toolInput = step.metadata?.toolInput;
|
|
1089
1398
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1090
1399
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1091
|
-
return /* @__PURE__ */
|
|
1400
|
+
return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1092
1401
|
}
|
|
1093
1402
|
return null;
|
|
1094
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1403
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
1095
1404
|
}
|
|
1096
1405
|
|
|
1097
1406
|
// src/components/MessageList.tsx
|
|
1098
|
-
import
|
|
1099
|
-
import { Box as Box10, Text as
|
|
1407
|
+
import React11 from "react";
|
|
1408
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1100
1409
|
function stripThinkingTags(content) {
|
|
1101
1410
|
return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
1102
1411
|
}
|
|
1103
|
-
var MessageList =
|
|
1412
|
+
var MessageList = React11.memo(
|
|
1104
1413
|
function MessageList2({ messages }) {
|
|
1105
1414
|
if (messages.length === 0) {
|
|
1106
|
-
return /* @__PURE__ */
|
|
1415
|
+
return /* @__PURE__ */ React11.createElement(Box10, { paddingY: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "No messages yet. Type a message to start!"));
|
|
1107
1416
|
}
|
|
1108
|
-
return /* @__PURE__ */
|
|
1417
|
+
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React11.createElement(Box10, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React11.createElement(Box10, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u26A0\uFE0F ", stripThinkingTags(message.content)) : /* @__PURE__ */ React11.createElement(Text10, null, stripThinkingTags(message.content))), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React11.createElement(Box10, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " tools used,", " ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1109
1418
|
if (step.type === "thought") {
|
|
1110
|
-
return /* @__PURE__ */
|
|
1111
|
-
|
|
1419
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text10, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React11.createElement(
|
|
1420
|
+
Text10,
|
|
1112
1421
|
{
|
|
1113
1422
|
dimColor: true
|
|
1114
1423
|
},
|
|
@@ -1120,10 +1429,10 @@ var MessageList = React10.memo(
|
|
|
1120
1429
|
const toolInput = step.metadata?.toolInput;
|
|
1121
1430
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1122
1431
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1123
|
-
return /* @__PURE__ */
|
|
1432
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1124
1433
|
}
|
|
1125
1434
|
return null;
|
|
1126
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1435
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React11.createElement(Box10, { paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
|
|
1127
1436
|
},
|
|
1128
1437
|
(prevProps, nextProps) => {
|
|
1129
1438
|
if (prevProps.messages.length !== nextProps.messages.length) {
|
|
@@ -1134,8 +1443,8 @@ var MessageList = React10.memo(
|
|
|
1134
1443
|
);
|
|
1135
1444
|
|
|
1136
1445
|
// src/components/TrustLocationSelector.tsx
|
|
1137
|
-
import
|
|
1138
|
-
import { Box as Box11, Text as
|
|
1446
|
+
import React12 from "react";
|
|
1447
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1139
1448
|
import SelectInput2 from "ink-select-input";
|
|
1140
1449
|
function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
1141
1450
|
const items = [];
|
|
@@ -1156,19 +1465,19 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
1156
1465
|
const handleSelect = (item) => {
|
|
1157
1466
|
onSelect(item.value);
|
|
1158
1467
|
};
|
|
1159
|
-
return /* @__PURE__ */
|
|
1468
|
+
return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React12.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Where should this rule be saved?")), /* @__PURE__ */ React12.createElement(
|
|
1160
1469
|
SelectInput2,
|
|
1161
1470
|
{
|
|
1162
1471
|
items,
|
|
1163
1472
|
onSelect: handleSelect,
|
|
1164
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1473
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1165
1474
|
}
|
|
1166
|
-
), /* @__PURE__ */
|
|
1475
|
+
), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Ctrl+C to cancel")));
|
|
1167
1476
|
}
|
|
1168
1477
|
|
|
1169
1478
|
// src/components/LoginFlow.tsx
|
|
1170
|
-
import
|
|
1171
|
-
import { Box as Box12, Text as
|
|
1479
|
+
import React13, { useState as useState4, useEffect as useEffect2 } from "react";
|
|
1480
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1172
1481
|
import Spinner2 from "ink-spinner";
|
|
1173
1482
|
import jwt from "jsonwebtoken";
|
|
1174
1483
|
import open from "open";
|
|
@@ -1284,10 +1593,10 @@ var OAuthClient = class {
|
|
|
1284
1593
|
|
|
1285
1594
|
// src/components/LoginFlow.tsx
|
|
1286
1595
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
1287
|
-
const [status, setStatus] =
|
|
1288
|
-
const [deviceFlow, setDeviceFlow] =
|
|
1289
|
-
const [statusMessage, setStatusMessage] =
|
|
1290
|
-
const [error, setError] =
|
|
1596
|
+
const [status, setStatus] = useState4("initiating");
|
|
1597
|
+
const [deviceFlow, setDeviceFlow] = useState4(null);
|
|
1598
|
+
const [statusMessage, setStatusMessage] = useState4("Initiating device authorization...");
|
|
1599
|
+
const [error, setError] = useState4(null);
|
|
1291
1600
|
useEffect2(() => {
|
|
1292
1601
|
const runLoginFlow = async () => {
|
|
1293
1602
|
const oauth = new OAuthClient(apiUrl);
|
|
@@ -1331,15 +1640,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
1331
1640
|
}
|
|
1332
1641
|
}, [deviceFlow, status]);
|
|
1333
1642
|
if (status === "initiating") {
|
|
1334
|
-
return /* @__PURE__ */
|
|
1643
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
|
|
1335
1644
|
}
|
|
1336
1645
|
if (status === "error") {
|
|
1337
|
-
return /* @__PURE__ */
|
|
1646
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { color: "red" }, error)));
|
|
1338
1647
|
}
|
|
1339
1648
|
if (status === "success") {
|
|
1340
|
-
return /* @__PURE__ */
|
|
1649
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "You can now use B4M CLI with your account.")));
|
|
1341
1650
|
}
|
|
1342
|
-
return /* @__PURE__ */
|
|
1651
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "And enter this code when prompted:")), /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
|
|
1343
1652
|
}
|
|
1344
1653
|
|
|
1345
1654
|
// src/storage/SessionStore.ts
|
|
@@ -1461,7 +1770,7 @@ var SessionStore = class {
|
|
|
1461
1770
|
};
|
|
1462
1771
|
|
|
1463
1772
|
// src/storage/ConfigStore.ts
|
|
1464
|
-
import { promises as fs4, existsSync as
|
|
1773
|
+
import { promises as fs4, existsSync as existsSync3 } from "fs";
|
|
1465
1774
|
import path4 from "path";
|
|
1466
1775
|
import { homedir as homedir2 } from "os";
|
|
1467
1776
|
import { v4 as uuidv4 } from "uuid";
|
|
@@ -1588,7 +1897,7 @@ function findProjectConfigDir(startDir = process.cwd()) {
|
|
|
1588
1897
|
while (currentDir !== root) {
|
|
1589
1898
|
const gitPath = path4.join(currentDir, ".git");
|
|
1590
1899
|
try {
|
|
1591
|
-
if (
|
|
1900
|
+
if (existsSync3(gitPath)) {
|
|
1592
1901
|
return currentDir;
|
|
1593
1902
|
}
|
|
1594
1903
|
} catch {
|
|
@@ -2196,7 +2505,7 @@ var ReActAgent = class extends EventEmitter {
|
|
|
2196
2505
|
/**
|
|
2197
2506
|
* Run the agent to completion
|
|
2198
2507
|
*
|
|
2199
|
-
* @param query - The user's question or task
|
|
2508
|
+
* @param query - The user's question or task (can be text or multimodal content with images)
|
|
2200
2509
|
* @param options - Optional overrides for this run
|
|
2201
2510
|
* @returns Agent result with final answer and all steps
|
|
2202
2511
|
*/
|
|
@@ -7673,7 +7982,7 @@ var planetVisibilityTool = {
|
|
|
7673
7982
|
|
|
7674
7983
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
|
|
7675
7984
|
import { promises as fs7 } from "fs";
|
|
7676
|
-
import { existsSync as
|
|
7985
|
+
import { existsSync as existsSync4, statSync as statSync4 } from "fs";
|
|
7677
7986
|
import path6 from "path";
|
|
7678
7987
|
var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
|
|
7679
7988
|
async function readFileContent(params) {
|
|
@@ -7684,10 +7993,10 @@ async function readFileContent(params) {
|
|
|
7684
7993
|
if (!resolvedPath.startsWith(cwd)) {
|
|
7685
7994
|
throw new Error(`Access denied: Cannot read files outside of current working directory`);
|
|
7686
7995
|
}
|
|
7687
|
-
if (!
|
|
7996
|
+
if (!existsSync4(resolvedPath)) {
|
|
7688
7997
|
throw new Error(`File not found: ${filePath}`);
|
|
7689
7998
|
}
|
|
7690
|
-
const stats =
|
|
7999
|
+
const stats = statSync4(resolvedPath);
|
|
7691
8000
|
if (stats.isDirectory()) {
|
|
7692
8001
|
throw new Error(`Path is a directory, not a file: ${filePath}`);
|
|
7693
8002
|
}
|
|
@@ -7730,7 +8039,7 @@ var fileReadTool = {
|
|
|
7730
8039
|
context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
|
|
7731
8040
|
try {
|
|
7732
8041
|
const content = await readFileContent(params);
|
|
7733
|
-
const stats =
|
|
8042
|
+
const stats = statSync4(path6.resolve(process.cwd(), path6.normalize(params.path)));
|
|
7734
8043
|
context.logger.info("\u2705 FileRead: Success", {
|
|
7735
8044
|
path: params.path,
|
|
7736
8045
|
size: stats.size,
|
|
@@ -7770,7 +8079,7 @@ var fileReadTool = {
|
|
|
7770
8079
|
|
|
7771
8080
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
|
|
7772
8081
|
import { promises as fs8 } from "fs";
|
|
7773
|
-
import { existsSync as
|
|
8082
|
+
import { existsSync as existsSync5 } from "fs";
|
|
7774
8083
|
import path7 from "path";
|
|
7775
8084
|
async function createFile(params) {
|
|
7776
8085
|
const { path: filePath, content, createDirectories = true } = params;
|
|
@@ -7780,7 +8089,7 @@ async function createFile(params) {
|
|
|
7780
8089
|
if (!resolvedPath.startsWith(cwd)) {
|
|
7781
8090
|
throw new Error(`Access denied: Cannot create files outside of current working directory`);
|
|
7782
8091
|
}
|
|
7783
|
-
const fileExists =
|
|
8092
|
+
const fileExists = existsSync5(resolvedPath);
|
|
7784
8093
|
const action = fileExists ? "overwritten" : "created";
|
|
7785
8094
|
if (createDirectories) {
|
|
7786
8095
|
const dir = path7.dirname(resolvedPath);
|
|
@@ -7798,7 +8107,7 @@ var createFileTool = {
|
|
|
7798
8107
|
implementation: (context) => ({
|
|
7799
8108
|
toolFn: async (value) => {
|
|
7800
8109
|
const params = value;
|
|
7801
|
-
const fileExists =
|
|
8110
|
+
const fileExists = existsSync5(path7.resolve(process.cwd(), path7.normalize(params.path)));
|
|
7802
8111
|
context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
|
|
7803
8112
|
path: params.path,
|
|
7804
8113
|
size: params.content.length
|
|
@@ -7839,7 +8148,7 @@ var createFileTool = {
|
|
|
7839
8148
|
|
|
7840
8149
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
|
|
7841
8150
|
import { readdir } from "fs/promises";
|
|
7842
|
-
import { statSync as
|
|
8151
|
+
import { statSync as statSync5 } from "fs";
|
|
7843
8152
|
import path8 from "path";
|
|
7844
8153
|
function matchGlob(filePath, pattern) {
|
|
7845
8154
|
let regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
|
|
@@ -7887,7 +8196,7 @@ async function listFiles(params) {
|
|
|
7887
8196
|
for (const match of matches) {
|
|
7888
8197
|
try {
|
|
7889
8198
|
const fullPath = path8.join(targetCwd, match);
|
|
7890
|
-
const stats =
|
|
8199
|
+
const stats = statSync5(fullPath);
|
|
7891
8200
|
filesInfo.push({
|
|
7892
8201
|
path: match,
|
|
7893
8202
|
size: stats.size,
|
|
@@ -7968,7 +8277,7 @@ var globFilesTool = {
|
|
|
7968
8277
|
|
|
7969
8278
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
|
|
7970
8279
|
import { readdir as readdir2 } from "fs/promises";
|
|
7971
|
-
import { readFileSync as
|
|
8280
|
+
import { readFileSync as readFileSync3, statSync as statSync6 } from "fs";
|
|
7972
8281
|
import path9 from "path";
|
|
7973
8282
|
var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
|
|
7974
8283
|
function matchGlob2(filePath, pattern) {
|
|
@@ -8020,7 +8329,7 @@ async function searchFiles2(params) {
|
|
|
8020
8329
|
for (const match of filesToSearch) {
|
|
8021
8330
|
const fullPath = path9.join(targetCwd, match);
|
|
8022
8331
|
try {
|
|
8023
|
-
const stats =
|
|
8332
|
+
const stats = statSync6(fullPath);
|
|
8024
8333
|
if (stats.isDirectory() || stats.size > MAX_FILE_SIZE3) {
|
|
8025
8334
|
continue;
|
|
8026
8335
|
}
|
|
@@ -8044,7 +8353,7 @@ async function searchFiles2(params) {
|
|
|
8044
8353
|
filesSearched++;
|
|
8045
8354
|
const fullPath = path9.join(targetCwd, file);
|
|
8046
8355
|
try {
|
|
8047
|
-
const content =
|
|
8356
|
+
const content = readFileSync3(fullPath, "utf-8");
|
|
8048
8357
|
const lines = content.split("\n");
|
|
8049
8358
|
for (let i = 0; i < lines.length; i++) {
|
|
8050
8359
|
if (matches.length >= maxResults) {
|
|
@@ -8178,7 +8487,7 @@ var grepSearchTool = {
|
|
|
8178
8487
|
|
|
8179
8488
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
|
|
8180
8489
|
import { promises as fs9 } from "fs";
|
|
8181
|
-
import { existsSync as
|
|
8490
|
+
import { existsSync as existsSync6, statSync as statSync7 } from "fs";
|
|
8182
8491
|
import path10 from "path";
|
|
8183
8492
|
async function deleteFile(params) {
|
|
8184
8493
|
const { path: filePath, recursive = false } = params;
|
|
@@ -8188,10 +8497,10 @@ async function deleteFile(params) {
|
|
|
8188
8497
|
if (!resolvedPath.startsWith(cwd)) {
|
|
8189
8498
|
throw new Error(`Access denied: Cannot delete files outside of current working directory`);
|
|
8190
8499
|
}
|
|
8191
|
-
if (!
|
|
8500
|
+
if (!existsSync6(resolvedPath)) {
|
|
8192
8501
|
throw new Error(`File or directory not found: ${filePath}`);
|
|
8193
8502
|
}
|
|
8194
|
-
const stats =
|
|
8503
|
+
const stats = statSync7(resolvedPath);
|
|
8195
8504
|
const isDirectory = stats.isDirectory();
|
|
8196
8505
|
const size = stats.size;
|
|
8197
8506
|
if (isDirectory && !recursive) {
|
|
@@ -8212,7 +8521,7 @@ var deleteFileTool = {
|
|
|
8212
8521
|
toolFn: async (value) => {
|
|
8213
8522
|
const params = value;
|
|
8214
8523
|
const resolvedPath = path10.resolve(process.cwd(), path10.normalize(params.path));
|
|
8215
|
-
const isDirectory =
|
|
8524
|
+
const isDirectory = existsSync6(resolvedPath) && statSync7(resolvedPath).isDirectory();
|
|
8216
8525
|
context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
|
|
8217
8526
|
path: params.path,
|
|
8218
8527
|
recursive: params.recursive
|
|
@@ -8502,10 +8811,10 @@ import jwt2 from "jsonwebtoken";
|
|
|
8502
8811
|
// src/utils/diffPreview.ts
|
|
8503
8812
|
import * as Diff from "diff";
|
|
8504
8813
|
import { readFile } from "fs/promises";
|
|
8505
|
-
import { existsSync as
|
|
8814
|
+
import { existsSync as existsSync7 } from "fs";
|
|
8506
8815
|
async function generateFileDiffPreview(args) {
|
|
8507
8816
|
try {
|
|
8508
|
-
if (!
|
|
8817
|
+
if (!existsSync7(args.path)) {
|
|
8509
8818
|
const lines2 = args.content.split("\n");
|
|
8510
8819
|
const preview = lines2.slice(0, 20).join("\n");
|
|
8511
8820
|
const hasMore = lines2.length > 20;
|
|
@@ -8535,7 +8844,7 @@ ${preview}${hasMore ? `
|
|
|
8535
8844
|
}
|
|
8536
8845
|
async function generateFileDeletePreview(args) {
|
|
8537
8846
|
try {
|
|
8538
|
-
if (!
|
|
8847
|
+
if (!existsSync7(args.path)) {
|
|
8539
8848
|
return `[File does not exist: ${args.path}]`;
|
|
8540
8849
|
}
|
|
8541
8850
|
const stats = await import("fs/promises").then((fs11) => fs11.stat(args.path));
|
|
@@ -9197,7 +9506,7 @@ var logger = Logger2.getInstance();
|
|
|
9197
9506
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
9198
9507
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
9199
9508
|
import path12 from "path";
|
|
9200
|
-
import { existsSync as
|
|
9509
|
+
import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
|
|
9201
9510
|
var MCPClient = class {
|
|
9202
9511
|
// Note: This class handles MCP server communication with repository filtering
|
|
9203
9512
|
mcp;
|
|
@@ -9248,7 +9557,7 @@ var MCPClient = class {
|
|
|
9248
9557
|
path12.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
9249
9558
|
path12.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
9250
9559
|
];
|
|
9251
|
-
const serverScriptPath = candidatePaths.find((p) =>
|
|
9560
|
+
const serverScriptPath = candidatePaths.find((p) => existsSync8(p));
|
|
9252
9561
|
if (!serverScriptPath) {
|
|
9253
9562
|
const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
9254
9563
|
console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
|
|
@@ -9446,6 +9755,190 @@ var McpManager = class {
|
|
|
9446
9755
|
}
|
|
9447
9756
|
};
|
|
9448
9757
|
|
|
9758
|
+
// src/utils/imageRenderer.ts
|
|
9759
|
+
var ImageRenderer = class {
|
|
9760
|
+
constructor() {
|
|
9761
|
+
this.imageCounter = 0;
|
|
9762
|
+
this.placeholderMap = /* @__PURE__ */ new Map();
|
|
9763
|
+
}
|
|
9764
|
+
/**
|
|
9765
|
+
* Generate placeholder for a new image
|
|
9766
|
+
*/
|
|
9767
|
+
createPlaceholder(hash) {
|
|
9768
|
+
const existing = this.placeholderMap.get(hash);
|
|
9769
|
+
if (existing) {
|
|
9770
|
+
return existing.placeholder;
|
|
9771
|
+
}
|
|
9772
|
+
this.imageCounter++;
|
|
9773
|
+
const placeholder = `[Image ${this.imageCounter}]`;
|
|
9774
|
+
this.placeholderMap.set(hash, {
|
|
9775
|
+
hash,
|
|
9776
|
+
number: this.imageCounter,
|
|
9777
|
+
placeholder
|
|
9778
|
+
});
|
|
9779
|
+
return placeholder;
|
|
9780
|
+
}
|
|
9781
|
+
/**
|
|
9782
|
+
* Get all image hashes from a message containing placeholders
|
|
9783
|
+
*/
|
|
9784
|
+
extractImageHashes(message) {
|
|
9785
|
+
const hashes = [];
|
|
9786
|
+
const placeholderRegex = /\[Image \d+\]/g;
|
|
9787
|
+
const matches = message.match(placeholderRegex);
|
|
9788
|
+
if (!matches) return hashes;
|
|
9789
|
+
for (const [hash, data] of this.placeholderMap.entries()) {
|
|
9790
|
+
if (matches.includes(data.placeholder)) {
|
|
9791
|
+
hashes.push(hash);
|
|
9792
|
+
}
|
|
9793
|
+
}
|
|
9794
|
+
return hashes;
|
|
9795
|
+
}
|
|
9796
|
+
/**
|
|
9797
|
+
* Get hash by placeholder text
|
|
9798
|
+
*/
|
|
9799
|
+
getHashFromPlaceholder(placeholder) {
|
|
9800
|
+
for (const [hash, data] of this.placeholderMap.entries()) {
|
|
9801
|
+
if (data.placeholder === placeholder) {
|
|
9802
|
+
return hash;
|
|
9803
|
+
}
|
|
9804
|
+
}
|
|
9805
|
+
return null;
|
|
9806
|
+
}
|
|
9807
|
+
};
|
|
9808
|
+
|
|
9809
|
+
// src/utils/messageBuilder.ts
|
|
9810
|
+
var MessageBuilder = class {
|
|
9811
|
+
constructor(imageStore, imageRenderer) {
|
|
9812
|
+
this.imageStore = imageStore;
|
|
9813
|
+
this.imageRenderer = imageRenderer;
|
|
9814
|
+
}
|
|
9815
|
+
/**
|
|
9816
|
+
* Build a multimodal message from user input
|
|
9817
|
+
* Extracts image placeholders and creates proper message structure with base64-encoded images
|
|
9818
|
+
*/
|
|
9819
|
+
async buildMessage(userInput, role = "user") {
|
|
9820
|
+
const imageHashes = this.imageRenderer.extractImageHashes(userInput);
|
|
9821
|
+
if (imageHashes.length === 0) {
|
|
9822
|
+
return {
|
|
9823
|
+
message: {
|
|
9824
|
+
role,
|
|
9825
|
+
content: userInput
|
|
9826
|
+
},
|
|
9827
|
+
uploadedImages: []
|
|
9828
|
+
};
|
|
9829
|
+
}
|
|
9830
|
+
const content = [];
|
|
9831
|
+
const parts = this.splitMessageByPlaceholders(userInput);
|
|
9832
|
+
for (const part of parts) {
|
|
9833
|
+
if (part.type === "text" && part.content.trim()) {
|
|
9834
|
+
content.push({
|
|
9835
|
+
type: "text",
|
|
9836
|
+
text: part.content
|
|
9837
|
+
});
|
|
9838
|
+
} else if (part.type === "image") {
|
|
9839
|
+
const hash = this.imageRenderer.getHashFromPlaceholder(part.content);
|
|
9840
|
+
if (hash) {
|
|
9841
|
+
const imageData = this.imageStore.readImage(hash);
|
|
9842
|
+
const metadata = this.imageStore.getMetadata(hash);
|
|
9843
|
+
if (imageData && metadata) {
|
|
9844
|
+
const base64Data = imageData.toString("base64");
|
|
9845
|
+
const mediaType = this.getMediaType(metadata.format);
|
|
9846
|
+
content.push({
|
|
9847
|
+
type: "image",
|
|
9848
|
+
source: {
|
|
9849
|
+
type: "base64",
|
|
9850
|
+
media_type: mediaType,
|
|
9851
|
+
data: base64Data
|
|
9852
|
+
}
|
|
9853
|
+
});
|
|
9854
|
+
}
|
|
9855
|
+
}
|
|
9856
|
+
}
|
|
9857
|
+
}
|
|
9858
|
+
return {
|
|
9859
|
+
message: {
|
|
9860
|
+
role,
|
|
9861
|
+
content
|
|
9862
|
+
},
|
|
9863
|
+
uploadedImages: []
|
|
9864
|
+
};
|
|
9865
|
+
}
|
|
9866
|
+
/**
|
|
9867
|
+
* Get MIME type for image format
|
|
9868
|
+
*/
|
|
9869
|
+
getMediaType(format) {
|
|
9870
|
+
switch (format.toLowerCase()) {
|
|
9871
|
+
case "png":
|
|
9872
|
+
return "image/png";
|
|
9873
|
+
case "jpg":
|
|
9874
|
+
case "jpeg":
|
|
9875
|
+
return "image/jpeg";
|
|
9876
|
+
case "gif":
|
|
9877
|
+
return "image/gif";
|
|
9878
|
+
case "webp":
|
|
9879
|
+
return "image/webp";
|
|
9880
|
+
default:
|
|
9881
|
+
return "image/png";
|
|
9882
|
+
}
|
|
9883
|
+
}
|
|
9884
|
+
/**
|
|
9885
|
+
* Split message text by image placeholders
|
|
9886
|
+
* Returns array of text and image parts in order
|
|
9887
|
+
*/
|
|
9888
|
+
splitMessageByPlaceholders(message) {
|
|
9889
|
+
const parts = [];
|
|
9890
|
+
const placeholderRegex = /(\[Image \d+\])/g;
|
|
9891
|
+
let lastIndex = 0;
|
|
9892
|
+
let match;
|
|
9893
|
+
while ((match = placeholderRegex.exec(message)) !== null) {
|
|
9894
|
+
if (match.index > lastIndex) {
|
|
9895
|
+
const text = message.substring(lastIndex, match.index);
|
|
9896
|
+
if (text) {
|
|
9897
|
+
parts.push({ type: "text", content: text });
|
|
9898
|
+
}
|
|
9899
|
+
}
|
|
9900
|
+
parts.push({ type: "image", content: match[0] });
|
|
9901
|
+
lastIndex = match.index + match[0].length;
|
|
9902
|
+
}
|
|
9903
|
+
if (lastIndex < message.length) {
|
|
9904
|
+
const text = message.substring(lastIndex);
|
|
9905
|
+
if (text) {
|
|
9906
|
+
parts.push({ type: "text", content: text });
|
|
9907
|
+
}
|
|
9908
|
+
}
|
|
9909
|
+
if (parts.length === 0) {
|
|
9910
|
+
parts.push({ type: "text", content: message });
|
|
9911
|
+
}
|
|
9912
|
+
return parts;
|
|
9913
|
+
}
|
|
9914
|
+
/**
|
|
9915
|
+
* Check if a message contains images
|
|
9916
|
+
*/
|
|
9917
|
+
hasImages(message) {
|
|
9918
|
+
return this.imageRenderer.extractImageHashes(message).length > 0;
|
|
9919
|
+
}
|
|
9920
|
+
/**
|
|
9921
|
+
* Convert multimodal message back to simple string (for display/logging)
|
|
9922
|
+
*/
|
|
9923
|
+
messageToString(message) {
|
|
9924
|
+
if (typeof message.content === "string") {
|
|
9925
|
+
return message.content;
|
|
9926
|
+
}
|
|
9927
|
+
return message.content.map((item) => {
|
|
9928
|
+
if (item.type === "text") {
|
|
9929
|
+
return item.text;
|
|
9930
|
+
} else if (item.type === "image") {
|
|
9931
|
+
if (item.source.type === "url") {
|
|
9932
|
+
return `[Image: ${item.source.url}]`;
|
|
9933
|
+
} else {
|
|
9934
|
+
return `[Image: base64 ${item.source.media_type}]`;
|
|
9935
|
+
}
|
|
9936
|
+
}
|
|
9937
|
+
return "";
|
|
9938
|
+
}).join("");
|
|
9939
|
+
}
|
|
9940
|
+
};
|
|
9941
|
+
|
|
9449
9942
|
// src/llm/ServerLlmBackend.ts
|
|
9450
9943
|
import { createParser } from "eventsource-parser";
|
|
9451
9944
|
import { isAxiosError } from "axios";
|
|
@@ -9863,7 +10356,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
9863
10356
|
// package.json
|
|
9864
10357
|
var package_default = {
|
|
9865
10358
|
name: "@bike4mind/cli",
|
|
9866
|
-
version: "0.2.
|
|
10359
|
+
version: "0.2.7-basic-cli-shortcuts.17131+b79f34724",
|
|
9867
10360
|
type: "module",
|
|
9868
10361
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
9869
10362
|
license: "UNLICENSED",
|
|
@@ -9924,6 +10417,7 @@ var package_default = {
|
|
|
9924
10417
|
"async-mutex": "^0.5.0",
|
|
9925
10418
|
axios: "^1.13.2",
|
|
9926
10419
|
bcryptjs: "^2.4.3",
|
|
10420
|
+
"better-sqlite3": "^12.5.0",
|
|
9927
10421
|
cheerio: "1.0.0-rc.12",
|
|
9928
10422
|
"csv-parse": "^5.5.6",
|
|
9929
10423
|
dayjs: "^1.11.13",
|
|
@@ -9949,7 +10443,7 @@ var package_default = {
|
|
|
9949
10443
|
"p-limit": "^6.2.0",
|
|
9950
10444
|
qrcode: "^1.5.4",
|
|
9951
10445
|
react: "^19.2.3",
|
|
9952
|
-
sharp: "^0.
|
|
10446
|
+
sharp: "^0.34.5",
|
|
9953
10447
|
speakeasy: "^2.0.0",
|
|
9954
10448
|
tiktoken: "^1.0.16",
|
|
9955
10449
|
turndown: "^7.2.0",
|
|
@@ -9965,10 +10459,11 @@ var package_default = {
|
|
|
9965
10459
|
},
|
|
9966
10460
|
devDependencies: {
|
|
9967
10461
|
"@bike4mind/agents": "0.1.0",
|
|
9968
|
-
"@bike4mind/common": "2.38.1-
|
|
9969
|
-
"@bike4mind/mcp": "1.20.1-
|
|
9970
|
-
"@bike4mind/services": "2.33.1-
|
|
9971
|
-
"@bike4mind/utils": "2.1.2-
|
|
10462
|
+
"@bike4mind/common": "2.38.1-basic-cli-shortcuts.17131+b79f34724",
|
|
10463
|
+
"@bike4mind/mcp": "1.20.1-basic-cli-shortcuts.17131+b79f34724",
|
|
10464
|
+
"@bike4mind/services": "2.33.1-basic-cli-shortcuts.17131+b79f34724",
|
|
10465
|
+
"@bike4mind/utils": "2.1.2-basic-cli-shortcuts.17131+b79f34724",
|
|
10466
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
9972
10467
|
"@types/diff": "^5.0.9",
|
|
9973
10468
|
"@types/jsonwebtoken": "^9.0.4",
|
|
9974
10469
|
"@types/node": "^22.9.0",
|
|
@@ -9980,7 +10475,7 @@ var package_default = {
|
|
|
9980
10475
|
typescript: "^5.9.3",
|
|
9981
10476
|
vitest: "^3.2.4"
|
|
9982
10477
|
},
|
|
9983
|
-
gitHead: "
|
|
10478
|
+
gitHead: "b79f34724ba9a60aae19010065d78ac75c2f8396"
|
|
9984
10479
|
};
|
|
9985
10480
|
|
|
9986
10481
|
// src/config/constants.ts
|
|
@@ -10001,20 +10496,26 @@ var EXIT_TIMEOUT_MS = 2e3;
|
|
|
10001
10496
|
var usageCache = null;
|
|
10002
10497
|
function CliApp() {
|
|
10003
10498
|
const { exit } = useApp();
|
|
10004
|
-
const
|
|
10499
|
+
const imageRenderer = new ImageRenderer();
|
|
10500
|
+
const [state, setState] = useState5({
|
|
10005
10501
|
session: null,
|
|
10006
10502
|
sessionStore: new SessionStore(),
|
|
10007
10503
|
configStore: new ConfigStore(),
|
|
10008
10504
|
commandHistoryStore: new CommandHistoryStore(),
|
|
10505
|
+
imageStore: null,
|
|
10506
|
+
// Lazy-loaded on first use
|
|
10507
|
+
imageRenderer,
|
|
10508
|
+
messageBuilder: null,
|
|
10009
10509
|
agent: null,
|
|
10010
10510
|
mcpManager: null,
|
|
10011
10511
|
permissionManager: null,
|
|
10012
10512
|
permissionPrompt: null,
|
|
10013
10513
|
trustLocationSelector: null
|
|
10014
10514
|
});
|
|
10015
|
-
const [isInitialized, setIsInitialized] =
|
|
10016
|
-
const [initError, setInitError] =
|
|
10017
|
-
const [commandHistory, setCommandHistory] =
|
|
10515
|
+
const [isInitialized, setIsInitialized] = useState5(false);
|
|
10516
|
+
const [initError, setInitError] = useState5(null);
|
|
10517
|
+
const [commandHistory, setCommandHistory] = useState5([]);
|
|
10518
|
+
const imageStoreInitPromise = useRef(null);
|
|
10018
10519
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
10019
10520
|
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
10020
10521
|
const setShowConfigEditor = useCliStore((state2) => state2.setShowConfigEditor);
|
|
@@ -10038,6 +10539,13 @@ function CliApp() {
|
|
|
10038
10539
|
if (state.agent) {
|
|
10039
10540
|
state.agent.removeAllListeners();
|
|
10040
10541
|
}
|
|
10542
|
+
if (state.imageStore) {
|
|
10543
|
+
try {
|
|
10544
|
+
state.imageStore.close();
|
|
10545
|
+
} catch (err) {
|
|
10546
|
+
logger.debug(`[CLEANUP] Image store close error: ${err}`);
|
|
10547
|
+
}
|
|
10548
|
+
}
|
|
10041
10549
|
await Promise.race([
|
|
10042
10550
|
Promise.all(cleanupTasks),
|
|
10043
10551
|
new Promise((resolve3) => setTimeout(resolve3, 500))
|
|
@@ -10046,8 +10554,8 @@ function CliApp() {
|
|
|
10046
10554
|
setTimeout(() => {
|
|
10047
10555
|
process.exit(0);
|
|
10048
10556
|
}, 100);
|
|
10049
|
-
}, [state.session, state.sessionStore, state.mcpManager, state.agent]);
|
|
10050
|
-
|
|
10557
|
+
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
10558
|
+
useInput4((input, key) => {
|
|
10051
10559
|
if (key.ctrl && input === "c") {
|
|
10052
10560
|
const now = Date.now();
|
|
10053
10561
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
@@ -10306,9 +10814,16 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10306
10814
|
return;
|
|
10307
10815
|
}
|
|
10308
10816
|
try {
|
|
10817
|
+
let messageContent = message;
|
|
10818
|
+
let userMessageContent = message;
|
|
10819
|
+
if (state.messageBuilder && state.messageBuilder.hasImages(message)) {
|
|
10820
|
+
const { message: multimodalMessage } = await state.messageBuilder.buildMessage(message);
|
|
10821
|
+
messageContent = multimodalMessage.content;
|
|
10822
|
+
userMessageContent = message;
|
|
10823
|
+
}
|
|
10309
10824
|
const userMessage = {
|
|
10310
10825
|
role: "user",
|
|
10311
|
-
content:
|
|
10826
|
+
content: userMessageContent,
|
|
10312
10827
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10313
10828
|
};
|
|
10314
10829
|
const recentMessages = state.session.messages.slice(-20);
|
|
@@ -10316,7 +10831,7 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10316
10831
|
role: msg.role,
|
|
10317
10832
|
content: msg.content
|
|
10318
10833
|
}));
|
|
10319
|
-
const result = await state.agent.run(
|
|
10834
|
+
const result = await state.agent.run(messageContent, {
|
|
10320
10835
|
previousMessages: previousMessages.length > 0 ? previousMessages : void 0
|
|
10321
10836
|
});
|
|
10322
10837
|
const permissionDenied = result.finalAnswer.startsWith("Permission denied for tool");
|
|
@@ -10365,6 +10880,35 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10365
10880
|
console.error("Error processing message:", error);
|
|
10366
10881
|
}
|
|
10367
10882
|
};
|
|
10883
|
+
const handleImageDetected = async (imageData) => {
|
|
10884
|
+
try {
|
|
10885
|
+
let imageStore = state.imageStore;
|
|
10886
|
+
if (!imageStore) {
|
|
10887
|
+
if (!imageStoreInitPromise.current) {
|
|
10888
|
+
imageStoreInitPromise.current = (async () => {
|
|
10889
|
+
const { ImageStore: ImageStoreClass } = await import("./ImageStore-RQZ7OF7P.js");
|
|
10890
|
+
const newImageStore = new ImageStoreClass();
|
|
10891
|
+
setState((prev) => ({
|
|
10892
|
+
...prev,
|
|
10893
|
+
imageStore: newImageStore,
|
|
10894
|
+
messageBuilder: new MessageBuilder(newImageStore, prev.imageRenderer)
|
|
10895
|
+
}));
|
|
10896
|
+
return newImageStore;
|
|
10897
|
+
})();
|
|
10898
|
+
}
|
|
10899
|
+
imageStore = await imageStoreInitPromise.current;
|
|
10900
|
+
}
|
|
10901
|
+
const imageRef = await imageStore.store(imageData);
|
|
10902
|
+
const imagePlaceholder = state.imageRenderer.createPlaceholder(imageRef.hash);
|
|
10903
|
+
console.log(`
|
|
10904
|
+
\u2713 Image detected and stored: ${imagePlaceholder}
|
|
10905
|
+
`);
|
|
10906
|
+
return imagePlaceholder;
|
|
10907
|
+
} catch (error) {
|
|
10908
|
+
console.error("Error storing image:", error);
|
|
10909
|
+
throw error;
|
|
10910
|
+
}
|
|
10911
|
+
};
|
|
10368
10912
|
const handleCommand = async (command, args) => {
|
|
10369
10913
|
switch (command) {
|
|
10370
10914
|
case "help":
|
|
@@ -10801,12 +11345,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
10801
11345
|
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
10802
11346
|
};
|
|
10803
11347
|
if (initError) {
|
|
10804
|
-
return /* @__PURE__ */
|
|
11348
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React14.createElement(Text13, null, initError), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
|
|
10805
11349
|
}
|
|
10806
11350
|
if (state.trustLocationSelector) {
|
|
10807
11351
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
10808
11352
|
const inProject = projectDir !== null;
|
|
10809
|
-
return /* @__PURE__ */
|
|
11353
|
+
return /* @__PURE__ */ React14.createElement(
|
|
10810
11354
|
TrustLocationSelector,
|
|
10811
11355
|
{
|
|
10812
11356
|
inProject,
|
|
@@ -10825,7 +11369,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
10825
11369
|
}
|
|
10826
11370
|
if (state.showLoginFlow) {
|
|
10827
11371
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
10828
|
-
return /* @__PURE__ */
|
|
11372
|
+
return /* @__PURE__ */ React14.createElement(
|
|
10829
11373
|
LoginFlow,
|
|
10830
11374
|
{
|
|
10831
11375
|
apiUrl: loginApiUrl,
|
|
@@ -10848,13 +11392,14 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
10848
11392
|
);
|
|
10849
11393
|
}
|
|
10850
11394
|
if (!isInitialized) {
|
|
10851
|
-
return /* @__PURE__ */
|
|
11395
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, "\u{1F680} Initializing..."));
|
|
10852
11396
|
}
|
|
10853
|
-
return /* @__PURE__ */
|
|
11397
|
+
return /* @__PURE__ */ React14.createElement(
|
|
10854
11398
|
App,
|
|
10855
11399
|
{
|
|
10856
11400
|
onMessage: handleMessage,
|
|
10857
11401
|
onCommand: handleCommand,
|
|
11402
|
+
onImageDetected: handleImageDetected,
|
|
10858
11403
|
commandHistory,
|
|
10859
11404
|
config: state.config,
|
|
10860
11405
|
availableModels: state.availableModels,
|
|
@@ -10884,6 +11429,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
10884
11429
|
if (isDevMode) {
|
|
10885
11430
|
console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
10886
11431
|
}
|
|
10887
|
-
render(/* @__PURE__ */
|
|
11432
|
+
render(/* @__PURE__ */ React14.createElement(CliApp, null), {
|
|
10888
11433
|
exitOnCtrlC: false
|
|
10889
11434
|
});
|