fluxflow-cli 1.5.1 → 1.5.3

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.
Files changed (2) hide show
  1. package/dist/fluxflow.js +128 -80
  2. package/package.json +2 -3
package/dist/fluxflow.js CHANGED
@@ -741,7 +741,7 @@ Every ${isMemoryEnabled ? "Prompt, Responses & Memories" : "Prompt & Responses"}
741
741
  - Use GFM tables for structured data to keep the terminal view organized. KEEP SENTENCES IN TABLE **SHORT & CONCISE**. AND MAX 3 COLUMNS. DO NOT OVERUSE TABLES.
742
742
  - **CRITICAL**: NEVER USE LaTeX IN TERMINAL RESPONSES (exception: file content).
743
743
  - Keep Poems & Literature in Code Block.
744
- - Use emojis & Kaomojis.
744
+ - Use emojis & Kaomojis. Prefer Kaomojis more.
745
745
  -- END FORMATTING RULES --
746
746
 
747
747
  -- START REPONSE FINISH PROTOCOL --
@@ -763,7 +763,7 @@ Current date and Time is: ${dateTimeStr}
763
763
  AGENT RAWS (responses from this turn):
764
764
  ${agentRes}
765
765
  ${userMemories ? `
766
-
766
+
767
767
  -- CURRENT PERSISTENT USER MEMORIES --
768
768
  ${userMemories}
769
769
  -------------------------------------------------
@@ -980,7 +980,7 @@ var init_arg_parser = __esm({
980
980
  });
981
981
 
982
982
  // src/tools/web_search.js
983
- import * as cuimp from "cuimp";
983
+ import puppeteer from "puppeteer";
984
984
  import fs6 from "fs";
985
985
  import path6 from "path";
986
986
  var web_search;
@@ -994,74 +994,80 @@ var init_web_search = __esm({
994
994
  const maxRetries = 3;
995
995
  let lastError = null;
996
996
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
997
+ let browser = null;
997
998
  try {
998
- const userAgents = [
999
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
1000
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
1001
- "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
1002
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0"
1003
- ];
1004
- const randomUA = userAgents[Math.floor(Math.random() * userAgents.length)];
999
+ browser = await puppeteer.launch({
1000
+ headless: true,
1001
+ args: [
1002
+ "--no-sandbox",
1003
+ "--disable-setuid-sandbox",
1004
+ "--disable-gpu",
1005
+ "--disable-dev-shm-usage"
1006
+ ]
1007
+ });
1008
+ const page = await browser.newPage();
1009
+ await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36");
1010
+ await page.setViewport({ width: 1280, height: 800 });
1005
1011
  const jitter = attempt === 1 ? Math.random() * 1e3 + 500 : Math.random() * 2e3 + 1e3;
1006
1012
  await new Promise((r) => setTimeout(r, jitter));
1007
- const response = await cuimp.get(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`, {
1008
- headers: {
1009
- "User-Agent": randomUA,
1010
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
1011
- "Accept-Language": "en-US,en;q=0.9",
1012
- "Referer": "https://duckduckgo.com/",
1013
- "Upgrade-Insecure-Requests": "1",
1014
- "Cache-Control": "max-age=0"
1015
- }
1016
- });
1017
- const html = response.data;
1018
- const results = [];
1019
- const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g;
1020
- let match;
1021
- let count = 0;
1022
- while ((match = resultRegex.exec(html)) !== null && count < limit) {
1023
- let url = match[1];
1024
- if (url.includes("uddg=")) {
1025
- url = decodeURIComponent(url.split("uddg=")[1].split("&")[0]);
1026
- }
1027
- const title = match[2].replace(/<[^>]*>/g, "").trim();
1028
- const snippet = match[3].replace(/<[^>]*>/g, "").trim();
1029
- results.push(`${count + 1}. ${title}
1013
+ const searchUrl = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
1014
+ await page.goto(searchUrl, { waitUntil: "networkidle2" });
1015
+ const results = await page.$$eval(".result", (elements, maxLimit) => {
1016
+ return elements.slice(0, maxLimit).map((el, i) => {
1017
+ const titleEl = el.querySelector(".result__a");
1018
+ const snippetEl = el.querySelector(".result__snippet");
1019
+ let url = titleEl ? titleEl.href : "";
1020
+ if (url.includes("uddg=")) {
1021
+ url = decodeURIComponent(url.split("uddg=")[1].split("&")[0]);
1022
+ }
1023
+ const title = titleEl ? titleEl.innerText.trim() : "No Title";
1024
+ const snippet = snippetEl ? snippetEl.innerText.trim() : "No Snippet";
1025
+ return `${i + 1}. ${title}
1030
1026
  Source: ${url}
1031
- Snippet: ${snippet}`);
1032
- count++;
1033
- }
1027
+ Snippet: ${snippet}`;
1028
+ });
1029
+ }, limit);
1034
1030
  if (results.length === 0) {
1035
- if (html.includes("anomaly")) {
1036
- throw new Error("DDG_ANOMALY_DETECTED");
1031
+ const bodyText = await page.evaluate(() => document.body.innerText);
1032
+ if (bodyText.includes("anomaly")) {
1033
+ throw new Error("ANOMALY_DETECTED");
1037
1034
  }
1035
+ await browser.close();
1038
1036
  return `No results found for query: [${query}].`;
1039
1037
  }
1040
1038
  const finalResults = results.join("\n\n");
1039
+ const toolLogDir = path6.join(LOGS_DIR, "tools");
1040
+ if (!fs6.existsSync(toolLogDir)) fs6.mkdirSync(toolLogDir, { recursive: true });
1041
+ fs6.appendFileSync(path6.join(toolLogDir, "search-results.log"), `SEARCH ${(/* @__PURE__ */ new Date()).toISOString()} - Query: [${query}]. Count: ${results.length}.
1042
+ Content:
1043
+ ${finalResults}
1044
+
1045
+ --------------------------------------------------------
1046
+
1047
+
1048
+ `);
1049
+ await browser.close();
1041
1050
  return `Search results for [${query}]:
1042
1051
 
1043
1052
  ${finalResults}`;
1044
1053
  } catch (err) {
1045
1054
  lastError = err;
1046
- const toolErrDir = path6.join(LOGS_DIR, "tools");
1047
- if (!fs6.existsSync(toolErrDir)) fs6.mkdirSync(toolErrDir, { recursive: true });
1048
- fs6.appendFileSync(path6.join(toolErrDir, "error.log"), `ERROR ${(/* @__PURE__ */ new Date()).toISOString()} - Attempt ${attempt}/${maxRetries} failed: ${err.message}
1049
- `);
1055
+ if (browser) await browser.close();
1050
1056
  if (attempt < maxRetries) {
1051
1057
  const backoff = Math.pow(2, attempt) * 1e3;
1052
1058
  await new Promise((r) => setTimeout(r, backoff));
1053
1059
  }
1054
1060
  }
