fluxflow-cli 1.9.30 → 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')]
@@ -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,
@@ -2737,8 +3024,8 @@ var init_ai = __esm({
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 = "";
@@ -3273,7 +3560,9 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3273
3560
  "SearchKeyword": "search_keyword",
3274
3561
  "Memory": "memory",
3275
3562
  "Chat": "chat",
3276
- "chat": "chat"
3563
+ "chat": "chat",
3564
+ "GenerateImage": "generate_image",
3565
+ "generate_image": "generate_image"
3277
3566
  };
3278
3567
  const potentialTool = NORMALIZE_MAP[rawToolName] || rawToolName;
3279
3568
  const partialArgs = toolContext.args || "";
@@ -3313,7 +3602,8 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3313
3602
  "search_keyword": "Searching Keywords",
3314
3603
  "exec_command": "Running Command",
3315
3604
  "ask": "Asking User",
3316
- "memory": "Updating Memory"
3605
+ "memory": "Updating Memory",
3606
+ "generate_image": "Generating Image"
3317
3607
  };
3318
3608
  const toolTitle = TOOL_TITLES[potentialTool] || "Working";
3319
3609
  process.stdout.write(`\x1B]0;${toolTitle}...\x07`);
@@ -3394,7 +3684,9 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3394
3684
  "SearchKeyword": "search_keyword",
3395
3685
  "Memory": "memory",
3396
3686
  "Chat": "chat",
3397
- "chat": "chat"
3687
+ "chat": "chat",
3688
+ "GenerateImage": "generate_image",
3689
+ "generate_image": "generate_image"
3398
3690
  };
3399
3691
  const normToolName = NORMALIZE_MAP[toolCall.toolName] || toolCall.toolName;
3400
3692
  const displayLabel = TOOL_LABELS2[normToolName] || toolCall.toolName;
@@ -3448,6 +3740,9 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3448
3740
  } else if (normToolName === "search_keyword") {
3449
3741
  const { keyword } = parseArgs(toolCall.args);
3450
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();
3451
3746
  } else if (normToolName === "exec_command" || normToolName === "ask") {
3452
3747
  label = "";
3453
3748
  } else {
@@ -3509,7 +3804,7 @@ ${boxBottom}` };
3509
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**" : ""}` });
3510
3805
  yield { type: "tool_result", content: `[TOOL RESULT]: DENIED: ${denyMsg}` };
3511
3806
  await incrementUsage("toolDenied");
3512
- if (settings.onToolResult) settings.onToolResult("denied");
3807
+ if (settings.onToolResult) settings.onToolResult("denied", normToolName);
3513
3808
  toolCallPointer++;
3514
3809
  continue;
3515
3810
  }
@@ -3543,11 +3838,11 @@ ${boxBottom}` };
3543
3838
  const isSuccess = result && !result.startsWith("ERROR:") && !isDenied;
3544
3839
  if (isSuccess) {
3545
3840
  await incrementUsage("toolSuccess");
3546
- if (settings.onToolResult) settings.onToolResult("success");
3841
+ if (settings.onToolResult) settings.onToolResult("success", normToolName);
3547
3842
  } else if (isDenied) {
3548
3843
  } else {
3549
3844
  await incrementUsage("toolFailure");
3550
- if (settings.onToolResult) settings.onToolResult("failure");
3845
+ if (settings.onToolResult) settings.onToolResult("failure", normToolName);
3551
3846
  }
3552
3847
  const aiContent = `[TOOL RESULT]: ${(result || "").toString().split(/\r?\n/).filter((line) => !line.includes("[UI_CONTEXT]")).join("\n")}`;
3553
3848
  toolResults.push({ role: "user", text: aiContent, binaryPart });
@@ -3692,97 +3987,6 @@ ${timestamp}`;
3692
3987
  }
3693
3988
  });
3694
3989
 
