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.
- package/dist/fluxflow.js +128 -80
- 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
|
|
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
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
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
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
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
|
-
|
|
1033
|
-
}
|
|
1027
|
+
Snippet: ${snippet}`;
|
|
1028
|
+
});
|
|
1029
|
+
}, limit);
|
|
1034
1030
|
if (results.length === 0) {
|
|
1035
|
-
|
|
1036
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
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
|
-
|
|
1096
|
-
|
|
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"), `
|
|
1107
|
-
|
|
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
|
-
|
|
1147
|
+
await browser.close();
|
|
1148
|
+
return `CLEANED HTML FROM [${url}]:
|
|
1111
1149
|
|
|
1112
|
-
${
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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.
|
|
4077
|
-
updatedOn = "2026-05-
|
|
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.
|
|
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:
|
|
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",
|