fluxflow-cli 1.9.28 → 1.10.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/fluxflow.js CHANGED
@@ -155,6 +155,7 @@ var init_ChatLayout = __esm({
155
155
  "search_keyword": "FindFiles",
156
156
  "write_pdf": "CreatePDF",
157
157
  "write_docx": "CreateDocument",
158
+ "generate_image": "GenerateImage",
158
159
  // PascalCase Support
159
160
  "WriteFile": "WriteFile",
160
161
  "PatchFile": "PatchFile",
@@ -167,7 +168,8 @@ var init_ChatLayout = __esm({
167
168
  "WritePDF": "WritePDF",
168
169
  "WriteDoc": "WriteDoc",
169
170
  "Memory": "Memory",
170
- "Chat": "Chat"
171
+ "Chat": "Chat",
172
+ "GenerateImage": "GenerateImage"
171
173
  };
172
174
  cleanSignals = (text) => {
173
175
  if (!text) return text;
@@ -223,7 +225,7 @@ var init_ChatLayout = __esm({
223
225
  }
224
226
  }
225
227
  }
226
- 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(/\[\s*turn\s*:?.*?$/gi, "").replace(/\n\s*turn\s*:?.*?$/gi, "").replace(/\[\s*$/gi, "").replace(/\n\nResponded on .*/g, "").replace(/\n\n\[Prompted on: .*\]/g, "").replace(/(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)/gi, "\u2192").replace(/(\$?\\?\/?\\leftarrow\$?|\$\\leftarrow\$)/gi, "\u2190").replace(/(\$?\\?\/?\\uparrow\$?|\$\\uparrow\$)/gi, "\u2191").replace(/(\$?\\?\/?\\downarrow\$?|\$\\downarrow\$)/gi, "\u2193").replace(/(\$?\\?\/?\\leftrightarrow\$?|\$\\leftrightarrow\$)/gi, "\u2194").replace(/@\[TerminalName:.*?, ProcessId:.*?\]/gi, "").replace(/\b(write_file|update_file|read_folder|view_file|exec_command|web_search|web_scrape|search_keyword|write_pdf|write_docx)\b/gi, (match) => TOOL_LABELS[match.toLowerCase()] || match).trim();
228
+ 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(/\[\s*turn\s*:?.*?$/gi, "").replace(/\n\s*turn\s*:?.*?$/gi, "").replace(/\[\s*$/gi, "").replace(/\n\nResponded on .*/g, "").replace(/\n\n\[Prompted on: .*\]/g, "").replace(/(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)/gi, "\u2192").replace(/(\$?\\?\/?\\leftarrow\$?|\$\\leftarrow\$)/gi, "\u2190").replace(/(\$?\\?\/?\\uparrow\$?|\$\\uparrow\$)/gi, "\u2191").replace(/(\$?\\?\/?\\downarrow\$?|\$\\downarrow\$)/gi, "\u2193").replace(/(\$?\\?\/?\\leftrightarrow\$?|\$\\leftrightarrow\$)/gi, "\u2194").replace(/@\[TerminalName:.*?, ProcessId:.*?\]/gi, "").replace(/\b(write_file|update_file|read_folder|view_file|exec_command|web_search|web_scrape|search_keyword|write_pdf|write_docx|generate_image)\b/gi, (match) => TOOL_LABELS[match.toLowerCase()] || match).trim();
227
229
  };
228
230
  formatThinkText = (cleaned, columns = 80) => {
229
231
  if (!cleaned) return null;
@@ -440,6 +442,9 @@ var init_ChatLayout = __esm({
440
442
  return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, paddingY: 0 }, /* @__PURE__ */ React2.createElement(Text2, { color: "red", bold: true, underline: true }, "\u274C PATCH FAILED"), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "Patch failed: ", /* @__PURE__ */ React2.createElement(Text2, { color: "white", bold: true }, "Model generated malformed edit.")))));
441
443
  }
442
444
  if (msg.role === "system" && msg.text?.includes("[TOOL RESULT]") && !isDiffResult && !isTerminalRecord && !isPatchError) return null;
445
+ if (msg.isImageStats) {
446
+ return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 0, width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { paddingX: 1, backgroundColor: "#0e1b21" }, /* @__PURE__ */ React2.createElement(Text2, { color: "cyan", bold: true }, "\u{1F4B3} IMAGE STATS")), /* @__PURE__ */ React2.createElement(Box2, { paddingX: 1, marginTop: 1, marginBottom: 1, flexDirection: "column" }, msg.text.split("\n").map((line, i) => /* @__PURE__ */ React2.createElement(Text2, { key: i, color: "white" }, line)))));
447
+ }
443
448
  if (msg.isAskRecord) {
444
449
  const selectionMatch = msg.text.match(/Selection: (.*)/);
445
450
  const selection = selectionMatch ? selectionMatch[1] : "No selection";
@@ -460,36 +465,10 @@ var init_ChatLayout = __esm({
460
465
  { cmd: "/resume", desc: "Load previous session" },
461
466
  { cmd: "/save", desc: "Force save current chat" },
462
467
  { cmd: "/chats", desc: "List all chat sessions" },
463
- {
464
- cmd: "/mode",
465
- desc: "Toggle Flux/Flow modes",
466
- subs: [
467
- { cmd: "flux", desc: "Enable Dev toolset" },
468
- { cmd: "flow", desc: "Enable Chat mode" }
469
- ]
470
- },
471
- {
472
- cmd: "/thinking",
473
- desc: "Set AI reasoning depth",
474
- subs: [
475
- { cmd: "low", desc: "Fastest reasoning" },
476
- { cmd: "medium", desc: "Balanced depth" },
477
- { cmd: "high", desc: "Complex coding" },
478
- { cmd: "max", desc: "Architectural depth" },
479
- { cmd: "show", desc: "Show full thoughts" },
480
- { cmd: "hide", desc: "Show concise thoughts" }
481
- ]
482
- },
483
- {
484
- cmd: "/model",
485
- desc: "Switch AI model",
486
- subs: [
487
- { cmd: "gemma-4-31b-it", desc: "Standard Default (Free, Recommended)" },
488
- { cmd: "gemini-3.1-pro-preview", desc: "Most Capable (Paid)" },
489
- { cmd: "gemini-3-flash-preview", desc: "Fast & Lightweight (Paid, Free limited quota)" },
490
- { cmd: "gemini-3.1-flash-lite", desc: "Ultra Fast (Paid, Free limited quota)" }
491
- ]
492
- },
468
+ { cmd: "/image", desc: "Generate images" },
469
+ { cmd: "/mode", desc: "Toggle Flux/Flow modes" },
470
+ { cmd: "/thinking", desc: "Set AI reasoning depth" },
471
+ { cmd: "/model", desc: "Switch AI model" },
493
472
  { cmd: "/settings", desc: "Configure system prefs" },
494
473
  { cmd: "/key", desc: "Manage API keys" },
495
474
  { cmd: "/profile", desc: "Edit developer persona" },
@@ -498,21 +477,8 @@ var init_ChatLayout = __esm({
498
477
  { cmd: "/reset", desc: "Wipe all project data" },
499
478
  { cmd: "/about", desc: "Project info & credits" },
500
479
  { cmd: "/changelog", desc: "View latest updates" },
501
- {
502
- cmd: "/fluxflow",
503
- desc: "Project management",
504
- subs: [
505
- { cmd: "init", desc: "Create FluxFlow.md template" }
506
- ]
507
- },
508
- {
509
- cmd: "/update",
510
- desc: "Check/Install updates",
511
- subs: [
512
- { cmd: "check", desc: "Check for new version" },
513
- { cmd: "latest", desc: "Install latest release" }
514
- ]
515
- }
480
+ { cmd: "/fluxflow", desc: "Project management" },
481
+ { cmd: "/update", desc: "Check/Install updates" }
516
482
  ];
517
483
  return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "magenta", bold: true, underline: true }, "\u{1F4DC} COMMAND REFERENCE"), /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1 }, commandList.map((c, i) => /* @__PURE__ */ React2.createElement(Box2, { key: i, flexDirection: "row" }, /* @__PURE__ */ React2.createElement(Box2, { width: 15 }, /* @__PURE__ */ React2.createElement(Text2, { color: "cyan", bold: true }, c.cmd)), /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, " - ", c.desc))))));
518
484
  }
@@ -894,7 +860,7 @@ Suggest best options; don't ask for preferences. System handles the rest
894
860
  1. Web Search: [tool:functions.WebSearch(query="...", limit=number)]. Find info (limit 3-10). Proactive use for unknown topics${mode === "Flux" ? " or docs" : ""}
895
861
  2. Web Scrape: [tool:functions.WebScrape(url="...")]. Visit URL
896
862
 
