fluxflow-cli 1.5.1 → 1.5.2
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 +92 -76
- package/package.json +2 -3
package/dist/fluxflow.js
CHANGED
|
@@ -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,63 @@ 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
|
-
|
|
1093
|
-
|
|
1097
|
+
await page.goto(url, { waitUntil: "networkidle2", timeout: 45e3 });
|
|
1098
|
+
await new Promise((r) => setTimeout(r, 5e3));
|
|
1099
|
+
let text = await page.evaluate(() => {
|
|
1100
|
+
const junk = document.querySelectorAll("script, style, nav, footer, header, noscript");
|
|
1101
|
+
junk.forEach((el) => el.remove());
|
|
1102
|
+
const links = document.querySelectorAll("a");
|
|
1103
|
+
links.forEach((a) => {
|
|
1104
|
+
const href = a.href;
|
|
1105
|
+
const content = a.innerText.trim();
|
|
1106
|
+
if (href && content && !href.startsWith("javascript:") && !href.startsWith("#")) {
|
|
1107
|
+
a.innerText = ` [${content}](${href}) `;
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
return document.body.innerText;
|
|
1094
1111
|
});
|
|
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);
|
|
1112
|
+
if (!text) throw new Error("EMPTY_RENDER_RESULT");
|
|
1113
|
+
const cleanedText = text.replace(/\s+/g, " ").trim().substring(0, 25e3);
|
|
1104
1114
|
const toolLogDir = path7.join(LOGS_DIR, "tools");
|
|
1105
1115
|
if (!fs7.existsSync(toolLogDir)) fs7.mkdirSync(toolLogDir, { recursive: true });
|
|
1106
|
-
fs7.appendFileSync(path7.join(toolLogDir, "search-scraped.log"), `
|
|
1107
|
-
|
|
1116
|
+
fs7.appendFileSync(path7.join(toolLogDir, "search-scraped.log"), `PUPPETEER ${(/* @__PURE__ */ new Date()).toISOString()} - URL: [${url}]. Length: ${cleanedText.length}.
|
|
1117
|
+
Content:
|
|
1118
|
+
${cleanedText}
|
|
1119
|
+
|
|
1120
|
+
--------------------------------------------------------
|
|
1121
|
+
|
|
1108
1122
|
|
|
1109
1123
|
`);
|
|
1124
|
+
await browser.close();
|
|
1110
1125
|
return `CONTENT FROM [${url}]:
|
|
1111
1126
|
|
|
1112
|
-
${
|
|
1127
|
+
${cleanedText}${text.length > 25e3 ? "\n\n[TRUNCATED AT 25K CHARS]" : ""}`;
|
|
1113
1128
|
} catch (err) {
|
|
1114
1129
|
lastError = err;
|
|
1130
|
+
if (browser) await browser.close();
|
|
1115
1131
|
if (attempt < maxRetries) {
|
|
1116
1132
|
const backoff = Math.pow(2, attempt) * 1e3;
|
|
1117
1133
|
await new Promise((r) => setTimeout(r, backoff));
|
|
1118
1134
|
}
|
|
1119
1135
|
}
|
|
1120
1136
|
}
|
|
1121
|
-
return `ERROR: Scrape failed after ${maxRetries} attempts. Last error: ${lastError.message}`;
|
|
1137
|
+
return `ERROR: Scrape failed after ${maxRetries + 1} attempts. Last error: ${lastError.message}`;
|
|
1122
1138
|
};
|
|
1123
1139
|
}
|
|
1124
1140
|
});
|
|
@@ -1667,7 +1683,7 @@ var init_ask_user = __esm({
|
|
|
1667
1683
|
});
|
|
1668
1684
|
|
|
1669
1685
|
// src/tools/write_pdf.js
|
|
1670
|
-
import
|
|
1686
|
+
import puppeteer3 from "puppeteer";
|
|
1671
1687
|
import path13 from "path";
|
|
1672
1688
|
import fs13 from "fs-extra";
|
|
1673
1689
|
var write_pdf;
|
|
@@ -1682,7 +1698,7 @@ var init_write_pdf = __esm({
|
|
|
1682
1698
|
let browser = null;
|
|
1683
1699
|
try {
|
|
1684
1700
|
await fs13.ensureDir(path13.dirname(absolutePath));
|
|
1685
|
-
browser = await
|
|
1701
|
+
browser = await puppeteer3.launch({
|
|
1686
1702
|
headless: true,
|
|
1687
1703
|
args: [
|
|
1688
1704
|
"--no-sandbox",
|
|
@@ -2495,7 +2511,7 @@ var init_UpdateProcessor = __esm({
|
|
|
2495
2511
|
});
|
|
2496
2512
|
|
|
2497
2513
|
// src/utils/setup.js
|
|
2498
|
-
import
|
|
2514
|
+
import puppeteer4 from "puppeteer";
|
|
2499
2515
|
import { exec as exec2 } from "child_process";
|
|
2500
2516
|
import { promisify } from "util";
|
|
2501
2517
|
import fs16 from "fs";
|
|
@@ -2505,7 +2521,7 @@ var init_setup = __esm({
|
|
|
2505
2521
|
execAsync = promisify(exec2);
|
|
2506
2522
|
checkPuppeteerReady = () => {
|
|
2507
2523
|
try {
|
|
2508
|
-
const exePath =
|
|
2524
|
+
const exePath = puppeteer4.executablePath();
|
|
2509
2525
|
const exists = exePath && fs16.existsSync(exePath);
|
|
2510
2526
|
if (exists) return true;
|
|
2511
2527
|
} catch (e) {
|
|
@@ -4073,7 +4089,7 @@ var init_app = __esm({
|
|
|
4073
4089
|
init_setup();
|
|
4074
4090
|
SESSION_START_TIME = Date.now();
|
|
4075
4091
|
CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
|
|
4076
|
-
versionFluxflow = "1.5.
|
|
4092
|
+
versionFluxflow = "1.5.2";
|
|
4077
4093
|
updatedOn = "2026-05-01";
|
|
4078
4094
|
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
4095
|
CommandMenu,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fluxflow-cli",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
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",
|