1055
1061
  }
1056
- return `ERROR: Search failed after ${maxRetries} attempts. Last error: ${lastError.message}`;
1062
+ return `ERROR: Search failed after ${maxRetries + 1} attempts. Last error: ${lastError.message}`;
1057
1063
  };
1058
1064
  }
1059
1065
  });
1060
1066
 
1061
1067
  // src/tools/web_scrape.js
1068
+ import puppeteer2 from "puppeteer";
1062
1069
  import fs7 from "fs";
1063
1070
  import path7 from "path";
1064
- import * as cuimp2 from "cuimp";
1065
1071
  var web_scrape;
1066
1072
  var init_web_scrape = __esm({
1067
1073
  "src/tools/web_scrape.js"() {
@@ -1072,53 +1078,86 @@ var init_web_scrape = __esm({
1072
1078
  const maxRetries = 3;
1073
1079
  let lastError = null;
1074
1080
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
1081
+ let browser = null;
1075
1082
  try {
1076
- const userAgents = [
1077
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
1078
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
1079
- "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
1080
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0"
1081
- ];
1082
- const randomUA = userAgents[Math.floor(Math.random() * userAgents.length)];
1083
+ browser = await puppeteer2.launch({
1084
+ headless: true,
1085
+ args: [
1086
+ "--no-sandbox",
1087
+ "--disable-setuid-sandbox",
1088
+ "--disable-gpu",
1089
+ "--disable-dev-shm-usage"
1090
+ ]
1091
+ });
1092
+ const page = await browser.newPage();
1093
+ await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36");
1094
+ await page.setViewport({ width: 1280, height: 1600 });
1083
1095
  const jitter = attempt === 1 ? Math.random() * 1e3 + 500 : Math.random() * 2e3 + 1e3;
1084
1096
  await new Promise((r) => setTimeout(r, jitter));
1085
- const response = await cuimp2.get(url, {
1086
- headers: {
1087
- "User-Agent": randomUA,
1088
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
1089
- "Accept-Language": "en-US,en;q=0.9",
1090
- "Referer": "https://www.google.com/",
1091
- "Upgrade-Insecure-Requests": "1",
1092
- "Cache-Control": "max-age=0"
1097
+ await page.goto(url, { waitUntil: "networkidle2", timeout: 45e3 });
1098
+ await new Promise((r) => setTimeout(r, 5e3));
1099
+ let htmlContent = await page.evaluate(() => {
1100
+ const junk = document.querySelectorAll("script, style, nav, footer, header, noscript, svg, canvas, iframe, ad, .ads, link, meta, img");
1101
+ junk.forEach((el) => el.remove());
1102
+ const iterator = document.createNodeIterator(document.body, NodeFilter.SHOW_COMMENT);
1103
+ let currentNode;
1104
+ while (currentNode = iterator.nextNode()) {
1105
+ currentNode.remove();
1093
1106
  }
1107
+ const allElements = document.querySelectorAll("*");
1108
+ allElements.forEach((el) => {
1109
+ const attributes = el.attributes;
1110
+ for (let i = attributes.length - 1; i >= 0; i--) {
1111
+ const attrName = attributes[i].name;
1112
+ if (attrName !== "href" && attrName !== "src") {
1113
+ el.removeAttribute(attrName);
1114
+ }
1115
+ }
1116
+ if ((el.tagName === "SPAN" || el.tagName === "DIV" || el.tagName === "SECTION") && el.attributes.length === 0) {
1117
+ if (el.tagName === "SPAN" || el.tagName === "DIV" && el.childNodes.length === 1 && el.childNodes[0].nodeType === Node.TEXT_NODE) {
1118
+ el.replaceWith(...el.childNodes);
1119
+ }
1120
+ }
1121
+ });
1122
+ const pruneEmpty = () => {
1123
+ let found = false;
1124
+ document.querySelectorAll("*:not(br)").forEach((el) => {
1125
+ if (el.childNodes.length === 0 && !el.innerText.trim()) {
1126
+ el.remove();
1127
+ found = true;
1128
+ }
1129
+ });
1130
+ if (found) pruneEmpty();
1131
+ };
1132
+ pruneEmpty();
1133
+ return document.body.innerHTML;
1094
1134
  });
1095
- let html = response.data;
1096
- if (!html) throw new Error("EMPTY_RESPONSE");
1097
- html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
1098
- html = html.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "");
1099
- html = html.replace(/<nav\b[^<]*(?:(?!<\/nav>)<[^<]*)*<\/nav>/gi, "");
1100
- html = html.replace(/<footer\b[^<]*(?:(?!<\/footer>)<[^<]*)*<\/footer>/gi, "");
1101
- html = html.replace(/<header\b[^<]*(?:(?!<\/header>)<[^<]*)*<\/header>/gi, "");
1102
- let text = html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
1103
- const finalContent = text.substring(0, 2e4);
1135
+ if (!htmlContent) throw new Error("EMPTY_RENDER_RESULT");
1136
+ const cleanedHtml = htmlContent.replace(/\s+/g, " ").replace(/>\s+</g, "><").trim().substring(0, 3e4);
1104
1137
  const toolLogDir = path7.join(LOGS_DIR, "tools");
1105
1138
  if (!fs7.existsSync(toolLogDir)) fs7.mkdirSync(toolLogDir, { recursive: true });
1106
- fs7.appendFileSync(path7.join(toolLogDir, "search-scraped.log"), `RESULTS ${(/* @__PURE__ */ new Date()).toISOString()} -
1107
- URL: [${url}]. Content Length: ${finalContent.length}
1139
+ fs7.appendFileSync(path7.join(toolLogDir, "search-scraped.log"), `PUPPETEER ${(/* @__PURE__ */ new Date()).toISOString()} - URL: [${url}]. Length: ${cleanedHtml.length}.
1140
+ Content:
1141
+ ${cleanedHtml}
1142
+
1143
+ --------------------------------------------------------
1144
+
1108
1145
 
1109
1146
  `);
1110
- return `CONTENT FROM [${url}]:
1147
+ await browser.close();
1148
+ return `CLEANED HTML FROM [${url}]:
1111
1149
 
1112
- ${finalContent}${text.length > 2e4 ? "\n\n[TRUNCATED AT 20K CHARS]" : ""}`;
1150
+ ${cleanedHtml}${htmlContent.length > 3e4 ? "\n\n[TRUNCATED AT 30K CHARS]" : ""}`;
1113
1151
  } catch (err) {
1114
1152
  lastError = err;
1153
+ if (browser) await browser.close();
1115
1154
  if (attempt < maxRetries) {
1116
1155
  const backoff = Math.pow(2, attempt) * 1e3;
1117
1156
  await new Promise((r) => setTimeout(r, backoff));
1118
1157
  }
1119
1158
  }
1120
1159
  }
1121
- return `ERROR: Scrape failed after ${maxRetries} attempts. Last error: ${lastError.message}`;
1160
+ return `ERROR: Scrape failed after ${maxRetries + 1} attempts. Last error: ${lastError.message}`;
1122
1161
  };
1123
1162
  }
1124
1163
  });
@@ -1667,7 +1706,7 @@ var init_ask_user = __esm({
1667
1706
  });
1668
1707
 
1669
1708
  // src/tools/write_pdf.js
1670
- import puppeteer from "puppeteer";
1709
+ import puppeteer3 from "puppeteer";
1671
1710
  import path13 from "path";
1672
1711
  import fs13 from "fs-extra";
1673
1712
  var write_pdf;
@@ -1682,7 +1721,7 @@ var init_write_pdf = __esm({
1682
1721
  let browser = null;
1683
1722
  try {
1684
1723
  await fs13.ensureDir(path13.dirname(absolutePath));
1685
- browser = await puppeteer.launch({
1724
+ browser = await puppeteer3.launch({
1686
1725
  headless: true,
1687
1726
  args: [
1688
1727
  "--no-sandbox",
@@ -2035,7 +2074,16 @@ USER_PROMPT: ${agentText}`.trim();
2035
2074
  }
2036
2075
  } catch (e) {
2037
2076
  }
2038
- label = `\u{1F4C4} READING FILE: ${targetPath2}. LINES ${start_line} - ${actualEndLine} FROM ${totalLines}`.toUpperCase();
2077
+ const pathLower = targetPath2.toLowerCase();
2078
+ const isPdf = pathLower.endsWith(".pdf");
2079
+ const isImage = /\.(png|jpg|jpeg|webp|gif|bmp)$/.test(pathLower);
2080
+ if (isPdf) {
2081
+ label = `\u{1F4C4} ANALYZING PDF: ${targetPath2}`.toUpperCase();
2082
+ } else if (isImage) {
2083
+ label = `\u{1F5BC}\uFE0F ANALYZING IMAGE: ${targetPath2}`.toUpperCase();
2084
+ } else {
2085
+ label = `\u{1F4C4} READING FILE: ${targetPath2}. LINES ${start_line} - ${actualEndLine} FROM ${totalLines}`.toUpperCase();
2086
+ }
2039
2087
  } else if (toolCall.toolName === "list_files" || toolCall.toolName === "read_folder") {
2040
2088
  const action = toolCall.toolName === "list_files" ? "LISTING" : "DISCOVERING";
2041
2089
  label = `\u{1F4C2} ${action} DIRECTORY: ${parseArgs(toolCall.args).path || "."}`.toUpperCase();
@@ -2495,7 +2543,7 @@ var init_UpdateProcessor = __esm({
2495
2543
  });
2496
2544
 
2497
2545
  // src/utils/setup.js
2498
- import puppeteer2 from "puppeteer";
2546
+ import puppeteer4 from "puppeteer";
2499
2547
  import { exec as exec2 } from "child_process";
2500
2548
  import { promisify } from "util";
2501
2549
  import fs16 from "fs";
@@ -2505,7 +2553,7 @@ var init_setup = __esm({
2505
2553
  execAsync = promisify(exec2);
2506
2554
  checkPuppeteerReady = () => {
2507
2555
  try {
2508
- const exePath = puppeteer2.executablePath();
2556
+ const exePath = puppeteer4.executablePath();
2509
2557
  const exists = exePath && fs16.existsSync(exePath);
2510
2558
  if (exists) return true;
2511
2559
  } catch (e) {
@@ -4073,8 +4121,8 @@ var init_app = __esm({
4073
4121
  init_setup();
4074
4122
  SESSION_START_TIME = Date.now();
4075
4123
  CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
4076
- versionFluxflow = "1.5.1";
4077
- updatedOn = "2026-05-01";
4124
+ versionFluxflow = "1.5.3";
4125
+ updatedOn = "2026-05-02";
4078
4126
  ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { color: "magenta", bold: true, underline: true }, "\u{1F7E3} STEERING HINT RESOLUTION"), /* @__PURE__ */ React10.createElement(Text10, { marginTop: 1 }, "The agent already finished the task (turn: finish) before your hint was consumed."), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1, backgroundColor: "#222", paddingX: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { italic: true, color: "gray" }, '"', data, '"')), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, "How would you like to proceed?")), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(
4079
4127
  CommandMenu,
4080
4128
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",
@@ -34,12 +34,11 @@
34
34
  },
35
35
  "scripts": {
36
36
  "start": "tsx ./src/cli.jsx",
37
- "build": "esbuild ./src/cli.jsx --bundle --platform=node --format=esm --outfile=./dist/fluxflow.js --external:react --external:ink --external:chalk --external:fs-extra --external:gradient-string --external:ink-text-input --external:ink-select-input --external:ink-spinner --external:ink-multiline-input --external:@google/genai --external:zod --external:duck-duck-scrape --external:nanoid --external:cuimp --external:puppeteer --external:typescript"
37
+ "build": "esbuild ./src/cli.jsx --bundle --platform=node --format=esm --outfile=./dist/fluxflow.js --external:react --external:ink --external:chalk --external:fs-extra --external:gradient-string --external:ink-text-input --external:ink-select-input --external:ink-spinner --external:ink-multiline-input --external:@google/genai --external:zod --external:nanoid --external:puppeteer --external:typescript"
38
38
  },
39
39
  "dependencies": {
40
40
  "@google/genai": "^1.50.1",
41
41
  "chalk": "^5.6.2",
42
- "cuimp": "^1.10.0",
43
42
  "fs-extra": "^11.3.4",
44
43
  "gradient-string": "^3.0.0",
45
44
  "ink": "^7.0.1",