897
- ${mode === "Flux" ? `- DEV TOOLS (path = relative to CWD) -
863
+ ${mode === "Flux" ? `- FILE TOOLS (path = relative to CWD) -
898
864
  1. [tool:functions.ReadFile(path="...", start_line=N, end_line=N)]. Reads contents. Supports images/docs. User gives image/doc: VIEW FIRST
899
865
  2. [tool:functions.ReadFolder(path="...")]. Detailed DIR stats
900
866
  3. [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? -> update_file > write_file
@@ -903,6 +869,7 @@ ${mode === "Flux" ? `- DEV TOOLS (path = relative to CWD) -
903
869
  6. [tool:functions.WriteDoc(path="...", content="...")]. A4 Word doc. Proper margins and page breaks
904
870
  7. [tool:functions.Run(command="...")]. Runs a shell command. Destructive/Irreversible ops -> ask user
905
871
  8. [tool:functions.SearchKeyword(keyword="...")]. Global search. Finds definitions/logic without reading every file
872
+ 9. Generate Image: [tool:functions.GenerateImage(title="...", prompt="...", path="... png", ratio="16:9, 9:16, 1:1, 4:3, 3:4")]. Generates image using AI. Usage: Mockups, PDF thumbnails, any visual content
906
873
 
907
874
  - VERIFY RESULT CONTENTS. Fix errors. No hallucinations
908
875
  - File tools > code chat
@@ -924,15 +891,12 @@ var init_janitor_tools = __esm({
924
891
  "src/data/janitor_tools.js"() {
925
892
  JANITOR_TOOLS_PROTOCOL = (isMemoryEnabled = true, needTitle = true) => `
926
893
  Your tool syntax is: '[tool:functions.ToolName(args...)]'
927
- ${needTitle ? `-- CHAT MANAGEMENT TOOLS --
928
- 1. YOU MUST UPDATE CHAT TITLE (URGENT PRIORITY, MUST CALL THIS TOOL):
929
- [tool:functions.Chat(title='<short creative title of FULL conversation in 3-5 words>')]. Consider full chat context to generate title NOT just latest message.`.trimEnd() : ""}
930
894
 
931
- You have access to the following memory functions to persist important information:
932
- 1. Temporary context (URGENT PRIORITY, MUST CALL THIS TOOL):
895
+ -- CHAT MANAGEMENT TOOLS (MUST CALL THESE 2 TOOLS ALWAYS) --
896
+ [tool:functions.Chat(title='<short creative title of FULL conversation in 3-5 words>')]. Consider full chat context to generate title NOT just latest message.
933
897
  [tool:functions.Memory(action='temp', content='<summary of the user prompt & model responses ONLY FROM LATEST PROMPT UNDER 40 WORDS>. [Talked on: <date> <hour>]')]
934
898
 
935
- ${isMemoryEnabled ? `2. User-specific long-term memory (USE BASED ON CONVERSATION CONTEXT):
899
+ ${isMemoryEnabled ? `-- User-specific long-term memory (USE BASED ON CONVERSATION CONTEXT) --
936
900
  - Add: [tool:functions.Memory(action='user', method='add', content='<string to add>. [Saved on: <date ONLY>]')]
937
901
  - Delete: [tool:functions.Memory(action='user', method='delete', content='exact memory id')]
938
902
  - Update: [tool:functions.Memory(action='user', method='update', content-new='string to update', content-old='exact memory id')]
@@ -1027,7 +991,7 @@ ${foundFiles.map((f) => `- ${f.name}: ${f.desc}`).join("\n")}
1027
991
  Check these first; these files > training data for project consistency. Safety rules apply` : "";
1028
992
  return `${nameStr}${nicknameStr}${userInstrStr}
1029
993
  [SYSTEM (OVERRIDES EVERYTHING)]
1030
- Identity: Flux Flow (by Kushal Roy Chowdhury). Sassy, Friendly, CLI Agent. No flirting
994
+ Identity: Flux Flow (by Kushal Roy Chowdhury). Sassy, Friendly, CLI Agent. No flirting ${mode === "Flux" ? "" : ""}
1031
995
  Mode: ${mode}${thinkingLevel !== "Fast" ? "(Thinking Mode)" : ""}. ${mode === "Flux" ? "Goal-oriented" : "Conversational & UX-focused"}
1032
996
  CWD: ${cwdStr}.${isSystemDir ? " [PROTECTED: ASK BEFORE MODIFYING]" : ""} OS: ${osDetected}${osDetected === "Windows" && mode === "Flux" ? ". PS via CMD" : ""}
1033
997
  High Priority: [SYSTEM], [STEERING HINT]
@@ -1062,7 +1026,7 @@ ${projectContextBlock}
1062
1026
  - Task Complete? End loop with [turn: finish]
1063
1027
  [/SYSTEM]`.trim();
1064
1028
  };
1065
- getJanitorInstruction = (originalText, agentRaws, userMemories = "", isMemoryEnabled = true, needTitle = true) => {
1029
+ getJanitorInstruction = (userMemories = "", isMemoryEnabled = true, needTitle = true) => {
1066
1030
  return `
1067
1031
  ${userMemories ? `
1068
1032
 
@@ -1200,7 +1164,7 @@ var init_history = __esm({
1200
1164
  // src/utils/usage.js
1201
1165
  import fs6 from "fs-extra";
1202
1166
  import path5 from "path";
1203
- var cachedUsage, writeTimeout, lastWriteTime, isDirty, loadUsageFromFile, flushUsage, queueFlush, initUsage, forceFlushUsage, getDailyUsage, incrementUsage, addToUsage, checkQuota;
1167
+ var cachedUsage, writeTimeout, lastWriteTime, isDirty, defaultStats, loadUsageFromFile, flushUsage, queueFlush, initUsage, forceFlushUsage, getDailyUsage, incrementUsage, addToUsage, checkQuota, checkImageQuota, getImageQuotaStats, recordImageGeneration;
1204
1168
  var init_usage = __esm({
1205
1169
  "src/utils/usage.js"() {
1206
1170
  init_paths();
@@ -1208,31 +1172,36 @@ var init_usage = __esm({
1208
1172
  writeTimeout = null;
1209
1173
  lastWriteTime = 0;
1210
1174
  isDirty = false;
1175
+ defaultStats = {
1176
+ agent: 0,
1177
+ background: 0,
1178
+ search: 0,
1179
+ toolSuccess: 0,
1180
+ toolFailure: 0,
1181
+ toolDenied: 0,
1182
+ duration: 0,
1183
+ tokens: 0,
1184
+ imageCalls: []
1185
+ };
1211
1186
  loadUsageFromFile = async () => {
1212
1187
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1213
1188
  try {
1214
1189
  if (await fs6.exists(USAGE_FILE)) {
1215
1190
  const data = await fs6.readJson(USAGE_FILE);
1216
1191
  if (data && data.date === today && data.stats) {
1192
+ const mergedStats = { ...defaultStats, ...data.stats };
1193
+ if (!Array.isArray(mergedStats.imageCalls)) {
1194
+ mergedStats.imageCalls = [];
1195
+ }
1217
1196
  return {
1218
1197
  ...data,
1219
- stats: { ...defaultStats, ...data.stats }
1198
+ stats: mergedStats
1220
1199
  };
1221
1200
  }
1222
1201
  }
1223
1202
  } catch (err) {
1224
1203
  }
1225
- const defaultStats = {
1226
- agent: 0,
1227
- background: 0,
1228
- search: 0,
1229
- toolSuccess: 0,
1230
- toolFailure: 0,
1231
- toolDenied: 0,
1232
- duration: 0,
1233
- tokens: 0
1234
- };
1235
- return { date: today, stats: defaultStats };
1204
+ return { date: today, stats: { ...defaultStats } };
1236
1205
  };
1237
1206
  flushUsage = async () => {
1238
1207
  if (!isDirty || !cachedUsage) return;
@@ -1248,7 +1217,19 @@ var init_usage = __esm({
1248
1217
  if (diskData && diskData.date === cachedUsage.date && diskData.stats) {
1249
1218
  for (const key in cachedUsage.stats) {
1250
1219
  if (diskData.stats[key] !== void 0) {
1251
- cachedUsage.stats[key] = Math.max(cachedUsage.stats[key], diskData.stats[key]);
1220
+ if (Array.isArray(cachedUsage.stats[key])) {
1221
+ const diskArr = Array.isArray(diskData.stats[key]) ? diskData.stats[key] : [];
1222
+ const memArr = cachedUsage.stats[key];
1223
+ const uniqueMap = /* @__PURE__ */ new Map();
1224
+ for (const item of [...diskArr, ...memArr]) {
1225
+ if (item && item.timestamp) {
1226
+ uniqueMap.set(item.timestamp, item);
1227
+ }
1228
+ }
1229
+ cachedUsage.stats[key] = Array.from(uniqueMap.values());
1230
+ } else if (typeof cachedUsage.stats[key] === "number") {
1231
+ cachedUsage.stats[key] = Math.max(cachedUsage.stats[key], Number(diskData.stats[key]) || 0);
1232
+ }
1252
1233
  }
1253
1234
  }
1254
1235
  }
@@ -1291,20 +1272,14 @@ var init_usage = __esm({
1291
1272
  } else if (cachedUsage.date !== today) {
1292
1273
  cachedUsage = {
1293
1274
  date: today,
1294
- stats: {
1295
- agent: 0,
1296
- background: 0,
1297
- search: 0,
1298
- toolSuccess: 0,
1299
- toolFailure: 0,
1300
- toolDenied: 0,
1301
- duration: 0,
1302
- tokens: 0
1303
- }
1275
+ stats: { ...defaultStats }
1304
1276
  };
1305
1277
  isDirty = true;
1306
1278
  await flushUsage();
1307
1279
  }
1280
+ if (cachedUsage && cachedUsage.stats && !Array.isArray(cachedUsage.stats.imageCalls)) {
1281
+ cachedUsage.stats.imageCalls = [];
1282
+ }
1308
1283
  return cachedUsage.stats;
1309
1284
  };
1310
1285
  incrementUsage = async (key) => {
@@ -1338,6 +1313,67 @@ var init_usage = __esm({
1338
1313
  }
1339
1314
  return true;
1340
1315
  };
1316
+ checkImageQuota = async (settings) => {
1317
+ const imageSettings = settings.imageSettings || { keyType: "Default", quality: "Low-High" };
1318
+ if (imageSettings.keyType !== "Default") return true;
1319
+ const costs = {
1320
+ "Low": 1e-3,
1321
+ "Low-High": 2e-3,
1322
+ "Medium": 8e-3,
1323
+ "Medium-High": 0.01,
1324
+ "High": 0.045,
1325
+ "Ultra": 0.0488,
1326
+ "Premium": 0.15
1327
+ };
1328
+ const currentCost = costs[imageSettings.quality] || 2e-3;
1329
+ const stats = await getDailyUsage();
1330
+ if (!stats.imageCalls) {
1331
+ stats.imageCalls = [];
1332
+ }
1333
+ const now = Date.now();
1334
+ const oneHourAgo = now - 60 * 60 * 1e3;
1335
+ const activeCalls = stats.imageCalls.filter((c) => c.timestamp >= oneHourAgo);
1336
+ const totalSpent = activeCalls.reduce((sum, c) => sum + c.cost, 0);
1337
+ return totalSpent + currentCost <= 0.02;
1338
+ };
1339
+ getImageQuotaStats = async () => {
1340
+ const stats = await getDailyUsage();
1341
+ if (!stats.imageCalls) {
1342
+ stats.imageCalls = [];
1343
+ }
1344
+ const now = Date.now();
1345
+ const oneHourAgo = now - 60 * 60 * 1e3;
1346
+ const activeCalls = stats.imageCalls.filter((c) => c.timestamp >= oneHourAgo);
1347
+ const totalSpent = activeCalls.reduce((sum, c) => sum + c.cost, 0);
1348
+ const remaining = Math.max(0, 0.02 - totalSpent);
1349
+ return {
1350
+ totalSpent,
1351
+ remaining,
1352
+ activeCallsCount: activeCalls.length
1353
+ };
1354
+ };
1355
+ recordImageGeneration = async (settings) => {
1356
+ const imageSettings = settings.imageSettings || { keyType: "Default", quality: "Low-High" };
1357
+ const costs = {
1358
+ "Low": 1e-3,
1359
+ "Low-High": 2e-3,
1360
+ "Medium": 8e-3,
1361
+ "Medium-High": 0.01,
1362
+ "High": 0.045,
1363
+ "Ultra": 0.0488,
1364
+ "Premium": 0.1
1365
+ };
1366
+ const cost = costs[imageSettings.quality] || 2e-3;
1367
+ const stats = await getDailyUsage();
1368
+ if (!stats.imageCalls) {
1369
+ stats.imageCalls = [];
1370
+ }
1371
+ stats.imageCalls.push({
1372
+ timestamp: Date.now(),
1373
+ cost
1374
+ });
1375
+ queueFlush();
1376
+ };
1341
1377
  }
1342
1378
  });
1343
1379
 
@@ -1460,8 +1496,6 @@ var init_arg_parser = __esm({
1460
1496
 
1461
1497
  // src/tools/web_search.js
1462
1498
  import puppeteer from "puppeteer";
1463
- import fs7 from "fs";
1464
- import path6 from "path";
1465
1499
  var web_search;
1466
1500
  var init_web_search = __esm({
1467
1501
  "src/tools/web_search.js"() {
@@ -1485,7 +1519,7 @@ var init_web_search = __esm({
1485
1519
  ]
1486
1520
  });
1487
1521
  const page = await browser.newPage();
1488
- await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36");
1522
+ await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.97 Safari/537.36");
1489
1523
  await page.setViewport({ width: 1366, height: 768 });
1490
1524
  const jitter = attempt === 1 ? Math.random() * 1e3 + 500 : Math.random() * 2e3 + 1e3;
1491
1525
  await new Promise((r) => setTimeout(r, jitter));
@@ -1515,16 +1549,6 @@ Snippet: ${snippet}`;
1515
1549
  return `No results found for query: [${query}].`;
1516
1550
  }
1517
1551
  const finalResults = results.join("\n\n");
1518
- const toolLogDir = path6.join(LOGS_DIR, "tools");
1519
- if (!fs7.existsSync(toolLogDir)) fs7.mkdirSync(toolLogDir, { recursive: true });
1520
- fs7.appendFileSync(path6.join(toolLogDir, "search-results.log"), `SEARCH ${(/* @__PURE__ */ new Date()).toLocaleString()} - Query: [${query}]. Count: ${results.length}.
1521
- Content:
1522
- ${finalResults}
1523
-
1524
- --------------------------------------------------------
1525
-
1526
-
1527
- `);
1528
1552
  await browser.close();
1529
1553
  return `Search results for [${query}]:
1530
1554
 
@@ -1545,8 +1569,6 @@ ${finalResults}`;
1545
1569
 
1546
1570
  // src/tools/web_scrape.js
1547
1571
  import puppeteer2 from "puppeteer";
1548
- import fs8 from "fs";
1549
- import path7 from "path";
1550
1572
  var web_scrape;
1551
1573
  var init_web_scrape = __esm({
1552
1574
  "src/tools/web_scrape.js"() {
@@ -1569,7 +1591,7 @@ var init_web_scrape = __esm({
1569
1591
  ]
1570
1592
  });
1571
1593
  const page = await browser.newPage();
1572
- await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36");
1594
+ await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.97 Safari/537.36");
1573
1595
  await page.setViewport({ width: 1366, height: 768 });
1574
1596
  const jitter = attempt === 1 ? Math.random() * 1e3 + 500 : Math.random() * 2e3 + 1e3;
1575
1597
  await new Promise((r) => setTimeout(r, jitter));
@@ -1613,16 +1635,6 @@ var init_web_scrape = __esm({
1613
1635
  });
1614
1636
  if (!htmlContent) throw new Error("EMPTY_RENDER_RESULT");
1615
1637
  const cleanedHtml = htmlContent.replace(/\s+/g, " ").replace(/>\s+</g, "><").trim().substring(0, 3e4);
1616
- const toolLogDir = path7.join(LOGS_DIR, "tools");
1617
- if (!fs8.existsSync(toolLogDir)) fs8.mkdirSync(toolLogDir, { recursive: true });
1618
- fs8.appendFileSync(path7.join(toolLogDir, "search-scraped.log"), `PUPPETEER ${(/* @__PURE__ */ new Date()).toLocaleString()} - URL: [${url}]. Length: ${cleanedHtml.length}.
1619
- Content:
1620
- ${cleanedHtml}${htmlContent.length > 3e4 ? "\n\n[TRUNCATED AT 30K CHARS]" : ""}
1621
-
1622
- --------------------------------------------------------
1623
-
1624
-
1625
- `);
1626
1638
  await browser.close();
1627
1639
  return `CLEANED HTML FROM [${url}]:
1628
1640
 
@@ -1736,24 +1748,24 @@ var init_chat = __esm({
1736
1748
  });
1737
1749
 
1738
1750
  // src/tools/list_files.js
1739
- import fs9 from "fs";
1740
- import path8 from "path";
1751
+ import fs7 from "fs";
1752
+ import path6 from "path";
1741
1753
  var list_files;
1742
1754
  var init_list_files = __esm({
1743
1755
  "src/tools/list_files.js"() {
1744
1756
  init_arg_parser();
1745
1757
  list_files = async (args) => {
1746
1758
  const { path: targetPath = "." } = parseArgs(args);
1747
- const absolutePath = path8.resolve(process.cwd(), targetPath);
1759
+ const absolutePath = path6.resolve(process.cwd(), targetPath);
1748
1760
  try {
1749
- if (!fs9.existsSync(absolutePath)) {
1761
+ if (!fs7.existsSync(absolutePath)) {
1750
1762
  return `ERROR: Path [${targetPath}] does not exist.`;
1751
1763
  }
1752
- const stats = fs9.statSync(absolutePath);
1764
+ const stats = fs7.statSync(absolutePath);
1753
1765
  if (!stats.isDirectory()) {
1754
1766
  return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
1755
1767
  }
1756
- const files = fs9.readdirSync(absolutePath);
1768
+ const files = fs7.readdirSync(absolutePath);
1757
1769
  if (files.length === 0) {
1758
1770
  return `Directory [${targetPath}] is empty.`;
1759
1771
  }
@@ -1761,11 +1773,11 @@ var init_list_files = __esm({
1761
1773
  const maxDisplay = 100;
1762
1774
  const displayFiles = files.slice(0, maxDisplay);
1763
1775
  const list = displayFiles.map((file) => {
1764
- const fPath = path8.join(absolutePath, file);
1776
+ const fPath = path6.join(absolutePath, file);
1765
1777
  let indicator = "\u{1F4C4}";
1766
1778
  let metaPart = "";
1767
1779
  try {
1768
- const fStats = fs9.statSync(fPath);
1780
+ const fStats = fs7.statSync(fPath);
1769
1781
  indicator = fStats.isDirectory() ? "\u{1F4C1}" : "\u{1F4C4}";
1770
1782
  const sizeKB = (fStats.size / 1024).toFixed(1);
1771
1783
  metaPart = fStats.isFile() ? ` - [${sizeKB} KB]` : "";
@@ -1797,8 +1809,8 @@ ${list}${footer}`;
1797
1809
  });
1798
1810
 
1799
1811
  // src/tools/view_file.js
1800
- import fs10 from "fs";
1801
- import path9 from "path";
1812
+ import fs8 from "fs";
1813
+ import path7 from "path";
1802
1814
  var view_file;
1803
1815
  var init_view_file = __esm({
1804
1816
  "src/tools/view_file.js"() {
@@ -1810,16 +1822,16 @@ var init_view_file = __esm({
1810
1822
  const finalStart = sLine || 1;
1811
1823
  const finalEnd = eLine || (sLine ? sLine + 800 : 800);
1812
1824
  if (!targetPath) return 'ERROR: Missing "path" argument for view_file.';
1813
- const absolutePath = path9.resolve(process.cwd(), targetPath);
1825
+ const absolutePath = path7.resolve(process.cwd(), targetPath);
1814
1826
  try {
1815
- if (!fs10.existsSync(absolutePath)) {
1827
+ if (!fs8.existsSync(absolutePath)) {
1816
1828
  return `ERROR: File [${targetPath}] does not exist.`;
1817
1829
  }
1818
- const stats = fs10.statSync(absolutePath);
1830
+ const stats = fs8.statSync(absolutePath);
1819
1831
  if (stats.isDirectory()) {
1820
1832
  return `ERROR: Path [${targetPath}] is a directory. Use list_files instead.`;
1821
1833
  }
1822
- const ext = path9.extname(targetPath).toLowerCase();
1834
+ const ext = path7.extname(targetPath).toLowerCase();
1823
1835
  const mimeMap = {
1824
1836
  ".pdf": "application/pdf",
1825
1837
  ".jpg": "image/jpeg",
@@ -1830,7 +1842,7 @@ var init_view_file = __esm({
1830
1842
  ".doc": "application/msword"
1831
1843
  };
1832
1844
  if (mimeMap[ext]) {
1833
- const buffer = fs10.readFileSync(absolutePath);
1845
+ const buffer = fs8.readFileSync(absolutePath);
1834
1846
  const base64 = buffer.toString("base64");
1835
1847
  const mimeType = mimeMap[ext];
1836
1848
  return {
@@ -1843,7 +1855,7 @@ var init_view_file = __esm({
1843
1855
  }
1844
1856
  };
1845
1857
  }
1846
- let content = fs10.readFileSync(absolutePath, "utf8");
1858
+ let content = fs8.readFileSync(absolutePath, "utf8");
1847
1859
  if (content.startsWith("\uFEFF")) {
1848
1860
  content = content.slice(1);
1849
1861
  }
@@ -1866,8 +1878,8 @@ ${code}`;
1866
1878
  });
1867
1879
 
1868
1880
  // src/tools/write_file.js
1869
- import fs11 from "fs";
1870
- import path10 from "path";
1881
+ import fs9 from "fs";
1882
+ import path8 from "path";
1871
1883
  var write_file;
1872
1884
  var init_write_file = __esm({
1873
1885
  "src/tools/write_file.js"() {
@@ -1877,13 +1889,13 @@ var init_write_file = __esm({
1877
1889
  if (!targetPath) return 'ERROR: Missing "path" argument for write_file.';
1878
1890
  if (content === void 0) return 'ERROR: Missing "content" argument for write_file.';
1879
1891
  content = content.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1880
- const absolutePath = path10.resolve(process.cwd(), targetPath);
1881
- const parentDir = path10.dirname(absolutePath);
1892
+ const absolutePath = path8.resolve(process.cwd(), targetPath);
1893
+ const parentDir = path8.dirname(absolutePath);
1882
1894
  try {
1883
1895
  let ancestry = "";
1884
- if (fs11.existsSync(absolutePath)) {
1896
+ if (fs9.existsSync(absolutePath)) {
1885
1897
  try {
1886
- const oldData = fs11.readFileSync(absolutePath, "utf8");
1898
+ const oldData = fs9.readFileSync(absolutePath, "utf8");
1887
1899
  const lines = oldData.split(/\r?\n/);
1888
1900
  ancestry = `Old File contents:
1889
1901
  ${lines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
@@ -1895,15 +1907,15 @@ ${lines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
1895
1907
  `;
1896
1908
  }
1897
1909
  }
1898
- if (!fs11.existsSync(parentDir)) {
1899
- fs11.mkdirSync(parentDir, { recursive: true });
1910
+ if (!fs9.existsSync(parentDir)) {
1911
+ fs9.mkdirSync(parentDir, { recursive: true });
1900
1912
  }
1901
1913
  const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1902
1914
  const processedContent = strip(content);
1903
1915
  const lineCount = processedContent.split(/\r?\n/).length;
1904
1916
  const originalSize = Buffer.byteLength(processedContent, "utf8");
1905
- fs11.writeFileSync(absolutePath, processedContent, "utf8");
1906
- let verifiedContent = fs11.readFileSync(absolutePath, "utf8");
1917
+ fs9.writeFileSync(absolutePath, processedContent, "utf8");
1918
+ let verifiedContent = fs9.readFileSync(absolutePath, "utf8");
1907
1919
  const verifiedSize = Buffer.byteLength(verifiedContent, "utf8");
1908
1920
  const verifiedLines = verifiedContent.split(/\r?\n/);
1909
1921
  const verifiedLineCount = verifiedLines.length;
@@ -1939,8 +1951,8 @@ Check if Starting and Ending matches your write.`;
1939
1951
  });
1940
1952
 
1941
1953
  // src/tools/update_file.js
1942
- import fs12 from "fs";
1943
- import path11 from "path";
1954
+ import fs10 from "fs";
1955
+ import path9 from "path";
1944
1956
  var update_file;
1945
1957
  var init_update_file = __esm({
1946
1958
  "src/tools/update_file.js"() {
@@ -1953,18 +1965,18 @@ var init_update_file = __esm({
1953
1965
  const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1954
1966
  content_to_replace = strip(content_to_replace);
1955
1967
  content_to_add = strip(content_to_add);
1956
- const absolutePath = path11.resolve(process.cwd(), targetPath);
1968
+ const absolutePath = path9.resolve(process.cwd(), targetPath);
1957
1969
  try {
1958
- if (!fs12.existsSync(absolutePath)) {
1970
+ if (!fs10.existsSync(absolutePath)) {
1959
1971
  return `ERROR: File [${targetPath}] does not exist. Use write_file instead.`;
1960
1972
  }
1961
- let diskContent = fs12.readFileSync(absolutePath, "utf8");
1973
+ let diskContent = fs10.readFileSync(absolutePath, "utf8");
1962
1974
  if (diskContent.startsWith("\uFEFF")) {
1963
1975
  diskContent = diskContent.slice(1);
1964
1976
  }
1965
1977
  const normalizedDisk = diskContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1966
1978
  if (diskContent !== normalizedDisk) {
1967
- fs12.writeFileSync(absolutePath, normalizedDisk, "utf8");
1979
+ fs10.writeFileSync(absolutePath, normalizedDisk, "utf8");
1968
1980
  diskContent = normalizedDisk;
1969
1981
  }
1970
1982
  const currentContent = diskContent;
@@ -2033,7 +2045,7 @@ var init_update_file = __esm({
2033
2045
  const firstLeadingContext = currentContent.substring(firstLineStart, startPos);
2034
2046
  const finalContentToAdd = adjustIndentation(content_to_add, firstMatchContent, firstLeadingContext);
2035
2047
  const finalContentToReplace = firstMatchContent;
2036
- fs12.writeFileSync(absolutePath, newFileContent, "utf8");
2048
+ fs10.writeFileSync(absolutePath, newFileContent, "utf8");
2037
2049
  const allOriginalLines = currentContent.split(/\r?\n/);
2038
2050
  const startLine = currentContent.substring(0, startPos).split(/\r?\n/).length;
2039
2051
  const oldLines = content_to_replace.split(/\r?\n/);
@@ -2296,34 +2308,34 @@ ${finalOutput}`);
2296
2308
  });
2297
2309
 
2298
2310
  // src/tools/read_folder.js
2299
- import fs13 from "fs";
2300
- import path12 from "path";
2311
+ import fs11 from "fs";
2312
+ import path10 from "path";
2301
2313
  var read_folder;
2302
2314
  var init_read_folder = __esm({
2303
2315
  "src/tools/read_folder.js"() {
2304
2316
  init_arg_parser();
2305
2317
  read_folder = async (args) => {
2306
2318
  const { path: targetPath = "." } = parseArgs(args);
2307
- const absolutePath = path12.resolve(process.cwd(), targetPath);
2319
+ const absolutePath = path10.resolve(process.cwd(), targetPath);
2308
2320
  try {
2309
- if (!fs13.existsSync(absolutePath)) {
2321
+ if (!fs11.existsSync(absolutePath)) {
2310
2322
  return `ERROR: Path [${targetPath}] does not exist.`;
2311
2323
  }
2312
- const stats = fs13.statSync(absolutePath);
2324
+ const stats = fs11.statSync(absolutePath);
2313
2325
  if (!stats.isDirectory()) {
2314
2326
  return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
2315
2327
  }
2316
- const files = fs13.readdirSync(absolutePath);
2328
+ const files = fs11.readdirSync(absolutePath);
2317
2329
  const totalItems = files.length;
2318
2330
  const maxDisplay = 100;
2319
2331
  const displayItems = files.slice(0, maxDisplay);
2320
2332
  const folderData = [];
2321
2333
  for (const file of displayItems) {
2322
- const fPath = path12.join(absolutePath, file);
2334
+ const fPath = path10.join(absolutePath, file);
2323
2335
  let indicator = "\u{1F4C4}";
2324
2336
  let info = { name: file, type: "unknown", size: "N/A", mtime: "N/A" };
2325
2337
  try {
2326
- const fStats = fs13.statSync(fPath);
2338
+ const fStats = fs11.statSync(fPath);
2327
2339
  info = {
2328
2340
  name: file,
2329
2341
  type: fStats.isDirectory() ? "directory" : "file",
@@ -2406,8 +2418,8 @@ var init_ask_user = __esm({
2406
2418
 
2407
2419
  // src/tools/write_pdf.js
2408
2420
  import puppeteer3 from "puppeteer";
2409
- import path13 from "path";
2410
- import fs14 from "fs-extra";
2421
+ import path11 from "path";
2422
+ import fs12 from "fs-extra";
2411
2423
  import { PDFDocument } from "pdf-lib";
2412
2424
  var write_pdf;
2413
2425
  var init_write_pdf = __esm({
@@ -2422,10 +2434,10 @@ var init_write_pdf = __esm({
2422
2434
  } = parseArgs(args);
2423
2435
  if (!targetPath) return 'ERROR: Missing "path" argument for write_pdf.';
2424
2436
  if (!content) return 'ERROR: Missing "content" (HTML/CSS) for write_pdf.';
2425
- const absolutePath = path13.resolve(process.cwd(), targetPath);
2437
+ const absolutePath = path11.resolve(process.cwd(), targetPath);
2426
2438
  let browser = null;
2427
2439
  try {
2428
- await fs14.ensureDir(path13.dirname(absolutePath));
2440
+ await fs12.ensureDir(path11.dirname(absolutePath));
2429
2441
  browser = await puppeteer3.launch({
2430
2442
  headless: true,
2431
2443
  args: [
@@ -2480,7 +2492,7 @@ var init_write_pdf = __esm({
2480
2492
  printBackground: true
2481
2493
  });
2482
2494
  const pdfDoc = await PDFDocument.load(pdfBytes);
2483
- const fileName = path13.basename(targetPath);
2495
+ const fileName = path11.basename(targetPath);
2484
2496
  pdfDoc.setTitle(`FluxFlow_${fileName}`);
2485
2497
  pdfDoc.setAuthor("FluxFlow CLI");
2486
2498
  pdfDoc.setSubject("Generated with Agentic AI System");
@@ -2488,8 +2500,8 @@ var init_write_pdf = __esm({
2488
2500
  pdfDoc.setCreator("FluxFlow PDF Engine");
2489
2501
  pdfDoc.setProducer("FluxFlow (Generative AI)");
2490
2502
  const finalPdfBytes = await pdfDoc.save();
2491
- await fs14.writeFile(absolutePath, finalPdfBytes);
2492
- const stats = await fs14.stat(absolutePath);
2503
+ await fs12.writeFile(absolutePath, finalPdfBytes);
2504
+ const stats = await fs12.stat(absolutePath);
2493
2505
  return `SUCCESS: PDF generated successfully at [${targetPath}] (${(stats.size / 1024).toFixed(2)} KB).`;
2494
2506
  } catch (err) {
2495
2507
  return `ERROR: Failed to generate PDF [${targetPath}]: ${err.message}`;
@@ -2501,8 +2513,8 @@ var init_write_pdf = __esm({
2501
2513
  });
2502
2514
 
2503
2515
  // src/tools/write_docx.js
2504
- import fs15 from "fs-extra";
2505
- import path14 from "path";
2516
+ import fs13 from "fs-extra";
2517
+ import path12 from "path";
2506
2518
  import HTMLtoDOCX from "html-to-docx";
2507
2519
  var write_docx;
2508
2520
  var init_write_docx = __esm({
@@ -2515,10 +2527,10 @@ var init_write_docx = __esm({
2515
2527
  } = parseArgs(args);
2516
2528
  if (!targetPath) return 'ERROR: Missing "path" argument for write_docx.';
2517
2529
  if (!content) return 'ERROR: Missing "content" (HTML) for write_docx.';
2518
- const absolutePath = path14.resolve(process.cwd(), targetPath);
2530
+ const absolutePath = path12.resolve(process.cwd(), targetPath);
2519
2531
  try {
2520
- await fs15.ensureDir(path14.dirname(absolutePath));
2521
- const fileName = path14.basename(targetPath);
2532
+ await fs13.ensureDir(path12.dirname(absolutePath));
2533
+ const fileName = path12.basename(targetPath);
2522
2534
  const fullHtml = content.includes("<html") ? content : `
2523
2535
  <!DOCTYPE html>
2524
2536
  <html lang="en">
@@ -2539,7 +2551,7 @@ var init_write_docx = __esm({
2539
2551
  footer: true,
2540
2552
  pageNumber: true
2541
2553
  });
2542
- await fs15.writeFile(absolutePath, docxBuffer);
2554
+ await fs13.writeFile(absolutePath, docxBuffer);
2543
2555
  return `SUCCESS: Word document [${targetPath}] generated successfully.
2544
2556
  - Size: ${(docxBuffer.length / 1024).toFixed(1)} KB`;
2545
2557
  } catch (err) {
@@ -2605,6 +2617,269 @@ var init_search_keyword = __esm({
2605
2617
  }
2606
2618
  });
2607
2619
 
2620
+ // src/utils/settings.js
2621
+ import fs14 from "fs-extra";
2622
+ import path13 from "path";
2623
+ var DEFAULT_SETTINGS, loadSettings, migrateToExternal, saveSettings;
2624
+ var init_settings = __esm({
2625
+ "src/utils/settings.js"() {
2626
+ init_paths();
2627
+ DEFAULT_SETTINGS = {
2628
+ mode: "Flux",
2629
+ thinkingLevel: "Medium",
2630
+ activeModel: "gemma-4-31b-it",
2631
+ showFullThinking: true,
2632
+ apiTier: "Free",
2633
+ quotas: {
2634
+ agentLimit: 1500,
2635
+ backgroundLimit: 1500,
2636
+ searchLimit: 100,
2637
+ customModelId: "",
2638
+ customLimit: 0
2639
+ },
2640
+ systemSettings: {
2641
+ memory: true,
2642
+ compression: 0,
2643
+ autoExec: false,
2644
+ allowExternalAccess: false,
2645
+ autoDeleteHistory: "7d",
2646
+ useExternalData: false,
2647
+ externalDataPath: ""
2648
+ },
2649
+ profileData: {
2650
+ name: null,
2651
+ nickname: null,
2652
+ instructions: null
2653
+ },
2654
+ imageSettings: {
2655
+ keyType: "Default",
2656
+ quality: "Low-High",
2657
+ apiKey: ""
2658
+ }
2659
+ };
2660
+ loadSettings = async () => {
2661
+ try {
2662
+ if (await fs14.exists(SETTINGS_FILE)) {
2663
+ const saved = await fs14.readJson(SETTINGS_FILE);
2664
+ const merged = {
2665
+ ...DEFAULT_SETTINGS,
2666
+ ...saved,
2667
+ quotas: { ...DEFAULT_SETTINGS.quotas, ...saved.quotas },
2668
+ systemSettings: { ...DEFAULT_SETTINGS.systemSettings, ...saved.systemSettings },
2669
+ profileData: { ...DEFAULT_SETTINGS.profileData, ...saved.profileData },
2670
+ imageSettings: { ...DEFAULT_SETTINGS.imageSettings, ...saved.imageSettings }
2671
+ };
2672
+ if (merged.showFullThinking === false) {
2673
+ merged.showFullThinking = true;
2674
+ await fs14.writeJson(SETTINGS_FILE, merged, { spaces: 2 });
2675
+ }
2676
+ return merged;
2677
+ }
2678
+ } catch (err) {
2679
+ console.error("Failed to load settings:", err);
2680
+ }
2681
+ return DEFAULT_SETTINGS;
2682
+ };
2683
+ migrateToExternal = async (newPath) => {
2684
+ const { FLUXFLOW_DIR: FLUXFLOW_DIR2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
2685
+ const folders = ["logs", "secret"];
2686
+ for (const folder of folders) {
2687
+ const src = path13.join(FLUXFLOW_DIR2, folder);
2688
+ const dest = path13.join(newPath, folder);
2689
+ try {
2690
+ if (await fs14.exists(src)) {
2691
+ await fs14.ensureDir(dest);
2692
+ await fs14.copy(src, dest, { overwrite: true });
2693
+ }
2694
+ } catch (err) {
2695
+ console.error(`Migration failed for ${folder}:`, err);
2696
+ }
2697
+ }
2698
+ };
2699
+ saveSettings = async (settings) => {
2700
+ try {
2701
+ const current = await loadSettings();
2702
+ if (!current.systemSettings.useExternalData && settings.systemSettings?.useExternalData && settings.systemSettings?.externalDataPath) {
2703
+ await migrateToExternal(settings.systemSettings.externalDataPath);
2704
+ }
2705
+ const updated = { ...current, ...settings };
2706
+ await fs14.ensureDir(path13.dirname(SETTINGS_FILE));
2707
+ await fs14.writeJson(SETTINGS_FILE, updated, { spaces: 2 });
2708
+ return true;
2709
+ } catch (err) {
2710
+ console.error("Failed to save settings:", err);
2711
+ return false;
2712
+ }
2713
+ };
2714
+ }
2715
+ });
2716
+
2717
+ // src/tools/generate_image.js
2718
+ import fs15 from "fs-extra";
2719
+ import path14 from "path";
2720
+ var injectPngMetadata, generate_image;
2721
+ var init_generate_image = __esm({
2722
+ "src/tools/generate_image.js"() {
2723
+ init_arg_parser();
2724
+ init_settings();
2725
+ init_usage();
2726
+ injectPngMetadata = (buffer, metadata = {}) => {
2727
+ try {
2728
+ if (buffer.length < 8 || buffer[0] !== 137 || buffer[1] !== 80 || buffer[2] !== 78 || buffer[3] !== 71) {
2729
+ return buffer;
2730
+ }
2731
+ const chunksToInject = [];
2732
+ const crcTable = [];
2733
+ for (let n = 0; n < 256; n++) {
2734
+ let c = n;
2735
+ for (let k = 0; k < 8; k++) {
2736
+ if (c & 1) {
2737
+ c = 3988292384 ^ c >>> 1;
2738
+ } else {
2739
+ c = c >>> 1;
2740
+ }
2741
+ }
2742
+ crcTable[n] = c;
2743
+ }
2744
+ const calculateCrc = (buf) => {
2745
+ let crc = 4294967295;
2746
+ for (let i = 0; i < buf.length; i++) {
2747
+ crc = crcTable[(crc ^ buf[i]) & 255] ^ crc >>> 8;
2748
+ }
2749
+ return (crc ^ 4294967295) >>> 0;
2750
+ };
2751
+ const createTextChunk = (keyword, text) => {
2752
+ const keywordBuf = Buffer.from(keyword, "ascii");
2753
+ const textBuf = Buffer.from(text, "utf-8");
2754
+ const dataLength = keywordBuf.length + 1 + textBuf.length;
2755
+ const chunkBuf = Buffer.alloc(4 + 4 + dataLength + 4);
2756
+ chunkBuf.writeUInt32BE(dataLength, 0);
2757
+ chunkBuf.write("tEXt", 4, "ascii");
2758
+ keywordBuf.copy(chunkBuf, 8);
2759
+ chunkBuf[8 + keywordBuf.length] = 0;
2760
+ textBuf.copy(chunkBuf, 8 + keywordBuf.length + 1);
2761
+ const crcValue = calculateCrc(chunkBuf.subarray(4, 8 + dataLength));
2762
+ chunkBuf.writeUInt32BE(crcValue, 8 + dataLength);
2763
+ return chunkBuf;
2764
+ };
2765
+ for (const [key, val] of Object.entries(metadata)) {
2766
+ if (val !== void 0 && val !== null) {
2767
+ chunksToInject.push(createTextChunk(key, String(val)));
2768
+ }
2769
+ }
2770
+ if (chunksToInject.length === 0) return buffer;
2771
+ if (buffer.subarray(12, 16).toString("ascii") === "IHDR") {
2772
+ const headerEnd = 33;
2773
+ const before = buffer.subarray(0, headerEnd);
2774
+ const after = buffer.subarray(headerEnd);
2775
+ return Buffer.concat([before, ...chunksToInject, after]);
2776
+ }
2777
+ return buffer;
2778
+ } catch (e) {
2779
+ return buffer;
2780
+ }
2781
+ };
2782
+ generate_image = async (args) => {
2783
+ const parsed = parseArgs(args);
2784
+ const prompt = parsed.prompt || parsed.text;
2785
+ const outputPath = parsed.path || parsed.outputPath || parsed.output || "generated_image.png";
2786
+ const ratio = parsed.ratio;
2787
+ if (!prompt) {
2788
+ return 'ERROR: Missing "prompt" argument for generate_image.';
2789
+ }
2790
+ try {
2791
+ const settings = await loadSettings();
2792
+ const hasQuota = await checkImageQuota(settings);
2793
+ if (!hasQuota) {
2794
+ return "ERROR: Insufficient Quota for selected quality. Either reduce quality for wait for next refresh cycle.";
2795
+ }
2796
+ const imageSettings = settings.imageSettings || { keyType: "Default", quality: "Low-High", apiKey: "" };
2797
+ const apiKey = imageSettings.keyType === "Custom" && imageSettings.apiKey ? imageSettings.apiKey : "pk_5i7Doib5fATyAN4i";
2798
+ const qualityMap = {
2799
+ "Low": "flux",
2800
+ "Low-High": "zimage",
2801
+ "Medium": "gptimage",
2802
+ "Medium-High": "gptimage",
2803
+ "High": "qwen-image",
2804
+ "Ultra": "gptimage-large",
2805
+ "Premium": "nanobanana-pro"
2806
+ };
2807
+ const selectedModel = qualityMap[imageSettings.quality] || "zimage";
2808
+ let width = 1024;
2809
+ let height = 1024;
2810
+ if (ratio) {
2811
+ const cleanRatio = ratio.replace(/\s+/g, "");
2812
+ if (cleanRatio === "16:9") {
2813
+ width = 1024;
2814
+ height = 576;
2815
+ } else if (cleanRatio === "9:16") {
2816
+ width = 576;
2817
+ height = 1024;
2818
+ } else if (cleanRatio === "4:3") {
2819
+ width = 1024;
2820
+ height = 768;
2821
+ } else if (cleanRatio === "3:4") {
2822
+ width = 768;
2823
+ height = 1024;
2824
+ } else if (cleanRatio === "1:1") {
2825
+ width = 1024;
2826
+ height = 1024;
2827
+ }
2828
+ }
2829
+ const seed = Math.floor(Math.random() * 1e7);
2830
+ const url = `https://gen.pollinations.ai/image/${encodeURIComponent(prompt)}?model=${selectedModel}&width=${width}&height=${height}&seed=${seed}`;
2831
+ const response = await fetch(url, {
2832
+ method: "GET",
2833
+ headers: {
2834
+ "Authorization": `Bearer ${apiKey}`
2835
+ }
2836
+ });
2837
+ if (!response.ok) {
2838
+ const status = response.status;
2839
+ let errorText = "";
2840
+ try {
2841
+ errorText = await response.text();
2842
+ } catch (e) {
2843
+ }
2844
+ if (status === 402 || errorText.includes("Insufficient balance") || errorText.includes("PAYMENT_REQUIRED")) {
2845
+ return "ERROR: Image Generation Currently unavailable. Try again later.";
2846
+ }
2847
+ return `ERROR: Image Generation failed with status [${status}]: ${errorText || "Unknown API Error"}`;
2848
+ }
2849
+ const contentType = response.headers.get("content-type") || "";
2850
+ if (contentType.includes("application/json")) {
2851
+ const json = await response.json();
2852
+ if (json.status === 402 || json.error && json.error.code === "PAYMENT_REQUIRED") {
2853
+ return "ERROR: Image Generation Currently unavailable. Try again later.";
2854
+ }
2855
+ return `ERROR: Image Generation failed: ${json.error?.message || JSON.stringify(json)}`;
2856
+ }
2857
+ const buffer = await response.arrayBuffer();
2858
+ let finalBuffer = Buffer.from(buffer);
2859
+ const metadata = {
2860
+ "Title": prompt,
2861
+ "Description": "Generated via FluxFlow CLI",
2862
+ "Software": "FluxFlow CLI",
2863
+ "Author": "FluxFlow",
2864
+ "Creation Time": (/* @__PURE__ */ new Date()).toISOString(),
2865
+ "Prompt": prompt,
2866
+ "Model": `Fluxflow:${selectedModel}`,
2867
+ "Ratio": ratio || "1:1",
2868
+ "Seed": String(seed)
2869
+ };
2870
+ finalBuffer = injectPngMetadata(finalBuffer, metadata);
2871
+ const absolutePath = path14.resolve(process.cwd(), outputPath);
2872
+ await fs15.ensureDir(path14.dirname(absolutePath));
2873
+ await fs15.writeFile(absolutePath, finalBuffer);
2874
+ await recordImageGeneration(settings);
2875
+ return `SUCCESS: Image successfully generated from prompt [${prompt}] and saved to [${outputPath}] with custom embedded metadata.`;
2876
+ } catch (err) {
2877
+ return `ERROR: Failed during image generation: ${err.message}`;
2878
+ }
2879
+ };
2880
+ }
2881
+ });
2882
+
2608
2883
  // src/utils/tools.js
2609
2884
  var TOOL_MAP, dispatchTool;
2610
2885
  var init_tools = __esm({
@@ -2623,6 +2898,7 @@ var init_tools = __esm({
2623
2898
  init_write_pdf();
2624
2899
  init_write_docx();
2625
2900
  init_search_keyword();
2901
+ init_generate_image();
2626
2902
  TOOL_MAP = {
2627
2903
  web_search,
2628
2904
  web_scrape,
@@ -2637,6 +2913,7 @@ var init_tools = __esm({
2637
2913
  write_pdf,
2638
2914
  write_docx,
2639
2915
  search_keyword,
2916
+ generate_image,
2640
2917
  ask: ask_user,
2641
2918
  // PascalCase Normalizations for Token Efficiency
2642
2919
  Ask: ask_user,
@@ -2651,7 +2928,8 @@ var init_tools = __esm({
2651
2928
  Run: exec_command,
2652
2929
  SearchKeyword: search_keyword,
2653
2930
  Memory: memory,
2654
- Chat: chat
2931
+ Chat: chat,
2932
+ GenerateImage: generate_image
2655
2933
  };
2656
2934
  dispatchTool = async (toolName, args, context = {}) => {
2657
2935
  const tool = TOOL_MAP[toolName];
@@ -2699,7 +2977,8 @@ var init_ai = __esm({
2699
2977
  "search_keyword": "Finding Files",
2700
2978
  "ask": "Asking User",
2701
2979
  "write_pdf": "Creating PDF",
2702
- "write_docx": "Creating Document"
2980
+ "write_docx": "Creating Document",
2981
+ "generate_image": "Generating Image"
2703
2982
  };
2704
2983
  getToolDetail = (toolName, argsStr) => {
2705
2984
  try {
@@ -2719,10 +2998,18 @@ var init_ai = __esm({
2719
2998
  const isMemoryEnabled = systemSettings?.memory !== false;
2720
2999
  const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
2721
3000
  const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
2722
- const janitorContents = history.slice(-12).filter((msg) => msg.text && !msg.text.includes("[TOOL RESULT]") && !msg.text.includes("OBSERVATION:")).map((msg) => ({
2723
- role: msg.role === "user" ? "user" : "model",
2724
- parts: [{ text: msg.text.replace(/<think>[\s\S]*?<\/think>/g, "").trim() }]
2725
- }));
3001
+ const janitorContents = history.slice(-12).filter((msg) => msg.text && !msg.text.includes("[TOOL RESULT]") && !msg.text.includes("OBSERVATION:")).map((msg) => {
3002
+ let processedText = msg.text.replace(/\[tool:functions\..*?\]/g, "").replace(/<think>[\s\S]*?<\/think>/g, "").replace(/\[Prompted on:.*?\]/g, "").replace(/\[turn: continue\]/g, "").replace(/\[turn: finish\]/g, "").replace(/\[TOOL RESULTS\]/g, "").replace(/\[tool results\]/g, "").replace(/\r?\n\r?\n/g, "\n").replace(/\n\n/g, "\n").replace(/\\n\\n/g, "").trim();
3003
+ const limit = msg.role === "user" ? 1500 : 24e3;
3004
+ let truncatedText = processedText.substring(0, limit);
3005
+ if (processedText.length > limit) {
3006
+ truncatedText += "\n... (truncated) ...";
3007
+ }
3008
+ return {
3009
+ role: msg.role === "user" ? "user" : "model",
3010
+ parts: [{ text: truncatedText }]
3011
+ };
3012
+ });
2726
3013
  const cleanedFullResponse = fullAgentTextRaw.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
2727
3014
  const janitorPrompt = getJanitorInstruction(
2728
3015
  agentText,
@@ -2731,14 +3018,14 @@ var init_ai = __esm({
2731
3018
  isMemoryEnabled,
2732
3019
  true
2733
3020
  );
2734
- let agentRes = `${cleanedFullResponse.replace(/\[tool:functions\..*?\]/g, "").replace(/<think>.*<\/think>/g, "").replace(/\[Prompted on:.*?\]/g, "").replace(/\[turn: continue\]/g, "").replace(/\[turn: finish\]/g, "").replace(/\[TOOL RESULTS\]/g, "").replace(/\[tool results\]/g, "").substring(0, 3500)}`;
2735
- if (agentRes.length > 3500) {
3021
+ let agentRes = `${cleanedFullResponse.replace(/\[tool:functions\..*?\]/g, "").replace(/<think>.*<\/think>/g, "").replace(/\[Prompted on:.*?\]/g, "").replace(/\[turn: continue\]/g, "").replace(/\[turn: finish\]/g, "").replace(/\[TOOL RESULTS\]/g, "").replace(/\[tool results\]/g, "").substring(0, 24e3)}`;
3022
+ if (agentRes.length > 24e3) {
2736
3023
  agentRes += "\n... (truncated) ...";
2737
3024
  }
2738
3025
  let originalTextProcessed = agentText.replace(/\[Prompted on:.*?\]/g, "").trim();
2739
3026
  agentRes = agentRes.replace(/\r?\n\r?\n/g, "\n").replace(/\n\n/g, "\n").replace(/\\n\\n/g, "").trim();
2740
- let userPrompt = `[USER]: ${originalTextProcessed.substring(0, 600)}
2741
- ${originalTextProcessed.length > 600 ? "... (truncated) ...\n\n" : ""}
3027
+ let userPrompt = `[USER]: ${originalTextProcessed.substring(0, 1500)}
3028
+ ${originalTextProcessed.length > 1500 ? "... (truncated) ...\n\n" : ""}
2742
3029
  [AGENT (current turn)]: ${agentRes}`;
2743
3030
  janitorContents.push({ role: "user", parts: [{ text: userPrompt }] });
2744
3031
  let finalSynthesis = "";
@@ -2836,6 +3123,10 @@ DEBUG [${date}]: ${finalSynthesis}
2836
3123
  } catch (janitorErr) {
2837
3124
  attempts++;
2838
3125
  const date = (/* @__PURE__ */ new Date()).toLocaleString();
3126
+ if (process.stdout.isTTY) {
3127
+ process.stdout.write(`\x1B]0;Memory Error\x07`);
3128
+ }
3129
+ await new Promise((resolve) => setTimeout(resolve, 3e3));
2839
3130
  const janitorErrDir = path15.join(LOGS_DIR, "janitor");
2840
3131
  if (!fs16.existsSync(janitorErrDir)) fs16.mkdirSync(janitorErrDir, { recursive: true });
2841
3132
  fs16.appendFileSync(path15.join(janitorErrDir, "error.log"), `ERROR [Attempt ${attempts}/${MAX_JANITOR_RETRIES + 1}] [${date}]: ${String(janitorErr)}
@@ -3269,7 +3560,9 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3269
3560
  "SearchKeyword": "search_keyword",
3270
3561
  "Memory": "memory",
3271
3562
  "Chat": "chat",
3272
- "chat": "chat"
3563
+ "chat": "chat",
3564
+ "GenerateImage": "generate_image",
3565
+ "generate_image": "generate_image"
3273
3566
  };
3274
3567
  const potentialTool = NORMALIZE_MAP[rawToolName] || rawToolName;
3275
3568
  const partialArgs = toolContext.args || "";
@@ -3309,7 +3602,8 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3309
3602
  "search_keyword": "Searching Keywords",
3310
3603
  "exec_command": "Running Command",
3311
3604
  "ask": "Asking User",
3312
- "memory": "Updating Memory"
3605
+ "memory": "Updating Memory",
3606
+ "generate_image": "Generating Image"
3313
3607
  };
3314
3608
  const toolTitle = TOOL_TITLES[potentialTool] || "Working";
3315
3609
  process.stdout.write(`\x1B]0;${toolTitle}...\x07`);
@@ -3329,7 +3623,10 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3329
3623
  "low": 200,
3330
3624
  "medium": 500,
3331
3625
  "high": 2e3,
3332
- "max": 3500
3626
+ "max": 3500,
3627
+ "xhigh": 3500,
3628
+ "off": 50,
3629
+ "fast": 50
3333
3630
  };
3334
3631
  const cap = thinkingCaps[thinkingLevel?.toLowerCase()] || 2500;
3335
3632
  let isOverVerboseThinking = wordCount > cap;
@@ -3387,7 +3684,9 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3387
3684
  "SearchKeyword": "search_keyword",
3388
3685
  "Memory": "memory",
3389
3686
  "Chat": "chat",
3390
- "chat": "chat"
3687
+ "chat": "chat",
3688
+ "GenerateImage": "generate_image",
3689
+ "generate_image": "generate_image"
3391
3690
  };
3392
3691
  const normToolName = NORMALIZE_MAP[toolCall.toolName] || toolCall.toolName;
3393
3692
  const displayLabel = TOOL_LABELS2[normToolName] || toolCall.toolName;
@@ -3441,6 +3740,9 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3441
3740
  } else if (normToolName === "search_keyword") {
3442
3741
  const { keyword } = parseArgs(toolCall.args);
3443
3742
  label = `\u{1F50E} KEYWORD SEARCHED: "${keyword}"`.toUpperCase();
3743
+ } else if (normToolName === "generate_image") {
3744
+ const { title, prompt, outputPath, output } = parseArgs(toolCall.args);
3745
+ label = `\u{1F3A8} IMAGE GENERATED: "${title || prompt}" -> ${outputPath || output || "generated_image.png"}`.toUpperCase();
3444
3746
  } else if (normToolName === "exec_command" || normToolName === "ask") {
3445
3747
  label = "";
3446
3748
  } else {
@@ -3502,7 +3804,7 @@ ${boxBottom}` };
3502
3804
  toolResults.push({ role: "user", text: `[TOOL RESULT]: DENIED: ${denyMsg}${thinkingLevel != "Fast" ? "\n\n[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRICT PRIORITY. DO NOT START A RESPONSE WITHOUT THINKING**" : ""}` });
3503
3805
  yield { type: "tool_result", content: `[TOOL RESULT]: DENIED: ${denyMsg}` };
3504
3806
  await incrementUsage("toolDenied");
3505
- if (settings.onToolResult) settings.onToolResult("denied");
3807
+ if (settings.onToolResult) settings.onToolResult("denied", normToolName);
3506
3808
  toolCallPointer++;
3507
3809
  continue;
3508
3810
  }
@@ -3536,11 +3838,11 @@ ${boxBottom}` };
3536
3838
  const isSuccess = result && !result.startsWith("ERROR:") && !isDenied;
3537
3839
  if (isSuccess) {
3538
3840
  await incrementUsage("toolSuccess");
3539
- if (settings.onToolResult) settings.onToolResult("success");
3841
+ if (settings.onToolResult) settings.onToolResult("success", normToolName);
3540
3842
  } else if (isDenied) {
3541
3843
  } else {
3542
3844
  await incrementUsage("toolFailure");
3543
- if (settings.onToolResult) settings.onToolResult("failure");
3845
+ if (settings.onToolResult) settings.onToolResult("failure", normToolName);
3544
3846
  }
3545
3847
  const aiContent = `[TOOL RESULT]: ${(result || "").toString().split(/\r?\n/).filter((line) => !line.includes("[UI_CONTEXT]")).join("\n")}`;
3546
3848
  toolResults.push({ role: "user", text: aiContent, binaryPart });
@@ -3685,97 +3987,6 @@ ${timestamp}`;
3685
3987
  }
3686
3988
  });
3687
3989
 
3688
- // src/utils/settings.js
3689
- import fs17 from "fs-extra";
3690
- import path16 from "path";
3691
- var DEFAULT_SETTINGS, loadSettings, migrateToExternal, saveSettings;
3692
- var init_settings = __esm({
3693
- "src/utils/settings.js"() {
3694
- init_paths();
3695
- DEFAULT_SETTINGS = {
3696
- mode: "Flux",
3697
- thinkingLevel: "Medium",
3698
- activeModel: "gemma-4-31b-it",
3699
- showFullThinking: true,
3700
- apiTier: "Free",
3701
- quotas: {
3702
- agentLimit: 1500,
3703
- backgroundLimit: 1500,
3704
- searchLimit: 100,
3705
- customModelId: "",
3706
- customLimit: 0
3707
- },
3708
- systemSettings: {
3709
- memory: true,
3710
- compression: 0,
3711
- autoExec: false,
3712
- allowExternalAccess: false,
3713
- autoDeleteHistory: "7d",
3714
- useExternalData: false,
3715
- externalDataPath: ""
3716
- },
3717
- profileData: {
3718
- name: null,
3719
- nickname: null,
3720
- instructions: null
3721
- }
3722
- };
3723
- loadSettings = async () => {
3724
- try {
3725
- if (await fs17.exists(SETTINGS_FILE)) {
3726
- const saved = await fs17.readJson(SETTINGS_FILE);
3727
- const merged = {
3728
- ...DEFAULT_SETTINGS,
3729
- ...saved,
3730
- quotas: { ...DEFAULT_SETTINGS.quotas, ...saved.quotas },
3731
- systemSettings: { ...DEFAULT_SETTINGS.systemSettings, ...saved.systemSettings },
3732
- profileData: { ...DEFAULT_SETTINGS.profileData, ...saved.profileData }
3733
- };
3734
- if (merged.showFullThinking === false) {
3735
- merged.showFullThinking = true;
3736
- await fs17.writeJson(SETTINGS_FILE, merged, { spaces: 2 });
3737
- }
3738
- return merged;
3739
- }
3740
- } catch (err) {
3741
- console.error("Failed to load settings:", err);
3742
- }
3743
- return DEFAULT_SETTINGS;
3744
- };
3745
- migrateToExternal = async (newPath) => {
3746
- const { FLUXFLOW_DIR: FLUXFLOW_DIR2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
3747
- const folders = ["logs", "secret"];
3748
- for (const folder of folders) {
3749
- const src = path16.join(FLUXFLOW_DIR2, folder);
3750
- const dest = path16.join(newPath, folder);
3751
- try {
3752
- if (await fs17.exists(src)) {
3753
- await fs17.ensureDir(dest);
3754
- await fs17.copy(src, dest, { overwrite: true });
3755
- }
3756
- } catch (err) {
3757
- console.error(`Migration failed for ${folder}:`, err);
3758
- }
3759
- }
3760
- };
3761
- saveSettings = async (settings) => {
3762
- try {
3763
- const current = await loadSettings();
3764
- if (!current.systemSettings.useExternalData && settings.systemSettings?.useExternalData && settings.systemSettings?.externalDataPath) {
3765
- await migrateToExternal(settings.systemSettings.externalDataPath);
3766
- }
3767
- const updated = { ...current, ...settings };
3768
- await fs17.ensureDir(path16.dirname(SETTINGS_FILE));
3769
- await fs17.writeJson(SETTINGS_FILE, updated, { spaces: 2 });
3770
- return true;
3771
- } catch (err) {
3772
- console.error("Failed to save settings:", err);
3773
- return false;
3774
- }
3775
- };
3776
- }
3777
- });
3778
-
3779
3990
  // src/components/ResumeModal.jsx
3780
3991
  import React7, { useState as useState4, useEffect as useEffect2 } from "react";
3781
3992
  import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
@@ -3979,7 +4190,7 @@ var init_UpdateProcessor = __esm({
3979
4190
  import puppeteer4 from "puppeteer";
3980
4191
  import { exec as exec3 } from "child_process";
3981
4192
  import { promisify } from "util";
3982
- import fs18 from "fs";
4193
+ import fs17 from "fs";
3983
4194
  var execAsync, checkPuppeteerReady, installPuppeteerBrowser;
3984
4195
  var init_setup = __esm({
3985
4196
  "src/utils/setup.js"() {
@@ -3987,7 +4198,7 @@ var init_setup = __esm({
3987
4198
  checkPuppeteerReady = () => {
3988
4199
  try {
3989
4200
  const exePath = puppeteer4.executablePath();
3990
- const exists = exePath && fs18.existsSync(exePath);
4201
+ const exists = exePath && fs17.existsSync(exePath);
3991
4202
  if (exists) return true;
3992
4203
  } catch (e) {
3993
4204
  return false;
@@ -4021,8 +4232,8 @@ import os3 from "os";
4021
4232
  import React10, { useState as useState7, useEffect as useEffect5, useRef as useRef2, useMemo } from "react";
4022
4233
  import { Box as Box10, Text as Text10, useInput as useInput4, useStdout } from "ink";
4023
4234
  import Spinner2 from "ink-spinner";
4024
- import fs19 from "fs-extra";
4025
- import path17 from "path";
4235
+ import fs18 from "fs-extra";
4236
+ import path16 from "path";
4026
4237
  import { exec as exec4 } from "child_process";
4027
4238
  import { fileURLToPath } from "url";
4028
4239
  import { MultilineInput } from "ink-multiline-input";
@@ -4118,6 +4329,7 @@ function App() {
4118
4329
  const [inputConfig, setInputConfig] = useState7(null);
4119
4330
  const [systemSettings, setSystemSettings] = useState7({ memory: true, compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
4120
4331
  const [profileData, setProfileData] = useState7({ name: null, nickname: null, instructions: null });
4332
+ const [imageSettings, setImageSettings] = useState7({ keyType: "Default", quality: "Low-High", apiKey: "" });
4121
4333
  const [sessionStats, setSessionStats] = useState7({ tokens: 0 });
4122
4334
  const [sessionAgentCalls, setSessionAgentCalls] = useState7(0);
4123
4335
  const [sessionBackgroundCalls, setSessionBackgroundCalls] = useState7(0);
@@ -4127,6 +4339,8 @@ function App() {
4127
4339
  const [sessionToolDenied, setSessionToolDenied] = useState7(0);
4128
4340
  const [sessionApiTime, setSessionApiTime] = useState7(0);
4129
4341
  const [sessionToolTime, setSessionToolTime] = useState7(0);
4342
+ const [sessionImageCount, setSessionImageCount] = useState7(0);
4343
+ const [sessionImageCredits, setSessionImageCredits] = useState7(0);
4130
4344
  const [dailyUsage, setDailyUsage] = useState7(null);
4131
4345
  const [chatId, setChatId] = useState7(generateChatId());
4132
4346
  const [activeCommand, setActiveCommand] = useState7(null);
@@ -4376,6 +4590,7 @@ function App() {
4376
4590
  };
4377
4591
  setSystemSettings(freshSettings);
4378
4592
  setProfileData(saved.profileData);
4593
+ setImageSettings(saved.imageSettings || { keyType: "Default", quality: "Low-High", apiKey: "" });
4379
4594
  const key = await getAPIKey();
4380
4595
  if (key) {
4381
4596
  setApiKey(key);
@@ -4416,10 +4631,11 @@ function App() {
4416
4631
  activeModel,
4417
4632
  showFullThinking,
4418
4633
  systemSettings,
4419
- profileData
4634
+ profileData,
4635
+ imageSettings
4420
4636
  });
4421
4637
  }
4422
- }, [mode, thinkingLevel, activeModel, showFullThinking, systemSettings, profileData, isInitializing]);
4638
+ }, [mode, thinkingLevel, activeModel, showFullThinking, systemSettings, profileData, imageSettings, isInitializing]);
4423
4639
  const handleSetup = async (val) => {
4424
4640
  const key = val.trim();
4425
4641
  if (key.length >= 30) {
@@ -4471,6 +4687,40 @@ function App() {
4471
4687
  { cmd: "/resume", desc: "Load previous session" },
4472
4688
  { cmd: "/save", desc: "Force save current chat" },
4473
4689
  { cmd: "/chats", desc: "List all chat sessions" },
4690
+ {
4691
+ cmd: "/image",
4692
+ desc: "Generate images using Pollinations",
4693
+ subs: [
4694
+ {
4695
+ cmd: "setup",
4696
+ desc: "Configure defaults",
4697
+ subs: [
4698
+ {
4699
+ cmd: "key",
4700
+ desc: "Set API key strategy",
4701
+ subs: [
4702
+ { cmd: "default", desc: "Default (Quota: 0.020 credits/hr)" },
4703
+ { cmd: "custom", desc: "Custom Key" }
4704
+ ]
4705
+ },
4706
+ {
4707
+ cmd: "quality",
4708
+ desc: "Set default quality",
4709
+ subs: [
4710
+ { cmd: "low", desc: "(0.001/img)" },
4711
+ { cmd: "low-high", desc: "(0.002/img)" },
4712
+ { cmd: "medium", desc: "(0.008/img)" },
4713
+ { cmd: "medium-high", desc: "(0.01/img)" },
4714
+ { cmd: "high", desc: "(0.045/img)" },
4715
+ { cmd: "ultra", desc: "(0.0488/img)" },
4716
+ { cmd: "premium", desc: "(0.1/img)" }
4717
+ ]
4718
+ }
4719
+ ]
4720
+ },
4721
+ { cmd: "stats", desc: "Show remaining credits or Pollinations balance status" }
4722
+ ]
4723
+ },
4474
4724
  {
4475
4725
  cmd: "/mode",
4476
4726
  desc: "Toggle Flux/Flow modes",
@@ -4524,14 +4774,15 @@ function App() {
4524
4774
  ]
4525
4775
  }
4526
4776
  ];
4527
- const handleSubmit = (value) => {
4777
+ const handleSubmit = async (value) => {
4528
4778
  if (suggestions.length > 0) {
4529
4779
  const nextMatch = suggestions[selectedIndex] || suggestions[0];
4530
4780
  const parts = value.split(" ");
4531
4781
  if (parts.length === 1) {
4532
4782
  setInput(nextMatch.cmd + " ");
4533
4783
  } else {
4534
- setInput(parts[0] + " " + nextMatch.cmd + " ");
4784
+ const parentParts = parts.slice(0, -1);
4785
+ setInput(parentParts.join(" ") + " " + nextMatch.cmd + " ");
4535
4786
  }
4536
4787
  setSelectedIndex(0);
4537
4788
  return;
@@ -4630,6 +4881,135 @@ ${hintText}`, color: "magenta" }];
4630
4881
  }
4631
4882
  break;
4632
4883
  }
4884
+ case "/image": {
4885
+ if (parts[1]?.toLowerCase() === "stats") {
4886
+ const s2 = emojiSpace(2);
4887
+ if (imageSettings.keyType === "Custom") {
4888
+ setMessages((prev) => {
4889
+ setCompletedIndex(prev.length + 1);
4890
+ return [...prev, {
4891
+ id: Date.now(),
4892
+ role: "system",
4893
+ text: `\u{1F517}${s2}[SYSTEM] Key strategy is Custom. Redirecting to Pollinations dashboard (https://enter.pollinations.ai/#pollen)...`,
4894
+ isMeta: true
4895
+ }];
4896
+ });
4897
+ exec4("start https://enter.pollinations.ai/#pollen");
4898
+ } else {
4899
+ try {
4900
+ const stats = await getImageQuotaStats();
4901
+ setMessages((prev) => {
4902
+ setCompletedIndex(prev.length + 1);
4903
+ return [...prev, {
4904
+ id: Date.now(),
4905
+ role: "system",
4906
+ isImageStats: true,
4907
+ text: `\u2022 Hourly Limit: 0.0200 credits
4908
+ \u2022 Spent (Last 1hr): ${stats.totalSpent.toFixed(4)} credits
4909
+ \u2022 Remaining: ${stats.remaining.toFixed(4)} credits
4910
+ \u2022 Requests (Last 1hr): ${stats.activeCallsCount} requests`,
4911
+ isMeta: true
4912
+ }];
4913
+ });
4914
+ } catch (e) {
4915
+ setMessages((prev) => {
4916
+ setCompletedIndex(prev.length + 1);
4917
+ return [...prev, {
4918
+ id: Date.now(),
4919
+ role: "system",
4920
+ text: `\u274C [SYSTEM] Failed to load image quota stats.`,
4921
+ isMeta: true
4922
+ }];
4923
+ });
4924
+ }
4925
+ }
4926
+ } else if (parts[1]?.toLowerCase() === "setup") {
4927
+ if (parts[2]?.toLowerCase() === "key") {
4928
+ if (parts[3]) {
4929
+ const matchedKey = ["default", "custom"].find((k) => k === parts[3].toLowerCase());
4930
+ if (matchedKey) {
4931
+ const strategy = matchedKey === "default" ? "Default" : "Custom";
4932
+ setImageSettings((prev) => ({ ...prev, keyType: strategy }));
4933
+ const s2 = emojiSpace(2);
4934
+ setMessages((prev) => {
4935
+ setCompletedIndex(prev.length + 1);
4936
+ return [...prev, { id: Date.now(), role: "system", text: `\u{1F527}${s2}[SYSTEM] Image key strategy set to ${strategy}`, isMeta: true }];
4937
+ });
4938
+ if (strategy === "Custom") {
4939
+ setInputConfig({
4940
+ label: "Enter Pollinations API key (starting with sk_):",
4941
+ note: "Get a key from https://enter.pollinations.ai",
4942
+ key: "imageSettings",
4943
+ subKey: "apiKey",
4944
+ value: imageSettings.apiKey || "",
4945
+ returnView: "chat"
4946
+ });
4947
+ setActiveView("input");
4948
+ }
4949
+ } else {
4950
+ const s2 = emojiSpace(2);
4951
+ setMessages((prev) => {
4952
+ setCompletedIndex(prev.length + 1);
4953
+ return [...prev, { id: Date.now(), role: "system", text: `\u274C [SYSTEM] Invalid key option. Choose: Default or Custom.`, isMeta: true }];
4954
+ });
4955
+ }
4956
+ } else {
4957
+ const s2 = emojiSpace(2);
4958
+ setMessages((prev) => {
4959
+ setCompletedIndex(prev.length + 1);
4960
+ return [...prev, { id: Date.now(), role: "system", text: `\u274C [SYSTEM] Usage: /image setup Key <Default|Custom>`, isMeta: true }];
4961
+ });
4962
+ }
4963
+ } else if (parts[2]?.toLowerCase() === "quality") {
4964
+ if (parts[3]) {
4965
+ const matched = ["low", "low-high", "medium", "medium-high", "high", "ultra", "premium"].find((q) => q === parts[3].toLowerCase());
4966
+ if (matched) {
4967
+ const qualityMap = {
4968
+ "low": "Low",
4969
+ "low-high": "Low-High",
4970
+ "medium": "Medium",
4971
+ "medium-high": "Medium-High",
4972
+ "high": "High",
4973
+ "ultra": "Ultra",
4974
+ "premium": "Premium"
4975
+ };
4976
+ const chosenQuality = qualityMap[matched];
4977
+ setImageSettings((prev) => ({ ...prev, quality: chosenQuality }));
4978
+ const s2 = emojiSpace(2);
4979
+ setMessages((prev) => {
4980
+ setCompletedIndex(prev.length + 1);
4981
+ return [...prev, { id: Date.now(), role: "system", text: `\u{1F527}${s2}[SYSTEM] Image quality set to ${chosenQuality}`, isMeta: true }];
4982
+ });
4983
+ } else {
4984
+ const s2 = emojiSpace(2);
4985
+ setMessages((prev) => {
4986
+ setCompletedIndex(prev.length + 1);
4987
+ return [...prev, { id: Date.now(), role: "system", text: `\u274C [SYSTEM] Invalid quality level. Choose from: Low, Low-High, Medium, Medium-High, High, Ultra, Premium.`, isMeta: true }];
4988
+ });
4989
+ }
4990
+ } else {
4991
+ const s2 = emojiSpace(2);
4992
+ setMessages((prev) => {
4993
+ setCompletedIndex(prev.length + 1);
4994
+ return [...prev, { id: Date.now(), role: "system", text: `\u274C [SYSTEM] Usage: /image setup Quality <Low|Low-High|Medium|Medium-High|High|Ultra>`, isMeta: true }];
4995
+ });
4996
+ }
4997
+ } else {
4998
+ const s2 = emojiSpace(2);
4999
+ setMessages((prev) => {
5000
+ setCompletedIndex(prev.length + 1);
5001
+ return [...prev, { id: Date.now(), role: "system", text: `\u274C [SYSTEM] Usage: /image setup <Key|Quality> ...`, isMeta: true }];
5002
+ });
5003
+ }
5004
+ } else {
5005
+ const s2 = emojiSpace(2);
5006
+ setMessages((prev) => {
5007
+ setCompletedIndex(prev.length + 1);
5008
+ return [...prev, { id: Date.now(), role: "system", text: `\u274C [SYSTEM] Usage: /image setup <Key|Quality> ...`, isMeta: true }];
5009
+ });
5010
+ }
5011
+ break;
5012
+ }
4633
5013
  case "/thinking": {
4634
5014
  let formattedLevel;
4635
5015
  if (parts[1]) {
@@ -4737,12 +5117,12 @@ ${list || "No saved chats found."}`, isMeta: true }];
4737
5117
  setCompletedIndex(prev.length + 1);
4738
5118
  return [...prev, { id: Date.now(), role: "system", text: "\u2622\uFE0F [NUCLEAR] Initiating reset...", isMeta: true }];
4739
5119
  });
4740
- if (fs19.existsSync(LOGS_DIR)) fs19.removeSync(LOGS_DIR);
4741
- if (fs19.existsSync(SECRET_DIR)) fs19.removeSync(SECRET_DIR);
4742
- if (fs19.existsSync(SETTINGS_FILE)) fs19.removeSync(SETTINGS_FILE);
5120
+ if (fs18.existsSync(LOGS_DIR)) fs18.removeSync(LOGS_DIR);
5121
+ if (fs18.existsSync(SECRET_DIR)) fs18.removeSync(SECRET_DIR);
5122
+ if (fs18.existsSync(SETTINGS_FILE)) fs18.removeSync(SETTINGS_FILE);
4743
5123
  try {
4744
- const items = fs19.readdirSync(FLUXFLOW_DIR);
4745
- if (items.length === 0) fs19.removeSync(FLUXFLOW_DIR);
5124
+ const items = fs18.readdirSync(FLUXFLOW_DIR);
5125
+ if (items.length === 0) fs18.removeSync(FLUXFLOW_DIR);
4746
5126
  } catch (e) {
4747
5127
  }
4748
5128
  setTimeout(() => {
@@ -4799,14 +5179,14 @@ ${list || "No saved chats found."}`, isMeta: true }];
4799
5179
  # SKILLS & WORKFLOWS
4800
5180
  - [Define custom step-by-step recipes for this project here]
4801
5181
  `;
4802
- const filePath = path17.join(process.cwd(), "FluxFlow.md");
4803
- if (fs19.pathExistsSync(filePath)) {
5182
+ const filePath = path16.join(process.cwd(), "FluxFlow.md");
5183
+ if (fs18.pathExistsSync(filePath)) {
4804
5184
  setMessages((prev) => {
4805
5185
  setCompletedIndex(prev.length + 1);
4806
5186
  return [...prev, { id: "init-err-" + Date.now(), role: "system", text: "\u274C ERROR: FluxFlow.md already exists in this directory.", isMeta: true }];
4807
5187
  });
4808
5188
  } else {
4809
- fs19.writeFileSync(filePath, template);
5189
+ fs18.writeFileSync(filePath, template);
4810
5190
  setMessages((prev) => {
4811
5191
  setCompletedIndex(prev.length + 1);
4812
5192
  return [...prev, { id: "init-ok-" + Date.now(), role: "system", text: "\u2705 [SUCCESS] FluxFlow.md has been initialized. You can now customize it for this project.", isMeta: true }];
@@ -4894,10 +5274,28 @@ OUTPUT: ${execOutputRef.current}`;
4894
5274
  setIsTerminalFocused(false);
4895
5275
  setExecOutput("");
4896
5276
  },
4897
- onToolResult: (status) => {
4898
- if (status === "success") setSessionToolSuccess((prev) => prev + 1);
4899
- else if (status === "denied") setSessionToolDenied((prev) => prev + 1);
4900
- else setSessionToolFailure((prev) => prev + 1);
5277
+ onToolResult: (status, toolName) => {
5278
+ if (status === "success") {
5279
+ setSessionToolSuccess((prev) => prev + 1);
5280
+ if (toolName === "generate_image") {
5281
+ setSessionImageCount((prev) => prev + 1);
5282
+ const costs = {
5283
+ "Low": 1e-3,
5284
+ "Low-High": 2e-3,
5285
+ "Medium": 8e-3,
5286
+ "Medium-High": 0.01,
5287
+ "High": 0.045,
5288
+ "Ultra": 0.0488,
5289
+ "Premium": 0.1
5290
+ };
5291
+ const cost = costs[imageSettings.quality] || 2e-3;
5292
+ setSessionImageCredits((prev) => prev + cost);
5293
+ }
5294
+ } else if (status === "denied") {
5295
+ setSessionToolDenied((prev) => prev + 1);
5296
+ } else {
5297
+ setSessionToolFailure((prev) => prev + 1);
5298
+ }
4901
5299
  },
4902
5300
  onToolApproval: async (tool, args) => {
4903
5301
  const isAuto = autoAcceptWrites || systemSettings.autoExec;
@@ -5196,16 +5594,20 @@ Selection: ${val}`,
5196
5594
  const cleanQuery = query.startsWith("/") ? query.slice(1) : query;
5197
5595
  return COMMANDS.filter((c) => {
5198
5596
  const cleanCmd = c.cmd.startsWith("/") ? c.cmd.slice(1) : c.cmd;
5199
- return cleanCmd.includes(cleanQuery);
5597
+ return cleanCmd.toLowerCase().includes(cleanQuery);
5200
5598
  });
5201
5599
  }
5202
- if (parts.length === 2) {
5203
- const parent = COMMANDS.find((c) => c.cmd === parts[0].toLowerCase());
5204
- if (parent && parent.subs) {
5205
- return parent.subs.filter((s) => s.cmd.includes(query));
5600
+ let currentList = COMMANDS;
5601
+ for (let i = 0; i < parts.length - 1; i++) {
5602
+ const part = parts[i].toLowerCase();
5603
+ const found = currentList.find((c) => c.cmd.toLowerCase() === part);
5604
+ if (found && found.subs) {
5605
+ currentList = found.subs;
5606
+ } else {
5607
+ return [];
5206
5608
  }
5207
5609
  }
5208
- return [];
5610
+ return currentList.filter((s) => s.cmd.toLowerCase().includes(query));
5209
5611
  }, [input]);
5210
5612
  useEffect5(() => {
5211
5613
  setSelectedIndex(0);
@@ -5339,19 +5741,37 @@ Selection: ${val}`,
5339
5741
  setSystemSettings(newSysSettings);
5340
5742
  newSettings.systemSettings = newSysSettings;
5341
5743
  setMessages((prev) => [...prev, { id: Date.now(), role: "system", text: "\u{1F4C1} [EXTERNAL STORAGE] Flux Flow will use " + val.trim() + " for data after restart." }]);
5744
+ } else if (key === "imageSettings") {
5745
+ const apiKeyInput = val.trim();
5746
+ if (apiKeyInput.startsWith("sk_")) {
5747
+ const updatedSettings = { ...imageSettings, apiKey: apiKeyInput };
5748
+ setImageSettings(updatedSettings);
5749
+ newSettings.imageSettings = updatedSettings;
5750
+ setMessages((prev) => {
5751
+ setCompletedIndex(prev.length + 1);
5752
+ return [...prev, { id: Date.now(), role: "system", text: `\u{1F511} [IMAGE KEY] Custom API key saved successfully.`, isMeta: true }];
5753
+ });
5754
+ } else {
5755
+ setImageSettings((prev) => ({ ...prev, keyType: "Default" }));
5756
+ newSettings.imageSettings = { ...imageSettings, keyType: "Default" };
5757
+ setMessages((prev) => {
5758
+ setCompletedIndex(prev.length + 1);
5759
+ return [...prev, { id: Date.now(), role: "system", text: `\u274C [IMAGE KEY ERROR] API key must start with sk_. Key strategy reset to Default.`, isMeta: true }];
5760
+ });
5761
+ }
5342
5762
  }
5343
5763
  if (next) {
5344
5764
  setInputConfig(next(key === "quotas" ? newQuotas : val));
5345
5765
  } else {
5346
- saveSettings({ ...newSettings, apiTier, quotas: newQuotas });
5766
+ saveSettings({ ...newSettings, apiTier, quotas: newQuotas, imageSettings: newSettings.imageSettings || imageSettings });
5347
5767
  setInputConfig(null);
5348
- setActiveView("settings");
5768
+ setActiveView(inputConfig?.returnView || "settings");
5349
5769
  }
5350
5770
  }
5351
5771
  }
5352
5772
  )), /* @__PURE__ */ React10.createElement(Box10, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "gray", dimColor: true, italic: true }, "(Press Enter to confirm selection)")));
5353
5773
  case "stats":
5354
- return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", paddingX: 3, paddingY: 1, width: Math.min(100, (stdout?.columns || 100) - 2) }, /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "white", bold: true, underline: true }, "SESSION TELEMETRY")), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Session Duration:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(Date.now() - SESSION_START_TIME))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Agent Interactions:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionAgentCalls)), /* @__PURE__ */ React10.createElement(Box10, { marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Box10, { width: 23 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue", dimColor: true }, "\xBB API Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(sessionApiTime))), /* @__PURE__ */ React10.createElement(Box10, { marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Box10, { width: 23 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue", dimColor: true }, "\xBB Tool Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(sessionToolTime))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Background Tasks:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionBackgroundCalls)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tokens Consumed:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatTokens(sessionTotalTokens))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tool Calls (Sess):")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionToolSuccess + sessionToolFailure + sessionToolDenied, " ( "), /* @__PURE__ */ React10.createElement(Text10, { color: "green" }, "\u2713 ", sessionToolSuccess), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " "), /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, "\u2298 ", sessionToolDenied), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " "), /* @__PURE__ */ React10.createElement(Text10, { color: "red" }, "\u2715 ", sessionToolFailure), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " )"))), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "white", bold: true, underline: true }, "DAILY USAGE TRACKER"), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Wall Time Today:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatDuration(dailyUsage?.duration || 0))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Agent Interactions:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, dailyUsage?.agent || 0)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Background Tasks:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, dailyUsage?.background || 0)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tokens Used Today:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatTokens(dailyUsage?.tokens || 0))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tool Calls Today:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, (dailyUsage?.toolSuccess || 0) + (dailyUsage?.toolFailure || 0) + (dailyUsage?.toolDenied || 0), " ( "), /* @__PURE__ */ React10.createElement(Text10, { color: "green" }, "\u2713 ", dailyUsage?.toolSuccess || 0), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " "), /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, "\u2298 ", dailyUsage?.toolDenied || 0), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " "), /* @__PURE__ */ React10.createElement(Text10, { color: "red" }, "\u2715 ", dailyUsage?.toolFailure || 0), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " )"))), /* @__PURE__ */ React10.createElement(Text10, { dimColor: true, marginTop: 1, italic: true }, "(Press ESC to return to chat)"));
5774
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", paddingX: 3, paddingY: 1, width: Math.min(100, (stdout?.columns || 100) - 2) }, /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "white", bold: true, underline: true }, "SESSION TELEMETRY")), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Session Duration:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(Date.now() - SESSION_START_TIME))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Agent Interactions:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionAgentCalls)), /* @__PURE__ */ React10.createElement(Box10, { marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Box10, { width: 23 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue", dimColor: true }, "\xBB API Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(sessionApiTime))), /* @__PURE__ */ React10.createElement(Box10, { marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Box10, { width: 23 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue", dimColor: true }, "\xBB Tool Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(sessionToolTime))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Background Tasks:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionBackgroundCalls)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tokens Consumed:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatTokens(sessionTotalTokens))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Images Made:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionImageCount || 0)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Image Credits:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, (sessionImageCredits || 0).toFixed(4), " credits")), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tool Calls (Sess):")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionToolSuccess + sessionToolFailure + sessionToolDenied, " ( "), /* @__PURE__ */ React10.createElement(Text10, { color: "green" }, "\u2713 ", sessionToolSuccess), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " "), /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, "\u2298 ", sessionToolDenied), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " "), /* @__PURE__ */ React10.createElement(Text10, { color: "red" }, "\u2715 ", sessionToolFailure), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " )"))), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "white", bold: true, underline: true }, "DAILY USAGE TRACKER"), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Wall Time Today:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatDuration(dailyUsage?.duration || 0))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Agent Interactions:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, dailyUsage?.agent || 0)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Background Tasks:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, dailyUsage?.background || 0)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tokens Used Today:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatTokens(dailyUsage?.tokens || 0))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Images Made Today:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, dailyUsage?.imageCalls?.length || 0)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Image Credits Today:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, (dailyUsage?.imageCalls?.reduce((sum, c) => sum + c.cost, 0) || 0).toFixed(4), " credits")), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 25 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tool Calls Today:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, (dailyUsage?.toolSuccess || 0) + (dailyUsage?.toolFailure || 0) + (dailyUsage?.toolDenied || 0), " ( "), /* @__PURE__ */ React10.createElement(Text10, { color: "green" }, "\u2713 ", dailyUsage?.toolSuccess || 0), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " "), /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, "\u2298 ", dailyUsage?.toolDenied || 0), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " "), /* @__PURE__ */ React10.createElement(Text10, { color: "red" }, "\u2715 ", dailyUsage?.toolFailure || 0), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, " )"))), /* @__PURE__ */ React10.createElement(Text10, { dimColor: true, marginTop: 1, italic: true }, "(Press ESC to return to chat)"));
5355
5775
  case "autoExecDanger":
