fluxflow-cli 1.6.7 → 1.7.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/README.md +5 -2
- package/TOOLS.md +9 -0
- package/dist/fluxflow.js +214 -48
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -32,8 +32,11 @@ Experience a terminal UI that feels alive. Built with **Ink** and **React**, Flu
|
|
|
32
32
|
### 👁️ **Native Multimodality**
|
|
33
33
|
Flux Flow can now see! Use the `view_file` tool to analyze images (JPG, PNG) or deep-dive into PDF technical papers. The agent extracts high-fidelity visual context natively, making it a true multimodal companion.
|
|
34
34
|
|
|
35
|
-
### 📑 **
|
|
36
|
-
Need a report? Just ask. Flux Flow
|
|
35
|
+
### 📑 **Document Engineering Suite**
|
|
36
|
+
Need a report or a presentation? Just ask. Flux Flow features a high-fidelity "Printing Press" that generates professional, branded documents natively:
|
|
37
|
+
- **PDF**: Branded documents from HTML/CSS with automatic watermarking.
|
|
38
|
+
- **DOCX**: Native Word documents with multi-page support and automatic numbering.
|
|
39
|
+
- **PPTX**: High-fidelity PowerPoint presentations using native elements (selectable text, shapes) translated directly from HTML.
|
|
37
40
|
|
|
38
41
|
### 🚑 **Self-Healing Infrastructure**
|
|
39
42
|
Zero setup means zero setup. On first run, Flux Flow performs an integrity check and autonomously installs its own Chromium engine if needed, ensuring features like PDF generation work 100% of the time without manual intervention.
|
package/TOOLS.md
CHANGED
|
@@ -9,6 +9,8 @@ Flux Flow provides a robust set of tools that allow the AI to interact with the
|
|
|
9
9
|
| **Web Search** | ✅ | ✅ |
|
|
10
10
|
| **Web Scrape** | ✅ | ✅ |
|
|
11
11
|
| **Write PDF** | ✅ | ✅ |
|
|
12
|
+
| **Write DOCX** | ✅ | ✅ |
|
|
13
|
+
| **Write PPTX** | ✅ | ✅ |
|
|
12
14
|
| **View/Read Files** | ✅ | ❌ |
|
|
13
15
|
| **Write/Update Files** | ✅ | ❌ |
|
|
14
16
|
| **Execute Commands** | ✅ | ❌ |
|
|
@@ -20,7 +22,14 @@ Flux Flow provides a robust set of tools that allow the AI to interact with the
|
|
|
20
22
|
### 🌐 Web & Research
|
|
21
23
|
- **`web_search`**: Uses DuckDuckGo to find up-to-date information on the internet. Crucial for answering questions about recent events or unlearned documentation.
|
|
22
24
|
- **`web_scrape`**: Extracts the detailed text content from a specific URL, allowing the agent to read documentation or articles.
|
|
25
|
+
|
|
26
|
+
### 📄 Document Engineering (The Office Suite)
|
|
23
27
|
- **`write_pdf`**: Generates high-fidelity, branded PDF documents from HTML/CSS. Features automatic watermarking and page-aware layout management.
|
|
28
|
+
- **`write_docx`**: Generates professional Word documents (.docx) from HTML. Supports multi-page layouts, automatic page numbering, and native styling.
|
|
29
|
+
- **`write_pptx`**: Generates high-fidelity, native PowerPoint presentations (.pptx) from HTML.
|
|
30
|
+
- **Native Translation**: Uses `html2pptxgenjs` to translate HTML tags (`<h1>`, `<ul>`, `<b>`) directly into selectable PowerPoint text objects.
|
|
31
|
+
- **Rich Styling**: Supports CSS-like properties (color, font-size, text-align) for professional slide design.
|
|
32
|
+
- **Flat Protocol**: Optimized for AI stability using a flat HTML string interface.
|
|
24
33
|
|
|
25
34
|
### 📁 File System Operations
|
|
26
35
|
- **`list_files`**: Lists the contents of a directory to help the agent understand the project structure.
|
package/dist/fluxflow.js
CHANGED
|
@@ -670,10 +670,12 @@ var init_main_tools = __esm({
|
|
|
670
670
|
You have access to internal tools. To call a tool, you MUST use the following exact syntax on a new line:
|
|
671
671
|
tool:functions.tool_name(arguments)
|
|
672
672
|
|
|
673
|
+
- USER COMMUNICATION TOOLS (Available in Flux & Flow) -
|
|
674
|
+
1. Ask User: tool:functions.ask(question="...", optionA="Option::Desc", optionB="Option::Desc"). Generally use this tool for ANY ambiguity. Mandatory triggers include: 1) **Path Divergence**: When multiple architectural or technical solutions exist, present options via 'ask' instead of choosing arbitrarily. 2) **Security Boundaries**: Explicitly request permission via 'ask' before accessing sensitive files (e.g., .env, config keys, credentials). 3) **Ambiguity Resolution**: Use 'ask' to clarify vague prompts before executing terminal commands or writing code. 4) **Risk Mitigation**: Require a 'Yes/No' confirmation for any destructive or irreversible operations. Options must always follow the 'Short Label::Detailed Description' format. This tool is a non-terminating suspension so you can get guidance without losing context. PREFER USING THIS TOOL RATHER THAN FINISHING THE LOOP FOR USER CLARIFICATION.
|
|
675
|
+
|
|
673
676
|
- WEB TOOLS (Available in Flux & Flow) -
|
|
674
677
|
1. Web Search: tool:functions.web_search(query="<query>", limit=number). Find info. limit is optional (3-10, default 10). If user asks about something that is not in your training data, proactively use this tool to find the information.Winder search recomemded (limit = 10) when exploring a topic.
|
|
675
678
|
2. Web Scrape: tool:functions.web_scrape(url="<url>"). provides detail from a URL.
|
|
676
|
-
3. Ask User: tool:functions.ask(question="...", optionA="Option::Desc", optionB="Option::Desc"). Generally use this tool for ANY ambiguity. Mandatory triggers include: 1) **Path Divergence**: When multiple architectural or technical solutions exist, present options via 'ask' instead of choosing arbitrarily. 2) **Security Boundaries**: Explicitly request permission via 'ask' before accessing sensitive files (e.g., .env, config keys, credentials). 3) **Ambiguity Resolution**: Use 'ask' to clarify vague prompts before executing terminal commands or writing code. 4) **Risk Mitigation**: Require a 'Yes/No' confirmation for any destructive or irreversible operations. Options must always follow the 'Short Label::Detailed Description' format. This tool is a non-terminating suspension so you can get guidance without losing context. PREFER USING THIS TOOL RATHER THAN FINISHING THE LOOP FOR USER CLARIFICATION.
|
|
677
679
|
${mode === "Flux" ? `
|
|
678
680
|
- DEV & FILE TOOLS (Available in FLUX MODE ONLY) -
|
|
679
681
|
1. View File: tool:functions.view_file(path="relative/path", start_line=number, end_line=number). Reads file content. Auto-truncates at 500 lines unless start_line and end_line are provided. You can also use this tool to read images & documents.
|
|
@@ -682,7 +684,14 @@ ${mode === "Flux" ? `
|
|
|
682
684
|
4. Write File: tool:functions.write_file(path="path", content="content"). Creates/Overwrites. NO CODE BLOCKS. RETURNS: Disk verification + original content (if overwritten) for 100% reversibility. Escape your double quotes '"' using backslash.
|
|
683
685
|
5. Update File: tool:functions.update_file(path="relative/path", content_to_replace="old", content_to_add="new"). Surgical patching. RETURNS: High-fidelity visual diff and old code block. You MUST verify that the change specifically matches your intent using the returned diff. PREFFER UPDATE FILE OVER WRITE FILE if file already exists for better reversal tracking (if a file has 500+ lines, try to stick with update_file over full-rewrite). DONT WRAP UPDATE FILE CALL CONTENT IN MARKDOWN CODE BLOCKS.
|
|
684
686
|
6. Write PDF: tool:functions.write_pdf(path="path", content="<html/css content>", orientation="portrait/landscape"). Generates a professional PDF document. Orientation are optional. A4 size page will be used, so any multi-page PDFs calculate your alightment and page breaks to not mess up A4 page layout. DO NOT ADD FOOTER MANUALLY, the system will handle it automatically. USE CSS TO VISUALLY BEAUTIFY THE DOCUMENT, USE full 100vh & 100vw for page area. ENSURE THE CONTENT IS NEVER BROKEN IN BETWEEN PAGES, USE PAGE BREAKS PROACTIVELY FOR A A4 PAGE LAYOUT. Keep generous margins for better redability.
|
|
685
|
-
7.
|
|
687
|
+
7. Write DOCX: tool:functions.write_docx(path="path", content="<html content>"). Generates a professional Word document (.docx) from HTML. You can make multiple pages. Default Page dimentions will be A4, use proper margins and page break strategy.
|
|
688
|
+
8. Write PPTX: tool:functions.write_pptx(path="path", content="<h1 style='color: #0088CC;'>Title</h1><ul style='font-size: 14pt;'><li>Point A</li></ul>
|
|
689
|
+
---
|
|
690
|
+
<p align='center'>Styled Slide</p>"). Generates a professional PowerPoint presentation (.pptx) from a flat HTML string. Use '---' on a new line to separate slides. Aspect Ratio is 4:3.
|
|
691
|
+
- Supported Tags: <a>, <b>, <br>, <del>, <font>, <h1>-<h6>, <i>, <ol>, <ul>, <li>, <p>, <pre>, <s>, <sub>, <sup>, <u>.
|
|
692
|
+
- Supported Styles: background-color, color, font-family, font-size (use 'pt'), font-style (italic), font-weight (bold), margin, text-align, text-shadow.
|
|
693
|
+
- High-fidelity conversion to native PPTX text.
|
|
694
|
+
9. Execution: tool:functions.exec_command(command="terminal command"). Runs a shell command. Use ask tool to confirm before executing any destructive or irreversible operations.
|
|
686
695
|
|
|
687
696
|
AFTER GETTING THE TOOL RESULT, YOU MUST VERIFY THAT ITS A SUCCESS, IF IT GIVES A ERROR, TELL THE USER AND TRY TO FIX IF YOU CAN. DO NOT HALLUCINATE SUCCESS IF TOOL RETURNS ERROR.
|
|
688
697
|
|
|
@@ -1080,6 +1089,40 @@ var init_arg_parser = __esm({
|
|
|
1080
1089
|
} catch (e) {
|
|
1081
1090
|
value = value.replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\`/g, "`").replace(/\\\\/g, "\\");
|
|
1082
1091
|
}
|
|
1092
|
+
} else if (i < argsString.length && argsString[i] === "[") {
|
|
1093
|
+
let balance = 0;
|
|
1094
|
+
let inString = null;
|
|
1095
|
+
let start = i;
|
|
1096
|
+
let end = -1;
|
|
1097
|
+
for (let j = i; j < argsString.length; j++) {
|
|
1098
|
+
const char = argsString[j];
|
|
1099
|
+
if (!inString && (char === '"' || char === "'" || char === "`")) {
|
|
1100
|
+
inString = char;
|
|
1101
|
+
} else if (inString && char === inString && argsString[j - 1] !== "\\") {
|
|
1102
|
+
inString = null;
|
|
1103
|
+
}
|
|
1104
|
+
if (!inString) {
|
|
1105
|
+
if (char === "[") balance++;
|
|
1106
|
+
else if (char === "]") balance--;
|
|
1107
|
+
if (balance === 0) {
|
|
1108
|
+
end = j;
|
|
1109
|
+
break;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
if (end !== -1) {
|
|
1114
|
+
value = argsString.substring(start, end + 1);
|
|
1115
|
+
i = end + 1;
|
|
1116
|
+
try {
|
|
1117
|
+
let normalized = value.trim();
|
|
1118
|
+
if (normalized.startsWith("'") || normalized.includes("'")) {
|
|
1119
|
+
}
|
|
1120
|
+
} catch (e) {
|
|
1121
|
+
}
|
|
1122
|
+
} else {
|
|
1123
|
+
value = argsString.substring(start);
|
|
1124
|
+
i = argsString.length;
|
|
1125
|
+
}
|
|
1083
1126
|
} else {
|
|
1084
1127
|
let endMatch = argsString.substring(i).match(/([^,\s\)]+)/);
|
|
1085
1128
|
if (endMatch) {
|
|
@@ -1923,7 +1966,7 @@ var init_write_pdf = __esm({
|
|
|
1923
1966
|
});
|
|
1924
1967
|
const pdfDoc = await PDFDocument.load(pdfBytes);
|
|
1925
1968
|
const fileName = path13.basename(targetPath);
|
|
1926
|
-
pdfDoc.setTitle(`
|
|
1969
|
+
pdfDoc.setTitle(`FluxFlow_${fileName}`);
|
|
1927
1970
|
pdfDoc.setAuthor("FluxFlow CLI");
|
|
1928
1971
|
pdfDoc.setSubject("Generated with Agentic AI System");
|
|
1929
1972
|
pdfDoc.setKeywords(["FluxFlow", "AI", "Agentic", "Automated"]);
|
|
@@ -1942,6 +1985,121 @@ var init_write_pdf = __esm({
|
|
|
1942
1985
|
}
|
|
1943
1986
|
});
|
|
1944
1987
|
|
|
1988
|
+
// src/tools/write_docx.js
|
|
1989
|
+
import fs14 from "fs-extra";
|
|
1990
|
+
import path14 from "path";
|
|
1991
|
+
import HTMLtoDOCX from "html-to-docx";
|
|
1992
|
+
var write_docx;
|
|
1993
|
+
var init_write_docx = __esm({
|
|
1994
|
+
"src/tools/write_docx.js"() {
|
|
1995
|
+
init_arg_parser();
|
|
1996
|
+
write_docx = async (args) => {
|
|
1997
|
+
const {
|
|
1998
|
+
path: targetPath,
|
|
1999
|
+
content
|
|
2000
|
+
} = parseArgs(args);
|
|
2001
|
+
if (!targetPath) return 'ERROR: Missing "path" argument for write_docx.';
|
|
2002
|
+
if (!content) return 'ERROR: Missing "content" (HTML) for write_docx.';
|
|
2003
|
+
const absolutePath = path14.resolve(process.cwd(), targetPath);
|
|
2004
|
+
try {
|
|
2005
|
+
await fs14.ensureDir(path14.dirname(absolutePath));
|
|
2006
|
+
const fileName = path14.basename(targetPath);
|
|
2007
|
+
const fullHtml = content.includes("<html") ? content : `
|
|
2008
|
+
<!DOCTYPE html>
|
|
2009
|
+
<html lang="en">
|
|
2010
|
+
<head>
|
|
2011
|
+
<meta charset="UTF-8">
|
|
2012
|
+
<title>FluxFlow Document</title>
|
|
2013
|
+
</head>
|
|
2014
|
+
<body>
|
|
2015
|
+
${content}
|
|
2016
|
+
</body>
|
|
2017
|
+
</html>
|
|
2018
|
+
`;
|
|
2019
|
+
const docxBuffer = await HTMLtoDOCX(fullHtml, null, {
|
|
2020
|
+
title: `FluxFlow_${fileName}`,
|
|
2021
|
+
creator: "FluxFlow CLI",
|
|
2022
|
+
description: "Generated by Agentic AI System",
|
|
2023
|
+
table: { row: { cantSplit: true } },
|
|
2024
|
+
footer: true,
|
|
2025
|
+
pageNumber: true
|
|
2026
|
+
});
|
|
2027
|
+
await fs14.writeFile(absolutePath, docxBuffer);
|
|
2028
|
+
return `SUCCESS: Word document [${targetPath}] generated successfully.
|
|
2029
|
+
- Size: ${(docxBuffer.length / 1024).toFixed(1)} KB`;
|
|
2030
|
+
} catch (err) {
|
|
2031
|
+
return `ERROR: Failed to generate DOCX [${targetPath}]: ${err.message}`;
|
|
2032
|
+
}
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
});
|
|
2036
|
+
|
|
2037
|
+
// src/tools/write_pptx.js
|
|
2038
|
+
import fs15 from "fs-extra";
|
|
2039
|
+
import path15 from "path";
|
|
2040
|
+
import pptxgen from "pptxgenjs";
|
|
2041
|
+
import html2pptxgenjs from "html2pptxgenjs";
|
|
2042
|
+
var write_pptx;
|
|
2043
|
+
var init_write_pptx = __esm({
|
|
2044
|
+
"src/tools/write_pptx.js"() {
|
|
2045
|
+
init_arg_parser();
|
|
2046
|
+
write_pptx = async (args) => {
|
|
2047
|
+
let {
|
|
2048
|
+
path: targetPath,
|
|
2049
|
+
content = "",
|
|
2050
|
+
title = "Autonomous Agent Report"
|
|
2051
|
+
} = parseArgs(args);
|
|
2052
|
+
if (!targetPath) return 'ERROR: Missing "path" argument for write_pptx.';
|
|
2053
|
+
if (!content) return 'ERROR: No "content" (HTML) provided for write_pptx.';
|
|
2054
|
+
const absolutePath = path15.resolve(process.cwd(), targetPath);
|
|
2055
|
+
try {
|
|
2056
|
+
await fs15.ensureDir(path15.dirname(absolutePath));
|
|
2057
|
+
const PptxConstructor = typeof pptxgen === "function" ? pptxgen : pptxgen.default;
|
|
2058
|
+
if (!PptxConstructor) throw new Error("Could not find PptxGenJS constructor in module");
|
|
2059
|
+
const pres = new PptxConstructor();
|
|
2060
|
+
pres.layout = "LAYOUT_4x3";
|
|
2061
|
+
pres.title = title;
|
|
2062
|
+
pres.author = "FluxFlow CLI";
|
|
2063
|
+
pres.company = "Generated with Agentic AI System";
|
|
2064
|
+
const slideBlocks = content.split(/---/).filter((s) => s.trim().length > 0);
|
|
2065
|
+
if (slideBlocks.length === 0) {
|
|
2066
|
+
slideBlocks.push(content);
|
|
2067
|
+
}
|
|
2068
|
+
for (let i = 0; i < slideBlocks.length; i++) {
|
|
2069
|
+
const slide = pres.addSlide();
|
|
2070
|
+
const htmlSnippet = slideBlocks[i].trim();
|
|
2071
|
+
try {
|
|
2072
|
+
const items = html2pptxgenjs.htmlToPptxText(htmlSnippet);
|
|
2073
|
+
slide.addText(items, {
|
|
2074
|
+
x: 0.5,
|
|
2075
|
+
y: 0.5,
|
|
2076
|
+
w: "90%",
|
|
2077
|
+
h: "90%",
|
|
2078
|
+
valign: "top",
|
|
2079
|
+
fontSize: 18,
|
|
2080
|
+
color: "363636"
|
|
2081
|
+
});
|
|
2082
|
+
} catch (err) {
|
|
2083
|
+
slide.addText(htmlSnippet.replace(/<[^>]*>/g, ""), {
|
|
2084
|
+
x: 0.5,
|
|
2085
|
+
y: 0.5,
|
|
2086
|
+
w: "90%",
|
|
2087
|
+
h: "90%",
|
|
2088
|
+
fontSize: 14
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
const buffer = await pres.write({ outputType: "nodebuffer" });
|
|
2093
|
+
await fs15.writeFile(absolutePath, buffer);
|
|
2094
|
+
return `SUCCESS: Native HTML-to-PPTX [${targetPath}] generated with ${slideBlocks.length} slides.
|
|
2095
|
+
- Size: ${(buffer.length / 1024).toFixed(1)} KB`;
|
|
2096
|
+
} catch (err) {
|
|
2097
|
+
return `ERROR: Failed to generate native HTML-to-PPTX [${targetPath}]: ${err.message}`;
|
|
2098
|
+
}
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
});
|
|
2102
|
+
|
|
1945
2103
|
// src/utils/tools.js
|
|
1946
2104
|
var TOOL_MAP, dispatchTool;
|
|
1947
2105
|
var init_tools = __esm({
|
|
@@ -1958,6 +2116,8 @@ var init_tools = __esm({
|
|
|
1958
2116
|
init_read_folder();
|
|
1959
2117
|
init_ask_user();
|
|
1960
2118
|
init_write_pdf();
|
|
2119
|
+
init_write_docx();
|
|
2120
|
+
init_write_pptx();
|
|
1961
2121
|
TOOL_MAP = {
|
|
1962
2122
|
web_search,
|
|
1963
2123
|
web_scrape,
|
|
@@ -1970,6 +2130,8 @@ var init_tools = __esm({
|
|
|
1970
2130
|
exec_command,
|
|
1971
2131
|
read_folder,
|
|
1972
2132
|
write_pdf,
|
|
2133
|
+
write_docx,
|
|
2134
|
+
write_pptx,
|
|
1973
2135
|
ask: ask_user
|
|
1974
2136
|
};
|
|
1975
2137
|
dispatchTool = async (toolName, args, context = {}) => {
|
|
@@ -2010,8 +2172,8 @@ var init_terminal = __esm({
|
|
|
2010
2172
|
|
|
2011
2173
|
// src/utils/ai.js
|
|
2012
2174
|
import { GoogleGenAI, ThinkingLevel } from "@google/genai";
|
|
2013
|
-
import
|
|
2014
|
-
import
|
|
2175
|
+
import path16 from "path";
|
|
2176
|
+
import fs16 from "fs";
|
|
2015
2177
|
var client, TERMINATION_SIGNAL, signalTermination, detectToolCalls, initAI, getAIStream;
|
|
2016
2178
|
var init_ai = __esm({
|
|
2017
2179
|
"src/utils/ai.js"() {
|
|
@@ -2177,9 +2339,9 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
2177
2339
|
} catch (err) {
|
|
2178
2340
|
const errMsg = err.status || err.error && err.error.message || String(err);
|
|
2179
2341
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
2180
|
-
const agentErrDir =
|
|
2181
|
-
if (!
|
|
2182
|
-
|
|
2342
|
+
const agentErrDir = path16.join(LOGS_DIR, "agent");
|
|
2343
|
+
if (!fs16.existsSync(agentErrDir)) fs16.mkdirSync(agentErrDir, { recursive: true });
|
|
2344
|
+
fs16.appendFileSync(path16.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errMsg}
|
|
2183
2345
|
`);
|
|
2184
2346
|
if (retryCount < MAX_RETRIES) {
|
|
2185
2347
|
retryCount++;
|
|
@@ -2243,9 +2405,9 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
2243
2405
|
let totalLines = "...";
|
|
2244
2406
|
let actualEndLine = end_line;
|
|
2245
2407
|
try {
|
|
2246
|
-
const absPath =
|
|
2247
|
-
if (
|
|
2248
|
-
const content =
|
|
2408
|
+
const absPath = path16.resolve(process.cwd(), targetPath2);
|
|
2409
|
+
if (fs16.existsSync(absPath)) {
|
|
2410
|
+
const content = fs16.readFileSync(absPath, "utf8");
|
|
2249
2411
|
const lines = content.split("\n").length;
|
|
2250
2412
|
totalLines = lines;
|
|
2251
2413
|
actualEndLine = Math.min(end_line, lines);
|
|
@@ -2270,6 +2432,10 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
2270
2432
|
label = `\u{1F4BE} ${action} FILE: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
2271
2433
|
} else if (toolCall.toolName === "write_pdf") {
|
|
2272
2434
|
label = `\u{1F4D1} GENERATING PDF: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
2435
|
+
} else if (toolCall.toolName === "write_docx") {
|
|
2436
|
+
label = `\u{1F4DD} GENERATING DOCX: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
2437
|
+
} else if (toolCall.toolName === "write_pptx") {
|
|
2438
|
+
label = `\u{1F4CA} GENERATING PPTX: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
2273
2439
|
} else if (toolCall.toolName === "exec_command" || toolCall.toolName === "ask") {
|
|
2274
2440
|
label = "";
|
|
2275
2441
|
} else {
|
|
@@ -2304,7 +2470,7 @@ ${boxBottom}
|
|
|
2304
2470
|
/\/usr\//
|
|
2305
2471
|
// Sensitive Linux paths
|
|
2306
2472
|
];
|
|
2307
|
-
const currentDrive =
|
|
2473
|
+
const currentDrive = path16.resolve(process.cwd()).substring(0, 3).toLowerCase();
|
|
2308
2474
|
const isViolating = riskyPatterns.some((pattern) => {
|
|
2309
2475
|
if (pattern.source === "[a-zA-Z]:[\\\\\\/]") {
|
|
2310
2476
|
const driveMatch = command.match(/[a-zA-Z]:[\\\/]/i);
|
|
@@ -2326,8 +2492,8 @@ ${boxBottom}
|
|
|
2326
2492
|
const targetPath = parsedArgs.path || parsedArgs.targetPath || null;
|
|
2327
2493
|
if (targetPath) {
|
|
2328
2494
|
const isExternalOff = settings.systemSettings && settings.systemSettings.allowExternalAccess === false;
|
|
2329
|
-
const absoluteTarget =
|
|
2330
|
-
const absoluteCwd =
|
|
2495
|
+
const absoluteTarget = path16.resolve(targetPath);
|
|
2496
|
+
const absoluteCwd = path16.resolve(process.cwd());
|
|
2331
2497
|
if (isExternalOff && !absoluteTarget.startsWith(absoluteCwd)) {
|
|
2332
2498
|
const denyMsg = `Access Denied. You are not allowed to access files outside the current workspace. To enable this, ask the user to turn on "External Workspace Access" in /settings.`;
|
|
2333
2499
|
toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
|
|
@@ -2372,11 +2538,11 @@ ${boxBottom}
|
|
|
2372
2538
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
2373
2539
|
const isErr = result.startsWith("ERROR:");
|
|
2374
2540
|
const logStatus = isErr ? result.trim() : "SUCCESS";
|
|
2375
|
-
const toolHistDir =
|
|
2376
|
-
if (!
|
|
2377
|
-
|
|
2541
|
+
const toolHistDir = path16.join(LOGS_DIR, "tools");
|
|
2542
|
+
if (!fs16.existsSync(toolHistDir)) {
|
|
2543
|
+
fs16.mkdirSync(toolHistDir, { recursive: true });
|
|
2378
2544
|
}
|
|
2379
|
-
|
|
2545
|
+
fs16.appendFileSync(path16.join(toolHistDir, "history.log"), `HISTORY [${timestamp}]: ${toolCall.toolName} [${logStatus}]
|
|
2380
2546
|
`);
|
|
2381
2547
|
} catch (logErr) {
|
|
2382
2548
|
}
|
|
@@ -2438,11 +2604,11 @@ ${boxBottom}
|
|
|
2438
2604
|
if (parts && parts[1]?.text) {
|
|
2439
2605
|
finalSynthesis = parts[1].text;
|
|
2440
2606
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
2441
|
-
const janitorLogDir =
|
|
2442
|
-
if (!
|
|
2443
|
-
|
|
2607
|
+
const janitorLogDir = path16.join(LOGS_DIR, "janitor");
|
|
2608
|
+
if (!fs16.existsSync(janitorLogDir)) {
|
|
2609
|
+
fs16.mkdirSync(janitorLogDir, { recursive: true });
|
|
2444
2610
|
}
|
|
2445
|
-
|
|
2611
|
+
fs16.appendFileSync(path16.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: ${finalSynthesis}
|
|
2446
2612
|
`);
|
|
2447
2613
|
} else if (parts && parts[0]?.text) finalSynthesis = parts[0].text;
|
|
2448
2614
|
else if (janitorResult.response && janitorResult.response.text) finalSynthesis = janitorResult.response.text();
|
|
@@ -2454,8 +2620,8 @@ ${boxBottom}
|
|
|
2454
2620
|
const toolContext = { chatId, sessionId: chatId, history };
|
|
2455
2621
|
const result = await dispatchTool(janitorToolCall.toolName, janitorToolCall.args, toolContext);
|
|
2456
2622
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
2457
|
-
const janitorLogDir =
|
|
2458
|
-
|
|
2623
|
+
const janitorLogDir = path16.join(LOGS_DIR, "janitor");
|
|
2624
|
+
fs16.appendFileSync(path16.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${janitorToolCall.toolName}]: ${result}
|
|
2459
2625
|
`);
|
|
2460
2626
|
if (janitorToolCall.toolName === "memory" && !janitorToolCall.args.includes("action='temp'")) {
|
|
2461
2627
|
yield { type: "memory_updated" };
|
|
@@ -2463,11 +2629,11 @@ ${boxBottom}
|
|
|
2463
2629
|
}
|
|
2464
2630
|
} catch (janitorErr) {
|
|
2465
2631
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
2466
|
-
const janitorErrDir =
|
|
2467
|
-
if (!
|
|
2468
|
-
|
|
2632
|
+
const janitorErrDir = path16.join(LOGS_DIR, "janitor");
|
|
2633
|
+
if (!fs16.existsSync(janitorErrDir)) {
|
|
2634
|
+
fs16.mkdirSync(janitorErrDir, { recursive: true });
|
|
2469
2635
|
}
|
|
2470
|
-
|
|
2636
|
+
fs16.appendFileSync(path16.join(janitorErrDir, "error.log"), `ERROR [${date}]: ${janitorErr.message}
|
|
2471
2637
|
`);
|
|
2472
2638
|
console.error("Janitor Background Tasks Failed:", janitorErr.message);
|
|
2473
2639
|
}
|
|
@@ -2496,8 +2662,8 @@ ${timestamp}`;
|
|
|
2496
2662
|
});
|
|
2497
2663
|
|
|
2498
2664
|
// src/utils/settings.js
|
|
2499
|
-
import
|
|
2500
|
-
import
|
|
2665
|
+
import fs17 from "fs-extra";
|
|
2666
|
+
import path17 from "path";
|
|
2501
2667
|
var DEFAULT_SETTINGS, loadSettings, migrateToExternal, saveSettings;
|
|
2502
2668
|
var init_settings = __esm({
|
|
2503
2669
|
"src/utils/settings.js"() {
|
|
@@ -2532,8 +2698,8 @@ var init_settings = __esm({
|
|
|
2532
2698
|
};
|
|
2533
2699
|
loadSettings = async () => {
|
|
2534
2700
|
try {
|
|
2535
|
-
if (await
|
|
2536
|
-
const saved = await
|
|
2701
|
+
if (await fs17.exists(SETTINGS_FILE)) {
|
|
2702
|
+
const saved = await fs17.readJson(SETTINGS_FILE);
|
|
2537
2703
|
return {
|
|
2538
2704
|
...DEFAULT_SETTINGS,
|
|
2539
2705
|
...saved,
|
|
@@ -2551,12 +2717,12 @@ var init_settings = __esm({
|
|
|
2551
2717
|
const { FLUXFLOW_DIR: FLUXFLOW_DIR2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
2552
2718
|
const folders = ["logs", "secret"];
|
|
2553
2719
|
for (const folder of folders) {
|
|
2554
|
-
const src =
|
|
2555
|
-
const dest =
|
|
2720
|
+
const src = path17.join(FLUXFLOW_DIR2, folder);
|
|
2721
|
+
const dest = path17.join(newPath, folder);
|
|
2556
2722
|
try {
|
|
2557
|
-
if (await
|
|
2558
|
-
await
|
|
2559
|
-
await
|
|
2723
|
+
if (await fs17.exists(src)) {
|
|
2724
|
+
await fs17.ensureDir(dest);
|
|
2725
|
+
await fs17.copy(src, dest, { overwrite: true });
|
|
2560
2726
|
}
|
|
2561
2727
|
} catch (err) {
|
|
2562
2728
|
console.error(`Migration failed for ${folder}:`, err);
|
|
@@ -2570,8 +2736,8 @@ var init_settings = __esm({
|
|
|
2570
2736
|
await migrateToExternal(settings.systemSettings.externalDataPath);
|
|
2571
2737
|
}
|
|
2572
2738
|
const updated = { ...current, ...settings };
|
|
2573
|
-
await
|
|
2574
|
-
await
|
|
2739
|
+
await fs17.ensureDir(path17.dirname(SETTINGS_FILE));
|
|
2740
|
+
await fs17.writeJson(SETTINGS_FILE, updated, { spaces: 2 });
|
|
2575
2741
|
return true;
|
|
2576
2742
|
} catch (err) {
|
|
2577
2743
|
console.error("Failed to save settings:", err);
|
|
@@ -2724,7 +2890,7 @@ var init_UpdateProcessor = __esm({
|
|
|
2724
2890
|
import puppeteer4 from "puppeteer";
|
|
2725
2891
|
import { exec as exec2 } from "child_process";
|
|
2726
2892
|
import { promisify } from "util";
|
|
2727
|
-
import
|
|
2893
|
+
import fs18 from "fs";
|
|
2728
2894
|
var execAsync, checkPuppeteerReady, installPuppeteerBrowser;
|
|
2729
2895
|
var init_setup = __esm({
|
|
2730
2896
|
"src/utils/setup.js"() {
|
|
@@ -2732,7 +2898,7 @@ var init_setup = __esm({
|
|
|
2732
2898
|
checkPuppeteerReady = () => {
|
|
2733
2899
|
try {
|
|
2734
2900
|
const exePath = puppeteer4.executablePath();
|
|
2735
|
-
const exists = exePath &&
|
|
2901
|
+
const exists = exePath && fs18.existsSync(exePath);
|
|
2736
2902
|
if (exists) return true;
|
|
2737
2903
|
} catch (e) {
|
|
2738
2904
|
return false;
|
|
@@ -2765,7 +2931,7 @@ __export(app_exports, {
|
|
|
2765
2931
|
import React10, { useState as useState7, useEffect as useEffect5, useRef as useRef2, useMemo } from "react";
|
|
2766
2932
|
import { Box as Box10, Text as Text10, useInput as useInput4, useStdout } from "ink";
|
|
2767
2933
|
import Spinner2 from "ink-spinner";
|
|
2768
|
-
import
|
|
2934
|
+
import fs19 from "fs-extra";
|
|
2769
2935
|
import { exec as exec3 } from "child_process";
|
|
2770
2936
|
import { MultilineInput } from "ink-multiline-input";
|
|
2771
2937
|
import TextInput3 from "ink-text-input";
|
|
@@ -3310,12 +3476,12 @@ ${list || "No saved chats found."}`, isMeta: true }];
|
|
|
3310
3476
|
setCompletedIndex(prev.length + 1);
|
|
3311
3477
|
return [...prev, { id: Date.now(), role: "system", text: "\u2622\uFE0F [NUCLEAR] Initiating reset...", isMeta: true }];
|
|
3312
3478
|
});
|
|
3313
|
-
if (
|
|
3314
|
-
if (
|
|
3315
|
-
if (
|
|
3479
|
+
if (fs19.existsSync(LOGS_DIR)) fs19.removeSync(LOGS_DIR);
|
|
3480
|
+
if (fs19.existsSync(SECRET_DIR)) fs19.removeSync(SECRET_DIR);
|
|
3481
|
+
if (fs19.existsSync(SETTINGS_FILE)) fs19.removeSync(SETTINGS_FILE);
|
|
3316
3482
|
try {
|
|
3317
|
-
const items =
|
|
3318
|
-
if (items.length === 0)
|
|
3483
|
+
const items = fs19.readdirSync(FLUXFLOW_DIR);
|
|
3484
|
+
if (items.length === 0) fs19.removeSync(FLUXFLOW_DIR);
|
|
3319
3485
|
} catch (e) {
|
|
3320
3486
|
}
|
|
3321
3487
|
setTimeout(() => {
|
|
@@ -4349,7 +4515,7 @@ var init_app = __esm({
|
|
|
4349
4515
|
init_setup();
|
|
4350
4516
|
SESSION_START_TIME = Date.now();
|
|
4351
4517
|
CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
|
|
4352
|
-
versionFluxflow = "1.
|
|
4518
|
+
versionFluxflow = "1.7.0";
|
|
4353
4519
|
updatedOn = "2026-05-03";
|
|
4354
4520
|
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(
|
|
4355
4521
|
CommandMenu,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fluxflow-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "A high-fidelity agentic terminal assistant for the Flux Era.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -34,13 +34,15 @@
|
|
|
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:nanoid --external:puppeteer --external:pdf-lib --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:pdf-lib --external:html-to-docx --external:pptxgenjs --external:html2pptxgenjs --external:typescript"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@google/genai": "^1.50.1",
|
|
41
41
|
"chalk": "^5.6.2",
|
|
42
42
|
"fs-extra": "^11.3.4",
|
|
43
43
|
"gradient-string": "^3.0.0",
|
|
44
|
+
"html-to-docx": "^1.8.0",
|
|
45
|
+
"html2pptxgenjs": "^0.0.3",
|
|
44
46
|
"ink": "^7.0.1",
|
|
45
47
|
"ink-multiline-input": "^0.1.0",
|
|
46
48
|
"ink-select-input": "^6.2.0",
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
"ink-text-input": "^6.0.0",
|
|
49
51
|
"nanoid": "^5.1.9",
|
|
50
52
|
"pdf-lib": "^1.17.1",
|
|
53
|
+
"pptxgenjs": "^4.0.1",
|
|
51
54
|
"puppeteer": "^24.42.0",
|
|
52
55
|
"react": "^19.2.5",
|
|
53
56
|
"zod": "^4.3.6"
|