fluxflow-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2781 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.jsx
4
+ import React9 from "react";
5
+ import { render } from "ink";
6
+
7
+ // src/app.jsx
8
+ import React8, { useState as useState4, useEffect as useEffect3, useRef, useMemo } from "react";
9
+ import { Box as Box8, Text as Text8, useInput as useInput3, useStdout, Static } from "ink";
10
+ import { MultilineInput } from "ink-multiline-input";
11
+ import TextInput2 from "ink-text-input";
12
+
13
+ // src/components/ChatLayout.jsx
14
+ import React2 from "react";
15
+ import { Box as Box2, Text as Text2 } from "ink";
16
+
17
+ // src/components/TerminalBox.jsx
18
+ import React from "react";
19
+ import { Box, Text } from "ink";
20
+ var TerminalBox = ({ command, output, completed = false }) => {
21
+ const cleanOutput = (output || "").replace(/\r/g, "").trim();
22
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: completed ? "#334155" : "cyan", paddingX: 2, paddingY: completed ? 0 : 1, width: "100%" }, /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "cyan", bold: true }, completed ? "\u{1F3C1} FINISHED:" : "\u26A1 EXECUTING:", " "), /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "white" }, command)), /* @__PURE__ */ React.createElement(Text, { color: completed ? "#475569" : "yellow", bold: true }, completed ? "\u25CF ARCHIVED" : "\u25CF LIVE")), cleanOutput ? /* @__PURE__ */ React.createElement(Box, { marginTop: completed ? 0 : 1, backgroundColor: "#0a0a0a", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "green", wrap: "anywhere" }, cleanOutput)) : !completed && /* @__PURE__ */ React.createElement(Box, { marginTop: 1, backgroundColor: "#0a0a0a", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", italic: true }, "Waiting for output...")), !completed && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true, italic: true }, "Double-press ESC to terminate if hanging.")));
23
+ };
24
+
25
+ // src/components/ChatLayout.jsx
26
+ var cleanSignals = (text) => {
27
+ if (!text) return text;
28
+ let result = "";
29
+ let i = 0;
30
+ while (i < text.length) {
31
+ const trigger = "tool:functions.";
32
+ if (text.substring(i, i + trigger.length).toLowerCase() === trigger) {
33
+ let balance = 0;
34
+ let foundStart = false;
35
+ let j = i;
36
+ while (j < text.length) {
37
+ if (text[j] === "(") {
38
+ balance++;
39
+ foundStart = true;
40
+ } else if (text[j] === ")") {
41
+ balance--;
42
+ }
43
+ j++;
44
+ if (foundStart && balance === 0) break;
45
+ }
46
+ i = j;
47
+ } else {
48
+ result += text[i];
49
+ i++;
50
+ }
51
+ }
52
+ return result.replace(/^\[TOOL_RESULT\]:\s*/gi, "").split("\n").filter((line) => !line.trim().startsWith("SUCCESS:") && !line.trim().startsWith("ERROR:")).join("\n").replace(/\[\s*(turn\s*:)?\s*(continue|finish)\s*\]/gi, "").replace(/\n\s*turn\s*:\s*(continue|finish)\s*$/gi, "").replace(/\n\nResponded on .*/g, "").replace(/\n\n\[Prompted on: .*\]/g, "").trim();
53
+ };
54
+ var formatThinkText = (text) => {
55
+ const cleaned = cleanSignals(text);
56
+ if (!cleaned) return null;
57
+ const lines = cleaned.split("\n").filter((l) => l.trim() !== "");
58
+ return lines.map((line, i) => {
59
+ const trimmed = line.trim();
60
+ if (trimmed.startsWith("**") && trimmed.endsWith("**") || trimmed.startsWith("#")) {
61
+ return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginTop: i === 0 ? 0 : 1, marginBottom: 0 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, trimmed.replace(/\*|#/g, "").trim()));
62
+ }
63
+ const isBullet = trimmed.startsWith("*");
64
+ return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginLeft: isBullet ? 2 : 0 }, /* @__PURE__ */ React2.createElement(Text2, { italic: true, color: "gray", wrap: "anywhere" }, isBullet ? "\u2022 " : "", trimmed.replace(/^\*|\s\*/g, "").trim()));
65
+ });
66
+ };
67
+ var CodeRenderer = ({ text }) => {
68
+ if (!text) return null;
69
+ if (text.includes("[DIFF_START]")) {
70
+ const beforeDiff = text.substring(0, text.indexOf("[DIFF_START]")).trim();
71
+ const afterDiff = text.substring(text.indexOf("[DIFF_END]") + 10).trim();
72
+ const match = text.match(/\[DIFF_START\]([\s\S]*?)\[DIFF_END\]/);
73
+ const diffBody = match ? match[1].trim() : "";
74
+ const diffLines = diffBody.split("\n");
75
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, beforeDiff && /* @__PURE__ */ React2.createElement(MarkdownText, { text: beforeDiff }), /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, backgroundColor: "#1a1a1a", paddingY: 0 }, diffLines.map((line, i) => {
76
+ const isContext = line.includes("[UI_CONTEXT]");
77
+ const cleanLine = line.replace("[UI_CONTEXT]", "");
78
+ const isRemoval = cleanLine.startsWith("-");
79
+ const isAddition = cleanLine.startsWith("+");
80
+ const parts = cleanLine.substring(1).split("|");
81
+ const lineNum = parts[0]?.trim() || "";
82
+ const content = parts.slice(1).join("|");
83
+ const bgColor = isRemoval ? "#3a0c0c" : isAddition ? "#0c3a1a" : "#1a1a1a";
84
+ const textColor = isRemoval ? "#ff4d4d" : isAddition ? "#4dff88" : "white";
85
+ return /* @__PURE__ */ React2.createElement(Box2, { key: i, backgroundColor: bgColor, paddingX: 1 }, /* @__PURE__ */ React2.createElement(Box2, { width: 5, flexShrink: 0 }, /* @__PURE__ */ React2.createElement(Text2, { color: isRemoval ? "#cf3a3a" : isAddition ? "#3acf65" : "gray", dimColor: true }, lineNum)), /* @__PURE__ */ React2.createElement(Box2, { width: 2, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: textColor, bold: true }, isRemoval ? "-" : isAddition ? "+" : " ")), /* @__PURE__ */ React2.createElement(Box2, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: textColor, wrap: "anywhere" }, content)));
86
+ })), afterDiff && /* @__PURE__ */ React2.createElement(MarkdownText, { text: afterDiff }));
87
+ }
88
+ if (text.includes("```")) {
89
+ const parts = text.split(/(```[\s\S]*?```)/g);
90
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, parts.map((part, i) => {
91
+ if (part.startsWith("```") && part.endsWith("```")) {
92
+ const match = part.match(/```(\w*)\n([\s\S]*?)```/);
93
+ const lang = match ? match[1] : "code";
94
+ const code = match ? match[2] : part.slice(3, -3);
95
+ return /* @__PURE__ */ React2.createElement(Box2, { key: i, flexDirection: "column", marginY: 1, backgroundColor: "#111", borderStyle: "round", borderColor: "#333", paddingX: 1 }, /* @__PURE__ */ React2.createElement(Box2, { alignSelf: "flex-end", marginTop: -1, marginRight: 1 }, /* @__PURE__ */ React2.createElement(Text2, { backgroundColor: "#333", color: "white" }, " ", lang.toUpperCase(), " ")), /* @__PURE__ */ React2.createElement(Box2, { paddingY: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "cyan", wrap: "anywhere" }, code.trim())));
96
+ }
97
+ return /* @__PURE__ */ React2.createElement(MarkdownText, { key: i, text: part });
98
+ }));
99
+ }
100
+ return /* @__PURE__ */ React2.createElement(MarkdownText, { text });
101
+ };
102
+ var MarkdownText = ({ text, color = "white" }) => {
103
+ const cleaned = cleanSignals(text);
104
+ if (!cleaned) return null;
105
+ const lines = cleaned.split("\n");
106
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, lines.map((line, i) => {
107
+ const trimmed = line.trim();
108
+ if (trimmed === "---" || trimmed === "***" || trimmed === "___") {
109
+ return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginY: 1, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, width: "100%", borderColor: "#333" });
110
+ }
111
+ const headingMatch = trimmed.match(/^(#{1,3})\s+(.*)/);
112
+ if (headingMatch) {
113
+ const level = headingMatch[1].length;
114
+ const hText = headingMatch[2];
115
+ return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginTop: 1, marginBottom: 0 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: level === 1 ? "cyan" : level === 2 ? "magenta" : "yellow", underline: true }, hText.toUpperCase()));
116
+ }
117
+ const isUnordered = trimmed.startsWith("* ") || trimmed.startsWith("- ");
118
+ const isOrdered = /^\d+\.\s/.test(trimmed);
119
+ let content = trimmed;
120
+ if (isUnordered || isOrdered) {
121
+ content = (isUnordered ? " \u2022 " : "") + trimmed.replace(/^[\*\-\d+\.]+\s/, "");
122
+ }
123
+ const parts = content.split(/(\*\*.*?\*\*|\*.*?\*|`.*?`)/g);
124
+ return /* @__PURE__ */ React2.createElement(Text2, { key: i, color, wrap: "anywhere" }, parts.map((part, j) => {
125
+ if (part.startsWith("**") && part.endsWith("**")) {
126
+ return /* @__PURE__ */ React2.createElement(Text2, { key: j, bold: true, color: "white" }, part.slice(2, -2));
127
+ }
128
+ if (part.startsWith("*") && part.endsWith("*")) {
129
+ return /* @__PURE__ */ React2.createElement(Text2, { key: j, italic: true, color: "gray" }, part.slice(1, -1));
130
+ }
131
+ if (part.startsWith("`") && part.endsWith("`")) {
132
+ return /* @__PURE__ */ React2.createElement(Text2, { key: j, color: "cyan", backgroundColor: "#003333" }, " ", part.slice(1, -1), " ");
133
+ }
134
+ return part;
135
+ }));
136
+ }));
137
+ };
138
+ function ChatLayout({ messages, showFullThinking }) {
139
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, messages.map((msg, idx) => {
140
+ const isDiffResult = msg.role === "system" && msg.text.includes("[DIFF_START]");
141
+ const isTerminalRecord = msg.isTerminalRecord;
142
+ if (msg.role === "system" && msg.text.includes("[TOOL_RESULT]") && !isDiffResult && !isTerminalRecord) return null;
143
+ if (isTerminalRecord) {
144
+ const cmdMatch = msg.text.match(/COMMAND: (.*)\n/);
145
+ const outputMatch = msg.text.match(/OUTPUT: ([\s\S]*)$/);
146
+ const cmd = cmdMatch ? cmdMatch[1] : "Unknown";
147
+ const outputList = outputMatch ? outputMatch[1] : "";
148
+ return /* @__PURE__ */ React2.createElement(Box2, { key: idx, marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(TerminalBox, { command: cmd, output: outputList, completed: true }));
149
+ }
150
+ let color = "white";
151
+ let content = cleanSignals(msg.text);
152
+ if (msg.role === "think" && !showFullThinking) {
153
+ content = content.split("\n").filter((line) => {
154
+ const trimmed = line.trim();
155
+ const isHeading = trimmed.startsWith("# ");
156
+ const isActionStep = trimmed.startsWith("**") && trimmed.endsWith("**");
157
+ return isHeading || isActionStep;
158
+ }).join("\n");
159
+ if (!content.trim()) content = "*Reasoning...*";
160
+ }
161
+ return /* @__PURE__ */ React2.createElement(Box2, { key: idx, marginBottom: 1, flexDirection: "column", flexShrink: 0, width: "100%" }, msg.role === "user" ? /* @__PURE__ */ React2.createElement(
162
+ Box2,
163
+ {
164
+ backgroundColor: "#262626",
165
+ paddingX: 1,
166
+ paddingY: 1,
167
+ width: "100%",
168
+ flexDirection: "column"
169
+ },
170
+ content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\n/g, "\n").replace(/\\$/, "").split("\n").map((line, lineIdx) => /* @__PURE__ */ React2.createElement(Box2, { key: lineIdx, flexDirection: "row" }, /* @__PURE__ */ React2.createElement(Box2, { flexShrink: 0, width: 2 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, lineIdx === 0 ? "\u276F" : " ")), /* @__PURE__ */ React2.createElement(Box2, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: msg.color || "white", wrap: "anywhere" }, line))))
171
+ ) : msg.role === "think" ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Thinking..."), /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 2, flexDirection: "column" }, formatThinkText(content))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: content }), msg.memoryUpdated && /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", italic: true }, "\u2728 [Memory Updated]"))));
172
+ }));
173
+ }
174
+
175
+ // src/components/StatusBar.jsx
176
+ import React3 from "react";
177
+ import { Box as Box3, Text as Text3 } from "ink";
178
+ function StatusBar({ mode, thinkingLevel, tokens = "0.0k", chatId = "NEW-SESSION", isMemoryEnabled = true }) {
179
+ const modeColor = mode === "Flux" ? "yellow" : "cyan";
180
+ const modeIcon = mode === "Flux" ? "\u26A1" : "\u{1F30A}";
181
+ const memStatus = isMemoryEnabled ? "ON" : "OFF";
182
+ return /* @__PURE__ */ React3.createElement(
183
+ Box3,
184
+ {
185
+ borderStyle: "single",
186
+ borderColor: "gray",
187
+ flexDirection: "row",
188
+ justifyContent: "space-between",
189
+ paddingX: 1,
190
+ width: "100%"
191
+ },
192
+ /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: modeColor, bold: true }, modeIcon, " ", mode.toUpperCase()), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "magenta" }, "\u{1F9E0} ", thinkingLevel)),
193
+ /* @__PURE__ */ React3.createElement(Box3, { flexGrow: 1, justifyContent: "center", paddingX: 2 }, /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "\u{1F4C1} "), /* @__PURE__ */ React3.createElement(Text3, { color: "blue", dimColor: true, italic: true }, process.cwd())),
194
+ /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, "MEM: "), /* @__PURE__ */ React3.createElement(Text3, { color: memStatus === "ON" ? "green" : "red" }, memStatus), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "blue" }, " Tokens ", tokens > 1e3 ? `${(tokens / 1e3).toFixed(1)}k` : tokens, " (", Math.round(tokens / 128e3 * 100), "%)"), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "dim" }, "ID: ", chatId, " "))
195
+ );
196
+ }
197
+
198
+ // src/components/CommandMenu.jsx
199
+ import React4 from "react";
200
+ import { Box as Box4, Text as Text4 } from "ink";
201
+ import SelectInput from "ink-select-input";
202
+ var CustomItem = ({ label, isSelected }) => {
203
+ const isCancel = label === "Cancel" || label.toLowerCase().includes("exit");
204
+ return /* @__PURE__ */ React4.createElement(Box4, { marginTop: isCancel ? 1 : 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: isSelected ? "cyan" : "white" }, isSelected ? "\u276F " : " ", label));
205
+ };
206
+ function CommandMenu({ title, items, onSelect }) {
207
+ return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", padding: 1, marginTop: 1, flexShrink: 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: "magenta", bold: true }, title), /* @__PURE__ */ React4.createElement(Box4, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React4.createElement(
208
+ SelectInput,
209
+ {
210
+ items,
211
+ onSelect,
212
+ itemComponent: CustomItem,
213
+ indicatorComponent: () => null
214
+ }
215
+ )), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true, marginTop: 1 }, "(Use Up/Down arrows to select, Enter to confirm)"));
216
+ }
217
+
218
+ // src/components/ProfileForm.jsx
219
+ import React5, { useState } from "react";
220
+ import { Box as Box5, Text as Text5 } from "ink";
221
+ import TextInput from "ink-text-input";
222
+ function ProfileForm({ onSave, onCancel }) {
223
+ const [step, setStep] = useState(0);
224
+ const [currentInput, setCurrentInput] = useState("");
225
+ const [profile, setProfile] = useState({ name: "", nickname: "", instructions: "" });
226
+ const steps = [
227
+ { key: "name", label: "Enter your Name: " },
228
+ { key: "nickname", label: "Enter a Nickname (Agent will use this): " },
229
+ { key: "instructions", label: "System Instructions (Persona overrides): " }
230
+ ];
231
+ const handleSubmit = (val) => {
232
+ const currentKey = steps[step].key;
233
+ const newProfile = { ...profile, [currentKey]: val.trim() };
234
+ setProfile(newProfile);
235
+ setCurrentInput("");
236
+ if (step < steps.length - 1) {
237
+ setStep(step + 1);
238
+ } else {
239
+ onSave(newProfile);
240
+ }
241
+ };
242
+ return /* @__PURE__ */ React5.createElement(Box5, { borderStyle: "round", borderColor: "magenta", padding: 1, marginTop: 1, flexShrink: 0, flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta", bold: true }, "\u{1F464} Edit Profile Configuration"), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, steps[step].label), /* @__PURE__ */ React5.createElement(
243
+ TextInput,
244
+ {
245
+ value: currentInput,
246
+ onChange: setCurrentInput,
247
+ onSubmit: handleSubmit
248
+ }
249
+ )), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true, marginTop: 1 }, "(Press Enter to submit, type /cancel to abort)"));
250
+ }
251
+
252
+ // src/app.jsx
253
+ import gradient from "gradient-string";
254
+
255
+ // src/utils/secrets.js
256
+ import fs2 from "fs-extra";
257
+ import path2 from "path";
258
+ import { fileURLToPath } from "url";
259
+
260
+ // src/utils/crypto.js
261
+ import fs from "fs";
262
+ import path from "path";
263
+ var XOR_KEY = 66;
264
+ var xorTransform = (data) => {
265
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
266
+ const result = Buffer.alloc(buffer.length);
267
+ for (let i = 0; i < buffer.length; i++) {
268
+ result[i] = buffer[i] ^ XOR_KEY;
269
+ }
270
+ return result;
271
+ };
272
+ var readEncryptedJson = (filePath, defaultValue = {}) => {
273
+ try {
274
+ if (!fs.existsSync(filePath)) return defaultValue;
275
+ const encryptedData = fs.readFileSync(filePath);
276
+ const decryptedData = xorTransform(encryptedData).toString();
277
+ return JSON.parse(decryptedData);
278
+ } catch (err) {
279
+ console.error(`Vault Read Error [${path.basename(filePath)}]:`, err.message);
280
+ return defaultValue;
281
+ }
282
+ };
283
+ var writeEncryptedJson = (filePath, data) => {
284
+ try {
285
+ const dir = path.dirname(filePath);
286
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
287
+ const jsonData = JSON.stringify(data, null, 2);
288
+ const encryptedData = xorTransform(jsonData);
289
+ fs.writeFileSync(filePath, encryptedData);
290
+ } catch (err) {
291
+ console.error(`Vault Write Error [${path.basename(filePath)}]:`, err.message);
292
+ }
293
+ };
294
+
295
+ // src/utils/secrets.js
296
+ var __filename = fileURLToPath(import.meta.url);
297
+ var __dirname = path2.dirname(__filename);
298
+ var AGENT_ROOT = path2.join(__dirname, "../../");
299
+ var SECRET_DIR = path2.join(AGENT_ROOT, "secret");
300
+ var SECRET_FILE = path2.join(SECRET_DIR, "secrets.json");
301
+ var getAPIKey = async () => {
302
+ try {
303
+ const secrets = readEncryptedJson(SECRET_FILE, {});
304
+ if (secrets.API_KEY) return secrets.API_KEY;
305
+ } catch (e) {
306
+ }
307
+ return null;
308
+ };
309
+ var saveSecret = async (key, value) => {
310
+ await fs2.ensureDir(SECRET_DIR);
311
+ let current = readEncryptedJson(SECRET_FILE, {});
312
+ current[key] = value;
313
+ writeEncryptedJson(SECRET_FILE, current);
314
+ };
315
+ var saveAPIKey = async (apiKey) => saveSecret("API_KEY", apiKey);
316
+ var removeSecret = async (key) => {
317
+ try {
318
+ const secrets = readEncryptedJson(SECRET_FILE, {});
319
+ delete secrets[key];
320
+ writeEncryptedJson(SECRET_FILE, secrets);
321
+ } catch (e) {
322
+ }
323
+ };
324
+ var removeAPIKey = async () => removeSecret("API_KEY");
325
+
326
+ // src/utils/ai.js
327
+ import { GoogleGenAI, ThinkingLevel } from "@google/genai";
328
+
329
+ // src/data/main_tools.js
330
+ var TOOL_PROTOCOL = (mode) => `
331
+ -- START FUNCTION CALLING PROTOCOL --
332
+ You have access to internal tools. To call a tool, you MUST use the following exact syntax on a new line:
333
+ tool:functions.tool_name(arguments)
334
+
335
+ - WEB TOOLS (Available in Flux & Flow) -
336
+ 1. Web Search: tool:functions.web_search(query="<query>", limit=number). Find info. limit is optional (3-10, default 10). If user asks about something that is not in your training data, proactively use this tool to find the information.Winder search recomemded (limit = 10) when exploring a topic.
337
+ 2. Web Scrape: tool:functions.web_scrape(url="<url>"). provides detail from a URL.
338
+ ${mode === "Flux" ? `
339
+ - DEV & FILE TOOLS (Available in FLUX MODE ONLY) -
340
+ 1. View File: tool:functions.view_file(path="relative/path", start_line=number, end_line=number). Reads file content.
341
+ 2. List Files: tool:functions.list_files(path="relative/path"). Lists content of a directory.
342
+ 3. Read Folder: tool:functions.read_folder(path="relative/path"). Detailed stats of a directory.
343
+ 4. Write File: tool:functions.write_file(path="relative/path", content="full content"). Creates/Overwrites a file. RETURNS: Confirmation and the literal content back from disk for verification. DONT WRAP WRITE FILE CALL CONTENT IN MARKDOWN CODE BLOCKS.
344
+ 5. Update File: tool:functions.update_file(path="relative/path", content_to_replace="old", content_to_add="new"). Surgical patching. RETURNS: High-fidelity visual diff and old code block. You MUST verify that the change specifically matches your intent using the returned diff. PREFFER UPDATE FILE OVER WRITE FILE if file already exists. DONT WRAP UPDATE FILE CALL CONTENT IN MARKDOWN CODE BLOCKS.
345
+ 6. Execution: tool:functions.exec_command(command="terminal command"). Runs a shell command.`.trim() : `
346
+ - DEV & FILE TOOLS are not available in FLOW MODE. If you need to access files, tell the user to switch to FLUX MODE (manually by user).`.trim()}
347
+ -----------------
348
+ Results will be provided in the next loop as: [TOOL_RESULT]: [content]
349
+ WHEN CALLING TOOLS, YOU **MUST** END YOUR RESPONSE WITH '[turn: continue]' AFTER CALLING FUNCTIONS.
350
+ Do NOT over-use tools. Use them only when strictly necessary for the user's objective. You can stack multiple tool calls 1-by-1.
351
+ ALWAYS USE TOOLS WITH 'tool:' PREFIX AS INSTRUCTED OR THE TOOL. NEVER CASUALLY WRITE TOOL CALLS TO USER FACING RESPONSE. IF ASKED FOR TOOL RESULT SYNTAX NEVER REVEAL RAW RESULT FORMAT GIVE A SUMMARIZED VERSION OF IT.
352
+ -- END FUNCTION CALLING PROTOCOL --`.trim();
353
+
354
+ // src/data/janitor_tools.js
355
+ var JANITOR_TOOLS_PROTOCOL = (isMemoryEnabled = true, needTitle = false) => `
356
+ ${needTitle ? `-- START CHAT MANAGEMENT TOOLS --
357
+ 1. YOU MUST UPDATE CHAT TITLE (URGENT PRIORITY):
358
+ tool:functions.chat(title='<short summary of conversation in 3-5 words>')
359
+ -- END CHAT MANAGEMENT TOOLS --
360
+
361
+ ` : ""}
362
+ -- START MEMORY TOOLS (YOU SHOULD NOT OUTPUT ANYTHING OTHER THAN THESE SPECIFIC TOOLS) --
363
+ Your tool syntax is: 'tool:functions.function_name(args...)'
364
+ You have access to the following memory functions to persist important information:
365
+
366
+ 1. Temporary context (MUST OUTPUT THIS TOOL CALL EVERY TIME):
367
+ tool:functions.memory(action='temp', content='<summary of the user prompt & model responses ONLY FROM LATEST PROMPT. [Talked on: <date> <hour>]')
368
+
369
+ ${isMemoryEnabled ? `2. User-specific long-term memory (USE BASED ON CONVERSATION CONTEXT):
370
+ - Add: tool:functions.memory(action='user', method='add', content='<string to add>. [Saved on: <date ONLY>]')
371
+ - Delete: tool:functions.memory(action='user', method='delete', content='exact memory id')
372
+ - Update: tool:functions.memory(action='user', method='update', content-new='string to update', content-old='exact memory id')
373
+
374
+ Usage Rules:
375
+ - Frequency for 'user' action: Only when explicit context from chat is found or explicitly requested by the user.` : ""}
376
+ -- END MEMORY TOOLS --`.trim();
377
+
378
+ // src/data/thinking_prompts.json
379
+ var thinking_prompts_default = {
380
+ Max: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: MAX\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 30**, **MAX 50**. EACH HEADING SHOULD HAVE MIN 8, MAX 12 STEPS OF INTERNAL MONOLOGUE. Think LONGER & DEEPER before responding. EXPLORE EDGE CASES & NUANCES.\nPLAN LIKE A SCALABLE ARCHITECTURE\n-- END THINKING INSTRUCTIONS --",
381
+ High: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: HIGH\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 13**, **MAX 25**. EACH HEADING SHOULD HAVE MIN 6, MAX 8 STEPS OF INTERNAL MONOLOGUE. EXPLORE EDGE CASES & THINK LONGER before responding.\n-- END THINKING INSTRUCTIONS --",
382
+ Medium: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: MEDIUM\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 5**, **MAX 9**. EACH HEADING SHOULD HAVE MIN 4, MAX 6 STEPS OF INTERNAL MONOLOGUE. THINK LONGER FOR COMPLEX QUERIES.\n-- END THINKING INSTRUCTIONS --",
383
+ Minimal: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: MINIMAL\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 0**, **MAX 3**. EACH HEADING SHOULD HAVE MIN 1, MAX 2 STEPS OF INTERNAL MONOLOGUE. No Thinking is preferred if query is simple.\n-- END THINKING INSTRUCTIONS --"
384
+ };
385
+
386
+ // src/utils/prompts.js
387
+ var getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, tempMemories = "", userMemories = "", isMemoryEnabled = true) => {
388
+ let levelKey = thinkingLevel;
389
+ if (thinkingLevel === "Low") levelKey = "Minimal";
390
+ if (thinkingLevel === "xHigh" || thinkingLevel === "Max") levelKey = "Max";
391
+ const thinkingConfig = thinking_prompts_default[levelKey] || thinking_prompts_default["Medium"];
392
+ const nameStr = profile.name && profile.name?.length > 0 ? `User Name: ${profile.name}` : "";
393
+ const nicknameStr = profile.nickname && profile.nickname?.length > 0 ? `. User Nickname: ${profile.nickname}.` : "";
394
+ const userInstrStr = profile.instructions && profile.instructions?.length > 0 ? `. User Instructions: ${profile.instructions}.` : "";
395
+ const dateTimeStr = (/* @__PURE__ */ new Date()).toLocaleString();
396
+ const cwdStr = process.cwd();
397
+ const tempMemoriesStr = tempMemories?.length > 0 ? `
398
+ -- RECENT CONTEXT FROM OTHER CHAT THREADS --
399
+ ${tempMemories}
400
+ ------------------------------------------
401
+ ` : "";
402
+ const userMemoriesStr = userMemories?.length > 0 ? `
403
+ --- PERSISTENT USER MEMORIES ---
404
+ ${userMemories}
405
+ --------------------------------
406
+ ` : "";
407
+ return `${isMemoryEnabled ? `${userMemoriesStr}
408
+
409
+ ` : ""}${isMemoryEnabled ? `${tempMemoriesStr}
410
+
411
+ ` : ""}--- START SYSTEM INSTRUCTION ---
412
+ You are Flux Flow. A CLI AI Agent. Your tone will be friendly, warm, sassy, approchable, respectable, NO ROMANTIC OR FLIRTY WORDS. Dont mention modes if not explicitly asked. ${mode === "Flux" ? "You are currently operating in FLUX (dev) mode. Keep your agentic approach goal oriented. Use provided tools when needed. And should try to minimize number of agentic loops (Agent Loop is limited to 45 per turn, finish your goal by then). Analyze user prompt and project requirements, then plan your approach." : "You are currently operating in Flow (chat) mode. Focus more on conversation quality and user experience. Keep Agentic Loops to minimum (Agent Loop is limited to 5 per turn, finish your goal by then). You will get access to Web Tools only in this mode."}
413
+ CURRENT_WORKING_DIRECTORY: ${cwdStr}.
414
+ ${nameStr}${nicknameStr}${userInstrStr}
415
+
416
+ ${thinkingConfig}
417
+
418
+ ${TOOL_PROTOCOL(mode)}
419
+
420
+ -- START MEMORY INSTRUCTIONS --
421
+ ${isMemoryEnabled ? "You have a internal memory system. Data is saved by a background model working in parallel. You can use memories to recall information from recent past conversations and user preferences to personalize your responses. Dont over mention about memory, keep it light and contexual." : "Memory Features are currently turned off by user. You can ask them to enable it /settings."}
422
+ -- END MEMORY INSTRUCTIONS --
423
+
424
+ -- START SECURITY BOUNDARY --
425
+ - EXTERNAL_WORKSPACE_ACCESS: ${systemSettings.allowExternalAccess ? "ENABLED. You are permitted to use tools (Read/Write/Exec) on files and directories outside the current working directory if necessary for the task." : 'DISABLED. You are strictly confined to the current working directory. Do NOT attempt to access or modify any files outside this path. If important tell user to turn on "External File Access" in /settings.'}
426
+ - RESTRAIN from reading '.env', or any other secure files that might contain sensitive information or API Keys. If a task requires reading such files, ask user permission first.
427
+ -- END SECURITY BOUNDARY --
428
+
429
+ -- START TEMPORAL AWARENESS --
430
+ Every ${isMemoryEnabled ? "Prompt, Responses & Memories" : "Prompt & Responses"} are time stamped. You can use those times if temporal context is required. If recalled from ${isMemoryEnabled ? "Memories, Prompts, or Responses" : "Prompts, or Responses"} dont use absolute time in your responses, instead use relative time from current time.
431
+ -- END TEMPORAL AWARENESS --
432
+
433
+ -- START FORMATTING RULES --
434
+ - Use markdown.
435
+ - Structure responses VISUALLY pleasing, easy to read, and beautiful.
436
+ - NEVER USE table format markdown. Gently avoid if explicitly asked.
437
+ - NEVER USE LaTeX format.
438
+ - Use emojis. But dont overdo it.
439
+ -- END FORMATTING RULES --
440
+
441
+ -- START REPONSE FINISH PROTOCOL --
442
+ WHEN YOU ARE DONE AND NEED NO LONGER AGENT LOOP FOR THE TASK, WRITE [turn: finish] AT VERY END OF YOUR RESPONSE TO AVOID AGENT LOOPS. IF YOU ARE NOT COMPLETED YET AND WANT NEXT LOOP WRITE [turn: continue] AT VERY END OF YOUR RESPONSE TO CONTINUE AGENT LOOPS. YOU CAN STACK MULTIPLE TOOLS AT ONCE BUT **HAVE TO** WRITE [turn: continue] AFTER WRITING ALL TOOL CALLS.
443
+ -- END REPONSE FINISH PROTOCOL --
444
+ Current date and Time is: ${dateTimeStr}
445
+ --- END SYSTEM INSTRUCTION ---`.trim();
446
+ };
447
+ var getJanitorInstruction = (originalText, agentRaws, userMemories = "", isMemoryEnabled = true, needTitle = false) => {
448
+ return `ORIGINAL USER PROMPT: ${originalText.substring(0, 500)}
449
+ AGENT RAWS (responses from this turn):
450
+ ${agentRaws.substring(0, 2e3).replace(/tool:functions\..*\n/g, "").replace(/<think>.*<\/think>/g, "").replace(/\[Prompted on:.*?\]/g, "").replace(/\[turn: continue\]/g, "").replace(/\[turn: finish\]/g, "")}${agentRaws.length > 1500 ? "\n... (truncated) ..." : ""}
451
+
452
+ --- START SYSTEM INSTRUCTION (STRICT HEADLESS LOGIC WORKER: ZERO USER-FACING TEXT POLICY) ---
453
+ YOU ARE A SILENT BACKGROUND SYSTEM PROCESS. YOU HAVE NO MOUTH. YOUR ONLY OUTPUT MEDIUM IS VALID TOOL CALL SYNTAX.
454
+ [CRITICAL RULES]
455
+ 1. OUTPUT ONLY 'tool:functions.xxx' CALLS.
456
+ 2. DO NOT EXPLAIN. DO NOT SUMMARIZE AGENT RAWS. DO NOT TALK TO THE USER.
457
+ 3. NON-TOOL TEXT WILL BREAK THE SYSTEM.
458
+ 4.
459
+
460
+ YOUR JOB: Analyze the 'User prompt' and 'Agent Raws' to extract facts for long-term memory or handle system tasks.
461
+ ${isMemoryEnabled ? `If user tell something that is important (like, hobbies, preferences, facts about user, hates, likes, etc) to know user better over time, use long term memory tools.` : ""}
462
+
463
+ ${JANITOR_TOOLS_PROTOCOL(isMemoryEnabled, needTitle)}
464
+
465
+ ${userMemories ? `-- CURRENT PERSISTENT USER MEMORIES --
466
+ ${userMemories}
467
+ -------------------------------------------------
468
+ ` : ""}
469
+ Current date and Time: ${(/* @__PURE__ */ new Date()).toLocaleString()}
470
+
471
+ --- END SYSTEM INSTRUCTION ---`.trim();
472
+ };
473
+
474
+ // src/utils/history.js
475
+ import fs3 from "fs-extra";
476
+ import path3 from "path";
477
+ import { fileURLToPath as fileURLToPath2 } from "url";
478
+ import { nanoid } from "nanoid";
479
+ var __filename2 = fileURLToPath2(import.meta.url);
480
+ var __dirname2 = path3.dirname(__filename2);
481
+ var AGENT_ROOT2 = path3.join(__dirname2, "../../");
482
+ var HISTORY_FILE = path3.join(AGENT_ROOT2, "secret/history.json");
483
+ var WRITE_LOCK = Promise.resolve();
484
+ var withLock = (op) => {
485
+ const nextLock = WRITE_LOCK.then(async () => {
486
+ try {
487
+ return await op();
488
+ } catch (e) {
489
+ console.error("Lock Operation Failed:", e);
490
+ throw e;
491
+ }
492
+ });
493
+ WRITE_LOCK = nextLock.catch(() => {
494
+ });
495
+ return nextLock;
496
+ };
497
+ var loadHistory = async () => {
498
+ if (await fs3.pathExists(HISTORY_FILE)) {
499
+ try {
500
+ return await fs3.readJson(HISTORY_FILE);
501
+ } catch (e) {
502
+ return {};
503
+ }
504
+ }
505
+ return {};
506
+ };
507
+ var saveChat = async (id, name, messages) => {
508
+ return withLock(async () => {
509
+ const history = await loadHistory();
510
+ const existingChat = history[id];
511
+ const finalName = name || (existingChat ? existingChat.name : `Session ${id.slice(-6)}`);
512
+ history[id] = {
513
+ name: finalName,
514
+ messages,
515
+ updatedAt: Date.now()
516
+ };
517
+ await fs3.ensureDir(path3.dirname(HISTORY_FILE));
518
+ await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
519
+ });
520
+ };
521
+ var saveChatTitle = async (id, title) => {
522
+ return withLock(async () => {
523
+ const history = await loadHistory();
524
+ if (history[id]) {
525
+ history[id].name = title;
526
+ history[id].updatedAt = Date.now();
527
+ } else {
528
+ history[id] = { name: title, messages: [], updatedAt: Date.now() };
529
+ }
530
+ await fs3.ensureDir(path3.dirname(HISTORY_FILE));
531
+ await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
532
+ });
533
+ };
534
+ var deleteChat = async (id) => {
535
+ return withLock(async () => {
536
+ const history = await loadHistory();
537
+ delete history[id];
538
+ await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
539
+ const tempFile = path3.join(AGENT_ROOT2, "secret/memory-temp.json");
540
+ if (await fs3.pathExists(tempFile)) {
541
+ try {
542
+ const temp = await fs3.readJson(tempFile);
543
+ if (temp[id]) {
544
+ delete temp[id];
545
+ await fs3.writeJson(tempFile, temp, { spaces: 2 });
546
+ }
547
+ } catch (e) {
548
+ }
549
+ }
550
+ return history;
551
+ });
552
+ };
553
+ var generateChatId = () => `flow-${nanoid(6)}`;
554
+ var cleanupOldHistory = async (retentionSetting) => {
555
+ if (!retentionSetting || retentionSetting === "Never") return;
556
+ const days = parseInt(retentionSetting);
557
+ if (isNaN(days)) return;
558
+ const history = await loadHistory();
559
+ const now = Date.now();
560
+ const threshold = days * 24 * 60 * 60 * 1e3;
561
+ let deletedCount = 0;
562
+ for (const id in history) {
563
+ const chat2 = history[id];
564
+ if (chat2.updatedAt && now - chat2.updatedAt > threshold) {
565
+ await deleteChat(id);
566
+ deletedCount++;
567
+ }
568
+ }
569
+ return deletedCount;
570
+ };
571
+ var getTruncatedHistory = (history, exchangesToRemove = 4) => {
572
+ if (history.length <= 1) return history;
573
+ const welcome = history[0];
574
+ const rest = history.slice(1);
575
+ const sliceIndex = exchangesToRemove * 2;
576
+ const truncated = rest.slice(sliceIndex);
577
+ return [welcome, ...truncated];
578
+ };
579
+
580
+ // src/utils/usage.js
581
+ import fs4 from "fs-extra";
582
+ import path4 from "path";
583
+ import { fileURLToPath as fileURLToPath3 } from "url";
584
+ var __dirname3 = path4.dirname(fileURLToPath3(import.meta.url));
585
+ var USAGE_PATH = path4.join(__dirname3, "../../secret/usage.json");
586
+ var getDailyUsage = async () => {
587
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
588
+ try {
589
+ if (await fs4.exists(USAGE_PATH)) {
590
+ const data = await fs4.readJson(USAGE_PATH);
591
+ if (data.date === today) {
592
+ return data.stats;
593
+ }
594
+ }
595
+ } catch (err) {
596
+ console.error("Failed to read usage:", err);
597
+ }
598
+ const defaultStats = { agent: 0, background: 0, search: 0 };
599
+ await fs4.writeJson(USAGE_PATH, { date: today, stats: defaultStats }, { spaces: 2 });
600
+ return defaultStats;
601
+ };
602
+ var incrementUsage = async (key) => {
603
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
604
+ const data = await fs4.readJson(USAGE_PATH).catch(() => ({ date: today, stats: { agent: 0, background: 0, search: 0 } }));
605
+ if (data.date !== today) {
606
+ data.date = today;
607
+ data.stats = { agent: 0, background: 0, search: 0 };
608
+ }
609
+ if (data.stats[key] !== void 0) {
610
+ data.stats[key]++;
611
+ await fs4.writeJson(USAGE_PATH, data, { spaces: 2 });
612
+ }
613
+ };
614
+ var checkQuota = async (key, settings) => {
615
+ const usage = await getDailyUsage();
616
+ const tier = settings.apiTier || "Free";
617
+ const quotas = settings.quotas || {};
618
+ if (tier === "Free") {
619
+ if (key === "agent" || key === "background") {
620
+ return usage.agent + usage.background < 1500;
621
+ }
622
+ if (key === "search") return true;
623
+ }
624
+ if (tier === "Paid" || tier === "Custom") {
625
+ if (key === "agent") return usage.agent < (quotas.agentLimit || 1500);
626
+ if (key === "background") return usage.background < (quotas.backgroundLimit || 1500);
627
+ if (key === "search") return true;
628
+ return true;
629
+ }
630
+ return true;
631
+ };
632
+
633
+ // src/tools/web_search.js
634
+ import * as cuimp from "cuimp";
635
+
636
+ // src/utils/arg_parser.js
637
+ var parseArgs = (argsString) => {
638
+ const args = {};
639
+ const regex = /(\w+)\s*=\s*(?:(["'])((?:\\.|(?!\2)[\s\S])*)\2|([^,\s\)]+))/g;
640
+ let match;
641
+ while ((match = regex.exec(argsString)) !== null) {
642
+ const key = match[1];
643
+ let value = match[3] !== void 0 ? match[3] : match[4];
644
+ if (match[3] !== void 0) {
645
+ try {
646
+ value = JSON.parse(`"${value.replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`);
647
+ } catch (e) {
648
+ value = value.replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, "\\");
649
+ }
650
+ }
651
+ if (value === "true") value = true;
652
+ else if (value === "false") value = false;
653
+ else if (typeof value === "string" && !isNaN(value) && value.trim() !== "") value = Number(value);
654
+ args[key] = value;
655
+ }
656
+ return args;
657
+ };
658
+
659
+ // src/tools/web_search.js
660
+ import fs5 from "fs";
661
+ import path5 from "path";
662
+ import { fileURLToPath as fileURLToPath4 } from "url";
663
+ var __filename3 = fileURLToPath4(import.meta.url);
664
+ var __dirname4 = path5.dirname(__filename3);
665
+ var AGENT_ROOT3 = path5.join(__dirname4, "../../");
666
+ var web_search = async (argsString) => {
667
+ const { query, limit = 10 } = parseArgs(argsString);
668
+ if (!query) return 'ERROR: Missing "query" argument for web_search.';
669
+ try {
670
+ await new Promise((r) => setTimeout(r, Math.random() * 1e3 + 500));
671
+ const response = await cuimp.get(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`, {
672
+ headers: {
673
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
674
+ }
675
+ });
676
+ const html = response.data;
677
+ const results = [];
678
+ const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g;
679
+ let match;
680
+ let count = 0;
681
+ while ((match = resultRegex.exec(html)) !== null && count < limit) {
682
+ let url = match[1];
683
+ if (url.includes("uddg=")) {
684
+ url = decodeURIComponent(url.split("uddg=")[1].split("&")[0]);
685
+ }
686
+ const title = match[2].replace(/<[^>]*>/g, "").trim();
687
+ const snippet = match[3].replace(/<[^>]*>/g, "").trim();
688
+ results.push(`${count + 1}. ${title}
689
+ Source: ${url}
690
+ Snippet: ${snippet}`);
691
+ count++;
692
+ }
693
+ const toolLogDir = path5.join(AGENT_ROOT3, "logs", "tools");
694
+ if (!fs5.existsSync(toolLogDir)) {
695
+ fs5.mkdirSync(toolLogDir, { recursive: true });
696
+ }
697
+ fs5.appendFileSync(path5.join(toolLogDir, "search-results.log"), `RESULTS ${(/* @__PURE__ */ new Date()).toISOString()} -
698
+ Query: [${query}]. Results Count: ${results.length}.
699
+ Results: ${results}
700
+
701
+
702
+ `);
703
+ if (results.length === 0) {
704
+ if (html.includes("anomaly")) {
705
+ const toolErrDir = path5.join(AGENT_ROOT3, "logs", "tools");
706
+ if (!fs5.existsSync(toolErrDir)) {
707
+ fs5.mkdirSync(toolErrDir, { recursive: true });
708
+ }
709
+ fs5.appendFileSync(path5.join(toolErrDir, "error.log"), `ERROR ${(/* @__PURE__ */ new Date()).toISOString()} - DDG detected unusual activity. Cuimp impersonation might need adjustment.
710
+ `);
711
+ throw new Error("DDG detected unusual activity. Cuimp impersonation might need adjustment.");
712
+ }
713
+ return `No results found for query: [${query}].`;
714
+ }
715
+ const finalResults = results.join("\n\n");
716
+ return `Search results for [${query}]:
717
+
718
+ ${finalResults}`;
719
+ } catch (err) {
720
+ return `ERROR: Stealth Search failed. ${err.message}`;
721
+ }
722
+ };
723
+
724
+ // src/tools/web_scrape.js
725
+ import fs6 from "fs";
726
+ import path6 from "path";
727
+ import { fileURLToPath as fileURLToPath5 } from "url";
728
+ import * as cuimp2 from "cuimp";
729
+ var __filename4 = fileURLToPath5(import.meta.url);
730
+ var __dirname5 = path6.dirname(__filename4);
731
+ var AGENT_ROOT4 = path6.join(__dirname5, "../../");
732
+ var web_scrape = async (args) => {
733
+ const urlMatch = args.match(/url\s*=\s*["'](.*)["']/);
734
+ const url = urlMatch ? urlMatch[1] : args;
735
+ try {
736
+ const response = await cuimp2.get(url, {
737
+ headers: {
738
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
739
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
740
+ "Accept-Language": "en-US,en;q=0.5"
741
+ }
742
+ });
743
+ let html = response.data;
744
+ if (!html) throw new Error("No content received from URL.");
745
+ html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
746
+ html = html.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "");
747
+ html = html.replace(/<nav\b[^<]*(?:(?!<\/nav>)<[^<]*)*<\/nav>/gi, "");
748
+ html = html.replace(/<footer\b[^<]*(?:(?!<\/footer>)<[^<]*)*<\/footer>/gi, "");
749
+ html = html.replace(/<header\b[^<]*(?:(?!<\/header>)<[^<]*)*<\/header>/gi, "");
750
+ let text = html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
751
+ const finalContent = text.substring(0, 2e4);
752
+ const toolLogDir = path6.join(AGENT_ROOT4, "logs", "tools");
753
+ if (!fs6.existsSync(toolLogDir)) {
754
+ fs6.mkdirSync(toolLogDir, { recursive: true });
755
+ }
756
+ fs6.appendFileSync(path6.join(toolLogDir, "search-scraped.log"), `RESULTS ${(/* @__PURE__ */ new Date()).toISOString()} -
757
+ Query: [${url}].
758
+ Results: ${finalContent}
759
+
760
+
761
+ `);
762
+ return `CONTENT FROM [${url}]:
763
+
764
+ ${finalContent}${text.length > 2e4 ? "\n\n[TRUNCATED AT 20K CHARS]" : ""}`;
765
+ } catch (err) {
766
+ return `ERROR: Failed to read page at ${url}. ${err.message}`;
767
+ }
768
+ };
769
+
770
+ // src/tools/memory.js
771
+ import path7 from "path";
772
+ import { fileURLToPath as fileURLToPath6 } from "url";
773
+ var __filename5 = fileURLToPath6(import.meta.url);
774
+ var __dirname6 = path7.dirname(__filename5);
775
+ var AGENT_ROOT5 = path7.join(__dirname6, "../../");
776
+ var SECRET_DIR2 = path7.join(AGENT_ROOT5, "secret");
777
+ var MEMORIES_PATH = path7.join(SECRET_DIR2, "memories.json");
778
+ var TEMP_MEM_PATH = path7.join(SECRET_DIR2, "memory-temp.json");
779
+ var memory = async (rawArgs, context = {}) => {
780
+ const parseArg = (key) => {
781
+ const regex = new RegExp(`${key}\\s*=\\s*(["'])(.*?)\\1(?=\\s*[,)]|\\s+\\w+\\s*=|$)`, "s");
782
+ const match = rawArgs.match(regex);
783
+ return match ? match[2].trim() : null;
784
+ };
785
+ const action = parseArg("action");
786
+ const method = parseArg("method");
787
+ const content = parseArg("content");
788
+ const contentNew = parseArg("content-new");
789
+ const contentOld = parseArg("content-old");
790
+ const chatId = parseArg("chat-id") || context.chatId || context.sessionId || "default-session";
791
+ if (action === "temp") {
792
+ if (!content) return "ERROR: Missing 'content' for temp memory.";
793
+ const tempStorage = readEncryptedJson(TEMP_MEM_PATH, {});
794
+ if (!tempStorage[chatId]) tempStorage[chatId] = [];
795
+ const MAX_CHARS = 4e3 * 4;
796
+ let currentTotalLength = tempStorage[chatId].reduce((acc, m) => acc + m.length, 0);
797
+ while (tempStorage[chatId].length > 0 && currentTotalLength + content.length > MAX_CHARS) {
798
+ const removed = tempStorage[chatId].shift();
799
+ currentTotalLength -= removed.length;
800
+ }
801
+ tempStorage[chatId].push(content);
802
+ writeEncryptedJson(TEMP_MEM_PATH, tempStorage);
803
+ return `SUCCESS: Temporary context saved for session [${chatId}]. (Size: ${currentTotalLength + content.length} chars)`;
804
+ }
805
+ if (action === "user") {
806
+ const memories = readEncryptedJson(MEMORIES_PATH, []);
807
+ if (method === "add") {
808
+ if (!content) return "ERROR: Missing 'content' for memory addition.";
809
+ const MAX_CHARS = 3e3 * 4;
810
+ let currentTotalLength = memories.reduce((acc, m) => acc + (m.memory?.length || 0), 0);
811
+ while (memories.length > 0 && currentTotalLength + content.length > MAX_CHARS) {
812
+ const removed = memories.shift();
813
+ currentTotalLength -= removed.memory?.length || 0;
814
+ }
815
+ const newMemory = { id: `mem-${Date.now().toString(36)}`, memory: content };
816
+ memories.push(newMemory);
817
+ writeEncryptedJson(MEMORIES_PATH, memories);
818
+ return `SUCCESS: Memory added with ID [${newMemory.id}]. (Vault Size: ${currentTotalLength + content.length} chars)`;
819
+ }
820
+ if (method === "update") {
821
+ const memId = contentOld;
822
+ const newText = contentNew;
823
+ if (!memId || !newText) return "ERROR: Missing 'content-old' (id) or 'content-new' for update.";
824
+ const index = memories.findIndex((m) => m.id === memId);
825
+ if (index === -1) return `ERROR: Memory ID [${memId}] not found.`;
826
+ memories[index].memory = newText;
827
+ writeEncryptedJson(MEMORIES_PATH, memories);
828
+ return `SUCCESS: Memory [${memId}] updated.`;
829
+ }
830
+ if (method === "delete") {
831
+ const memId = content;
832
+ if (!memId) return "ERROR: Missing 'content' (id) for deletion.";
833
+ const initialLen = memories.length;
834
+ const updatedMemories = memories.filter((m) => m.id !== memId);
835
+ if (updatedMemories.length === initialLen) return `ERROR: Memory ID [${memId}] not found.`;
836
+ writeEncryptedJson(MEMORIES_PATH, updatedMemories);
837
+ return `SUCCESS: Memory [${memId}] deleted.`;
838
+ }
839
+ return `ERROR: Invalid method [${method}] for user memory. Use 'add', 'update', or 'delete'.`;
840
+ }
841
+ return `ERROR: Unknown action [${action}] for memory tool.`;
842
+ };
843
+
844
+ // src/tools/chat.js
845
+ var chat = async (rawArgs, context = {}) => {
846
+ const title = parseArgs(rawArgs).title;
847
+ const chatId = context.chatId || context.sessionId;
848
+ if (!chatId) return "ERROR: No active chatId found in tool context.";
849
+ if (!title) return "ERROR: Missing 'title' argument.";
850
+ try {
851
+ await saveChatTitle(chatId, title);
852
+ return `SUCCESS: Chat title updated to [${title}] for session [${chatId}].`;
853
+ } catch (err) {
854
+ return `ERROR: Failed to update chat title: ${err.message}`;
855
+ }
856
+ };
857
+
858
+ // src/tools/summary.js
859
+ var summary = async (rawArgs, context = {}) => {
860
+ const parseArg = (key) => {
861
+ const regex = new RegExp(`${key}\\s*=\\s*(["'])(.*?)\\1(?=\\s*[,)]|\\s+\\w+\\s*=|$)`, "s");
862
+ const match = rawArgs.match(regex);
863
+ return match ? match[2].trim() : null;
864
+ };
865
+ const content = parseArg("content");
866
+ const chatId = context.chatId;
867
+ const { startIndex, endIndex } = context.summarizedIndices || {};
868
+ if (!chatId) return "ERROR: No active chatId found in tool context.";
869
+ if (!content) return "ERROR: Missing 'content' argument.";
870
+ if (startIndex === void 0 || endIndex === void 0) {
871
+ return "ERROR: Summary tool called without target range indices in context.";
872
+ }
873
+ try {
874
+ const history = await loadHistory();
875
+ if (history[chatId]) {
876
+ const messages = history[chatId].messages;
877
+ const summaryMsg = {
878
+ id: `summary-${Date.now()}`,
879
+ role: "system",
880
+ text: content
881
+ };
882
+ const actualStart = Math.min(startIndex, messages.length - 1);
883
+ const actualEnd = Math.min(endIndex, messages.length - 1);
884
+ const count = actualEnd - actualStart + 1;
885
+ if (count > 0) {
886
+ messages.splice(actualStart, count, summaryMsg);
887
+ await saveChat(chatId, history[chatId].name, messages);
888
+ return `SUCCESS: Compressed ${count} turns into a summary block.`;
889
+ }
890
+ return "ERROR: Targeted range for summarization is invalid or empty.";
891
+ }
892
+ return "ERROR: Chat session not found.";
893
+ } catch (err) {
894
+ return `ERROR: Failed to save summary: ${err.message}`;
895
+ }
896
+ };
897
+
898
+ // src/tools/list_files.js
899
+ import fs7 from "fs";
900
+ import path8 from "path";
901
+ var list_files = async (args) => {
902
+ const { path: targetPath = "." } = parseArgs(args);
903
+ const absolutePath = path8.resolve(process.cwd(), targetPath);
904
+ try {
905
+ if (!fs7.existsSync(absolutePath)) {
906
+ return `ERROR: Path [${targetPath}] does not exist.`;
907
+ }
908
+ const stats = fs7.statSync(absolutePath);
909
+ if (!stats.isDirectory()) {
910
+ return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
911
+ }
912
+ const files2 = fs7.readdirSync(absolutePath);
913
+ if (files2.length === 0) {
914
+ return `Directory [${targetPath}] is empty.`;
915
+ }
916
+ const totalFiles = files2.length;
917
+ const maxDisplay = 100;
918
+ const displayFiles = files2.slice(0, maxDisplay);
919
+ const list = displayFiles.map((file) => {
920
+ const fPath = path8.join(absolutePath, file);
921
+ let indicator = "\u{1F4C4}";
922
+ let metaPart = "";
923
+ try {
924
+ const fStats = fs7.statSync(fPath);
925
+ indicator = fStats.isDirectory() ? "\u{1F4C1}" : "\u{1F4C4}";
926
+ const sizeKB = (fStats.size / 1024).toFixed(1);
927
+ metaPart = fStats.isFile() ? ` - [${sizeKB} KB]` : "";
928
+ } catch (e) {
929
+ indicator = "\u2753";
930
+ metaPart = " - [Access Denied]";
931
+ }
932
+ return `${indicator} ${file}${metaPart}`;
933
+ }).join("\n");
934
+ let footer = `
935
+
936
+ (Total items: ${totalFiles})`;
937
+ if (totalFiles > maxDisplay) {
938
+ footer = `
939
+
940
+ \u26A0\uFE0F TRUNCATED: Showing first ${maxDisplay} of ${totalFiles} items. Use more specific paths to see others.`;
941
+ }
942
+ const result = `Contents of [${targetPath}]:
943
+
944
+ ${list}${footer}`;
945
+ files2.length = 0;
946
+ displayFiles.length = 0;
947
+ return result;
948
+ } catch (err) {
949
+ return `ERROR: Failed to list files in [${targetPath}]: ${err.message}`;
950
+ }
951
+ };
952
+
953
+ // src/tools/view_file.js
954
+ import fs8 from "fs";
955
+ import path9 from "path";
956
+ var view_file = async (args) => {
957
+ const { path: targetPath, start_line = 1, end_line = 500 } = parseArgs(args);
958
+ if (!targetPath) return 'ERROR: Missing "path" argument for view_file.';
959
+ const absolutePath = path9.resolve(process.cwd(), targetPath);
960
+ try {
961
+ if (!fs8.existsSync(absolutePath)) {
962
+ return `ERROR: File [${targetPath}] does not exist.`;
963
+ }
964
+ const stats = fs8.statSync(absolutePath);
965
+ if (stats.isDirectory()) {
966
+ return `ERROR: Path [${targetPath}] is a directory. Use list_files instead.`;
967
+ }
968
+ const content = fs8.readFileSync(absolutePath, "utf8");
969
+ const lines = content.split("\n");
970
+ const totalLines = lines.length;
971
+ const start = Math.max(0, start_line - 1);
972
+ const end = Math.min(totalLines, end_line);
973
+ const resultLines = lines.slice(start, end);
974
+ const header = `File: [${targetPath}] (Showing lines ${start + 1}-${end} of ${totalLines})`;
975
+ const code = resultLines.map((line, i) => `${String(start + i + 1).padStart(4)}: ${line}`).join("\n");
976
+ return `${header}
977
+
978
+ ${code}`;
979
+ } catch (err) {
980
+ return `ERROR: Failed to read file [${targetPath}]: ${err.message}`;
981
+ }
982
+ };
983
+
984
+ // src/tools/write_file.js
985
+ import fs9 from "fs";
986
+ import path10 from "path";
987
+ var write_file = async (args) => {
988
+ let { path: targetPath, content } = parseArgs(args);
989
+ if (!targetPath) return 'ERROR: Missing "path" argument for write_file.';
990
+ if (content === void 0) return 'ERROR: Missing "content" argument for write_file.';
991
+ content = content.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").trim();
992
+ const absolutePath = path10.resolve(process.cwd(), targetPath);
993
+ const parentDir = path10.dirname(absolutePath);
994
+ try {
995
+ if (!fs9.existsSync(parentDir)) {
996
+ fs9.mkdirSync(parentDir, { recursive: true });
997
+ }
998
+ const lineCount = content.split(/\r?\n/).length;
999
+ const originalSize = Buffer.byteLength(content, "utf8");
1000
+ fs9.writeFileSync(absolutePath, content, "utf8");
1001
+ let verifiedContent = fs9.readFileSync(absolutePath, "utf8");
1002
+ const verifiedSize = Buffer.byteLength(verifiedContent, "utf8");
1003
+ const verifiedLines = verifiedContent.split(/\r?\n/);
1004
+ const verifiedLineCount = verifiedLines.length;
1005
+ if (verifiedSize === 0 && originalSize > 0) {
1006
+ verifiedContent = null;
1007
+ return `CRITICAL FAILURE: Verification failed. File [${targetPath}] is empty on disk despite success report!`;
1008
+ }
1009
+ let snippet = "";
1010
+ if (verifiedLineCount <= 30) {
1011
+ snippet = verifiedLines.join("\n");
1012
+ } else {
1013
+ const head = verifiedLines.slice(0, 15).join("\n");
1014
+ const tail = verifiedLines.slice(-15).join("\n");
1015
+ snippet = `${head}
1016
+
1017
+ ... [${verifiedLineCount - 30} lines truncated for history stability] ...
1018
+
1019
+ ${tail}`;
1020
+ }
1021
+ verifiedContent = null;
1022
+ return `SUCCESS: File [${targetPath}] verified and persisted.
1023
+
1024
+ - Stats: [${verifiedLineCount} lines, ${(verifiedSize / 1024).toFixed(1)} KB]
1025
+ - Content Preview:
1026
+ ${snippet}`;
1027
+ } catch (err) {
1028
+ return `ERROR: Failed to write file [${targetPath}]: ${err.message}`;
1029
+ }
1030
+ };
1031
+
1032
+ // src/tools/update_file.js
1033
+ import fs10 from "fs";
1034
+ import path11 from "path";
1035
+ var update_file = async (args) => {
1036
+ let { path: targetPath, content_to_replace, content_to_add } = parseArgs(args);
1037
+ if (!targetPath) return 'ERROR: Missing "path" argument for update_file.';
1038
+ if (content_to_replace === void 0) return 'ERROR: Missing "content_to_replace" argument.';
1039
+ if (content_to_add === void 0) return 'ERROR: Missing "content_to_add" argument.';
1040
+ const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").trim();
1041
+ content_to_replace = strip(content_to_replace);
1042
+ content_to_add = strip(content_to_add);
1043
+ const absolutePath = path11.resolve(process.cwd(), targetPath);
1044
+ try {
1045
+ if (!fs10.existsSync(absolutePath)) {
1046
+ return `ERROR: File [${targetPath}] does not exist. Use write_file instead.`;
1047
+ }
1048
+ const currentContent = fs10.readFileSync(absolutePath, "utf8");
1049
+ if (!currentContent.includes(content_to_replace)) {
1050
+ return `ERROR: Could not find exact match for the specified "content_to_replace" in [${targetPath}]. Check indentation/whitespace/line breaks(LF or CRLF)/string. Try re-reading the file for latest changes.`;
1051
+ }
1052
+ const startPos = currentContent.indexOf(content_to_replace);
1053
+ const startLine = currentContent.substring(0, startPos).split(/\r?\n/).length;
1054
+ const instances = currentContent.split(content_to_replace).length - 1;
1055
+ const newFileContent = currentContent.split(content_to_replace).join(content_to_add);
1056
+ fs10.writeFileSync(absolutePath, newFileContent, "utf8");
1057
+ const allOriginalLines = currentContent.split(/\r?\n/);
1058
+ const oldLines = content_to_replace.split(/\r?\n/);
1059
+ const newLines = content_to_add.split(/\r?\n/);
1060
+ const endLine = startLine + oldLines.length - 1;
1061
+ let diffText = `SUCCESS: File [${targetPath}] updated. [${instances}] instances replaced.
1062
+
1063
+ `;
1064
+ diffText += `[DIFF_START]
1065
+ `;
1066
+ const contextStart = Math.max(0, startLine - 16);
1067
+ for (let i = contextStart; i < startLine - 1; i++) {
1068
+ diffText += `[UI_CONTEXT] ${i + 1}| ${allOriginalLines[i]}
1069
+ `;
1070
+ }
1071
+ oldLines.forEach((line, i) => {
1072
+ diffText += `-${startLine + i}| ${line}
1073
+ `;
1074
+ });
1075
+ newLines.forEach((line, i) => {
1076
+ diffText += `+${startLine + i}| ${line}
1077
+ `;
1078
+ });
1079
+ for (let i = endLine; i < Math.min(allOriginalLines.length, endLine + 15); i++) {
1080
+ diffText += `[UI_CONTEXT] ${i + 1}| ${allOriginalLines[i]}
1081
+ `;
1082
+ }
1083
+ diffText += `[DIFF_END]`;
1084
+ return diffText;
1085
+ } catch (err) {
1086
+ return `ERROR: Failed to update file [${targetPath}]: ${err.message}`;
1087
+ }
1088
+ };
1089
+
1090
+ // src/tools/exec_command.js
1091
+ import { spawn } from "child_process";
1092
+ var exec_command = async (args, options = {}) => {
1093
+ const { command } = parseArgs(args);
1094
+ const { onChunk } = options;
1095
+ if (!command) return 'ERROR: Missing "command" argument for exec_command.';
1096
+ return new Promise((resolve) => {
1097
+ const child = spawn(command, { shell: true, cwd: process.cwd() });
1098
+ let stdout = "";
1099
+ let stderr = "";
1100
+ child.stdout.on("data", (data) => {
1101
+ const chunk = data.toString();
1102
+ stdout += chunk;
1103
+ if (onChunk) onChunk(chunk);
1104
+ });
1105
+ child.stderr.on("data", (data) => {
1106
+ const chunk = data.toString();
1107
+ stderr += chunk;
1108
+ if (onChunk) onChunk(chunk);
1109
+ });
1110
+ child.on("close", (code) => {
1111
+ const result = [];
1112
+ if (stdout) result.push(`STDOUT:
1113
+ ${stdout}`);
1114
+ if (stderr) result.push(`STDERR:
1115
+ ${stderr}`);
1116
+ if (code !== 0) result.push(`EXIT CODE: ${code}`);
1117
+ const finalOutput = result.join("\n\n") || "Command executed with no output.";
1118
+ if (code !== 0) {
1119
+ resolve(`FAILURE: Command [${command}] failed.
1120
+
1121
+ ${finalOutput}`);
1122
+ } else {
1123
+ resolve(`SUCCESS: Command [${command}] completed.
1124
+
1125
+ ${finalOutput}`);
1126
+ }
1127
+ });
1128
+ child.on("error", (err) => {
1129
+ resolve(`ERROR: Failed to start command [${command}]: ${err.message}`);
1130
+ });
1131
+ });
1132
+ };
1133
+
1134
+ // src/tools/read_folder.js
1135
+ import fs11 from "fs";
1136
+ import path12 from "path";
1137
+ var read_folder = async (args) => {
1138
+ const { path: targetPath = "." } = parseArgs(args);
1139
+ const absolutePath = path12.resolve(process.cwd(), targetPath);
1140
+ try {
1141
+ if (!fs11.existsSync(absolutePath)) {
1142
+ return `ERROR: Path [${targetPath}] does not exist.`;
1143
+ }
1144
+ const stats = fs11.statSync(absolutePath);
1145
+ if (!stats.isDirectory()) {
1146
+ return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
1147
+ }
1148
+ const totalItems = files.length;
1149
+ const maxDisplay = 100;
1150
+ const displayItems = files.slice(0, maxDisplay);
1151
+ const folderData = [];
1152
+ for (const file of displayItems) {
1153
+ const fPath = path12.join(absolutePath, file);
1154
+ let indicator = "\u{1F4C4}";
1155
+ let info = { name: file, type: "unknown", size: "N/A", mtime: "N/A" };
1156
+ try {
1157
+ const fStats = fs11.statSync(fPath);
1158
+ info = {
1159
+ name: file,
1160
+ type: fStats.isDirectory() ? "directory" : "file",
1161
+ size: (fStats.size / 1024).toFixed(1) + " KB",
1162
+ mtime: fStats.mtime.toLocaleString()
1163
+ };
1164
+ } catch (e) {
1165
+ info.type = "inaccessible";
1166
+ }
1167
+ folderData.push(info);
1168
+ }
1169
+ const formatted = folderData.map((f) => {
1170
+ const indicator = f.type === "directory" ? "\u{1F4C1}" : f.type === "file" ? "\u{1F4C4}" : "\u2753";
1171
+ if (f.type === "directory") {
1172
+ return `${indicator} ${f.name} - [DIR] - [Modified: ${f.mtime}]`;
1173
+ }
1174
+ return `${indicator} ${f.name} - [Size: ${f.size}] - [Modified: ${f.mtime}]`;
1175
+ }).join("\n");
1176
+ let footer = `
1177
+
1178
+ (Total items in folder: ${totalItems})`;
1179
+ if (totalItems > maxDisplay) {
1180
+ footer = `
1181
+
1182
+ \u26A0\uFE0F TRUNCATED: Showing first ${maxDisplay} of ${totalItems} items.`;
1183
+ }
1184
+ const result = `Detailed folder stats for [${targetPath}]:
1185
+
1186
+ ${formatted}${footer}`;
1187
+ files.length = 0;
1188
+ displayItems.length = 0;
1189
+ folderData.length = 0;
1190
+ return result;
1191
+ } catch (err) {
1192
+ return `ERROR: Failed to read folder [${targetPath}]: ${err.message}`;
1193
+ }
1194
+ };
1195
+
1196
+ // src/utils/tools.js
1197
+ var TOOL_MAP = {
1198
+ web_search,
1199
+ web_scrape,
1200
+ memory,
1201
+ chat,
1202
+ summary,
1203
+ list_files,
1204
+ view_file,
1205
+ write_file,
1206
+ update_file,
1207
+ exec_command,
1208
+ read_folder
1209
+ };
1210
+ var dispatchTool = async (toolName, args, context = {}) => {
1211
+ const tool = TOOL_MAP[toolName];
1212
+ if (!tool) {
1213
+ return `ERROR: Tool [${toolName}] not found in registry.`;
1214
+ }
1215
+ try {
1216
+ return await tool(args, context);
1217
+ } catch (err) {
1218
+ return `ERROR: Execution failed for [${toolName}]: ${err.message}`;
1219
+ }
1220
+ };
1221
+
1222
+ // src/utils/ai.js
1223
+ import { fileURLToPath as fileURLToPath7 } from "url";
1224
+ import path13 from "path";
1225
+ import fs12 from "fs";
1226
+ var __filename6 = fileURLToPath7(import.meta.url);
1227
+ var __dirname7 = path13.dirname(__filename6);
1228
+ var AGENT_ROOT6 = path13.join(__dirname7, "../../");
1229
+ var client = null;
1230
+ var TEMP_MEM_PATH2 = path13.join(AGENT_ROOT6, "secret", "memory-temp.json");
1231
+ var PERSISTENT_MEM_PATH = path13.join(AGENT_ROOT6, "secret", "memories.json");
1232
+ var TERMINATION_SIGNAL = false;
1233
+ var signalTermination = () => {
1234
+ TERMINATION_SIGNAL = true;
1235
+ };
1236
+ var detectToolCalls = (text) => {
1237
+ const results = [];
1238
+ const trigger = "tool:functions.";
1239
+ let searchIdx = 0;
1240
+ while (true) {
1241
+ const startIdx = text.indexOf(trigger, searchIdx);
1242
+ if (startIdx === -1) break;
1243
+ const openParenIdx = text.indexOf("(", startIdx + trigger.length);
1244
+ if (openParenIdx === -1) {
1245
+ searchIdx = startIdx + trigger.length;
1246
+ continue;
1247
+ }
1248
+ const toolName = text.substring(startIdx + trigger.length, openParenIdx).trim();
1249
+ let balance = 1;
1250
+ let endIdx = -1;
1251
+ for (let i = openParenIdx + 1; i < text.length; i++) {
1252
+ if (text[i] === "(") balance++;
1253
+ if (text[i] === ")") balance--;
1254
+ if (balance === 0) {
1255
+ endIdx = i;
1256
+ break;
1257
+ }
1258
+ }
1259
+ if (endIdx === -1) {
1260
+ searchIdx = openParenIdx + 1;
1261
+ continue;
1262
+ }
1263
+ const fullMatch = text.substring(startIdx, endIdx + 1);
1264
+ const args = text.substring(openParenIdx + 1, endIdx);
1265
+ results.push({ fullMatch, toolName, args });
1266
+ searchIdx = endIdx + 1;
1267
+ }
1268
+ return results;
1269
+ };
1270
+ var initAI = (apiKey) => {
1271
+ if (!apiKey) return null;
1272
+ client = new GoogleGenAI({ apiKey });
1273
+ return client;
1274
+ };
1275
+ var getAIStream = async function* (modelName, history, settings, steeringCallback) {
1276
+ if (!client) throw new Error("AI not initialized");
1277
+ const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats } = settings;
1278
+ const isMemoryEnabled = systemSettings?.memory !== false;
1279
+ const originalText = history[history.length - 1].text;
1280
+ const isFirstPrompt = history.filter((m) => m.role === "user").length === 1;
1281
+ const hasTitleSignal = originalText.includes("[TITLE-UPDATE]");
1282
+ const needTitle = isFirstPrompt || hasTitleSignal;
1283
+ const agentText = originalText.replace(/\[TITLE-UPDATE\]/g, "").trim();
1284
+ let modifiedHistory = [...history.slice(0, -1)];
1285
+ if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > 128e3) {
1286
+ modifiedHistory = getTruncatedHistory(modifiedHistory, 4);
1287
+ }
1288
+ const tempStorage = readEncryptedJson(TEMP_MEM_PATH2, {});
1289
+ const otherMemories = Object.entries(tempStorage).filter(([id]) => id !== chatId).flatMap(([_, mems]) => mems).map((mem) => `- ${mem}`).join("\n");
1290
+ const persistentStorage = readEncryptedJson(PERSISTENT_MEM_PATH, []);
1291
+ const mainUserMemories = persistentStorage.map((m) => `- ${m.memory}`).join("\n");
1292
+ const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
1293
+ const systemInstruction = getSystemInstruction(profile, thinkingLevel, mode, systemSettings, otherMemories, mainUserMemories, isMemoryEnabled);
1294
+ const firstUserMsg = `${systemInstruction}
1295
+
1296
+ USER_PROMPT: ${agentText}`.trim();
1297
+ modifiedHistory.push({ role: "user", text: firstUserMsg });
1298
+ let lastUsage = null;
1299
+ const MAX_LOOPS = mode === "Flux" ? 50 : 5;
1300
+ const MAX_RETRIES = 3;
1301
+ yield { type: "status", content: "Working..." };
1302
+ TERMINATION_SIGNAL = false;
1303
+ let fullAgentResponse = "";
1304
+ for (let loop = 0; loop < MAX_LOOPS; loop++) {
1305
+ if (TERMINATION_SIGNAL) {
1306
+ yield { type: "status", content: "Termination Signal Received." };
1307
+ break;
1308
+ }
1309
+ if (steeringCallback) {
1310
+ const hint = await steeringCallback();
1311
+ if (hint) {
1312
+ modifiedHistory.push({ role: "user", text: `[STEERING HINT]: ${hint}` });
1313
+ yield { type: "status", content: "Steering Hint Injected." };
1314
+ }
1315
+ }
1316
+ yield { type: "turn_reset", content: true };
1317
+ const contents = modifiedHistory.filter((msg) => (msg.role === "user" || msg.role === "agent" || msg.role === "system") && !String(msg.id).startsWith("welcome")).map((msg) => ({
1318
+ role: msg.role === "user" || msg.role === "system" ? "user" : "model",
1319
+ parts: [{ text: msg.text }]
1320
+ }));
1321
+ let stream;
1322
+ let success = false;
1323
+ let retryCount = 0;
1324
+ while (retryCount <= MAX_RETRIES && !success) {
1325
+ try {
1326
+ if (!await checkQuota("agent", settings)) {
1327
+ throw new Error("Error: Daily Quota Exausted for Agent");
1328
+ }
1329
+ stream = await client.models.generateContentStream({
1330
+ model: modelName,
1331
+ contents,
1332
+ config: {
1333
+ temperature: mode === "Flux" ? 0.9 : 1.3,
1334
+ thinkingConfig: {
1335
+ includeThoughts: false,
1336
+ thinkingLevel: ThinkingLevel.MINIMAL
1337
+ }
1338
+ }
1339
+ });
1340
+ success = true;
1341
+ } catch (err) {
1342
+ const errMsg = err.message || String(err);
1343
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1344
+ const agentErrDir = path13.join(AGENT_ROOT6, "logs", "agent");
1345
+ if (!fs12.existsSync(agentErrDir)) {
1346
+ fs12.mkdirSync(agentErrDir, { recursive: true });
1347
+ }
1348
+ fs12.appendFileSync(path13.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errMsg}
1349
+ `);
1350
+ const isRetryable = errMsg.includes("429") || errMsg.includes("503") || errMsg.includes("overloaded") || errMsg.includes("deadline");
1351
+ if (isRetryable && retryCount < MAX_RETRIES) {
1352
+ retryCount++;
1353
+ yield { type: "status", content: `Retrying (${retryCount}/${MAX_RETRIES})...` };
1354
+ await new Promise((resolve) => setTimeout(resolve, 2e3 * retryCount));
1355
+ } else {
1356
+ throw new Error(`Model cannot be reached: ${errMsg}`);
1357
+ }
1358
+ }
1359
+ }
1360
+ let turnText = "";
1361
+ for await (const chunk of stream) {
1362
+ if (TERMINATION_SIGNAL) break;
1363
+ if (chunk.text) {
1364
+ turnText += chunk.text;
1365
+ yield { type: "text", content: chunk.text };
1366
+ }
1367
+ if (chunk.usageMetadata) {
1368
+ lastUsage = chunk.usageMetadata;
1369
+ }
1370
+ }
1371
+ await incrementUsage("agent");
1372
+ if (lastUsage) {
1373
+ yield { type: "usage", content: lastUsage };
1374
+ }
1375
+ fullAgentResponse += turnText + "\n";
1376
+ const turnTextLower = turnText.toLowerCase();
1377
+ const hasFinish = /\[?\s*(turn\s*:)?\s*finish\s*\]?/i.test(turnTextLower);
1378
+ const hasContinue = /\[?\s*(turn\s*:)?\s*continue\s*\]?/i.test(turnTextLower);
1379
+ const toolCalls = detectToolCalls(turnText);
1380
+ let toolResults = [];
1381
+ const shouldContinue = hasContinue || toolCalls.length > 0;
1382
+ if (toolCalls.length > 0) {
1383
+ for (const toolCall of toolCalls) {
1384
+ yield { type: "turn_reset", content: true };
1385
+ yield { type: "status", content: `Working (${toolCall.toolName})...` };
1386
+ let label = "";
1387
+ if (toolCall.toolName === "web_search") {
1388
+ const { query, limit = 10 } = parseArgs(toolCall.args);
1389
+ label = `\u{1F50D} SEARCHING: "${query}" (${limit})`.toUpperCase();
1390
+ } else if (toolCall.toolName === "web_scrape") {
1391
+ const url = parseArgs(toolCall.args).url || "...";
1392
+ label = `\u{1F4D6} READING SITE: ${url}`.toUpperCase();
1393
+ } else if (toolCall.toolName === "view_file") {
1394
+ label = `\u{1F4C4} READING FILE: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
1395
+ } else if (toolCall.toolName === "list_files" || toolCall.toolName === "read_folder") {
1396
+ const action = toolCall.toolName === "list_files" ? "LISTING" : "DISCOVERING";
1397
+ label = `\u{1F4C2} ${action} DIRECTORY: ${parseArgs(toolCall.args).path || "."}`.toUpperCase();
1398
+ } else if (toolCall.toolName === "write_file" || toolCall.toolName === "update_file") {
1399
+ const action = toolCall.toolName === "write_file" ? "WRITING" : "PATCHING";
1400
+ label = `\u{1F4BE} ${action} FILE: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
1401
+ } else if (toolCall.toolName === "exec_command") {
1402
+ label = "";
1403
+ } else {
1404
+ label = `EXECUTING ${toolCall.toolName}`.toUpperCase();
1405
+ }
1406
+ if (label) {
1407
+ const boxWidth = Math.min(label.length + 4, 85);
1408
+ const boxTop = `\u256D${"\u2500".repeat(boxWidth)}\u256E`;
1409
+ const boxMid = `\u2502 ${label.padEnd(boxWidth - 2).substring(0, boxWidth - 2)} \u2502`;
1410
+ const boxBottom = `\u2570${"\u2500".repeat(boxWidth)}\u256F`;
1411
+ yield { type: "text", content: `
1412
+
1413
+ ${boxTop}
1414
+ ${boxMid}
1415
+ ${boxBottom}
1416
+ ` };
1417
+ }
1418
+ if (toolCall.toolName === "exec_command") {
1419
+ const { command } = parseArgs(toolCall.args);
1420
+ if (command && settings.systemSettings && settings.systemSettings.allowExternalAccess === false) {
1421
+ const riskyPatterns = [
1422
+ /[a-zA-Z]:[\\\/]/i,
1423
+ // Any drive letter path (C:\, D:/, etc)
1424
+ /^\//,
1425
+ // Root path on Unix
1426
+ /\.\.[\\\/]/,
1427
+ // Parent directory traversal
1428
+ /\/etc\//,
1429
+ /\/var\//,
1430
+ /\/root\//,
1431
+ /\/bin\//,
1432
+ /\/usr\//
1433
+ // Sensitive Linux paths
1434
+ ];
1435
+ const currentDrive = path13.resolve(process.cwd()).substring(0, 3).toLowerCase();
1436
+ const isViolating = riskyPatterns.some((pattern) => {
1437
+ if (pattern.source === "[a-zA-Z]:[\\\\\\/]") {
1438
+ const driveMatch = command.match(/[a-zA-Z]:[\\\/]/i);
1439
+ return driveMatch && driveMatch[0].toLowerCase() !== currentDrive;
1440
+ }
1441
+ return pattern.test(command);
1442
+ });
1443
+ if (isViolating) {
1444
+ const denyMsg = `Access Denied. Terminal is prohibited from accessing system drives (C://) or external directories while "External Workspace Access" is disabled.`;
1445
+ toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
1446
+ yield { type: "tool_result", content: `[TOOL_RESULT]: ERROR: ${denyMsg}` };
1447
+ continue;
1448
+ }
1449
+ }
1450
+ if (settings.onExecStart) settings.onExecStart(command || "Unknown");
1451
+ yield { type: "exec_start" };
1452
+ }
1453
+ const parsedArgs = parseArgs(toolCall.args);
1454
+ const targetPath = parsedArgs.path || parsedArgs.targetPath || null;
1455
+ if (targetPath) {
1456
+ const isExternalOff = settings.systemSettings && settings.systemSettings.allowExternalAccess === false;
1457
+ const absoluteTarget = path13.resolve(targetPath);
1458
+ const absoluteCwd = path13.resolve(process.cwd());
1459
+ if (isExternalOff && !absoluteTarget.startsWith(absoluteCwd)) {
1460
+ const denyMsg = `Access Denied. You are not allowed to access files outside the current workspace. To enable this, ask the user to turn on "External Workspace Access" in /settings.`;
1461
+ toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
1462
+ yield { type: "tool_result", content: `[TOOL_RESULT]: ERROR: ${denyMsg}` };
1463
+ continue;
1464
+ }
1465
+ }
1466
+ if (settings.onToolApproval) {
1467
+ let shouldPrompt = false;
1468
+ if (toolCall.toolName === "write_file" || toolCall.toolName === "update_file") {
1469
+ shouldPrompt = true;
1470
+ } else if (toolCall.toolName === "exec_command") {
1471
+ shouldPrompt = true;
1472
+ }
1473
+ if (shouldPrompt) {
1474
+ const approval = await settings.onToolApproval(toolCall.toolName, toolCall.args);
1475
+ if (approval === "deny") {
1476
+ if (toolCall.toolName === "exec_command" && settings.onExecEnd) settings.onExecEnd();
1477
+ const denyMsg = `Permission Denied: User rejected the ${toolCall.toolName === "exec_command" ? "terminal execution" : "file edit"}.`;
1478
+ toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
1479
+ yield { type: "tool_result", content: `[TOOL_RESULT]: ERROR: ${denyMsg}` };
1480
+ continue;
1481
+ }
1482
+ }
1483
+ }
1484
+ const result = await dispatchTool(toolCall.toolName, toolCall.args, {
1485
+ chatId,
1486
+ history,
1487
+ onChunk: (chunk) => settings.onExecChunk ? settings.onExecChunk(chunk) : null
1488
+ });
1489
+ if (toolCall.toolName === "exec_command" && settings.onExecEnd) {
1490
+ await new Promise((resolve) => setTimeout(resolve, 800));
1491
+ settings.onExecEnd();
1492
+ }
1493
+ try {
1494
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1495
+ const isErr = result.startsWith("ERROR:");
1496
+ const logStatus = isErr ? result.trim() : "SUCCESS";
1497
+ const toolHistDir = path13.join(AGENT_ROOT6, "logs", "tools");
1498
+ if (!fs12.existsSync(toolHistDir)) {
1499
+ fs12.mkdirSync(toolHistDir, { recursive: true });
1500
+ }
1501
+ fs12.appendFileSync(path13.join(toolHistDir, "history.log"), `HISTORY [${timestamp}]: ${toolCall.toolName} [${logStatus}]
1502
+ `);
1503
+ } catch (logErr) {
1504
+ }
1505
+ const cleanResultForAI = result.split(/\r?\n/).filter((line) => !line.includes("[UI_CONTEXT]")).join("\n");
1506
+ toolResults.push(`[TOOL_RESULT]: ${cleanResultForAI}`);
1507
+ yield { type: "tool_result", content: `[TOOL_RESULT]: ${result}` };
1508
+ if (toolCall.toolName === "memory" && result.includes("SUCCESS")) {
1509
+ yield { type: "memory_updated" };
1510
+ }
1511
+ }
1512
+ }
1513
+ const cleanedTurnText = turnText.replace(/<think>[\s\S]*?<\/think>/g, "").replace(/\[?\s*(turn\s*:)?\s*(continue|finish)\s*\]?/gi, "").trim();
1514
+ if (hasFinish || !shouldContinue && toolResults.length === 0) {
1515
+ const lateHint = await steeringCallback();
1516
+ if (lateHint) {
1517
+ toolResults.push(`[USER STEERING HINT]: ${lateHint}`);
1518
+ yield { type: "status", content: "Steering detected... resuming!" };
1519
+ continue;
1520
+ }
1521
+ yield { type: "status", content: "Finalizing..." };
1522
+ const janitorContents = history.slice(-3).filter((msg) => msg.text && !msg.text.includes("[TOOL_RESULT]") && !msg.text.includes("OBSERVATION:")).map((msg) => ({
1523
+ role: msg.role === "user" ? "user" : "model",
1524
+ parts: [{ text: msg.text.replace(/<think>[\s\S]*?<\/think>/g, "").trim() }]
1525
+ }));
1526
+ const cleanedFullResponse = fullAgentResponse.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
1527
+ const janitorPrompt = getJanitorInstruction(
1528
+ agentText,
1529
+ cleanedFullResponse,
1530
+ janitorUserMemories,
1531
+ isMemoryEnabled,
1532
+ needTitle
1533
+ );
1534
+ janitorContents.push({ role: "user", parts: [{ text: janitorPrompt }] });
1535
+ let finalSynthesis = "";
1536
+ try {
1537
+ if (!await checkQuota("background", settings)) {
1538
+ console.warn("Quota Exhausted for Background Model. Skipping refinement.");
1539
+ throw new Error("QUOTA_BLOCKED");
1540
+ }
1541
+ const janitorResult = await client.models.generateContent({
1542
+ model: janitorModel || "gemma-4-26b-a4b-it",
1543
+ contents: janitorContents,
1544
+ config: {
1545
+ thinkingConfig: {
1546
+ includeThoughts: false,
1547
+ thinkingLevel: ThinkingLevel.MINIMAL
1548
+ }
1549
+ }
1550
+ });
1551
+ const parts = janitorResult.candidates?.[0]?.content?.parts;
1552
+ if (parts && parts[1]?.text) {
1553
+ finalSynthesis = parts[1].text;
1554
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1555
+ const janitorLogDir = path13.join(AGENT_ROOT6, "logs", "janitor");
1556
+ if (!fs12.existsSync(janitorLogDir)) {
1557
+ fs12.mkdirSync(janitorLogDir, { recursive: true });
1558
+ }
1559
+ fs12.appendFileSync(path13.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: ${finalSynthesis}
1560
+ `);
1561
+ } else if (parts && parts[0]?.text) finalSynthesis = parts[0].text;
1562
+ else if (janitorResult.response && janitorResult.response.text) finalSynthesis = janitorResult.response.text();
1563
+ else throw new Error("No synthesis generated by Janitor.");
1564
+ await incrementUsage("background");
1565
+ yield { type: "background_increment" };
1566
+ const janitorToolCalls = detectToolCalls(finalSynthesis);
1567
+ for (const janitorToolCall of janitorToolCalls) {
1568
+ const toolContext = { chatId, sessionId: chatId, history };
1569
+ const result = await dispatchTool(janitorToolCall.toolName, janitorToolCall.args, toolContext);
1570
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1571
+ const janitorLogDir = path13.join(AGENT_ROOT6, "logs", "janitor");
1572
+ fs12.appendFileSync(path13.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${janitorToolCall.toolName}]: ${result}
1573
+ `);
1574
+ if (janitorToolCall.toolName === "memory" && !janitorToolCall.args.includes("action='temp'")) {
1575
+ yield { type: "memory_updated" };
1576
+ }
1577
+ }
1578
+ } catch (janitorErr) {
1579
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1580
+ const janitorErrDir = path13.join(AGENT_ROOT6, "logs", "janitor");
1581
+ if (!fs12.existsSync(janitorErrDir)) {
1582
+ fs12.mkdirSync(janitorErrDir, { recursive: true });
1583
+ }
1584
+ fs12.appendFileSync(path13.join(janitorErrDir, "error.log"), `ERROR [${date}]: ${janitorErr.message}
1585
+ `);
1586
+ console.error("Janitor Background Tasks Failed:", janitorErr.message);
1587
+ }
1588
+ const timestamp = `Responded on ${(/* @__PURE__ */ new Date()).toLocaleString()}`;
1589
+ const finalWithTime = `${cleanedFullResponse}
1590
+
1591
+ ${timestamp}`;
1592
+ if (modifiedHistory.length > 0 && modifiedHistory[modifiedHistory.length - 1].role === "agent") {
1593
+ modifiedHistory[modifiedHistory.length - 1].text = finalWithTime;
1594
+ } else {
1595
+ modifiedHistory.push({ role: "agent", text: finalWithTime });
1596
+ }
1597
+ break;
1598
+ }
1599
+ if (!shouldContinue && toolResults.length === 0) break;
1600
+ const nextAgentMsg = cleanedTurnText.trim() || "*Working...*";
1601
+ modifiedHistory.push({ role: "agent", text: nextAgentMsg });
1602
+ const nextUserMsg = toolResults.length > 0 ? `${toolResults.join("\n")}` : "";
1603
+ if (nextUserMsg) {
1604
+ modifiedHistory.push({ role: "user", text: nextUserMsg });
1605
+ }
1606
+ }
1607
+ yield { type: "status", content: null };
1608
+ };
1609
+
1610
+ // src/utils/settings.js
1611
+ import fs13 from "fs-extra";
1612
+ import path14 from "path";
1613
+ import { fileURLToPath as fileURLToPath8 } from "url";
1614
+ var __dirname8 = path14.dirname(fileURLToPath8(import.meta.url));
1615
+ var SETTINGS_PATH = path14.join(__dirname8, "../../settings.json");
1616
+ var DEFAULT_SETTINGS = {
1617
+ mode: "Flux",
1618
+ thinkingLevel: "Medium",
1619
+ activeModel: "gemma-4-31b-it",
1620
+ showFullThinking: false,
1621
+ apiTier: "Free",
1622
+ quotas: {
1623
+ agentLimit: 1500,
1624
+ backgroundLimit: 1500,
1625
+ searchLimit: 100,
1626
+ customModelId: "",
1627
+ customLimit: 0
1628
+ },
1629
+ systemSettings: {
1630
+ memory: true,
1631
+ compression: 0,
1632
+ autoExec: false,
1633
+ allowExternalAccess: false,
1634
+ autoDeleteHistory: "7d"
1635
+ },
1636
+ profileData: {
1637
+ name: null,
1638
+ nickname: null,
1639
+ instructions: null
1640
+ }
1641
+ };
1642
+ var loadSettings = async () => {
1643
+ try {
1644
+ if (await fs13.exists(SETTINGS_PATH)) {
1645
+ const saved = await fs13.readJson(SETTINGS_PATH);
1646
+ return {
1647
+ ...DEFAULT_SETTINGS,
1648
+ ...saved,
1649
+ quotas: { ...DEFAULT_SETTINGS.quotas, ...saved.quotas },
1650
+ systemSettings: { ...DEFAULT_SETTINGS.systemSettings, ...saved.systemSettings },
1651
+ profileData: { ...DEFAULT_SETTINGS.profileData, ...saved.profileData }
1652
+ };
1653
+ }
1654
+ } catch (err) {
1655
+ console.error("Failed to load settings:", err);
1656
+ }
1657
+ return DEFAULT_SETTINGS;
1658
+ };
1659
+ var saveSettings = async (settings) => {
1660
+ try {
1661
+ const current = await loadSettings();
1662
+ const updated = { ...current, ...settings };
1663
+ await fs13.writeJson(SETTINGS_PATH, updated, { spaces: 2 });
1664
+ return true;
1665
+ } catch (err) {
1666
+ console.error("Failed to save settings:", err);
1667
+ return false;
1668
+ }
1669
+ };
1670
+
1671
+ // src/components/ResumeModal.jsx
1672
+ import React6, { useState as useState2, useEffect } from "react";
1673
+ import { Box as Box6, Text as Text6, useInput } from "ink";
1674
+ function ResumeModal({ onSelect, onDelete, onClose }) {
1675
+ const [history, setHistory] = useState2({});
1676
+ const [keys, setKeys] = useState2([]);
1677
+ const [selectedIndex, setSelectedIndex] = useState2(0);
1678
+ useEffect(() => {
1679
+ const fetchHistory = async () => {
1680
+ const h = await loadHistory();
1681
+ setHistory(h);
1682
+ setKeys(Object.keys(h).sort((a, b) => (h[b].updatedAt || 0) - (h[a].updatedAt || 0)));
1683
+ };
1684
+ fetchHistory();
1685
+ }, []);
1686
+ useInput((input, key) => {
1687
+ if (key.escape) onClose();
1688
+ if (key.upArrow) setSelectedIndex((prev) => Math.max(0, prev - 1));
1689
+ if (key.downArrow) setSelectedIndex((prev) => Math.min(keys.length - 1, prev + 1));
1690
+ if (key.return && keys[selectedIndex]) onSelect(keys[selectedIndex]);
1691
+ if (input === "x" && keys[selectedIndex]) {
1692
+ const targetId = keys[selectedIndex];
1693
+ onDelete(targetId).then((newHistory) => {
1694
+ const safeHistory = newHistory || {};
1695
+ setHistory(safeHistory);
1696
+ const newKeys = Object.keys(safeHistory).sort((a, b) => (safeHistory[b]?.updatedAt || 0) - (safeHistory[a]?.updatedAt || 0));
1697
+ setKeys(newKeys);
1698
+ setSelectedIndex((prev) => Math.max(0, Math.min(newKeys.length - 1, prev)));
1699
+ });
1700
+ }
1701
+ });
1702
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", borderStyle: "double", borderColor: "cyan", padding: 1, width: 80 }, /* @__PURE__ */ React6.createElement(Box6, { justifyContent: "center", marginBottom: 1 }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, " \u{1F4C2} RESUME SESSION ")), keys.length === 0 ? /* @__PURE__ */ React6.createElement(Text6, { italic: true, color: "gray" }, " No saved chats found. ") : keys.map((id, index) => {
1703
+ const chat2 = history[id];
1704
+ const isSelected = index === selectedIndex;
1705
+ return /* @__PURE__ */ React6.createElement(Box6, { key: id, paddingX: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: isSelected ? "cyan" : "white" }, isSelected ? "\u276F " : " ", /* @__PURE__ */ React6.createElement(Text6, { bold: isSelected }, chat2.name || id), /* @__PURE__ */ React6.createElement(Text6, { color: "gray" }, " [", id.slice(5), "]")), isSelected && /* @__PURE__ */ React6.createElement(Box6, { marginLeft: "auto" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, " (x to delete) ")));
1706
+ }), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1, justifyContent: "center", borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \u2191\u2193 navigate \u2022 Enter select \u2022 x delete \u2022 Esc close ")));
1707
+ }
1708
+
1709
+ // src/components/MemoryModal.jsx
1710
+ import React7, { useState as useState3, useEffect as useEffect2 } from "react";
1711
+ import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
1712
+ import path15 from "path";
1713
+ var MEMORIES_PATH2 = path15.join(process.cwd(), "secret", "memories.json");
1714
+ function MemoryModal({ onClose }) {
1715
+ const [memories, setMemories] = useState3([]);
1716
+ const [selectedIndex, setSelectedIndex] = useState3(0);
1717
+ const loadMemories = () => {
1718
+ const data = readEncryptedJson(MEMORIES_PATH2, []);
1719
+ setMemories(data);
1720
+ };
1721
+ useEffect2(() => {
1722
+ loadMemories();
1723
+ }, []);
1724
+ useInput2((input, key) => {
1725
+ if (key.escape) onClose();
1726
+ if (key.upArrow) setSelectedIndex((prev) => Math.max(0, prev - 1));
1727
+ if (key.downArrow) setSelectedIndex((prev) => Math.min(memories.length - 1, prev + 1));
1728
+ if (input === "x" && memories.length > 0) {
1729
+ const idToDelete = memories[selectedIndex].id;
1730
+ const updated = memories.filter((m) => m.id !== idToDelete);
1731
+ writeEncryptedJson(MEMORIES_PATH2, updated);
1732
+ setMemories(updated);
1733
+ if (selectedIndex >= updated.length && updated.length > 0) {
1734
+ setSelectedIndex(updated.length - 1);
1735
+ }
1736
+ }
1737
+ });
1738
+ const cleanDisplay = (text) => {
1739
+ return text.replace(/\[Saved on: .*?\]/g, "").trim();
1740
+ };
1741
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "double", borderColor: "yellow", padding: 1, width: 80 }, /* @__PURE__ */ React7.createElement(Box7, { justifyContent: "center", marginBottom: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "yellow" }, "\u{1F9E0} LONG-TERM MEMORY VAULT")), memories.length === 0 ? /* @__PURE__ */ React7.createElement(Box7, { justifyContent: "center", paddingY: 2 }, /* @__PURE__ */ React7.createElement(Text7, { italic: true, color: "gray" }, "The vault is currently empty...")) : memories.map((mem, idx) => /* @__PURE__ */ React7.createElement(Box7, { key: mem.id, paddingX: 1, backgroundColor: idx === selectedIndex ? "#333" : void 0 }, /* @__PURE__ */ React7.createElement(Text7, { color: idx === selectedIndex ? "yellow" : "white" }, idx === selectedIndex ? "\u276F " : " ", idx + 1, ". ", cleanDisplay(mem.memory)))), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "\u2191/\u2193 Navigate \u2022 ", /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, "x"), " Delete Memory \u2022 ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, "Esc"), " Back to Chat")));
1742
+ }
1743
+
1744
+ // src/app.jsx
1745
+ var SESSION_START_TIME = Date.now();
1746
+ var ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Text8, { color: "magenta", bold: true, underline: true }, "\u{1F7E3} STEERING HINT RESOLUTION"), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1 }, "The agent already finished the task (turn: finish) before your hint was consumed."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1, backgroundColor: "#222", paddingX: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Text8, { italic: true, color: "gray" }, '"', data, '"')), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, "How would you like to proceed?")), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(
1747
+ CommandMenu,
1748
+ {
1749
+ title: "Select Action",
1750
+ items: [
1751
+ { label: "Send Anyway", value: "send" },
1752
+ { label: "Edit Prompt", value: "edit" }
1753
+ ],
1754
+ onSelect: (val) => {
1755
+ if (val === "send") onResolve(data);
1756
+ else onEdit(data);
1757
+ }
1758
+ }
1759
+ )));
1760
+ var FLUX_LOGO = gradient(["#00ffff", "#0077ff", "#ff00ff"]).multiline(
1761
+ `\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
1762
+ \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
1763
+ \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2557 \u2588\u2588\u2551
1764
+ \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2557\u2588\u2588\u2551
1765
+ \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u255D \u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2554\u2588\u2588\u2588\u2554\u255D
1766
+ \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D`
1767
+ );
1768
+ function App() {
1769
+ const { stdout } = useStdout();
1770
+ const [input, setInput] = useState4("");
1771
+ const [mode, setMode] = useState4("Flux");
1772
+ const [terminalSize, setTerminalSize] = useState4({
1773
+ columns: stdout?.columns || 80,
1774
+ rows: stdout?.rows || 24
1775
+ });
1776
+ useEffect3(() => {
1777
+ const handleResize = () => {
1778
+ stdout.write("\x1Bc");
1779
+ setTerminalSize({
1780
+ columns: stdout.columns,
1781
+ rows: stdout.rows
1782
+ });
1783
+ };
1784
+ stdout.on("resize", handleResize);
1785
+ return () => {
1786
+ stdout.off("resize", handleResize);
1787
+ };
1788
+ }, [stdout]);
1789
+ const [thinkingLevel, setThinkingLevel] = useState4("Medium");
1790
+ const [showFullThinking, setShowFullThinking] = useState4(false);
1791
+ const [activeModel, setActiveModel] = useState4("gemma-4-31b-it");
1792
+ const [janitorModel, setJanitorModel] = useState4("gemma-4-26b-a4b-it");
1793
+ const [isInitializing, setIsInitializing] = useState4(true);
1794
+ const [apiKey, setApiKey] = useState4(null);
1795
+ const [tempKey, setTempKey] = useState4("");
1796
+ const [activeView, setActiveView] = useState4("chat");
1797
+ const [apiTier, setApiTier] = useState4("Free");
1798
+ const [quotas, setQuotas] = useState4({ agentLimit: 1500, backgroundLimit: 1500, searchLimit: 100, customModelId: "", customLimit: 0 });
1799
+ const [inputConfig, setInputConfig] = useState4(null);
1800
+ const [systemSettings, setSystemSettings] = useState4({ memory: true, compression: 0, autoExec: false, autoDeleteHistory: "7d" });
1801
+ const [profileData, setProfileData] = useState4({ name: null, nickname: null, instructions: null });
1802
+ const [sessionStats, setSessionStats] = useState4({ tokens: 0 });
1803
+ const [sessionAgentCalls, setSessionAgentCalls] = useState4(0);
1804
+ const [sessionBackgroundCalls, setSessionBackgroundCalls] = useState4(0);
1805
+ const [sessionTotalTokens, setSessionTotalTokens] = useState4(0);
1806
+ const [dailyUsage, setDailyUsage] = useState4(null);
1807
+ const [chatId, setChatId] = useState4(generateChatId());
1808
+ const [activeCommand, setActiveCommand] = useState4(null);
1809
+ const [execOutput, setExecOutput] = useState4("");
1810
+ const activeCommandRef = useRef(null);
1811
+ const execOutputRef = useRef("");
1812
+ useEffect3(() => {
1813
+ activeCommandRef.current = activeCommand;
1814
+ }, [activeCommand]);
1815
+ useEffect3(() => {
1816
+ execOutputRef.current = execOutput;
1817
+ }, [execOutput]);
1818
+ const [autoAcceptWrites, setAutoAcceptWrites] = useState4(false);
1819
+ const [pendingApproval, setPendingApproval] = useState4(null);
1820
+ const formatDuration = (totalSecs) => {
1821
+ const h = Math.floor(totalSecs / 3600);
1822
+ const m = Math.floor(totalSecs % 3600 / 60);
1823
+ const s = totalSecs % 60;
1824
+ if (h > 0) {
1825
+ return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
1826
+ }
1827
+ return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
1828
+ };
1829
+ const [statusText, setStatusText] = useState4(null);
1830
+ const [isProcessing, setIsProcessing] = useState4(false);
1831
+ const [escPressed, setEscPressed] = useState4(false);
1832
+ const [escTimer, setEscTimer] = useState4(null);
1833
+ const [queuedPrompt, setQueuedPrompt] = useState4(null);
1834
+ const [resolutionData, setResolutionData] = useState4(null);
1835
+ const [messages, setMessages] = useState4([
1836
+ { id: "welcome", role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome to Flux Flow! Type /help for commands.\n" }
1837
+ ]);
1838
+ const [completedIndex, setCompletedIndex] = useState4(1);
1839
+ useInput3((inputText, key) => {
1840
+ if (key.escape) {
1841
+ if (isProcessing) {
1842
+ if (!escPressed) {
1843
+ setEscPressed(true);
1844
+ if (escTimer) clearTimeout(escTimer);
1845
+ setEscTimer(setTimeout(() => setEscPressed(false), 3e3));
1846
+ } else {
1847
+ signalTermination();
1848
+ setEscPressed(false);
1849
+ if (escTimer) clearTimeout(escTimer);
1850
+ }
1851
+ } else if (activeView !== "chat") {
1852
+ setActiveView("chat");
1853
+ }
1854
+ }
1855
+ if (key.tab && suggestions.length > 0 && activeView === "chat") {
1856
+ const nextCmd = suggestions[0];
1857
+ setInput(nextCmd + " ");
1858
+ }
1859
+ if (key.ctrl && inputText === "c" && activeView !== "exit") {
1860
+ setActiveView("exit");
1861
+ }
1862
+ if (key.return && (key.ctrl || key.meta)) {
1863
+ setInput((prev) => prev.replace(/\\\r?$/, "").replace(/\r?$/, "") + "\n");
1864
+ }
1865
+ });
1866
+ useEffect3(() => {
1867
+ async function init() {
1868
+ const saved = await loadSettings();
1869
+ setMode(saved.mode);
1870
+ setThinkingLevel(saved.thinkingLevel);
1871
+ setActiveModel(saved.activeModel);
1872
+ setShowFullThinking(saved.showFullThinking);
1873
+ setApiTier(saved.apiTier || "Free");
1874
+ setQuotas(saved.quotas || { agentLimit: 1500, searchLimit: 100, customModelId: "", customLimit: 0 });
1875
+ setSystemSettings(saved.systemSettings);
1876
+ setProfileData(saved.profileData);
1877
+ const key = await getAPIKey();
1878
+ if (key) {
1879
+ setApiKey(key);
1880
+ initAI(key);
1881
+ }
1882
+ if (saved.systemSettings?.autoDeleteHistory) {
1883
+ cleanupOldHistory(saved.systemSettings.autoDeleteHistory);
1884
+ }
1885
+ setIsInitializing(false);
1886
+ }
1887
+ init();
1888
+ }, []);
1889
+ useEffect3(() => {
1890
+ if (!isInitializing) {
1891
+ saveSettings({
1892
+ mode,
1893
+ thinkingLevel,
1894
+ activeModel,
1895
+ showFullThinking,
1896
+ systemSettings,
1897
+ profileData
1898
+ });
1899
+ }
1900
+ }, [mode, thinkingLevel, activeModel, showFullThinking, systemSettings, profileData, isInitializing]);
1901
+ const handleSetup = async (val) => {
1902
+ const key = val.trim();
1903
+ if (key.length >= 30) {
1904
+ await saveAPIKey(key);
1905
+ setApiKey(key);
1906
+ initAI(key);
1907
+ setMessages((prev) => [...prev, { role: "system", text: "\u2705 API Key saved successfully! Initialization complete." }]);
1908
+ } else {
1909
+ setMessages((prev) => [...prev, { role: "system", text: `\u274C INVALID KEY: Gemini API keys must be at least 30 characters.` }]);
1910
+ setTempKey("");
1911
+ }
1912
+ };
1913
+ const COMMANDS = ["/mode", "/thinking", "/model", "/resume", "/memory", "/profile", "/settings", "/key", "/stats", "/help", "/clear", "/quit"];
1914
+ const handleSubmit = (value) => {
1915
+ const normalizedValue = value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trimEnd();
1916
+ if (normalizedValue.endsWith("\\")) {
1917
+ setInput(normalizedValue.slice(0, -1) + "\n");
1918
+ return;
1919
+ }
1920
+ const absoluteClean = normalizedValue.replace(/\\\s*\n/g, "\n").split("\n").map((l) => l.replace(/\\$/, "")).join("\n");
1921
+ if (!absoluteClean.trim()) return;
1922
+ if (isProcessing) {
1923
+ const hintText = absoluteClean.trim();
1924
+ setQueuedPrompt(hintText);
1925
+ setMessages((prev) => {
1926
+ setCompletedIndex(prev.length + 1);
1927
+ return [...prev, { id: "hint-" + Date.now(), role: "user", text: `[STEERING HINT: QUEUED]
1928
+ ${hintText}`, color: "magenta" }];
1929
+ });
1930
+ setInput("");
1931
+ return;
1932
+ }
1933
+ if (!apiKey) {
1934
+ handleSetup(absoluteClean);
1935
+ setTempKey("");
1936
+ return;
1937
+ }
1938
+ if (absoluteClean.startsWith("/")) {
1939
+ const parts = absoluteClean.split(" ");
1940
+ const cmd = parts[0]?.toLowerCase();
1941
+ switch (cmd) {
1942
+ case "/quit": {
1943
+ setActiveView("exit");
1944
+ break;
1945
+ }
1946
+ case "/resume": {
1947
+ if (parts[1]) {
1948
+ const targetId = parts[1];
1949
+ const resumeSession = async () => {
1950
+ const h = await loadHistory();
1951
+ const target = h[targetId] || Object.values(h).find((h2) => h2.name.toLowerCase() === targetId.toLowerCase());
1952
+ if (target) {
1953
+ process.stdout.write("\x1Bc");
1954
+ setChatId(targetId);
1955
+ setMessages(target.messages);
1956
+ setMessages((prev) => [...prev, { id: "sys-" + Date.now(), role: "system", text: `\u{1F4E1} SESSION RESUMED: [${targetId}]` }]);
1957
+ setCompletedIndex(0);
1958
+ } else {
1959
+ setMessages((prev) => [...prev, { id: "err-" + Date.now(), role: "system", text: `\u274C ERROR: Session [${targetId}] not found.` }]);
1960
+ }
1961
+ };
1962
+ resumeSession();
1963
+ } else {
1964
+ setActiveView("resume");
1965
+ }
1966
+ break;
1967
+ }
1968
+ case "/clear": {
1969
+ process.stdout.write("\x1Bc");
1970
+ setMessages([{ id: "welcome-" + Date.now(), role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome back to Flux Flow! Context cleared.\n" }]);
1971
+ setCompletedIndex(0);
1972
+ setChatId(generateChatId());
1973
+ setSessionStats({ tokens: 0 });
1974
+ break;
1975
+ }
1976
+ case "/mode": {
1977
+ if (parts[1]) {
1978
+ const newMode = parts[1].toLowerCase() === "flow" ? "Flow" : "Flux";
1979
+ setMode(newMode);
1980
+ setMessages((prev) => {
1981
+ setCompletedIndex(prev.length + 1);
1982
+ return [...prev, { id: Date.now(), role: "system", text: `\u2699\uFE0F [SYSTEM] Mode switched to ${newMode}` }];
1983
+ });
1984
+ } else {
1985
+ setActiveView("mode");
1986
+ }
1987
+ break;
1988
+ }
1989
+ case "/thinking": {
1990
+ const arg = parts[1]?.toLowerCase();
1991
+ if (arg === "show") {
1992
+ setShowFullThinking(true);
1993
+ setMessages((prev) => {
1994
+ setCompletedIndex(prev.length + 1);
1995
+ return [...prev, { id: Date.now(), role: "system", text: "\u2699\uFE0F [SYSTEM] Full Thinking Process: VISIBLE" }];
1996
+ });
1997
+ } else if (arg === "hide") {
1998
+ setShowFullThinking(false);
1999
+ setMessages((prev) => {
2000
+ setCompletedIndex(prev.length + 1);
2001
+ return [...prev, { id: Date.now(), role: "system", text: "\u2699\uFE0F [SYSTEM] Full Thinking Process: HIDDEN (Headings only)" }];
2002
+ });
2003
+ } else if (parts[1]) {
2004
+ let val = parts[1].toLowerCase();
2005
+ if (val === "xhigh") val = "max";
2006
+ const formattedLevel = val.charAt(0).toUpperCase() + val.slice(1);
2007
+ if (mode === "Flow" && (formattedLevel === "High" || formattedLevel === "Max")) {
2008
+ setMessages((prev) => {
2009
+ setCompletedIndex(prev.length + 1);
2010
+ return [...prev, { id: Date.now(), role: "system", text: `\u274C [RESTRICTED] "${formattedLevel}" is restricted in Flow mode. Switch to /mode Flux to enable Deep Thinking.` }];
2011
+ });
2012
+ } else {
2013
+ setThinkingLevel(formattedLevel);
2014
+ setMessages((prev) => {
2015
+ setCompletedIndex(prev.length + 1);
2016
+ return [...prev, { id: Date.now(), role: "system", text: `\u2699\uFE0F [SYSTEM] Thinking level set to ${formattedLevel}` }];
2017
+ });
2018
+ }
2019
+ } else {
2020
+ setActiveView("thinking");
2021
+ }
2022
+ break;
2023
+ }
2024
+ case "/model": {
2025
+ if (parts[1]) {
2026
+ const mod = parts.slice(1).join(" ");
2027
+ setActiveModel(mod);
2028
+ setMessages((prev) => {
2029
+ setCompletedIndex(prev.length + 1);
2030
+ return [...prev, { id: Date.now(), role: "system", text: `\u2699\uFE0F [SYSTEM] Model switched to ${mod}` }];
2031
+ });
2032
+ } else {
2033
+ setActiveView("model");
2034
+ }
2035
+ break;
2036
+ }
2037
+ case "/settings": {
2038
+ setActiveView("settings");
2039
+ break;
2040
+ }
2041
+ case "/key": {
2042
+ setActiveView("key");
2043
+ break;
2044
+ }
2045
+ case "/profile": {
2046
+ setActiveView("profile");
2047
+ break;
2048
+ }
2049
+ case "/stats": {
2050
+ const run = async () => {
2051
+ const usage = await getDailyUsage();
2052
+ setDailyUsage(usage);
2053
+ setActiveView("stats");
2054
+ };
2055
+ run();
2056
+ break;
2057
+ }
2058
+ case "/save": {
2059
+ const name = parts.slice(1).join(" ") || `Session ${(/* @__PURE__ */ new Date()).toLocaleTimeString()}`;
2060
+ saveChat(chatId, name, messages);
2061
+ setMessages((prev) => {
2062
+ setCompletedIndex(prev.length + 1);
2063
+ return [...prev, { id: Date.now(), role: "system", text: `\u{1F4BE} [MEMORY] Chat saved as "${name}" (ID: ${chatId})` }];
2064
+ });
2065
+ break;
2066
+ }
2067
+ case "/chats": {
2068
+ const run = async () => {
2069
+ const history = await loadHistory();
2070
+ const list = Object.entries(history).map(([id, info]) => `\u2022 ${id}: ${info.name}`).join("\n");
2071
+ setMessages((prev) => {
2072
+ setCompletedIndex(prev.length + 1);
2073
+ return [...prev, { id: Date.now(), role: "system", text: `\u{1F5C3}\uFE0F [HISTORY] Saved Chats:
2074
+ ${list || "No saved chats found."}` }];
2075
+ });
2076
+ };
2077
+ run();
2078
+ break;
2079
+ }
2080
+ case "/memory": {
2081
+ setActiveView("memory");
2082
+ break;
2083
+ }
2084
+ case "/help": {
2085
+ setMessages((prev) => {
2086
+ setCompletedIndex(prev.length + 1);
2087
+ return [...prev, { id: Date.now(), role: "system", text: "\u2699\uFE0F [SYSTEM] Available commands: " + COMMANDS.join(", ") }];
2088
+ });
2089
+ break;
2090
+ }
2091
+ default:
2092
+ setMessages((prev) => {
2093
+ setCompletedIndex(prev.length + 1);
2094
+ return [...prev, { id: Date.now(), role: "system", text: `\u2699\uFE0F [SYSTEM] Unknown command: ${cmd}` }];
2095
+ });
2096
+ }
2097
+ } else {
2098
+ const timestamp = `[Prompted on: ${(/* @__PURE__ */ new Date()).toLocaleString()}]`;
2099
+ const userMessage = { id: "user-" + Date.now(), role: "user", text: `${absoluteClean}
2100
+
2101
+ ${timestamp}` };
2102
+ setMessages((prev) => {
2103
+ setCompletedIndex(prev.length + 1);
2104
+ return [...prev, userMessage];
2105
+ });
2106
+ const streamChat = async () => {
2107
+ setIsProcessing(true);
2108
+ try {
2109
+ const cleanHistoryForAI = [...messages, userMessage].filter(
2110
+ (m) => m.role !== "think" && !String(m.id).startsWith("welcome")
2111
+ );
2112
+ const stream = getAIStream(
2113
+ activeModel,
2114
+ cleanHistoryForAI,
2115
+ {
2116
+ profile: profileData,
2117
+ thinkingLevel,
2118
+ mode,
2119
+ systemSettings,
2120
+ janitorModel,
2121
+ sessionStats,
2122
+ chatId,
2123
+ onExecStart: (cmd) => {
2124
+ setActiveCommand(cmd);
2125
+ setExecOutput("");
2126
+ },
2127
+ onExecChunk: (chunk) => {
2128
+ setExecOutput((prev) => prev + chunk);
2129
+ },
2130
+ onExecEnd: () => {
2131
+ setMessages((prev) => {
2132
+ const finalStatus = `[TERMINAL_RECORD]
2133
+ COMMAND: ${activeCommandRef.current}
2134
+ OUTPUT: ${execOutputRef.current}`;
2135
+ return [...prev, { id: "term-" + Date.now(), role: "system", text: finalStatus, isTerminalRecord: true }];
2136
+ });
2137
+ setActiveCommand(null);
2138
+ setExecOutput("");
2139
+ },
2140
+ onToolApproval: async (tool, args) => {
2141
+ const isAuto = autoAcceptWrites || systemSettings.autoExec;
2142
+ if (tool === "exec_command") {
2143
+ const { command } = parseArgs(args || "{}");
2144
+ const safeRegex = /^(echo|ls|dir|pwd|cd|git status|git log|git diff|type|cat|help)\b/i;
2145
+ if (isAuto || command && safeRegex.test(command.trim())) return "allow";
2146
+ return new Promise((resolve) => {
2147
+ setPendingApproval({ tool, args, resolve });
2148
+ setActiveView("terminalApproval");
2149
+ });
2150
+ }
2151
+ if (isAuto) return "allow";
2152
+ return new Promise((resolve) => {
2153
+ setPendingApproval({ tool, args, resolve });
2154
+ setActiveView("approval");
2155
+ });
2156
+ }
2157
+ },
2158
+ async () => {
2159
+ if (queuedPrompt) {
2160
+ const p = queuedPrompt;
2161
+ setQueuedPrompt(null);
2162
+ return p;
2163
+ }
2164
+ return null;
2165
+ }
2166
+ );
2167
+ let inThinkMode = false;
2168
+ let currentThinkId = null;
2169
+ let currentAgentId = null;
2170
+ const signalRegex = /\[?_DISABLED_SIGNAL_REGEX_\]?/gi;
2171
+ for await (const packet of stream) {
2172
+ if (packet.type === "status") {
2173
+ setStatusText(packet.content);
2174
+ continue;
2175
+ }
2176
+ if (packet.type === "turn_reset") {
2177
+ currentThinkId = null;
2178
+ currentAgentId = null;
2179
+ inThinkMode = false;
2180
+ continue;
2181
+ }
2182
+ if (packet.type === "memory_updated") {
2183
+ setMessages((prev) => {
2184
+ const newMsgs = [...prev];
2185
+ if (newMsgs.length > 0) {
2186
+ newMsgs[newMsgs.length - 1].memoryUpdated = true;
2187
+ }
2188
+ return newMsgs;
2189
+ });
2190
+ continue;
2191
+ }
2192
+ if (packet.type === "exec_start") {
2193
+ continue;
2194
+ }
2195
+ if (packet.type === "usage") {
2196
+ const total = packet.content.totalTokenCount || 0;
2197
+ setSessionStats({ tokens: total });
2198
+ setSessionTotalTokens((prev) => prev + total);
2199
+ setSessionAgentCalls((prev) => prev + 1);
2200
+ continue;
2201
+ }
2202
+ if (packet.type === "background_increment") {
2203
+ setSessionBackgroundCalls((prev) => prev + 1);
2204
+ continue;
2205
+ }
2206
+ if (packet.type === "tool_result") {
2207
+ setMessages((prev) => [...prev, { id: "tool-" + Date.now(), role: "system", text: packet.content }]);
2208
+ continue;
2209
+ }
2210
+ let chunkText = packet.content;
2211
+ if (chunkText.includes("<think>") && !inThinkMode) {
2212
+ inThinkMode = true;
2213
+ chunkText = chunkText.replace("<think>", "");
2214
+ currentThinkId = "think-" + Date.now();
2215
+ setMessages((prev) => [...prev, { id: currentThinkId, role: "think", text: "" }]);
2216
+ }
2217
+ if (chunkText.includes("</think>")) {
2218
+ const [thinkPart, agentPart] = chunkText.split("</think>");
2219
+ if (inThinkMode) {
2220
+ setMessages((prev) => {
2221
+ const newMsgs = [...prev];
2222
+ const thinkMsg = newMsgs.find((m) => m.id === currentThinkId);
2223
+ if (thinkMsg) thinkMsg.text += thinkPart.replace(signalRegex, "");
2224
+ currentAgentId = "agent-" + Date.now();
2225
+ const cleanedAgentPart = (agentPart || "").replace(signalRegex, "");
2226
+ return [...newMsgs, { id: currentAgentId, role: "agent", text: cleanedAgentPart }];
2227
+ });
2228
+ inThinkMode = false;
2229
+ } else {
2230
+ const cleanedContent = (agentPart || thinkPart || "").replace(signalRegex, "");
2231
+ if (!currentAgentId) {
2232
+ currentAgentId = "agent-" + Date.now();
2233
+ setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: cleanedContent }]);
2234
+ } else {
2235
+ setMessages((prev) => {
2236
+ const newMsgs = [...prev];
2237
+ const msg = newMsgs.find((m) => m.id === currentAgentId);
2238
+ if (msg) msg.text += cleanedContent;
2239
+ return newMsgs;
2240
+ });
2241
+ }
2242
+ }
2243
+ continue;
2244
+ }
2245
+ if (inThinkMode && currentThinkId) {
2246
+ setMessages((prev) => {
2247
+ const newMsgs = [...prev];
2248
+ const msg = newMsgs.find((m) => m.id === currentThinkId);
2249
+ if (msg) msg.text += chunkText.replace(signalRegex, "");
2250
+ return newMsgs;
2251
+ });
2252
+ } else if (!inThinkMode) {
2253
+ const cleanedText = chunkText.replace("<think>", "").replace(signalRegex, "");
2254
+ if (!currentAgentId) {
2255
+ currentAgentId = "agent-" + Date.now();
2256
+ setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: cleanedText }]);
2257
+ } else {
2258
+ setMessages((prev) => {
2259
+ const newMsgs = [...prev];
2260
+ const msg = newMsgs.find((m) => m.id === currentAgentId);
2261
+ if (msg) msg.text += cleanedText;
2262
+ return newMsgs;
2263
+ });
2264
+ }
2265
+ }
2266
+ }
2267
+ } catch (err) {
2268
+ setMessages((prev) => {
2269
+ setCompletedIndex(prev.length + 1);
2270
+ return [...prev, { id: "error-" + Date.now(), role: "system", text: `\u274C ERROR: ${err.message}` }];
2271
+ });
2272
+ } finally {
2273
+ setIsProcessing(false);
2274
+ setStatusText(null);
2275
+ if (queuedPrompt) {
2276
+ setResolutionData(queuedPrompt);
2277
+ setQueuedPrompt(null);
2278
+ setActiveView("resolution");
2279
+ }
2280
+ setMessages((prev) => {
2281
+ const historyToSave = prev.filter((m) => !String(m.id).startsWith("welcome"));
2282
+ saveChat(chatId, null, historyToSave);
2283
+ setCompletedIndex(prev.length);
2284
+ return prev;
2285
+ });
2286
+ }
2287
+ };
2288
+ streamChat();
2289
+ }
2290
+ setInput("");
2291
+ };
2292
+ const getSuggestions = () => {
2293
+ if (!input.startsWith("/") || input.includes(" ")) return [];
2294
+ return COMMANDS.filter((c) => c.startsWith(input.toLowerCase()));
2295
+ };
2296
+ const suggestions = getSuggestions();
2297
+ const renderActiveView = () => {
2298
+ switch (activeView) {
2299
+ case "mode":
2300
+ return /* @__PURE__ */ React8.createElement(
2301
+ CommandMenu,
2302
+ {
2303
+ title: "\u26A1 Select Operating Mode",
2304
+ items: [{ label: "Flux (Dev mode - Tools Enabled)", value: "Flux" }, { label: "Flow (Chat mode - No Tools)", value: "Flow" }, { label: "Cancel", value: "Cancel" }],
2305
+ onSelect: (item) => {
2306
+ if (item.value !== "Cancel") {
2307
+ setMode(item.value);
2308
+ if (item.value === "Flow") {
2309
+ setThinkingLevel("Medium");
2310
+ } else if (item.value === "Flux") {
2311
+ setThinkingLevel("High");
2312
+ }
2313
+ }
2314
+ setActiveView("chat");
2315
+ }
2316
+ }
2317
+ );
2318
+ case "thinking": {
2319
+ const options = mode === "Flow" ? [
2320
+ { label: "Low (Fastest)", value: "Low" },
2321
+ { label: "Medium (Balanced)", value: "Medium" }
2322
+ ] : [
2323
+ { label: "Low (Fastest)", value: "Low" },
2324
+ { label: "Medium (Balanced)", value: "Medium" },
2325
+ { label: "High (Complex coding)", value: "High" },
2326
+ { label: "Max (Architecture)", value: "Max" }
2327
+ ];
2328
+ options.push({ label: "Cancel", value: "Cancel" });
2329
+ return /* @__PURE__ */ React8.createElement(
2330
+ CommandMenu,
2331
+ {
2332
+ title: `\u{1F9E0} Select Thinking Level (${mode} Mode)`,
2333
+ items: options,
2334
+ onSelect: (item) => {
2335
+ if (item.value !== "Cancel") setThinkingLevel(item.value);
2336
+ setActiveView("chat");
2337
+ }
2338
+ }
2339
+ );
2340
+ }
2341
+ case "model":
2342
+ return /* @__PURE__ */ React8.createElement(
2343
+ CommandMenu,
2344
+ {
2345
+ title: "\u{1F916} Select AI Model",
2346
+ items: [{ label: "Gemma 4 31B (Default)", value: "gemma-4-31b-it" }, { label: "Gemini 3.1 Pro (Req. paid API Key)", value: "gemini-3.1-pro-preview" }, { label: "Gemini 3 Flash", value: "gemini-3-flash-preview" }, { label: "Gemini 3.1 Flash Lite", value: "gemini-3.1-flash-lite" }, { label: "Cancel", value: "Cancel" }],
2347
+ onSelect: (item) => {
2348
+ if (item.value !== "Cancel") setActiveModel(item.value);
2349
+ setActiveView("chat");
2350
+ }
2351
+ }
2352
+ );
2353
+ case "settings":
2354
+ return /* @__PURE__ */ React8.createElement(
2355
+ CommandMenu,
2356
+ {
2357
+ title: "System Settings",
2358
+ items: [
2359
+ { label: `Toggle Memory [ ${systemSettings.memory ? "ON" : "OFF"} ]`, value: "memory" },
2360
+ { label: `Toggle Auto-Exec [ ${systemSettings.autoExec ? "ON" : "OFF"} ]`, value: "autoExec" },
2361
+ { label: `External Workspace Access [ ${systemSettings.allowExternalAccess ? "ON" : "OFF"} ]`, value: "externalAccess" },
2362
+ { label: `API Tier [ ${apiTier} ]`, value: "apiTier" },
2363
+ { label: `Auto-Delete History [ ${systemSettings.autoDeleteHistory} ]`, value: "autoDelete" },
2364
+ { label: "Exit Settings", value: "Cancel" }
2365
+ ],
2366
+ onSelect: (item) => {
2367
+ if (item.value === "memory") setSystemSettings((s) => ({ ...s, memory: !s.memory }));
2368
+ else if (item.value === "autoExec") {
2369
+ if (!systemSettings.autoExec) {
2370
+ if (systemSettings.allowExternalAccess) {
2371
+ setActiveView("doubleDanger");
2372
+ } else {
2373
+ setActiveView("autoExecDanger");
2374
+ }
2375
+ } else {
2376
+ setSystemSettings((s) => ({ ...s, autoExec: false }));
2377
+ }
2378
+ } else if (item.value === "externalAccess") {
2379
+ if (!systemSettings.allowExternalAccess) {
2380
+ if (systemSettings.autoExec) {
2381
+ setActiveView("doubleDanger");
2382
+ } else {
2383
+ setActiveView("externalDanger");
2384
+ }
2385
+ } else {
2386
+ setSystemSettings((s) => ({ ...s, allowExternalAccess: false }));
2387
+ }
2388
+ } else if (item.value === "apiTier") setActiveView("apiTier");
2389
+ else if (item.value === "autoDelete") {
2390
+ const options = ["1d", "7d", "30d"];
2391
+ const currentIndex = options.indexOf(systemSettings.autoDeleteHistory || "30d");
2392
+ const nextIndex = (currentIndex + 1) % options.length;
2393
+ setSystemSettings((s) => ({ ...s, autoDeleteHistory: options[nextIndex] }));
2394
+ } else if (item.value === "Cancel") setActiveView("chat");
2395
+ }
2396
+ }
2397
+ );
2398
+ case "apiTier":
2399
+ return /* @__PURE__ */ React8.createElement(
2400
+ CommandMenu,
2401
+ {
2402
+ title: `API Tier: ${apiTier}`,
2403
+ items: [
2404
+ { label: "Free Tier (1,500/day)", value: "Free" },
2405
+ { label: `Paid Tier (Budget: ${quotas.agentLimit})`, value: "Paid" },
2406
+ { label: `Custom Model (Endpoint: ${quotas.customModelId || "None"})`, value: "Custom" },
2407
+ { label: "Back", value: "settings" }
2408
+ ],
2409
+ onSelect: (item) => {
2410
+ if (item.value === "settings" || item.value === "Back") {
2411
+ setActiveView("settings");
2412
+ return;
2413
+ }
2414
+ const newTier = item.value;
2415
+ setApiTier(newTier);
2416
+ if (newTier === "Paid") {
2417
+ setInputConfig({
2418
+ label: "Daily Agent Limit for default model (Gemma 4 31B):",
2419
+ note: "NOTE: If you have your own Gemini API supported model, use Custom mode.",
2420
+ key: "quotas",
2421
+ subKey: "agentLimit",
2422
+ value: String(quotas.agentLimit),
2423
+ next: (q) => ({
2424
+ label: "Daily default background model limit (Gemma 4 26B A4B):",
2425
+ key: "quotas",
2426
+ subKey: "backgroundLimit",
2427
+ value: String(q.backgroundLimit)
2428
+ })
2429
+ });
2430
+ setActiveView("input");
2431
+ } else if (newTier === "Custom") {
2432
+ setInputConfig({
2433
+ label: "Enter Agent Model ID:",
2434
+ key: "activeModel",
2435
+ value: activeModel,
2436
+ next: (val) => ({
2437
+ label: "Enter Background Model ID:",
2438
+ key: "janitorModel",
2439
+ value: janitorModel,
2440
+ next: (val2) => ({
2441
+ label: "Enter Agent daily limit (calls):",
2442
+ key: "quotas",
2443
+ subKey: "agentLimit",
2444
+ value: String(quotas.agentLimit),
2445
+ next: (q) => ({
2446
+ label: "Enter Background daily limit (calls):",
2447
+ key: "quotas",
2448
+ subKey: "backgroundLimit",
2449
+ value: String(quotas.backgroundLimit)
2450
+ })
2451
+ })
2452
+ })
2453
+ });
2454
+ setActiveView("input");
2455
+ } else {
2456
+ saveSettings({ apiTier: newTier, quotas });
2457
+ setActiveView("settings");
2458
+ }
2459
+ },
2460
+ onClose: () => setActiveView("settings")
2461
+ }
2462
+ );
2463
+ case "input":
2464
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 }, inputConfig?.note && /* @__PURE__ */ React8.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow", dimColor: true }, inputConfig.note)), /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, inputConfig?.label), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(
2465
+ TextInput2,
2466
+ {
2467
+ value: inputConfig?.value || "",
2468
+ onChange: (val) => setInputConfig((prev) => ({ ...prev, value: val })),
2469
+ onSubmit: (val) => {
2470
+ const { key, subKey, next } = inputConfig;
2471
+ let newQuotas = { ...quotas };
2472
+ let newSettings = {};
2473
+ if (key === "quotas") {
2474
+ const parsedValue = subKey.toLowerCase().includes("limit") ? parseInt(val) || 0 : val;
2475
+ newQuotas[subKey] = parsedValue;
2476
+ setQuotas(newQuotas);
2477
+ newSettings.quotas = newQuotas;
2478
+ } else if (key === "activeModel") {
2479
+ setActiveModel(val);
2480
+ newSettings.activeModel = val;
2481
+ } else if (key === "janitorModel") {
2482
+ setJanitorModel(val);
2483
+ newSettings.janitorModel = val;
2484
+ }
2485
+ if (next) {
2486
+ setInputConfig(next(key === "quotas" ? newQuotas : val));
2487
+ } else {
2488
+ saveSettings({ ...newSettings, apiTier, quotas: newQuotas });
2489
+ setInputConfig(null);
2490
+ setActiveView("settings");
2491
+ }
2492
+ }
2493
+ }
2494
+ )), /* @__PURE__ */ React8.createElement(Text8, { dimColor: true, marginTop: 1 }, "(Press Enter to confirm)"));
2495
+ case "stats":
2496
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan", bold: true }, "\u{1F4CA} DAILY PERFORMANCE LEDGER"), /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Agent Model Calls: ", /* @__PURE__ */ React8.createElement(Text8, { color: "green" }, dailyUsage?.agent || 0)), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Background Tasks: ", /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, dailyUsage?.background || 0))), /* @__PURE__ */ React8.createElement(Text8, { dimColor: true, marginTop: 1 }, "(Press ESC to return to chat)"));
2497
+ case "autoExecDanger":
2498
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow", bold: true, underline: true }, "\u26A0\uFE0F SECURITY WARNING: AUTO-EXEC MODE"), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1 }, "Turning this ON allows the agent to execute terminal commands automatically without requiring your approval for each step."), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1, color: "yellow" }, "RISKS INVOLVED:"), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 The agent may execute destructive commands (rm -rf, etc.) by mistake."), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Unintended system changes if the agent hallucinates a path or command."), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Reduced control over the agent's step-by-step decision making."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(
2499
+ CommandMenu,
2500
+ {
2501
+ title: "Confirm Intent",
2502
+ items: [
2503
+ { label: "I know the risk and turning on intentionally", value: "on" },
2504
+ { label: "Keep Off (Recommended)", value: "off" }
2505
+ ],
2506
+ onSelect: (item) => {
2507
+ if (item.value === "on") {
2508
+ setSystemSettings((s) => ({ ...s, autoExec: true }));
2509
+ }
2510
+ setActiveView("settings");
2511
+ }
2512
+ }
2513
+ )));
2514
+ case "externalDanger":
2515
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Text8, { color: "red", bold: true, underline: true }, "\u26A0\uFE0F SECURITY WARNING: EXTERNAL WORKSPACE ACCESS"), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1 }, "Turning this ON allows the agent to execute tools (Read/Write/Exec) outside of the current active workspace directory."), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1, color: "yellow" }, "RISKS INVOLVED:"), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Access to sensitive system files (SSH keys, Browser data, etc.)"), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Potential for accidental or malicious deletion of OS-critical files."), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Unauthorized script execution across your entire file system."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(
2516
+ CommandMenu,
2517
+ {
2518
+ title: "Confirm Intent",
2519
+ items: [
2520
+ { label: "I know the risk and turning on intentionally", value: "on" },
2521
+ { label: "Keep Off (Recommended)", value: "off" }
2522
+ ],
2523
+ onSelect: (item) => {
2524
+ if (item.value === "on") {
2525
+ setSystemSettings((s) => ({ ...s, allowExternalAccess: true }));
2526
+ }
2527
+ setActiveView("settings");
2528
+ }
2529
+ }
2530
+ )));
2531
+ case "doubleDanger":
2532
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Text8, { color: "red", bold: true, underline: true }, "\u26D4 CRITICAL SECURITY WARNING: COMBINED SYSTEM RISK"), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1 }, "You are attempting to enable BOTH [Auto-Exec] and [External Workspace Access] simultaneously."), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1, color: "red", bold: true }, "THIS IS NOT RECOMMENDED."), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1, color: "yellow" }, "THE CRITICAL RISK:"), /* @__PURE__ */ React8.createElement(Text8, null, "The agent will have the power to execute any command across your entire system WITHOUT your approval or supervision."), /* @__PURE__ */ React8.createElement(Text8, { color: "red", italic: true, marginTop: 1 }, "A single hallucination or error could result in full system wipe or data theft."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(
2533
+ CommandMenu,
2534
+ {
2535
+ title: "Final Confirmation",
2536
+ items: [
2537
+ { label: "I agree knowing the consequences", value: "on" },
2538
+ { label: "Keep Off", value: "off" }
2539
+ ],
2540
+ onSelect: (item) => {
2541
+ if (item.value === "on") {
2542
+ setSystemSettings((s) => ({ ...s, autoExec: true, allowExternalAccess: true }));
2543
+ }
2544
+ setActiveView("settings");
2545
+ }
2546
+ }
2547
+ )));
2548
+ case "key":
2549
+ return /* @__PURE__ */ React8.createElement(
2550
+ CommandMenu,
2551
+ {
2552
+ title: "\u{1F511} API KEY MANAGEMENT",
2553
+ items: [
2554
+ { label: "Edit Current Key (Update)", value: "edit" },
2555
+ { label: "Remove Current Key (Purge)", value: "remove" },
2556
+ { label: "Cancel", value: "Cancel" }
2557
+ ],
2558
+ onSelect: (item) => {
2559
+ if (item.value === "edit") {
2560
+ setApiKey(null);
2561
+ setActiveView("chat");
2562
+ setMessages((prev) => [...prev, { id: Date.now(), role: "system", text: "\u{1F5DD}\uFE0F [ACTION] Flux waiting for new API Key..." }]);
2563
+ } else if (item.value === "remove") {
2564
+ setActiveView("deleteKey");
2565
+ } else {
2566
+ setActiveView("chat");
2567
+ }
2568
+ }
2569
+ }
2570
+ );
2571
+ case "deleteKey":
2572
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "red", bold: true }, "\u26A0\uFE0F DANGER: PURGE API KEY"), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1 }, "This will permanently delete the saved API key from the project vault. You will need to enter it again to use Flux."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(
2573
+ CommandMenu,
2574
+ {
2575
+ title: "Are you absolutely sure?",
2576
+ items: [
2577
+ { label: "YES, PURGE KEY", value: "yes" },
2578
+ { label: "NO, GO BACK", value: "no" }
2579
+ ],
2580
+ onSelect: async (item) => {
2581
+ if (item.value === "yes") {
2582
+ await removeAPIKey();
2583
+ setApiKey(null);
2584
+ setActiveView("chat");
2585
+ setMessages((prev) => [...prev, { id: Date.now(), role: "system", text: "\u{1F9FC} [VAULT PURGED] API Key removed successfully." }]);
2586
+ } else {
2587
+ setActiveView("key");
2588
+ }
2589
+ }
2590
+ }
2591
+ )));
2592
+ case "exit":
2593
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", paddingX: 3, paddingY: 1, borderColor: "red" }, /* @__PURE__ */ React8.createElement(Text8, { color: "red", bold: true }, "\u{1F3C1} SESSION DASHBOARD"), /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Agent Active For: ", /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, formatDuration(Math.floor((Date.now() - SESSION_START_TIME) / 1e3)))), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Total Agent Queries: ", /* @__PURE__ */ React8.createElement(Text8, { color: "green" }, sessionAgentCalls)), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Memory Tasks: ", /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, sessionBackgroundCalls)), /* @__PURE__ */ React8.createElement(Text8, null, "\u2022 Total Tokens Consumed: ", /* @__PURE__ */ React8.createElement(Text8, { color: "magenta" }, (sessionTotalTokens / 1e3).toFixed(2), "k"))), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1 }, "Are you sure you want to exit?"), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(
2594
+ CommandMenu,
2595
+ {
2596
+ title: "Exit Confirmation",
2597
+ items: [
2598
+ { label: "Yes, Shutdown Flux", value: "yes" },
2599
+ { label: "No, Back to terminal", value: "no" }
2600
+ ],
2601
+ onSelect: (item) => {
2602
+ if (item.value === "yes") {
2603
+ process.exit(0);
2604
+ } else {
2605
+ setActiveView("chat");
2606
+ }
2607
+ }
2608
+ }
2609
+ )));
2610
+ case "resume":
2611
+ return /* @__PURE__ */ React8.createElement(Box8, { width: "100%", alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ React8.createElement(
2612
+ ResumeModal,
2613
+ {
2614
+ onSelect: async (id) => {
2615
+ const h = await loadHistory();
2616
+ if (h[id]) {
2617
+ setChatId(id);
2618
+ setMessages(h[id].messages);
2619
+ setActiveView("chat");
2620
+ setMessages((prev) => [...prev, { id: "sys-" + Date.now(), role: "system", text: `\u{1F4E1} SESSION RESUMED: [${id}]` }]);
2621
+ setCompletedIndex(0);
2622
+ }
2623
+ },
2624
+ onDelete: async (id) => {
2625
+ const newHistory = await deleteChat(id);
2626
+ return newHistory;
2627
+ },
2628
+ onClose: () => setActiveView("chat")
2629
+ }
2630
+ ));
2631
+ case "memory":
2632
+ return /* @__PURE__ */ React8.createElement(Box8, { width: "100%", alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ React8.createElement(MemoryModal, { onClose: () => setActiveView("chat") }));
2633
+ case "profile":
2634
+ return /* @__PURE__ */ React8.createElement(
2635
+ ProfileForm,
2636
+ {
2637
+ onSave: (profile) => {
2638
+ setProfileData(profile);
2639
+ setMessages((prev) => [...prev, { id: Date.now(), role: "system", text: `\u2705 Profile updated: ${profile.name} (${profile.nickname})` }]);
2640
+ setActiveView("chat");
2641
+ },
2642
+ onCancel: () => setActiveView("chat")
2643
+ }
2644
+ );
2645
+ case "resolution":
2646
+ return /* @__PURE__ */ React8.createElement(Box8, { width: "100%", alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ React8.createElement(
2647
+ ResolutionModal,
2648
+ {
2649
+ data: resolutionData,
2650
+ onResolve: (val) => {
2651
+ setResolutionData(null);
2652
+ setActiveView("chat");
2653
+ handleSubmit(val);
2654
+ },
2655
+ onEdit: (val) => {
2656
+ setResolutionData(null);
2657
+ setActiveView("chat");
2658
+ setInput(val);
2659
+ }
2660
+ }
2661
+ ));
2662
+ case "approval":
2663
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow", bold: true, underline: true }, "\u{1F6E1}\uFE0F SECURITY GATE: FILE WRITE PERMISSION"), /* @__PURE__ */ React8.createElement(Text8, { marginTop: 1 }, "The agent is attempting to modify: ", /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, parseArgs(pendingApproval?.args || "{}").path || "Unknown File")), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1, borderStyle: "single", borderColor: "#333", paddingX: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "--- PROPOSED CONTENT / DIFF ---"), (() => {
2664
+ const args = parseArgs(pendingApproval?.args || "{}");
2665
+ const oldVal = args.TargetContent || args.content_to_replace || null;
2666
+ const newVal = args.content || args.ReplacementContent || args.content_to_add || args.replacementContent || null;
2667
+ if (oldVal && newVal) {
2668
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "red", wrap: "anywhere", bold: true }, "- ", oldVal)), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "green", wrap: "anywhere", bold: true }, "+ ", newVal)));
2669
+ }
2670
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "white", wrap: "anywhere" }, newVal || "Updating file content...");
2671
+ })()), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(
2672
+ CommandMenu,
2673
+ {
2674
+ title: "Action Required",
2675
+ items: [
2676
+ { label: "\u2705 Accept this time", value: "allow" },
2677
+ { label: "\u{1F6E1}\uFE0F Accept for this session", value: "always" },
2678
+ { label: "\u274C Don't accept", value: "deny" }
2679
+ ],
2680
+ onSelect: (item) => {
2681
+ if (item.value === "always") setAutoAcceptWrites(true);
2682
+ const decision = item.value === "deny" ? "deny" : "allow";
2683
+ pendingApproval.resolve(decision);
2684
+ setPendingApproval(null);
2685
+ setActiveView("chat");
2686
+ }
2687
+ }
2688
+ )));
2689
+ case "terminalApproval":
2690
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Text8, { color: "red", bold: true, underline: true }, "\u{1F6E1}\uFE0F SECURITY GATE: TERMINAL COMMAND OVERSIGHT"), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, null, "Agent requested to run: ", /* @__PURE__ */ React8.createElement(Text8, { color: "yellow", bold: true }, parseArgs(pendingApproval?.args || "{}").command || "Unknown Command"))), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(
2691
+ CommandMenu,
2692
+ {
2693
+ title: "Risk Assessment Required",
2694
+ items: [
2695
+ { label: "\u{1F680} Run", value: "allow" },
2696
+ { label: "\u274C Deny", value: "deny" }
2697
+ ],
2698
+ onSelect: (item) => {
2699
+ pendingApproval.resolve(item.value);
2700
+ setPendingApproval(null);
2701
+ setActiveView("chat");
2702
+ }
2703
+ }
2704
+ )));
2705
+ default:
2706
+ const terminalWidth = stdout.columns || 80;
2707
+ const wrapWidth = Math.max(20, terminalWidth - 10);
2708
+ const wrappedLines = input.split("\n").reduce((acc, line) => {
2709
+ return acc + Math.max(1, Math.ceil(line.length / wrapWidth));
2710
+ }, 0);
2711
+ const maxLines = Math.max(1, wrappedLines);
2712
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginTop: 1, flexShrink: 0, width: "100%" }, statusText && /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "magenta", italic: true }, "\u23F3 ", statusText)), suggestions.length > 0 && /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "\u{1F4A1} Suggestions: "), suggestions.map((s, i) => /* @__PURE__ */ React8.createElement(Text8, { key: s, color: "yellow", bold: i === 0 }, " ", s, " "))), /* @__PURE__ */ React8.createElement(Box8, { backgroundColor: "#333333", paddingX: 1, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, maxLines > 3 ? /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%", paddingY: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, "[\u{1F4E6} ", maxLines, " lines of text in buffer - Full content will be sent]"), /* @__PURE__ */ React8.createElement(
2713
+ Box8,
2714
+ {
2715
+ flexDirection: "row",
2716
+ width: "100%",
2717
+ height: 1,
2718
+ overflow: "hidden",
2719
+ alignItems: "flex-end"
2720
+ },
2721
+ /* @__PURE__ */ React8.createElement(Box8, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "\u276F ")),
2722
+ /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(
2723
+ MultilineInput,
2724
+ {
2725
+ value: input.split("\n").pop() || "",
2726
+ onChange: (val) => {
2727
+ const cleanVal = val.replace(/\\$/, "");
2728
+ const lines = input.split("\n");
2729
+ lines[lines.length - 1] = cleanVal;
2730
+ setInput(lines.join("\n"));
2731
+ },
2732
+ onSubmit: handleSubmit,
2733
+ placeholder: escPressed ? "Press ESC again to cancel the request." : isProcessing ? "Flux Flow is thinking..." : "Type your message or /command...",
2734
+ keyBindings: {
2735
+ submit: (key) => key.return && !key.shift && !key.ctrl,
2736
+ newline: (key) => key.return && key.shift || key.return && key.ctrl
2737
+ }
2738
+ }
2739
+ ))
2740
+ )) : /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "row", width: "100%", paddingY: 0 }, /* @__PURE__ */ React8.createElement(Box8, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "\u276F ")), /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(
2741
+ MultilineInput,
2742
+ {
2743
+ value: input,
2744
+ onChange: (val) => {
2745
+ const cleanVal = val.replace(/\\\s*\n/g, "\n");
2746
+ setInput(cleanVal);
2747
+ },
2748
+ onSubmit: handleSubmit,
2749
+ placeholder: escPressed ? "Press ESC again to cancel the request." : isProcessing ? "Flux Flow is thinking..." : "Type your message or /command...",
2750
+ maxRows: 3,
2751
+ keyBindings: {
2752
+ submit: (key) => key.return && !key.shift && !key.ctrl,
2753
+ newline: (key) => key.return && key.shift || key.return && key.ctrl
2754
+ }
2755
+ }
2756
+ ))))));
2757
+ }
2758
+ };
2759
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, messages.slice(0, completedIndex).map((msg, idx) => /* @__PURE__ */ React8.createElement(ChatLayout, { key: msg.id || idx, messages: [msg], showFullThinking }))), /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", padding: 1, width: "100%" }, activeView === "chat" && /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React8.createElement(ChatLayout, { messages: messages.slice(completedIndex), showFullThinking }), activeCommand && /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(TerminalBox, { command: activeCommand, output: execOutput }))), isInitializing ? /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "double", borderColor: "magenta", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "magenta" }, "\u{1F30A} Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "bold", borderColor: "yellow", padding: 1, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow", bold: true }, "\u{1F511} API KEY REQUIRED"), /* @__PURE__ */ React8.createElement(Text8, null, "Please enter your Gemini API Key to initialize the agent's brain."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, "\u276F "), /* @__PURE__ */ React8.createElement(
2760
+ TextInput2,
2761
+ {
2762
+ value: tempKey,
2763
+ onChange: setTempKey,
2764
+ onSubmit: handleSetup,
2765
+ mask: "*"
2766
+ }
2767
+ ))) : renderActiveView(), /* @__PURE__ */ React8.createElement(Box8, { flexShrink: 0, width: "100%" }, /* @__PURE__ */ React8.createElement(
2768
+ StatusBar,
2769
+ {
2770
+ mode,
2771
+ thinkingLevel,
2772
+ tokens: sessionStats.tokens,
2773
+ chatId,
2774
+ isMemoryEnabled: systemSettings.memory
2775
+ }
2776
+ ))));
2777
+ }
2778
+
2779
+ // src/cli.jsx
2780
+ process.stdout.write("\x1Bc");
2781
+ render(/* @__PURE__ */ React9.createElement(App, null));