5356
5776
  return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { color: "yellow", bold: true, underline: true }, "\u26A0\uFE0F SECURITY WARNING: AUTO-EXEC MODE"), /* @__PURE__ */ React10.createElement(Text10, { marginTop: 1 }, "Turning this ON allows the agent to execute terminal commands automatically without requiring your approval for each step."), /* @__PURE__ */ React10.createElement(Text10, { marginTop: 1, color: "yellow" }, "RISKS INVOLVED:"), /* @__PURE__ */ React10.createElement(Text10, null, "\u2022 The agent may execute destructive commands (rm -rf, etc.) by mistake."), /* @__PURE__ */ React10.createElement(Text10, null, "\u2022 Unintended system changes if the agent hallucinates a path or command."), /* @__PURE__ */ React10.createElement(Text10, null, "\u2022 Reduced control over the agent's step-by-step decision making."), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(
5357
5777
  CommandMenu,
@@ -5717,7 +6137,7 @@ Selection: ${val}`,
5717
6137
  const agentActiveMs = sessionApiTime + sessionToolTime;
5718
6138
  const apiPercent = agentActiveMs > 0 ? (sessionApiTime / agentActiveMs * 100).toFixed(1) : "0.0";
5719
6139
  const toolPercent = agentActiveMs > 0 ? (sessionToolTime / agentActiveMs * 100).toFixed(1) : "0.0";
5720
- return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", paddingX: 3, paddingY: 1, borderColor: "red", width: Math.min(100, (stdout?.columns || 100) - 2), marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan", bold: true }, "Agent powering down. ", /* @__PURE__ */ React10.createElement(Text10, { color: "magenta" }, "Goodbye!"))), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text10, { color: "white", bold: true, underline: true }, "Interaction Summary"), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Session ID:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, chatId)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tool Calls:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionToolSuccess + sessionToolFailure + sessionToolDenied, " ( ", /* @__PURE__ */ React10.createElement(Text10, { color: "green" }, "\u2713 ", sessionToolSuccess), " ", /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, "\u2298 ", sessionToolDenied), " ", /* @__PURE__ */ React10.createElement(Text10, { color: "red" }, "\u2715 ", sessionToolFailure), " )")), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Success Rate:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, successRate, "%")), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tokens Consumed:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatTokens(sessionTotalTokens)))), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "white", bold: true, underline: true }, "Performance"), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Wall Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(wallTimeMs))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Agent Active:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(agentActiveMs))), /* @__PURE__ */ React10.createElement(Box10, { marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Box10, { width: 18 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue", dimColor: true }, "\xBB API Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(sessionApiTime), " (", apiPercent, "%)")), /* @__PURE__ */ React10.createElement(Box10, { marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Box10, { width: 18 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue", dimColor: true }, "\xBB Tool Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(sessionToolTime), " (", toolPercent, "%)"))));
6140
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", paddingX: 3, paddingY: 1, borderColor: "red", width: Math.min(100, (stdout?.columns || 100) - 2), marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan", bold: true }, "Agent powering down. ", /* @__PURE__ */ React10.createElement(Text10, { color: "magenta" }, "Goodbye!"))), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text10, { color: "white", bold: true, underline: true }, "Interaction Summary"), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Session ID:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, chatId)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tool Calls:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionToolSuccess + sessionToolFailure + sessionToolDenied, " ( ", /* @__PURE__ */ React10.createElement(Text10, { color: "green" }, "\u2713 ", sessionToolSuccess), " ", /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, "\u2298 ", sessionToolDenied), " ", /* @__PURE__ */ React10.createElement(Text10, { color: "red" }, "\u2715 ", sessionToolFailure), " )")), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Success Rate:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, successRate, "%")), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Tokens Consumed:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatTokens(sessionTotalTokens))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Images Made:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, sessionImageCount || 0)), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Image Credits:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, (sessionImageCredits || 0).toFixed(4), " credits"))), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "white", bold: true, underline: true }, "Performance"), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Wall Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(wallTimeMs))), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Box10, { width: 20 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue" }, "Agent Active:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(agentActiveMs))), /* @__PURE__ */ React10.createElement(Box10, { marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Box10, { width: 18 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue", dimColor: true }, "\xBB API Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(sessionApiTime), " (", apiPercent, "%)")), /* @__PURE__ */ React10.createElement(Box10, { marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Box10, { width: 18 }, /* @__PURE__ */ React10.createElement(Text10, { color: "blue", dimColor: true }, "\xBB Tool Time:")), /* @__PURE__ */ React10.createElement(Text10, { color: "white" }, formatMsDuration(sessionToolTime), " (", toolPercent, "%)"))));
5721
6141
  })(), suggestions.length > 0 && (() => {
5722
6142
  const windowSize = 5;
5723
6143
  const startIdx = Math.max(0, Math.min(selectedIndex - 2, suggestions.length - windowSize));
@@ -5788,8 +6208,8 @@ var init_app = __esm({
5788
6208
  init_text();
5789
6209
  SESSION_START_TIME = Date.now();
5790
6210
  CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
5791
- packageJsonPath = path17.join(path17.dirname(fileURLToPath(import.meta.url)), "../package.json");
5792
- packageJson = JSON.parse(fs19.readFileSync(packageJsonPath, "utf8"));
6211
+ packageJsonPath = path16.join(path16.dirname(fileURLToPath(import.meta.url)), "../package.json");
6212
+ packageJson = JSON.parse(fs18.readFileSync(packageJsonPath, "utf8"));
5793
6213
  versionFluxflow = packageJson.version;
5794
6214
  updatedOn = "2026-05-17";
5795
6215
  ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React10.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "magenta", bold: true, underline: true }, "\u{1F7E3} STEERING HINT RESOLUTION")), /* @__PURE__ */ React10.createElement(Box10, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, null, "The agent already finished the task before your hint was consumed.")), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1, backgroundColor: "#222", paddingX: 2, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { italic: true, color: "gray" }, '"', data, '"')), /* @__PURE__ */ React10.createElement(Box10, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, "How would you like to proceed?")), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 0 }, /* @__PURE__ */ React10.createElement(