3695
- // src/utils/settings.js
3696
- import fs17 from "fs-extra";
3697
- import path16 from "path";
3698
- var DEFAULT_SETTINGS, loadSettings, migrateToExternal, saveSettings;
3699
- var init_settings = __esm({
3700
- "src/utils/settings.js"() {
3701
- init_paths();
3702
- DEFAULT_SETTINGS = {
3703
- mode: "Flux",
3704
- thinkingLevel: "Medium",
3705
- activeModel: "gemma-4-31b-it",
3706
- showFullThinking: true,
3707
- apiTier: "Free",
3708
- quotas: {
3709
- agentLimit: 1500,
3710
- backgroundLimit: 1500,
3711
- searchLimit: 100,
3712
- customModelId: "",
3713
- customLimit: 0
3714
- },
3715
- systemSettings: {
3716
- memory: true,
3717
- compression: 0,
3718
- autoExec: false,
3719
- allowExternalAccess: false,
3720
- autoDeleteHistory: "7d",
3721
- useExternalData: false,
3722
- externalDataPath: ""
3723
- },
3724
- profileData: {
3725
- name: null,
3726
- nickname: null,
3727
- instructions: null
3728
- }
3729
- };
3730
- loadSettings = async () => {
3731
- try {
3732
- if (await fs17.exists(SETTINGS_FILE)) {
3733
- const saved = await fs17.readJson(SETTINGS_FILE);
3734
- const merged = {
3735
- ...DEFAULT_SETTINGS,
3736
- ...saved,
3737
- quotas: { ...DEFAULT_SETTINGS.quotas, ...saved.quotas },
3738
- systemSettings: { ...DEFAULT_SETTINGS.systemSettings, ...saved.systemSettings },
3739
- profileData: { ...DEFAULT_SETTINGS.profileData, ...saved.profileData }
3740
- };
3741
- if (merged.showFullThinking === false) {
3742
- merged.showFullThinking = true;
3743
- await fs17.writeJson(SETTINGS_FILE, merged, { spaces: 2 });
3744
- }
3745
- return merged;
3746
- }
3747
- } catch (err) {
3748
- console.error("Failed to load settings:", err);
3749
- }
3750
- return DEFAULT_SETTINGS;
3751
- };
3752
- migrateToExternal = async (newPath) => {
3753
- const { FLUXFLOW_DIR: FLUXFLOW_DIR2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
3754
- const folders = ["logs", "secret"];
3755
- for (const folder of folders) {
3756
- const src = path16.join(FLUXFLOW_DIR2, folder);
3757
- const dest = path16.join(newPath, folder);
3758
- try {
3759
- if (await fs17.exists(src)) {
3760
- await fs17.ensureDir(dest);
3761
- await fs17.copy(src, dest, { overwrite: true });
3762
- }
3763
- } catch (err) {
3764
- console.error(`Migration failed for ${folder}:`, err);
3765
- }
3766
- }
3767
- };
3768
- saveSettings = async (settings) => {
3769
- try {
3770
- const current = await loadSettings();
3771
- if (!current.systemSettings.useExternalData && settings.systemSettings?.useExternalData && settings.systemSettings?.externalDataPath) {
3772
- await migrateToExternal(settings.systemSettings.externalDataPath);
3773
- }
3774
- const updated = { ...current, ...settings };
3775
- await fs17.ensureDir(path16.dirname(SETTINGS_FILE));
3776
- await fs17.writeJson(SETTINGS_FILE, updated, { spaces: 2 });
3777
- return true;
3778
- } catch (err) {
3779
- console.error("Failed to save settings:", err);
3780
- return false;
3781
- }
3782
- };
3783
- }
3784
- });
3785
-
3786
3990
  // src/components/ResumeModal.jsx
3787
3991
  import React7, { useState as useState4, useEffect as useEffect2 } from "react";
3788
3992
  import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
@@ -3986,7 +4190,7 @@ var init_UpdateProcessor = __esm({
3986
4190
  import puppeteer4 from "puppeteer";
3987
4191
  import { exec as exec3 } from "child_process";
3988
4192
  import { promisify } from "util";
3989
- import fs18 from "fs";
4193
+ import fs17 from "fs";
3990
4194
  var execAsync, checkPuppeteerReady, installPuppeteerBrowser;
3991
4195
  var init_setup = __esm({
3992
4196
  "src/utils/setup.js"() {
@@ -3994,7 +4198,7 @@ var init_setup = __esm({
3994
4198
  checkPuppeteerReady = () => {
3995
4199
  try {
3996
4200
  const exePath = puppeteer4.executablePath();
3997
- const exists = exePath && fs18.existsSync(exePath);
4201
+ const exists = exePath && fs17.existsSync(exePath);
3998
4202
  if (exists) return true;
3999
4203
  } catch (e) {
4000
4204
  return false;
@@ -4028,8 +4232,8 @@ import os3 from "os";
4028
4232
  import React10, { useState as useState7, useEffect as useEffect5, useRef as useRef2, useMemo } from "react";
4029
4233
  import { Box as Box10, Text as Text10, useInput as useInput4, useStdout } from "ink";
4030
4234
  import Spinner2 from "ink-spinner";
4031
- import fs19 from "fs-extra";
4032
- import path17 from "path";
4235
+ import fs18 from "fs-extra";
4236
+ import path16 from "path";
4033
4237
  import { exec as exec4 } from "child_process";
4034
4238
  import { fileURLToPath } from "url";
4035
4239
  import { MultilineInput } from "ink-multiline-input";
@@ -4125,6 +4329,7 @@ function App() {
4125
4329
  const [inputConfig, setInputConfig] = useState7(null);
4126
4330
  const [systemSettings, setSystemSettings] = useState7({ memory: true, compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
4127
4331
  const [profileData, setProfileData] = useState7({ name: null, nickname: null, instructions: null });
4332
+ const [imageSettings, setImageSettings] = useState7({ keyType: "Default", quality: "Low-High", apiKey: "" });
4128
4333
  const [sessionStats, setSessionStats] = useState7({ tokens: 0 });
4129
4334
  const [sessionAgentCalls, setSessionAgentCalls] = useState7(0);
4130
4335
  const [sessionBackgroundCalls, setSessionBackgroundCalls] = useState7(0);
@@ -4134,6 +4339,8 @@ function App() {
4134
4339
  const [sessionToolDenied, setSessionToolDenied] = useState7(0);
4135
4340
  const [sessionApiTime, setSessionApiTime] = useState7(0);
4136
4341
  const [sessionToolTime, setSessionToolTime] = useState7(0);
4342
+ const [sessionImageCount, setSessionImageCount] = useState7(0);
4343
+ const [sessionImageCredits, setSessionImageCredits] = useState7(0);
4137
4344
  const [dailyUsage, setDailyUsage] = useState7(null);
4138
4345
  const [chatId, setChatId] = useState7(generateChatId());
4139
4346
  const [activeCommand, setActiveCommand] = useState7(null);
@@ -4383,6 +4590,7 @@ function App() {
4383
4590
  };
4384
4591
  setSystemSettings(freshSettings);
4385
4592
  setProfileData(saved.profileData);
4593
+ setImageSettings(saved.imageSettings || { keyType: "Default", quality: "Low-High", apiKey: "" });
4386
4594
  const key = await getAPIKey();
4387
4595
  if (key) {
4388
4596
  setApiKey(key);
@@ -4423,10 +4631,11 @@ function App() {
4423
4631
  activeModel,
4424
4632
  showFullThinking,
4425
4633
  systemSettings,
4426
- profileData
4634
+ profileData,
4635
+ imageSettings
4427
4636
  });
4428
4637
  }
4429
- }, [mode, thinkingLevel, activeModel, showFullThinking, systemSettings, profileData, isInitializing]);
4638
+ }, [mode, thinkingLevel, activeModel, showFullThinking, systemSettings, profileData, imageSettings, isInitializing]);
4430
4639
  const handleSetup = async (val) => {
4431
4640
  const key = val.trim();
4432
4641
  if (key.length >= 30) {
@@ -4478,6 +4687,40 @@ function App() {
4478
4687
  { cmd: "/resume", desc: "Load previous session" },
4479
4688
  { cmd: "/save", desc: "Force save current chat" },
4480
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
+ },
4481
4724
  {
4482
4725
  cmd: "/mode",
4483
4726
  desc: "Toggle Flux/Flow modes",
@@ -4531,14 +4774,15 @@ function App() {
4531
4774
  ]
4532
4775
  }
4533
4776
  ];
4534
- const handleSubmit = (value) => {
4777
+ const handleSubmit = async (value) => {
4535
4778
  if (suggestions.length > 0) {
4536
4779
  const nextMatch = suggestions[selectedIndex] || suggestions[0];
4537
4780
  const parts = value.split(" ");
4538
4781
  if (parts.length === 1) {
4539
4782
  setInput(nextMatch.cmd + " ");
4540
4783
  } else {
4541
- setInput(parts[0] + " " + nextMatch.cmd + " ");
4784
+ const parentParts = parts.slice(0, -1);
4785
+ setInput(parentParts.join(" ") + " " + nextMatch.cmd + " ");
4542
4786
  }
4543
4787
  setSelectedIndex(0);
4544
4788
  return;
@@ -4637,6 +4881,135 @@ ${hintText}`, color: "magenta" }];
4637
4881
  }
4638
4882
  break;
4639
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
+ }
4640
5013
  case "/thinking": {
4641
5014
  let formattedLevel;
4642
5015
  if (parts[1]) {
@@ -4744,12 +5117,12 @@ ${list || "No saved chats found."}`, isMeta: true }];
4744
5117
  setCompletedIndex(prev.length + 1);
4745
5118
  return [...prev, { id: Date.now(), role: "system", text: "\u2622\uFE0F [NUCLEAR] Initiating reset...", isMeta: true }];
4746
5119
  });
4747
- if (fs19.existsSync(LOGS_DIR)) fs19.removeSync(LOGS_DIR);
4748
- if (fs19.existsSync(SECRET_DIR)) fs19.removeSync(SECRET_DIR);
4749
- 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);
4750
5123
  try {
4751
- const items = fs19.readdirSync(FLUXFLOW_DIR);
4752
- if (items.length === 0) fs19.removeSync(FLUXFLOW_DIR);
5124
+ const items = fs18.readdirSync(FLUXFLOW_DIR);
5125
+ if (items.length === 0) fs18.removeSync(FLUXFLOW_DIR);
4753
5126
  } catch (e) {
4754
5127
  }
4755
5128
  setTimeout(() => {
@@ -4806,14 +5179,14 @@ ${list || "No saved chats found."}`, isMeta: true }];
4806
5179
  # SKILLS & WORKFLOWS
4807
5180
  - [Define custom step-by-step recipes for this project here]
4808
5181
  `;
4809
- const filePath = path17.join(process.cwd(), "FluxFlow.md");
4810
- if (fs19.pathExistsSync(filePath)) {
5182
+ const filePath = path16.join(process.cwd(), "FluxFlow.md");
5183
+ if (fs18.pathExistsSync(filePath)) {
4811
5184
  setMessages((prev) => {
4812
5185
  setCompletedIndex(prev.length + 1);
4813
5186
  return [...prev, { id: "init-err-" + Date.now(), role: "system", text: "\u274C ERROR: FluxFlow.md already exists in this directory.", isMeta: true }];
4814
5187
  });
4815
5188
  } else {
4816
- fs19.writeFileSync(filePath, template);
5189
+ fs18.writeFileSync(filePath, template);
4817
5190
  setMessages((prev) => {
4818
5191
  setCompletedIndex(prev.length + 1);
4819
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 }];
@@ -4901,10 +5274,28 @@ OUTPUT: ${execOutputRef.current}`;
4901
5274
  setIsTerminalFocused(false);
4902
5275
  setExecOutput("");
4903
5276
  },
4904
- onToolResult: (status) => {
4905
- if (status === "success") setSessionToolSuccess((prev) => prev + 1);
4906
- else if (status === "denied") setSessionToolDenied((prev) => prev + 1);
4907
- 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
+ }
4908
5299
  },
4909
5300
  onToolApproval: async (tool, args) => {
4910
5301
  const isAuto = autoAcceptWrites || systemSettings.autoExec;
@@ -5203,16 +5594,20 @@ Selection: ${val}`,
5203
5594
  const cleanQuery = query.startsWith("/") ? query.slice(1) : query;
5204
5595
  return COMMANDS.filter((c) => {
5205
5596
  const cleanCmd = c.cmd.startsWith("/") ? c.cmd.slice(1) : c.cmd;
5206
- return cleanCmd.includes(cleanQuery);
5597
+ return cleanCmd.toLowerCase().includes(cleanQuery);
5207
5598
  });
5208
5599
  }
5209
- if (parts.length === 2) {
5210
- const parent = COMMANDS.find((c) => c.cmd === parts[0].toLowerCase());
5211
- if (parent && parent.subs) {
5212
- 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 [];
5213
5608
  }
5214
5609
  }
5215
- return [];
5610
+ return currentList.filter((s) => s.cmd.toLowerCase().includes(query));
5216
5611
  }, [input]);
5217
5612
  useEffect5(() => {
5218
5613
  setSelectedIndex(0);
@@ -5346,19 +5741,37 @@ Selection: ${val}`,
5346
5741
  setSystemSettings(newSysSettings);
5347
5742
  newSettings.systemSettings = newSysSettings;
5348
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
+ }
5349
5762
  }
5350
5763
  if (next) {
5351
5764
  setInputConfig(next(key === "quotas" ? newQuotas : val));
5352
5765
  } else {
5353
- saveSettings({ ...newSettings, apiTier, quotas: newQuotas });
5766
+ saveSettings({ ...newSettings, apiTier, quotas: newQuotas, imageSettings: newSettings.imageSettings || imageSettings });
5354
5767
  setInputConfig(null);
5355
- setActiveView("settings");
5768
+ setActiveView(inputConfig?.returnView || "settings");
5356
5769
  }
5357
5770
  }
5358
5771
  }
5359
5772
  )), /* @__PURE__ */ React10.createElement(Box10, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "gray", dimColor: true, italic: true }, "(Press Enter to confirm selection)")));
5360
5773
  case "stats":
5361
- 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)"));
5362
5775
  case "autoExecDanger":
5363
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(
5364
5777
  CommandMenu,
@@ -5724,7 +6137,7 @@ Selection: ${val}`,
5724
6137
  const agentActiveMs = sessionApiTime + sessionToolTime;
5725
6138
  const apiPercent = agentActiveMs > 0 ? (sessionApiTime / agentActiveMs * 100).toFixed(1) : "0.0";
5726
6139
  const toolPercent = agentActiveMs > 0 ? (sessionToolTime / agentActiveMs * 100).toFixed(1) : "0.0";
5727
- 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, "%)"))));
5728
6141
  })(), suggestions.length > 0 && (() => {
5729
6142
  const windowSize = 5;
5730
6143
  const startIdx = Math.max(0, Math.min(selectedIndex - 2, suggestions.length - windowSize));
@@ -5795,8 +6208,8 @@ var init_app = __esm({
5795
6208
  init_text();
5796
6209
  SESSION_START_TIME = Date.now();
5797
6210
  CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
5798
- packageJsonPath = path17.join(path17.dirname(fileURLToPath(import.meta.url)), "../package.json");
5799
- 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"));
5800
6213
  versionFluxflow = packageJson.version;
5801
6214
  updatedOn = "2026-05-17";
5802
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(