fluxflow-cli 1.4.3 → 1.5.1
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/ARCHITECTURE.md +18 -1
- package/README.md +10 -0
- package/TOOLS.md +5 -1
- package/UI_FEATURES.md +18 -1
- package/dist/fluxflow.js +488 -212
- package/package.json +3 -2
package/dist/fluxflow.js
CHANGED
|
@@ -77,7 +77,7 @@ var init_ChatLayout = __esm({
|
|
|
77
77
|
};
|
|
78
78
|
InlineMarkdown = React2.memo(({ text, color }) => {
|
|
79
79
|
if (!text) return null;
|
|
80
|
-
const parts = text.split(/(
|
|
80
|
+
const parts = text.split(/(\*\*.*?\*\*|\*.*?\*|`.*?`|\$.*?\$|\[.*?\]\s*\(.*?\)|\[.*?\]\s*\[.*?\]|https?:\/\/[^\s]+)/g);
|
|
81
81
|
return /* @__PURE__ */ React2.createElement(Text2, { color, wrap: "anywhere" }, parts.map((part, j) => {
|
|
82
82
|
if (!part) return null;
|
|
83
83
|
if (part.startsWith("**") && part.endsWith("**")) {
|
|
@@ -89,9 +89,13 @@ var init_ChatLayout = __esm({
|
|
|
89
89
|
if (part.startsWith("`") && part.endsWith("`")) {
|
|
90
90
|
return /* @__PURE__ */ React2.createElement(Text2, { key: j, color: "cyan", backgroundColor: "#003333" }, " ", part.slice(1, -1), " ");
|
|
91
91
|
}
|
|
92
|
-
if (part.startsWith("
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
if (part.startsWith("$") && part.endsWith("$")) {
|
|
93
|
+
let content = part.slice(1, -1);
|
|
94
|
+
if (content.startsWith("\\text{") && content.endsWith("}")) {
|
|
95
|
+
content = content.slice(6, -1);
|
|
96
|
+
}
|
|
97
|
+
const mathContent = content.replace(/\\multiply/g, "\xD7").replace(/\\divide/g, "\xF7");
|
|
98
|
+
return /* @__PURE__ */ React2.createElement(Text2, { key: j, color: "white", backgroundColor: "#4c0099", bold: true, italic: true }, " ", mathContent, " ");
|
|
95
99
|
}
|
|
96
100
|
if (part.startsWith("[") && (part.includes("](") || part.includes("] ("))) {
|
|
97
101
|
const match = part.match(/\[(.*?)\]\s*\((.*?)\)/);
|
|
@@ -511,15 +515,40 @@ var init_crypto = __esm({
|
|
|
511
515
|
});
|
|
512
516
|
|
|
513
517
|
// src/utils/paths.js
|
|
518
|
+
var paths_exports = {};
|
|
519
|
+
__export(paths_exports, {
|
|
520
|
+
DATA_DIR: () => DATA_DIR,
|
|
521
|
+
FLUXFLOW_DIR: () => FLUXFLOW_DIR,
|
|
522
|
+
HISTORY_FILE: () => HISTORY_FILE,
|
|
523
|
+
LOGS_DIR: () => LOGS_DIR,
|
|
524
|
+
MEMORIES_FILE: () => MEMORIES_FILE,
|
|
525
|
+
SECRET_DIR: () => SECRET_DIR,
|
|
526
|
+
SETTINGS_FILE: () => SETTINGS_FILE,
|
|
527
|
+
TEMP_MEM_FILE: () => TEMP_MEM_FILE,
|
|
528
|
+
USAGE_FILE: () => USAGE_FILE
|
|
529
|
+
});
|
|
514
530
|
import os from "os";
|
|
515
531
|
import path2 from "path";
|
|
516
|
-
|
|
532
|
+
import fs2 from "fs";
|
|
533
|
+
var FLUXFLOW_DIR, SETTINGS_FILE, externalDir, DATA_DIR, LOGS_DIR, SECRET_DIR, HISTORY_FILE, USAGE_FILE, MEMORIES_FILE, TEMP_MEM_FILE;
|
|
517
534
|
var init_paths = __esm({
|
|
518
535
|
"src/utils/paths.js"() {
|
|
519
536
|
FLUXFLOW_DIR = path2.join(os.homedir(), ".fluxflow");
|
|
520
|
-
LOGS_DIR = path2.join(FLUXFLOW_DIR, "logs");
|
|
521
|
-
SECRET_DIR = path2.join(FLUXFLOW_DIR, "secret");
|
|
522
537
|
SETTINGS_FILE = path2.join(FLUXFLOW_DIR, "settings.json");
|
|
538
|
+
externalDir = null;
|
|
539
|
+
try {
|
|
540
|
+
if (fs2.existsSync(SETTINGS_FILE)) {
|
|
541
|
+
const settings = JSON.parse(fs2.readFileSync(SETTINGS_FILE, "utf8"));
|
|
542
|
+
const sys = settings.systemSettings || {};
|
|
543
|
+
if (sys.useExternalData && sys.externalDataPath) {
|
|
544
|
+
externalDir = sys.externalDataPath;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
} catch (e) {
|
|
548
|
+
}
|
|
549
|
+
DATA_DIR = externalDir || FLUXFLOW_DIR;
|
|
550
|
+
LOGS_DIR = path2.join(DATA_DIR, "logs");
|
|
551
|
+
SECRET_DIR = path2.join(DATA_DIR, "secret");
|
|
523
552
|
HISTORY_FILE = path2.join(SECRET_DIR, "history.json");
|
|
524
553
|
USAGE_FILE = path2.join(SECRET_DIR, "usage.json");
|
|
525
554
|
MEMORIES_FILE = path2.join(SECRET_DIR, "memories.json");
|
|
@@ -528,7 +557,7 @@ var init_paths = __esm({
|
|
|
528
557
|
});
|
|
529
558
|
|
|
530
559
|
// src/utils/secrets.js
|
|
531
|
-
import
|
|
560
|
+
import fs3 from "fs-extra";
|
|
532
561
|
import path3 from "path";
|
|
533
562
|
var SECRET_FILE, getAPIKey, saveSecret, saveAPIKey, removeSecret, removeAPIKey;
|
|
534
563
|
var init_secrets = __esm({
|
|
@@ -545,7 +574,7 @@ var init_secrets = __esm({
|
|
|
545
574
|
return null;
|
|
546
575
|
};
|
|
547
576
|
saveSecret = async (key, value) => {
|
|
548
|
-
await
|
|
577
|
+
await fs3.ensureDir(SECRET_DIR);
|
|
549
578
|
let current = readEncryptedJson(SECRET_FILE, {});
|
|
550
579
|
current[key] = value;
|
|
551
580
|
writeEncryptedJson(SECRET_FILE, current);
|
|
@@ -578,12 +607,13 @@ tool:functions.tool_name(arguments)
|
|
|
578
607
|
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.
|
|
579
608
|
${mode === "Flux" ? `
|
|
580
609
|
- DEV & FILE TOOLS (Available in FLUX MODE ONLY) -
|
|
581
|
-
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.
|
|
610
|
+
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.
|
|
582
611
|
2. List Files: tool:functions.list_files(path="relative/path"). Lists content of a directory.
|
|
583
612
|
3. Read Folder: tool:functions.read_folder(path="relative/path"). Detailed stats of a directory.
|
|
584
613
|
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.
|
|
585
614
|
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.
|
|
586
|
-
6.
|
|
615
|
+
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. DO NOT ADD FOOTER MANUALLY, the system will handle it automatically. USE CSS TO VISUALLY BEAUTIFY THE DOCUMENT.
|
|
616
|
+
7. Execution: tool:functions.exec_command(command="terminal command"). Runs a shell command.
|
|
587
617
|
|
|
588
618
|
**NOTE:** WHEN WRITING/UPDATING FILES, USE ACTUAL NEW LINE CHARACTER FOR LINE BREAKS RATHER THAN STRING '\\n'`.trim() : `
|
|
589
619
|
- DEV & FILE TOOLS ARE NOT AVAILABLE IN FLOW MODE. If you need to access files, tell the user to switch to FLUX MODE (manually by user).`.trim()}
|
|
@@ -592,6 +622,7 @@ Results will be provided in the next loop as: [TOOL_RESULT]: [content]
|
|
|
592
622
|
WHEN CALLING TOOLS, YOU **MUST** end your response with '[turn: continue]'. NEVER use '[turn: finish]' in the same turn as a tool call. After receiving the [TOOL_RESULT], acknowledge the output and verify if the goal is met; only then may you use '[turn: finish]', otherwise use '[turn: continue]'.
|
|
593
623
|
Do NOT over-use tools. Use them only when strictly necessary for the user's objective. You can stack multiple tool calls 1-by-1.
|
|
594
624
|
Distinguish clearly between tool discussion and execution. Use the 'tool:' prefix ONLY when calling a function. When discussing tools with the user, refer to them by name as nouns (e.g., 'write_file', 'list_files') to avoid accidental triggers and context bloat. Even in your <think> ... </think> tags, do not use the 'tool:' prefix when planning to select a tool.
|
|
625
|
+
Use tools contextually when needed, don't flood with unnecessary tool calls.
|
|
595
626
|
-- END FUNCTION CALLING PROTOCOL --`.trim();
|
|
596
627
|
}
|
|
597
628
|
});
|
|
@@ -723,28 +754,35 @@ Current date and Time is: ${dateTimeStr}
|
|
|
723
754
|
--- END SYSTEM INSTRUCTION ---`.trim();
|
|
724
755
|
};
|
|
725
756
|
getJanitorInstruction = (originalText, agentRaws, userMemories = "", isMemoryEnabled = true, needTitle = false) => {
|
|
726
|
-
|
|
757
|
+
let agentRes = `${agentRaws.replace(/tool:functions\..*\n/g, "").replace(/<think>.*<\/think>/g, "").replace(/\[Prompted on:.*?\]/g, "").replace(/\[turn: continue\]/g, "").replace(/\[turn: finish\]/g, "").replace(/\[TOOL_RESULTS\]/g, "").replace(/\[tool_results\]/g, "").substring(0, 3500)}`;
|
|
758
|
+
if (agentRes.length > 3500) {
|
|
759
|
+
agentRes += "\n... (truncated) ...";
|
|
760
|
+
}
|
|
761
|
+
let originalTextProcessed = originalText.replace(/\[Prompted on:.*?\]/g, "");
|
|
762
|
+
return `USER PROMPT: ${originalTextProcessed.substring(0, 600)}${originalTextProcessed.length > 600 ? "\n... (truncated) ..." : ""}
|
|
727
763
|
AGENT RAWS (responses from this turn):
|
|
728
|
-
${
|
|
764
|
+
${agentRes}
|
|
765
|
+
${userMemories ? `
|
|
766
|
+
|
|
767
|
+
-- CURRENT PERSISTENT USER MEMORIES --
|
|
768
|
+
${userMemories}
|
|
769
|
+
-------------------------------------------------
|
|
770
|
+
` : ""}
|
|
729
771
|
|
|
730
772
|
--- START SYSTEM INSTRUCTION (STRICT HEADLESS LOGIC WORKER: ZERO USER-FACING TEXT POLICY) ---
|
|
731
|
-
YOU ARE A SILENT BACKGROUND SYSTEM PROCESS. YOU HAVE NO MOUTH. YOUR ONLY OUTPUT MEDIUM IS VALID TOOL
|
|
773
|
+
YOU ARE A SILENT BACKGROUND SYSTEM PROCESS. YOU HAVE NO MOUTH. YOUR ONLY OUTPUT MEDIUM IS VALID TOOL CALLS.
|
|
732
774
|
[CRITICAL RULES]
|
|
733
775
|
1. OUTPUT ONLY 'tool:functions.xxx' CALLS.
|
|
734
|
-
2. DO NOT EXPLAIN. DO NOT
|
|
776
|
+
2. DO NOT EXPLAIN. DO NOT TALK TO THE USER.
|
|
735
777
|
3. NON-TOOL TEXT WILL BREAK THE SYSTEM.
|
|
736
|
-
4. DO NOT REPEAT AGENT RAWS IN YOUR RESPONSE.
|
|
737
|
-
5. IF YOU GET ONLY USER
|
|
778
|
+
4. DO NOT REPEAT AGENT RAWS AND TOOL RESULTS IN YOUR RESPONSE.
|
|
779
|
+
5. IF YOU GET ONLY USER QUERY AND NO AGENT RAWS, THEN JUST USE TEMP MEMORY TO LOG THE SUMMARY OF USER QUERY.
|
|
738
780
|
|
|
739
781
|
YOUR JOB: Analyze the 'User prompt' and 'Agent Raws' to extract facts for long-term memory or handle system tasks.
|
|
740
782
|
${isMemoryEnabled ? `If user tell something that is important (like, hobbies, preferences, facts about user, hates, likes, etc) to know user better over time, use long term memory tools.` : ""}
|
|
741
783
|
|
|
742
784
|
${JANITOR_TOOLS_PROTOCOL(isMemoryEnabled, needTitle)}
|
|
743
785
|
|
|
744
|
-
${userMemories ? `-- CURRENT PERSISTENT USER MEMORIES --
|
|
745
|
-
${userMemories}
|
|
746
|
-
-------------------------------------------------
|
|
747
|
-
` : ""}
|
|
748
786
|
Current date and Time: ${(/* @__PURE__ */ new Date()).toLocaleString()}
|
|
749
787
|
|
|
750
788
|
--- END SYSTEM INSTRUCTION ---`.trim();
|
|
@@ -753,7 +791,7 @@ Current date and Time: ${(/* @__PURE__ */ new Date()).toLocaleString()}
|
|
|
753
791
|
});
|
|
754
792
|
|
|
755
793
|
// src/utils/history.js
|
|
756
|
-
import
|
|
794
|
+
import fs4 from "fs-extra";
|
|
757
795
|
import path4 from "path";
|
|
758
796
|
import { nanoid } from "nanoid";
|
|
759
797
|
var WRITE_LOCK, withLock, loadHistory, saveChat, saveChatTitle, deleteChat, generateChatId, cleanupOldHistory, getTruncatedHistory;
|
|
@@ -776,9 +814,9 @@ var init_history = __esm({
|
|
|
776
814
|
return nextLock;
|
|
777
815
|
};
|
|
778
816
|
loadHistory = async () => {
|
|
779
|
-
if (await
|
|
817
|
+
if (await fs4.pathExists(HISTORY_FILE)) {
|
|
780
818
|
try {
|
|
781
|
-
return await
|
|
819
|
+
return await fs4.readJson(HISTORY_FILE);
|
|
782
820
|
} catch (e) {
|
|
783
821
|
return {};
|
|
784
822
|
}
|
|
@@ -796,8 +834,8 @@ var init_history = __esm({
|
|
|
796
834
|
messages: persistentMessages,
|
|
797
835
|
updatedAt: Date.now()
|
|
798
836
|
};
|
|
799
|
-
await
|
|
800
|
-
await
|
|
837
|
+
await fs4.ensureDir(path4.dirname(HISTORY_FILE));
|
|
838
|
+
await fs4.writeJson(HISTORY_FILE, history, { spaces: 2 });
|
|
801
839
|
});
|
|
802
840
|
};
|
|
803
841
|
saveChatTitle = async (id, title) => {
|
|
@@ -809,15 +847,15 @@ var init_history = __esm({
|
|
|
809
847
|
} else {
|
|
810
848
|
history[id] = { name: title, messages: [], updatedAt: Date.now() };
|
|
811
849
|
}
|
|
812
|
-
await
|
|
813
|
-
await
|
|
850
|
+
await fs4.ensureDir(path4.dirname(HISTORY_FILE));
|
|
851
|
+
await fs4.writeJson(HISTORY_FILE, history, { spaces: 2 });
|
|
814
852
|
});
|
|
815
853
|
};
|
|
816
854
|
deleteChat = async (id) => {
|
|
817
855
|
return withLock(async () => {
|
|
818
856
|
const history = await loadHistory();
|
|
819
857
|
delete history[id];
|
|
820
|
-
await
|
|
858
|
+
await fs4.writeJson(HISTORY_FILE, history, { spaces: 2 });
|
|
821
859
|
const temp = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
822
860
|
if (temp[id]) {
|
|
823
861
|
delete temp[id];
|
|
@@ -856,7 +894,7 @@ var init_history = __esm({
|
|
|
856
894
|
});
|
|
857
895
|
|
|
858
896
|
// src/utils/usage.js
|
|
859
|
-
import
|
|
897
|
+
import fs5 from "fs-extra";
|
|
860
898
|
import path5 from "path";
|
|
861
899
|
var getDailyUsage, incrementUsage, checkQuota;
|
|
862
900
|
var init_usage = __esm({
|
|
@@ -865,8 +903,8 @@ var init_usage = __esm({
|
|
|
865
903
|
getDailyUsage = async () => {
|
|
866
904
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
867
905
|
try {
|
|
868
|
-
if (await
|
|
869
|
-
const data = await
|
|
906
|
+
if (await fs5.exists(USAGE_FILE)) {
|
|
907
|
+
const data = await fs5.readJson(USAGE_FILE);
|
|
870
908
|
if (data.date === today) {
|
|
871
909
|
return data.stats;
|
|
872
910
|
}
|
|
@@ -875,21 +913,21 @@ var init_usage = __esm({
|
|
|
875
913
|
console.error("Failed to read usage:", err);
|
|
876
914
|
}
|
|
877
915
|
const defaultStats = { agent: 0, background: 0, search: 0 };
|
|
878
|
-
await
|
|
879
|
-
await
|
|
916
|
+
await fs5.ensureDir(path5.dirname(USAGE_FILE));
|
|
917
|
+
await fs5.writeJson(USAGE_FILE, { date: today, stats: defaultStats }, { spaces: 2 });
|
|
880
918
|
return defaultStats;
|
|
881
919
|
};
|
|
882
920
|
incrementUsage = async (key) => {
|
|
883
921
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
884
|
-
const data = await
|
|
922
|
+
const data = await fs5.readJson(USAGE_FILE).catch(() => ({ date: today, stats: { agent: 0, background: 0, search: 0 } }));
|
|
885
923
|
if (data.date !== today) {
|
|
886
924
|
data.date = today;
|
|
887
925
|
data.stats = { agent: 0, background: 0, search: 0 };
|
|
888
926
|
}
|
|
889
927
|
if (data.stats[key] !== void 0) {
|
|
890
928
|
data.stats[key]++;
|
|
891
|
-
await
|
|
892
|
-
await
|
|
929
|
+
await fs5.ensureDir(path5.dirname(USAGE_FILE));
|
|
930
|
+
await fs5.writeJson(USAGE_FILE, data, { spaces: 2 });
|
|
893
931
|
}
|
|
894
932
|
};
|
|
895
933
|
checkQuota = async (key, settings) => {
|
|
@@ -943,7 +981,7 @@ var init_arg_parser = __esm({
|
|
|
943
981
|
|
|
944
982
|
// src/tools/web_search.js
|
|
945
983
|
import * as cuimp from "cuimp";
|
|
946
|
-
import
|
|
984
|
+
import fs6 from "fs";
|
|
947
985
|
import path6 from "path";
|
|
948
986
|
var web_search;
|
|
949
987
|
var init_web_search = __esm({
|
|
@@ -953,65 +991,75 @@ var init_web_search = __esm({
|
|
|
953
991
|
web_search = async (argsString) => {
|
|
954
992
|
const { query, limit = 10 } = parseArgs(argsString);
|
|
955
993
|
if (!query) return 'ERROR: Missing "query" argument for web_search.';
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
994
|
+
const maxRetries = 3;
|
|
995
|
+
let lastError = null;
|
|
996
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
997
|
+
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)];
|
|
1005
|
+
const jitter = attempt === 1 ? Math.random() * 1e3 + 500 : Math.random() * 2e3 + 1e3;
|
|
1006
|
+
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}
|
|
976
1030
|
Source: ${url}
|
|
977
1031
|
Snippet: ${snippet}`);
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
}
|
|
984
|
-
fs5.appendFileSync(path6.join(toolLogDir, "search-results.log"), `RESULTS ${(/* @__PURE__ */ new Date()).toISOString()} -
|
|
985
|
-
Query: [${query}]. Results Count: ${results.length}.
|
|
986
|
-
Results: ${results}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
`);
|
|
990
|
-
if (results.length === 0) {
|
|
991
|
-
if (html.includes("anomaly")) {
|
|
992
|
-
const toolErrDir = path6.join(LOGS_DIR, "tools");
|
|
993
|
-
if (!fs5.existsSync(toolErrDir)) {
|
|
994
|
-
fs5.mkdirSync(toolErrDir, { recursive: true });
|
|
1032
|
+
count++;
|
|
1033
|
+
}
|
|
1034
|
+
if (results.length === 0) {
|
|
1035
|
+
if (html.includes("anomaly")) {
|
|
1036
|
+
throw new Error("DDG_ANOMALY_DETECTED");
|
|
995
1037
|
}
|
|
996
|
-
|
|
997
|
-
`);
|
|
998
|
-
throw new Error("DDG detected unusual activity. Cuimp impersonation might need adjustment.");
|
|
1038
|
+
return `No results found for query: [${query}].`;
|
|
999
1039
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
const finalResults = results.join("\n\n");
|
|
1003
|
-
return `Search results for [${query}]:
|
|
1040
|
+
const finalResults = results.join("\n\n");
|
|
1041
|
+
return `Search results for [${query}]:
|
|
1004
1042
|
|
|
1005
1043
|
${finalResults}`;
|
|
1006
|
-
|
|
1007
|
-
|
|
1044
|
+
} catch (err) {
|
|
1045
|
+
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
|
+
`);
|
|
1050
|
+
if (attempt < maxRetries) {
|
|
1051
|
+
const backoff = Math.pow(2, attempt) * 1e3;
|
|
1052
|
+
await new Promise((r) => setTimeout(r, backoff));
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1008
1055
|
}
|
|
1056
|
+
return `ERROR: Search failed after ${maxRetries} attempts. Last error: ${lastError.message}`;
|
|
1009
1057
|
};
|
|
1010
1058
|
}
|
|
1011
1059
|
});
|
|
1012
1060
|
|
|
1013
1061
|
// src/tools/web_scrape.js
|
|
1014
|
-
import
|
|
1062
|
+
import fs7 from "fs";
|
|
1015
1063
|
import path7 from "path";
|
|
1016
1064
|
import * as cuimp2 from "cuimp";
|
|
1017
1065
|
var web_scrape;
|
|
@@ -1021,39 +1069,56 @@ var init_web_scrape = __esm({
|
|
|
1021
1069
|
web_scrape = async (args) => {
|
|
1022
1070
|
const urlMatch = args.match(/url\s*=\s*["'](.*)["']/);
|
|
1023
1071
|
const url = urlMatch ? urlMatch[1] : args;
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
"
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1072
|
+
const maxRetries = 3;
|
|
1073
|
+
let lastError = null;
|
|
1074
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1075
|
+
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
|
+
const jitter = attempt === 1 ? Math.random() * 1e3 + 500 : Math.random() * 2e3 + 1e3;
|
|
1084
|
+
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"
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
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);
|
|
1104
|
+
const toolLogDir = path7.join(LOGS_DIR, "tools");
|
|
1105
|
+
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}
|
|
1049
1108
|
|
|
1050
1109
|
`);
|
|
1051
|
-
|
|
1110
|
+
return `CONTENT FROM [${url}]:
|
|
1052
1111
|
|
|
1053
1112
|
${finalContent}${text.length > 2e4 ? "\n\n[TRUNCATED AT 20K CHARS]" : ""}`;
|
|
1054
|
-
|
|
1055
|
-
|
|
1113
|
+
} catch (err) {
|
|
1114
|
+
lastError = err;
|
|
1115
|
+
if (attempt < maxRetries) {
|
|
1116
|
+
const backoff = Math.pow(2, attempt) * 1e3;
|
|
1117
|
+
await new Promise((r) => setTimeout(r, backoff));
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1056
1120
|
}
|
|
1121
|
+
return `ERROR: Scrape failed after ${maxRetries} attempts. Last error: ${lastError.message}`;
|
|
1057
1122
|
};
|
|
1058
1123
|
}
|
|
1059
1124
|
});
|
|
@@ -1153,7 +1218,7 @@ var init_chat = __esm({
|
|
|
1153
1218
|
});
|
|
1154
1219
|
|
|
1155
1220
|
// src/tools/list_files.js
|
|
1156
|
-
import
|
|
1221
|
+
import fs8 from "fs";
|
|
1157
1222
|
import path8 from "path";
|
|
1158
1223
|
var list_files;
|
|
1159
1224
|
var init_list_files = __esm({
|
|
@@ -1163,14 +1228,14 @@ var init_list_files = __esm({
|
|
|
1163
1228
|
const { path: targetPath = "." } = parseArgs(args);
|
|
1164
1229
|
const absolutePath = path8.resolve(process.cwd(), targetPath);
|
|
1165
1230
|
try {
|
|
1166
|
-
if (!
|
|
1231
|
+
if (!fs8.existsSync(absolutePath)) {
|
|
1167
1232
|
return `ERROR: Path [${targetPath}] does not exist.`;
|
|
1168
1233
|
}
|
|
1169
|
-
const stats =
|
|
1234
|
+
const stats = fs8.statSync(absolutePath);
|
|
1170
1235
|
if (!stats.isDirectory()) {
|
|
1171
1236
|
return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
|
|
1172
1237
|
}
|
|
1173
|
-
const files =
|
|
1238
|
+
const files = fs8.readdirSync(absolutePath);
|
|
1174
1239
|
if (files.length === 0) {
|
|
1175
1240
|
return `Directory [${targetPath}] is empty.`;
|
|
1176
1241
|
}
|
|
@@ -1182,7 +1247,7 @@ var init_list_files = __esm({
|
|
|
1182
1247
|
let indicator = "\u{1F4C4}";
|
|
1183
1248
|
let metaPart = "";
|
|
1184
1249
|
try {
|
|
1185
|
-
const fStats =
|
|
1250
|
+
const fStats = fs8.statSync(fPath);
|
|
1186
1251
|
indicator = fStats.isDirectory() ? "\u{1F4C1}" : "\u{1F4C4}";
|
|
1187
1252
|
const sizeKB = (fStats.size / 1024).toFixed(1);
|
|
1188
1253
|
metaPart = fStats.isFile() ? ` - [${sizeKB} KB]` : "";
|
|
@@ -1214,7 +1279,7 @@ ${list}${footer}`;
|
|
|
1214
1279
|
});
|
|
1215
1280
|
|
|
1216
1281
|
// src/tools/view_file.js
|
|
1217
|
-
import
|
|
1282
|
+
import fs9 from "fs";
|
|
1218
1283
|
import path9 from "path";
|
|
1219
1284
|
var view_file;
|
|
1220
1285
|
var init_view_file = __esm({
|
|
@@ -1225,14 +1290,38 @@ var init_view_file = __esm({
|
|
|
1225
1290
|
if (!targetPath) return 'ERROR: Missing "path" argument for view_file.';
|
|
1226
1291
|
const absolutePath = path9.resolve(process.cwd(), targetPath);
|
|
1227
1292
|
try {
|
|
1228
|
-
if (!
|
|
1293
|
+
if (!fs9.existsSync(absolutePath)) {
|
|
1229
1294
|
return `ERROR: File [${targetPath}] does not exist.`;
|
|
1230
1295
|
}
|
|
1231
|
-
const stats =
|
|
1296
|
+
const stats = fs9.statSync(absolutePath);
|
|
1232
1297
|
if (stats.isDirectory()) {
|
|
1233
1298
|
return `ERROR: Path [${targetPath}] is a directory. Use list_files instead.`;
|
|
1234
1299
|
}
|
|
1235
|
-
const
|
|
1300
|
+
const ext = path9.extname(targetPath).toLowerCase();
|
|
1301
|
+
const mimeMap = {
|
|
1302
|
+
".pdf": "application/pdf",
|
|
1303
|
+
".jpg": "image/jpeg",
|
|
1304
|
+
".jpeg": "image/jpeg",
|
|
1305
|
+
".png": "image/png",
|
|
1306
|
+
".webp": "image/webp",
|
|
1307
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1308
|
+
".doc": "application/msword"
|
|
1309
|
+
};
|
|
1310
|
+
if (mimeMap[ext]) {
|
|
1311
|
+
const buffer = fs9.readFileSync(absolutePath);
|
|
1312
|
+
const base64 = buffer.toString("base64");
|
|
1313
|
+
const mimeType = mimeMap[ext];
|
|
1314
|
+
return {
|
|
1315
|
+
text: `[BINARY_FILE]: ${targetPath} (${mimeType}) - Loaded as multimodal part.`,
|
|
1316
|
+
binaryPart: {
|
|
1317
|
+
inlineData: {
|
|
1318
|
+
data: base64,
|
|
1319
|
+
mimeType
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
const content = fs9.readFileSync(absolutePath, "utf8");
|
|
1236
1325
|
const lines = content.split("\n");
|
|
1237
1326
|
const totalLines = lines.length;
|
|
1238
1327
|
const start = Math.max(0, start_line - 1);
|
|
@@ -1251,7 +1340,7 @@ ${code}`;
|
|
|
1251
1340
|
});
|
|
1252
1341
|
|
|
1253
1342
|
// src/tools/write_file.js
|
|
1254
|
-
import
|
|
1343
|
+
import fs10 from "fs";
|
|
1255
1344
|
import path10 from "path";
|
|
1256
1345
|
var write_file;
|
|
1257
1346
|
var init_write_file = __esm({
|
|
@@ -1266,9 +1355,9 @@ var init_write_file = __esm({
|
|
|
1266
1355
|
const parentDir = path10.dirname(absolutePath);
|
|
1267
1356
|
try {
|
|
1268
1357
|
let ancestry = "";
|
|
1269
|
-
if (
|
|
1358
|
+
if (fs10.existsSync(absolutePath)) {
|
|
1270
1359
|
try {
|
|
1271
|
-
const oldData =
|
|
1360
|
+
const oldData = fs10.readFileSync(absolutePath, "utf8");
|
|
1272
1361
|
const lines = oldData.split(/\r?\n/);
|
|
1273
1362
|
ancestry = `Old File contents:
|
|
1274
1363
|
${lines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
|
|
@@ -1280,13 +1369,13 @@ ${lines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
|
|
|
1280
1369
|
`;
|
|
1281
1370
|
}
|
|
1282
1371
|
}
|
|
1283
|
-
if (!
|
|
1284
|
-
|
|
1372
|
+
if (!fs10.existsSync(parentDir)) {
|
|
1373
|
+
fs10.mkdirSync(parentDir, { recursive: true });
|
|
1285
1374
|
}
|
|
1286
1375
|
const lineCount = content.split(/\r?\n/).length;
|
|
1287
1376
|
const originalSize = Buffer.byteLength(content, "utf8");
|
|
1288
|
-
|
|
1289
|
-
let verifiedContent =
|
|
1377
|
+
fs10.writeFileSync(absolutePath, content, "utf8");
|
|
1378
|
+
let verifiedContent = fs10.readFileSync(absolutePath, "utf8");
|
|
1290
1379
|
const verifiedSize = Buffer.byteLength(verifiedContent, "utf8");
|
|
1291
1380
|
const verifiedLines = verifiedContent.split(/\r?\n/);
|
|
1292
1381
|
const verifiedLineCount = verifiedLines.length;
|
|
@@ -1320,7 +1409,7 @@ ${snippet}`;
|
|
|
1320
1409
|
});
|
|
1321
1410
|
|
|
1322
1411
|
// src/tools/update_file.js
|
|
1323
|
-
import
|
|
1412
|
+
import fs11 from "fs";
|
|
1324
1413
|
import path11 from "path";
|
|
1325
1414
|
var update_file;
|
|
1326
1415
|
var init_update_file = __esm({
|
|
@@ -1336,10 +1425,10 @@ var init_update_file = __esm({
|
|
|
1336
1425
|
content_to_add = strip(content_to_add);
|
|
1337
1426
|
const absolutePath = path11.resolve(process.cwd(), targetPath);
|
|
1338
1427
|
try {
|
|
1339
|
-
if (!
|
|
1428
|
+
if (!fs11.existsSync(absolutePath)) {
|
|
1340
1429
|
return `ERROR: File [${targetPath}] does not exist. Use write_file instead.`;
|
|
1341
1430
|
}
|
|
1342
|
-
const currentContent =
|
|
1431
|
+
const currentContent = fs11.readFileSync(absolutePath, "utf8");
|
|
1343
1432
|
if (!currentContent.includes(content_to_replace)) {
|
|
1344
1433
|
return `ERROR: Could not find exact match for the specified "content_to_replace" in [${targetPath}]. Check indentation/whitespace/line breaks(LF or CRLF)/string. Try re-reading the file for latest changes.`;
|
|
1345
1434
|
}
|
|
@@ -1347,7 +1436,7 @@ var init_update_file = __esm({
|
|
|
1347
1436
|
const startLine = currentContent.substring(0, startPos).split(/\r?\n/).length;
|
|
1348
1437
|
const instances = currentContent.split(content_to_replace).length - 1;
|
|
1349
1438
|
const newFileContent = currentContent.split(content_to_replace).join(content_to_add);
|
|
1350
|
-
|
|
1439
|
+
fs11.writeFileSync(absolutePath, newFileContent, "utf8");
|
|
1351
1440
|
const allOriginalLines = currentContent.split(/\r?\n/);
|
|
1352
1441
|
const oldLines = content_to_replace.split(/\r?\n/);
|
|
1353
1442
|
const newLines = content_to_add.split(/\r?\n/);
|
|
@@ -1469,7 +1558,7 @@ ${finalOutput}`);
|
|
|
1469
1558
|
});
|
|
1470
1559
|
|
|
1471
1560
|
// src/tools/read_folder.js
|
|
1472
|
-
import
|
|
1561
|
+
import fs12 from "fs";
|
|
1473
1562
|
import path12 from "path";
|
|
1474
1563
|
var read_folder;
|
|
1475
1564
|
var init_read_folder = __esm({
|
|
@@ -1479,14 +1568,14 @@ var init_read_folder = __esm({
|
|
|
1479
1568
|
const { path: targetPath = "." } = parseArgs(args);
|
|
1480
1569
|
const absolutePath = path12.resolve(process.cwd(), targetPath);
|
|
1481
1570
|
try {
|
|
1482
|
-
if (!
|
|
1571
|
+
if (!fs12.existsSync(absolutePath)) {
|
|
1483
1572
|
return `ERROR: Path [${targetPath}] does not exist.`;
|
|
1484
1573
|
}
|
|
1485
|
-
const stats =
|
|
1574
|
+
const stats = fs12.statSync(absolutePath);
|
|
1486
1575
|
if (!stats.isDirectory()) {
|
|
1487
1576
|
return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
|
|
1488
1577
|
}
|
|
1489
|
-
const files =
|
|
1578
|
+
const files = fs12.readdirSync(absolutePath);
|
|
1490
1579
|
const totalItems = files.length;
|
|
1491
1580
|
const maxDisplay = 100;
|
|
1492
1581
|
const displayItems = files.slice(0, maxDisplay);
|
|
@@ -1496,7 +1585,7 @@ var init_read_folder = __esm({
|
|
|
1496
1585
|
let indicator = "\u{1F4C4}";
|
|
1497
1586
|
let info = { name: file, type: "unknown", size: "N/A", mtime: "N/A" };
|
|
1498
1587
|
try {
|
|
1499
|
-
const fStats =
|
|
1588
|
+
const fStats = fs12.statSync(fPath);
|
|
1500
1589
|
info = {
|
|
1501
1590
|
name: file,
|
|
1502
1591
|
type: fStats.isDirectory() ? "directory" : "file",
|
|
@@ -1577,6 +1666,78 @@ var init_ask_user = __esm({
|
|
|
1577
1666
|
}
|
|
1578
1667
|
});
|
|
1579
1668
|
|
|
1669
|
+
// src/tools/write_pdf.js
|
|
1670
|
+
import puppeteer from "puppeteer";
|
|
1671
|
+
import path13 from "path";
|
|
1672
|
+
import fs13 from "fs-extra";
|
|
1673
|
+
var write_pdf;
|
|
1674
|
+
var init_write_pdf = __esm({
|
|
1675
|
+
"src/tools/write_pdf.js"() {
|
|
1676
|
+
init_arg_parser();
|
|
1677
|
+
write_pdf = async (args) => {
|
|
1678
|
+
const { path: targetPath, content, orientation = "portrait", margin = "10px" } = parseArgs(args);
|
|
1679
|
+
if (!targetPath) return 'ERROR: Missing "path" argument for write_pdf.';
|
|
1680
|
+
if (!content) return 'ERROR: Missing "content" (HTML/CSS) for write_pdf.';
|
|
1681
|
+
const absolutePath = path13.resolve(process.cwd(), targetPath);
|
|
1682
|
+
let browser = null;
|
|
1683
|
+
try {
|
|
1684
|
+
await fs13.ensureDir(path13.dirname(absolutePath));
|
|
1685
|
+
browser = await puppeteer.launch({
|
|
1686
|
+
headless: true,
|
|
1687
|
+
args: [
|
|
1688
|
+
"--no-sandbox",
|
|
1689
|
+
"--disable-setuid-sandbox",
|
|
1690
|
+
"--disable-gpu",
|
|
1691
|
+
"--disable-dev-shm-usage"
|
|
1692
|
+
]
|
|
1693
|
+
});
|
|
1694
|
+
const page = await browser.newPage();
|
|
1695
|
+
const styledContent = `
|
|
1696
|
+
<style>
|
|
1697
|
+
@page {
|
|
1698
|
+
margin: ${margin};
|
|
1699
|
+
}
|
|
1700
|
+
body {
|
|
1701
|
+
margin: 0;
|
|
1702
|
+
padding: 0;
|
|
1703
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
1704
|
+
}
|
|
1705
|
+
* { box-sizing: border-box; }
|
|
1706
|
+
</style>
|
|
1707
|
+
${content}
|
|
1708
|
+
`;
|
|
1709
|
+
await page.setContent(styledContent, { waitUntil: "networkidle0" });
|
|
1710
|
+
await page.pdf({
|
|
1711
|
+
path: absolutePath,
|
|
1712
|
+
format: "A4",
|
|
1713
|
+
landscape: orientation.toLowerCase() === "landscape",
|
|
1714
|
+
margin: {
|
|
1715
|
+
top: margin,
|
|
1716
|
+
right: margin,
|
|
1717
|
+
bottom: "15mm",
|
|
1718
|
+
// Space for watermark
|
|
1719
|
+
left: margin
|
|
1720
|
+
},
|
|
1721
|
+
displayHeaderFooter: true,
|
|
1722
|
+
headerTemplate: "<span></span>",
|
|
1723
|
+
footerTemplate: `
|
|
1724
|
+
<div style="font-size: 9px; color: rgba(0,0,0,0.2); width: 100%; text-align: right; padding-right: 15mm; font-family: system-ui, sans-serif; -webkit-print-color-adjust: exact;">
|
|
1725
|
+
FluxFlow CLI
|
|
1726
|
+
</div>
|
|
1727
|
+
`,
|
|
1728
|
+
printBackground: true
|
|
1729
|
+
});
|
|
1730
|
+
const stats = await fs13.stat(absolutePath);
|
|
1731
|
+
return `SUCCESS: PDF generated successfully at [${targetPath}] (${(stats.size / 1024).toFixed(2)} KB).`;
|
|
1732
|
+
} catch (err) {
|
|
1733
|
+
return `ERROR: Failed to generate PDF [${targetPath}]: ${err.message}`;
|
|
1734
|
+
} finally {
|
|
1735
|
+
if (browser) await browser.close();
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1580
1741
|
// src/utils/tools.js
|
|
1581
1742
|
var TOOL_MAP, dispatchTool;
|
|
1582
1743
|
var init_tools = __esm({
|
|
@@ -1592,6 +1753,7 @@ var init_tools = __esm({
|
|
|
1592
1753
|
init_exec_command();
|
|
1593
1754
|
init_read_folder();
|
|
1594
1755
|
init_ask_user();
|
|
1756
|
+
init_write_pdf();
|
|
1595
1757
|
TOOL_MAP = {
|
|
1596
1758
|
web_search,
|
|
1597
1759
|
web_scrape,
|
|
@@ -1603,6 +1765,7 @@ var init_tools = __esm({
|
|
|
1603
1765
|
update_file,
|
|
1604
1766
|
exec_command,
|
|
1605
1767
|
read_folder,
|
|
1768
|
+
write_pdf,
|
|
1606
1769
|
ask: ask_user
|
|
1607
1770
|
};
|
|
1608
1771
|
dispatchTool = async (toolName, args, context = {}) => {
|
|
@@ -1619,10 +1782,32 @@ var init_tools = __esm({
|
|
|
1619
1782
|
}
|
|
1620
1783
|
});
|
|
1621
1784
|
|
|
1785
|
+
// src/utils/terminal.js
|
|
1786
|
+
var getTerminalEnv, emojiSpace;
|
|
1787
|
+
var init_terminal = __esm({
|
|
1788
|
+
"src/utils/terminal.js"() {
|
|
1789
|
+
getTerminalEnv = () => {
|
|
1790
|
+
if (process.env.TERM_PROGRAM === "vscode") return "vscode";
|
|
1791
|
+
if (process.env.WT_SESSION) return "wt";
|
|
1792
|
+
return "default";
|
|
1793
|
+
};
|
|
1794
|
+
emojiSpace = (baseSpaces = 2) => {
|
|
1795
|
+
const env = getTerminalEnv();
|
|
1796
|
+
if (env === "wt") {
|
|
1797
|
+
return " ".repeat(Math.max(1, baseSpaces - 1));
|
|
1798
|
+
}
|
|
1799
|
+
if (env === "vscode") {
|
|
1800
|
+
return " ".repeat(baseSpaces);
|
|
1801
|
+
}
|
|
1802
|
+
return " ".repeat(baseSpaces);
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1622
1807
|
// src/utils/ai.js
|
|
1623
1808
|
import { GoogleGenAI, ThinkingLevel } from "@google/genai";
|
|
1624
|
-
import
|
|
1625
|
-
import
|
|
1809
|
+
import path14 from "path";
|
|
1810
|
+
import fs14 from "fs";
|
|
1626
1811
|
var client, TERMINATION_SIGNAL, signalTermination, detectToolCalls, initAI, getAIStream;
|
|
1627
1812
|
var init_ai = __esm({
|
|
1628
1813
|
"src/utils/ai.js"() {
|
|
@@ -1632,6 +1817,7 @@ var init_ai = __esm({
|
|
|
1632
1817
|
init_tools();
|
|
1633
1818
|
init_crypto();
|
|
1634
1819
|
init_arg_parser();
|
|
1820
|
+
init_terminal();
|
|
1635
1821
|
init_paths();
|
|
1636
1822
|
client = null;
|
|
1637
1823
|
TERMINATION_SIGNAL = false;
|
|
@@ -1735,10 +1921,16 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
1735
1921
|
}
|
|
1736
1922
|
}
|
|
1737
1923
|
yield { type: "turn_reset", content: true };
|
|
1738
|
-
const contents = modifiedHistory.filter((msg) => (msg.role === "user" || msg.role === "agent" || msg.role === "system") && !String(msg.id).startsWith("welcome") && !msg.isMeta).map((msg) =>
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1924
|
+
const contents = modifiedHistory.filter((msg) => (msg.role === "user" || msg.role === "agent" || msg.role === "system") && !String(msg.id).startsWith("welcome") && !msg.isMeta).map((msg) => {
|
|
1925
|
+
const parts = [{ text: msg.text }];
|
|
1926
|
+
if (msg.binaryPart) {
|
|
1927
|
+
parts.push(msg.binaryPart);
|
|
1928
|
+
}
|
|
1929
|
+
return {
|
|
1930
|
+
role: msg.role === "user" || msg.role === "system" ? "user" : "model",
|
|
1931
|
+
parts
|
|
1932
|
+
};
|
|
1933
|
+
});
|
|
1742
1934
|
let stream;
|
|
1743
1935
|
let success = false;
|
|
1744
1936
|
let retryCount = 0;
|
|
@@ -1772,9 +1964,9 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
1772
1964
|
} catch (err) {
|
|
1773
1965
|
const errMsg = err.status || err.error && err.error.message || String(err);
|
|
1774
1966
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1775
|
-
const agentErrDir =
|
|
1776
|
-
if (!
|
|
1777
|
-
|
|
1967
|
+
const agentErrDir = path14.join(LOGS_DIR, "agent");
|
|
1968
|
+
if (!fs14.existsSync(agentErrDir)) fs14.mkdirSync(agentErrDir, { recursive: true });
|
|
1969
|
+
fs14.appendFileSync(path14.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errMsg}
|
|
1778
1970
|
`);
|
|
1779
1971
|
if (retryCount < MAX_RETRIES) {
|
|
1780
1972
|
retryCount++;
|
|
@@ -1834,9 +2026,9 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
1834
2026
|
let totalLines = "...";
|
|
1835
2027
|
let actualEndLine = end_line;
|
|
1836
2028
|
try {
|
|
1837
|
-
const absPath =
|
|
1838
|
-
if (
|
|
1839
|
-
const content =
|
|
2029
|
+
const absPath = path14.resolve(process.cwd(), targetPath2);
|
|
2030
|
+
if (fs14.existsSync(absPath)) {
|
|
2031
|
+
const content = fs14.readFileSync(absPath, "utf8");
|
|
1840
2032
|
const lines = content.split("\n").length;
|
|
1841
2033
|
totalLines = lines;
|
|
1842
2034
|
actualEndLine = Math.min(end_line, lines);
|
|
@@ -1850,6 +2042,8 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
1850
2042
|
} else if (toolCall.toolName === "write_file" || toolCall.toolName === "update_file") {
|
|
1851
2043
|
const action = toolCall.toolName === "write_file" ? "WRITING" : "PATCHING";
|
|
1852
2044
|
label = `\u{1F4BE} ${action} FILE: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
2045
|
+
} else if (toolCall.toolName === "write_pdf") {
|
|
2046
|
+
label = `\u{1F4D1} GENERATING PDF: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
1853
2047
|
} else if (toolCall.toolName === "exec_command" || toolCall.toolName === "ask") {
|
|
1854
2048
|
label = "";
|
|
1855
2049
|
} else {
|
|
@@ -1884,7 +2078,7 @@ ${boxBottom}
|
|
|
1884
2078
|
/\/usr\//
|
|
1885
2079
|
// Sensitive Linux paths
|
|
1886
2080
|
];
|
|
1887
|
-
const currentDrive =
|
|
2081
|
+
const currentDrive = path14.resolve(process.cwd()).substring(0, 3).toLowerCase();
|
|
1888
2082
|
const isViolating = riskyPatterns.some((pattern) => {
|
|
1889
2083
|
if (pattern.source === "[a-zA-Z]:[\\\\\\/]") {
|
|
1890
2084
|
const driveMatch = command.match(/[a-zA-Z]:[\\\/]/i);
|
|
@@ -1906,8 +2100,8 @@ ${boxBottom}
|
|
|
1906
2100
|
const targetPath = parsedArgs.path || parsedArgs.targetPath || null;
|
|
1907
2101
|
if (targetPath) {
|
|
1908
2102
|
const isExternalOff = settings.systemSettings && settings.systemSettings.allowExternalAccess === false;
|
|
1909
|
-
const absoluteTarget =
|
|
1910
|
-
const absoluteCwd =
|
|
2103
|
+
const absoluteTarget = path14.resolve(targetPath);
|
|
2104
|
+
const absoluteCwd = path14.resolve(process.cwd());
|
|
1911
2105
|
if (isExternalOff && !absoluteTarget.startsWith(absoluteCwd)) {
|
|
1912
2106
|
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.`;
|
|
1913
2107
|
toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
|
|
@@ -1933,12 +2127,17 @@ ${boxBottom}
|
|
|
1933
2127
|
}
|
|
1934
2128
|
}
|
|
1935
2129
|
}
|
|
1936
|
-
|
|
2130
|
+
let result = await dispatchTool(toolCall.toolName, toolCall.args, {
|
|
1937
2131
|
chatId,
|
|
1938
2132
|
history,
|
|
1939
2133
|
onChunk: (chunk) => settings.onExecChunk ? settings.onExecChunk(chunk) : null,
|
|
1940
2134
|
onAskUser: settings.onAskUser
|
|
1941
2135
|
});
|
|
2136
|
+
let binaryPart = null;
|
|
2137
|
+
if (typeof result === "object" && result.binaryPart) {
|
|
2138
|
+
binaryPart = result.binaryPart;
|
|
2139
|
+
result = result.text;
|
|
2140
|
+
}
|
|
1942
2141
|
if (toolCall.toolName === "exec_command" && settings.onExecEnd) {
|
|
1943
2142
|
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
1944
2143
|
settings.onExecEnd();
|
|
@@ -1947,17 +2146,17 @@ ${boxBottom}
|
|
|
1947
2146
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1948
2147
|
const isErr = result.startsWith("ERROR:");
|
|
1949
2148
|
const logStatus = isErr ? result.trim() : "SUCCESS";
|
|
1950
|
-
const toolHistDir =
|
|
1951
|
-
if (!
|
|
1952
|
-
|
|
2149
|
+
const toolHistDir = path14.join(LOGS_DIR, "tools");
|
|
2150
|
+
if (!fs14.existsSync(toolHistDir)) {
|
|
2151
|
+
fs14.mkdirSync(toolHistDir, { recursive: true });
|
|
1953
2152
|
}
|
|
1954
|
-
|
|
2153
|
+
fs14.appendFileSync(path14.join(toolHistDir, "history.log"), `HISTORY [${timestamp}]: ${toolCall.toolName} [${logStatus}]
|
|
1955
2154
|
`);
|
|
1956
2155
|
} catch (logErr) {
|
|
1957
2156
|
}
|
|
1958
2157
|
const cleanResultForAI = result.split(/\r?\n/).filter((line) => !line.includes("[UI_CONTEXT]")).join("\n");
|
|
1959
2158
|
const aiContent = `[TOOL_RESULT]: ${cleanResultForAI}`;
|
|
1960
|
-
toolResults.push(aiContent);
|
|
2159
|
+
toolResults.push({ role: "user", text: aiContent, binaryPart });
|
|
1961
2160
|
let uiContent = `[TOOL_RESULT]: ${result}`;
|
|
1962
2161
|
if (toolCall.toolName === "view_file" || toolCall.toolName === "web_scrape") {
|
|
1963
2162
|
uiContent = `[TOOL_RESULT]: ${label} (Context Locked for UI Clarity)`;
|
|
@@ -1965,7 +2164,9 @@ ${boxBottom}
|
|
|
1965
2164
|
yield {
|
|
1966
2165
|
type: "tool_result",
|
|
1967
2166
|
content: uiContent,
|
|
1968
|
-
aiContent
|
|
2167
|
+
aiContent,
|
|
2168
|
+
binaryPart
|
|
2169
|
+
// Multi-modal stage (v1.5.0)
|
|
1969
2170
|
};
|
|
1970
2171
|
if (toolCall.toolName === "memory" && result.includes("SUCCESS")) {
|
|
1971
2172
|
yield { type: "memory_updated" };
|
|
@@ -2011,11 +2212,11 @@ ${boxBottom}
|
|
|
2011
2212
|
if (parts && parts[1]?.text) {
|
|
2012
2213
|
finalSynthesis = parts[1].text;
|
|
2013
2214
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
2014
|
-
const janitorLogDir =
|
|
2015
|
-
if (!
|
|
2016
|
-
|
|
2215
|
+
const janitorLogDir = path14.join(LOGS_DIR, "janitor");
|
|
2216
|
+
if (!fs14.existsSync(janitorLogDir)) {
|
|
2217
|
+
fs14.mkdirSync(janitorLogDir, { recursive: true });
|
|
2017
2218
|
}
|
|
2018
|
-
|
|
2219
|
+
fs14.appendFileSync(path14.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: ${finalSynthesis}
|
|
2019
2220
|
`);
|
|
2020
2221
|
} else if (parts && parts[0]?.text) finalSynthesis = parts[0].text;
|
|
2021
2222
|
else if (janitorResult.response && janitorResult.response.text) finalSynthesis = janitorResult.response.text();
|
|
@@ -2027,8 +2228,8 @@ ${boxBottom}
|
|
|
2027
2228
|
const toolContext = { chatId, sessionId: chatId, history };
|
|
2028
2229
|
const result = await dispatchTool(janitorToolCall.toolName, janitorToolCall.args, toolContext);
|
|
2029
2230
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
2030
|
-
const janitorLogDir =
|
|
2031
|
-
|
|
2231
|
+
const janitorLogDir = path14.join(LOGS_DIR, "janitor");
|
|
2232
|
+
fs14.appendFileSync(path14.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${janitorToolCall.toolName}]: ${result}
|
|
2032
2233
|
`);
|
|
2033
2234
|
if (janitorToolCall.toolName === "memory" && !janitorToolCall.args.includes("action='temp'")) {
|
|
2034
2235
|
yield { type: "memory_updated" };
|
|
@@ -2036,11 +2237,11 @@ ${boxBottom}
|
|
|
2036
2237
|
}
|
|
2037
2238
|
} catch (janitorErr) {
|
|
2038
2239
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
2039
|
-
const janitorErrDir =
|
|
2040
|
-
if (!
|
|
2041
|
-
|
|
2240
|
+
const janitorErrDir = path14.join(LOGS_DIR, "janitor");
|
|
2241
|
+
if (!fs14.existsSync(janitorErrDir)) {
|
|
2242
|
+
fs14.mkdirSync(janitorErrDir, { recursive: true });
|
|
2042
2243
|
}
|
|
2043
|
-
|
|
2244
|
+
fs14.appendFileSync(path14.join(janitorErrDir, "error.log"), `ERROR [${date}]: ${janitorErr.message}
|
|
2044
2245
|
`);
|
|
2045
2246
|
console.error("Janitor Background Tasks Failed:", janitorErr.message);
|
|
2046
2247
|
}
|
|
@@ -2057,8 +2258,11 @@ ${timestamp}`;
|
|
|
2057
2258
|
if (isActuallyFinished) break;
|
|
2058
2259
|
const nextAgentMsg = cleanedTurnText.trim() || "*Working...*";
|
|
2059
2260
|
modifiedHistory.push({ role: "agent", text: nextAgentMsg });
|
|
2060
|
-
|
|
2061
|
-
|
|
2261
|
+
if (toolResults.length > 0) {
|
|
2262
|
+
toolResults.forEach((tr) => modifiedHistory.push(tr));
|
|
2263
|
+
} else {
|
|
2264
|
+
modifiedHistory.push({ role: "user", text: "[turn: continue]" });
|
|
2265
|
+
}
|
|
2062
2266
|
}
|
|
2063
2267
|
yield { type: "status", content: null };
|
|
2064
2268
|
};
|
|
@@ -2066,9 +2270,9 @@ ${timestamp}`;
|
|
|
2066
2270
|
});
|
|
2067
2271
|
|
|
2068
2272
|
// src/utils/settings.js
|
|
2069
|
-
import
|
|
2070
|
-
import
|
|
2071
|
-
var DEFAULT_SETTINGS, loadSettings, saveSettings;
|
|
2273
|
+
import fs15 from "fs-extra";
|
|
2274
|
+
import path15 from "path";
|
|
2275
|
+
var DEFAULT_SETTINGS, loadSettings, migrateToExternal, saveSettings;
|
|
2072
2276
|
var init_settings = __esm({
|
|
2073
2277
|
"src/utils/settings.js"() {
|
|
2074
2278
|
init_paths();
|
|
@@ -2090,7 +2294,9 @@ var init_settings = __esm({
|
|
|
2090
2294
|
compression: 0,
|
|
2091
2295
|
autoExec: false,
|
|
2092
2296
|
allowExternalAccess: false,
|
|
2093
|
-
autoDeleteHistory: "7d"
|
|
2297
|
+
autoDeleteHistory: "7d",
|
|
2298
|
+
useExternalData: false,
|
|
2299
|
+
externalDataPath: ""
|
|
2094
2300
|
},
|
|
2095
2301
|
profileData: {
|
|
2096
2302
|
name: null,
|
|
@@ -2100,8 +2306,8 @@ var init_settings = __esm({
|
|
|
2100
2306
|
};
|
|
2101
2307
|
loadSettings = async () => {
|
|
2102
2308
|
try {
|
|
2103
|
-
if (await
|
|
2104
|
-
const saved = await
|
|
2309
|
+
if (await fs15.exists(SETTINGS_FILE)) {
|
|
2310
|
+
const saved = await fs15.readJson(SETTINGS_FILE);
|
|
2105
2311
|
return {
|
|
2106
2312
|
...DEFAULT_SETTINGS,
|
|
2107
2313
|
...saved,
|
|
@@ -2115,12 +2321,31 @@ var init_settings = __esm({
|
|
|
2115
2321
|
}
|
|
2116
2322
|
return DEFAULT_SETTINGS;
|
|
2117
2323
|
};
|
|
2324
|
+
migrateToExternal = async (newPath) => {
|
|
2325
|
+
const { FLUXFLOW_DIR: FLUXFLOW_DIR2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
2326
|
+
const folders = ["logs", "secret"];
|
|
2327
|
+
for (const folder of folders) {
|
|
2328
|
+
const src = path15.join(FLUXFLOW_DIR2, folder);
|
|
2329
|
+
const dest = path15.join(newPath, folder);
|
|
2330
|
+
try {
|
|
2331
|
+
if (await fs15.exists(src)) {
|
|
2332
|
+
await fs15.ensureDir(dest);
|
|
2333
|
+
await fs15.copy(src, dest, { overwrite: false });
|
|
2334
|
+
}
|
|
2335
|
+
} catch (err) {
|
|
2336
|
+
console.error(`Migration failed for ${folder}:`, err);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
};
|
|
2118
2340
|
saveSettings = async (settings) => {
|
|
2119
2341
|
try {
|
|
2120
2342
|
const current = await loadSettings();
|
|
2343
|
+
if (!current.systemSettings.useExternalData && settings.systemSettings?.useExternalData && settings.systemSettings?.externalDataPath) {
|
|
2344
|
+
await migrateToExternal(settings.systemSettings.externalDataPath);
|
|
2345
|
+
}
|
|
2121
2346
|
const updated = { ...current, ...settings };
|
|
2122
|
-
await
|
|
2123
|
-
await
|
|
2347
|
+
await fs15.ensureDir(path15.dirname(SETTINGS_FILE));
|
|
2348
|
+
await fs15.writeJson(SETTINGS_FILE, updated, { spaces: 2 });
|
|
2124
2349
|
return true;
|
|
2125
2350
|
} catch (err) {
|
|
2126
2351
|
console.error("Failed to save settings:", err);
|
|
@@ -2269,24 +2494,39 @@ var init_UpdateProcessor = __esm({
|
|
|
2269
2494
|
}
|
|
2270
2495
|
});
|
|
2271
2496
|
|
|
2272
|
-
// src/utils/
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2497
|
+
// src/utils/setup.js
|
|
2498
|
+
import puppeteer2 from "puppeteer";
|
|
2499
|
+
import { exec as exec2 } from "child_process";
|
|
2500
|
+
import { promisify } from "util";
|
|
2501
|
+
import fs16 from "fs";
|
|
2502
|
+
var execAsync, checkPuppeteerReady, installPuppeteerBrowser;
|
|
2503
|
+
var init_setup = __esm({
|
|
2504
|
+
"src/utils/setup.js"() {
|
|
2505
|
+
execAsync = promisify(exec2);
|
|
2506
|
+
checkPuppeteerReady = () => {
|
|
2507
|
+
try {
|
|
2508
|
+
const exePath = puppeteer2.executablePath();
|
|
2509
|
+
const exists = exePath && fs16.existsSync(exePath);
|
|
2510
|
+
if (exists) return true;
|
|
2511
|
+
} catch (e) {
|
|
2512
|
+
return false;
|
|
2285
2513
|
}
|
|
2286
|
-
|
|
2287
|
-
|
|
2514
|
+
return false;
|
|
2515
|
+
};
|
|
2516
|
+
installPuppeteerBrowser = async (onStatus) => {
|
|
2517
|
+
if (onStatus) onStatus("\u{1F4E5} Downloading Chromium engine (Wait a moment)...");
|
|
2518
|
+
try {
|
|
2519
|
+
try {
|
|
2520
|
+
await execAsync("pnpm exec puppeteer browsers install chrome");
|
|
2521
|
+
} catch (pnpmErr) {
|
|
2522
|
+
await execAsync("npx -y puppeteer browsers install chrome");
|
|
2523
|
+
}
|
|
2524
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
2525
|
+
return { success: true };
|
|
2526
|
+
} catch (err) {
|
|
2527
|
+
console.error("[SETUP ERROR]", err);
|
|
2528
|
+
return { success: false, error: err.message };
|
|
2288
2529
|
}
|
|
2289
|
-
return " ".repeat(baseSpaces);
|
|
2290
2530
|
};
|
|
2291
2531
|
}
|
|
2292
2532
|
});
|
|
@@ -2299,8 +2539,8 @@ __export(app_exports, {
|
|
|
2299
2539
|
import React10, { useState as useState6, useEffect as useEffect4, useRef, useMemo } from "react";
|
|
2300
2540
|
import { Box as Box10, Text as Text10, useInput as useInput4, useStdout } from "ink";
|
|
2301
2541
|
import Spinner2 from "ink-spinner";
|
|
2302
|
-
import
|
|
2303
|
-
import { exec as
|
|
2542
|
+
import fs17 from "fs-extra";
|
|
2543
|
+
import { exec as exec3 } from "child_process";
|
|
2304
2544
|
import { MultilineInput } from "ink-multiline-input";
|
|
2305
2545
|
import TextInput3 from "ink-text-input";
|
|
2306
2546
|
import gradient from "gradient-string";
|
|
@@ -2355,7 +2595,7 @@ Check what's new using \`/changelog\` command.`,
|
|
|
2355
2595
|
if (manual) {
|
|
2356
2596
|
setMessages((prev) => {
|
|
2357
2597
|
setCompletedIndex(prev.length + 1);
|
|
2358
|
-
return [...prev, { id: "check-err-" + Date.now(), role: "system", text: `\u274C ERROR: Failed to check for updates: ${err.message}
|
|
2598
|
+
return [...prev, { id: "check-err-" + Date.now(), role: "system", text: `\u274C ERROR: Failed to check for updates: ${err.message}`, isMeta: true }];
|
|
2359
2599
|
});
|
|
2360
2600
|
}
|
|
2361
2601
|
}
|
|
@@ -2432,7 +2672,7 @@ Check what's new using \`/changelog\` command.`,
|
|
|
2432
2672
|
const [resolutionData, setResolutionData] = useState6(null);
|
|
2433
2673
|
const [tempModelOverride, setTempModelOverride] = useState6(null);
|
|
2434
2674
|
const [messages, setMessages] = useState6([
|
|
2435
|
-
{ id: "welcome", role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome to Flux Flow! Type /help for commands.\n" }
|
|
2675
|
+
{ id: "welcome", role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome to Flux Flow! Type /help for commands.\n", isMeta: true }
|
|
2436
2676
|
]);
|
|
2437
2677
|
const queuedPromptRef = useRef(null);
|
|
2438
2678
|
const [completedIndex, setCompletedIndex] = useState6(1);
|
|
@@ -2539,6 +2779,17 @@ Check what's new using \`/changelog\` command.`,
|
|
|
2539
2779
|
});
|
|
2540
2780
|
useEffect4(() => {
|
|
2541
2781
|
async function init() {
|
|
2782
|
+
if (!checkPuppeteerReady()) {
|
|
2783
|
+
setMessages((prev) => {
|
|
2784
|
+
setCompletedIndex(prev.length + 1);
|
|
2785
|
+
return [...prev, { id: "setup-" + Date.now(), role: "system", text: "\u{1F527} [SYSTEM] Installing Required dependencies... (One-time setup)", isMeta: true }];
|
|
2786
|
+
});
|
|
2787
|
+
await installPuppeteerBrowser();
|
|
2788
|
+
setMessages((prev) => {
|
|
2789
|
+
setCompletedIndex(prev.length + 1);
|
|
2790
|
+
return [...prev, { id: "setup-done-" + Date.now(), role: "system", text: "\u2705 [SYSTEM] All dependencies installed successfully.", isMeta: true }];
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2542
2793
|
const saved = await loadSettings();
|
|
2543
2794
|
setMode(saved.mode);
|
|
2544
2795
|
setThinkingLevel(saved.thinkingLevel);
|
|
@@ -2589,9 +2840,9 @@ Check what's new using \`/changelog\` command.`,
|
|
|
2589
2840
|
await saveAPIKey(key);
|
|
2590
2841
|
setApiKey(key);
|
|
2591
2842
|
initAI(key);
|
|
2592
|
-
setMessages((prev) => [...prev, { role: "system", text: "\u2705 API Key saved successfully! Initialization complete." }]);
|
|
2843
|
+
setMessages((prev) => [...prev, { role: "system", text: "\u2705 API Key saved successfully! Initialization complete.", isMeta: true }]);
|
|
2593
2844
|
} else {
|
|
2594
|
-
setMessages((prev) => [...prev, { role: "system", text: `\u274C INVALID KEY: Gemini API keys must be at least 30 characters
|
|
2845
|
+
setMessages((prev) => [...prev, { role: "system", text: `\u274C INVALID KEY: Gemini API keys must be at least 30 characters.`, isMeta: true }]);
|
|
2595
2846
|
setTempKey("");
|
|
2596
2847
|
}
|
|
2597
2848
|
};
|
|
@@ -2706,7 +2957,7 @@ ${hintText}`, color: "magenta" }];
|
|
|
2706
2957
|
}
|
|
2707
2958
|
case "/clear": {
|
|
2708
2959
|
stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
2709
|
-
setMessages([{ id: "welcome-" + Date.now(), role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome back to Flux Flow! Context cleared.\n" }]);
|
|
2960
|
+
setMessages([{ id: "welcome-" + Date.now(), role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome back to Flux Flow! Context cleared.\n", isMeta: true }]);
|
|
2710
2961
|
setCompletedIndex(0);
|
|
2711
2962
|
setChatId(generateChatId());
|
|
2712
2963
|
setSessionStats({ tokens: 0 });
|
|
@@ -2833,12 +3084,12 @@ ${list || "No saved chats found."}`, isMeta: true }];
|
|
|
2833
3084
|
setCompletedIndex(prev.length + 1);
|
|
2834
3085
|
return [...prev, { id: Date.now(), role: "system", text: "\u2622\uFE0F [NUCLEAR] Initiating reset...", isMeta: true }];
|
|
2835
3086
|
});
|
|
2836
|
-
if (
|
|
2837
|
-
if (
|
|
2838
|
-
if (
|
|
3087
|
+
if (fs17.existsSync(LOGS_DIR)) fs17.removeSync(LOGS_DIR);
|
|
3088
|
+
if (fs17.existsSync(SECRET_DIR)) fs17.removeSync(SECRET_DIR);
|
|
3089
|
+
if (fs17.existsSync(SETTINGS_FILE)) fs17.removeSync(SETTINGS_FILE);
|
|
2839
3090
|
try {
|
|
2840
|
-
const items =
|
|
2841
|
-
if (items.length === 0)
|
|
3091
|
+
const items = fs17.readdirSync(FLUXFLOW_DIR);
|
|
3092
|
+
if (items.length === 0) fs17.removeSync(FLUXFLOW_DIR);
|
|
2842
3093
|
} catch (e) {
|
|
2843
3094
|
}
|
|
2844
3095
|
setTimeout(() => {
|
|
@@ -2870,7 +3121,7 @@ ${list || "No saved chats found."}`, isMeta: true }];
|
|
|
2870
3121
|
case "/changelog": {
|
|
2871
3122
|
const platform = process.platform;
|
|
2872
3123
|
const command = platform === "win32" ? "start" : platform === "darwin" ? "open" : "xdg-open";
|
|
2873
|
-
|
|
3124
|
+
exec3(`${command} ${CHANGELOG_URL}`);
|
|
2874
3125
|
setMessages((prev) => {
|
|
2875
3126
|
setCompletedIndex(prev.length + 1);
|
|
2876
3127
|
return [...prev, { id: Date.now(), role: "system", text: `\u{1F310} [BROWSER] Opening changelog: ${CHANGELOG_URL}`, isMeta: true }];
|
|
@@ -3065,8 +3316,10 @@ Selection: ${val}`,
|
|
|
3065
3316
|
id: "tool-" + Date.now(),
|
|
3066
3317
|
role: "system",
|
|
3067
3318
|
text: packet.content,
|
|
3068
|
-
fullText: packet.aiContent
|
|
3319
|
+
fullText: packet.aiContent,
|
|
3069
3320
|
// Preserve raw data for next turn
|
|
3321
|
+
binaryPart: packet.binaryPart
|
|
3322
|
+
// v1.5.0 Multimodal Support
|
|
3070
3323
|
}]);
|
|
3071
3324
|
continue;
|
|
3072
3325
|
}
|
|
@@ -3253,6 +3506,7 @@ Selection: ${val}`,
|
|
|
3253
3506
|
{ label: `API Tier [ ${apiTier} ]`, value: "apiTier" },
|
|
3254
3507
|
{ label: `Auto-Update [ ${systemSettings.autoUpdate ? "ON" : "OFF"} ]`, value: "autoUpdate" },
|
|
3255
3508
|
{ label: `Preferred Updater [ ${(systemSettings.updateManager || "npm") === "custom" ? "Custom" : (systemSettings.updateManager || "npm").toUpperCase()} ]`, value: "updateManager" },
|
|
3509
|
+
{ label: `Save AppData Externally [ ${systemSettings.useExternalData ? "ON" : "OFF"} ]`, value: "externalData" },
|
|
3256
3510
|
{ label: "Exit Settings", value: "Cancel" }
|
|
3257
3511
|
],
|
|
3258
3512
|
onSelect: (item) => {
|
|
@@ -3285,6 +3539,22 @@ Selection: ${val}`,
|
|
|
3285
3539
|
setSystemSettings((s) => ({ ...s, autoDeleteHistory: options[nextIndex] }));
|
|
3286
3540
|
} else if (item.value === "autoUpdate") {
|
|
3287
3541
|
setSystemSettings((s) => ({ ...s, autoUpdate: !s.autoUpdate }));
|
|
3542
|
+
} else if (item.value === "externalData") {
|
|
3543
|
+
if (!systemSettings.useExternalData) {
|
|
3544
|
+
setInputConfig({
|
|
3545
|
+
label: "Enter absolute path for External AppData:",
|
|
3546
|
+
note: "All history, logs and secrets will be stored here. ~/.fluxflow/settings.json stays as anchor.",
|
|
3547
|
+
key: "externalDataPath",
|
|
3548
|
+
value: systemSettings.externalDataPath || ""
|
|
3549
|
+
});
|
|
3550
|
+
setActiveView("input");
|
|
3551
|
+
} else {
|
|
3552
|
+
const newSettings = { ...systemSettings, useExternalData: false };
|
|
3553
|
+
setSystemSettings(newSettings);
|
|
3554
|
+
saveSettings({ systemSettings: newSettings, apiTier, quotas });
|
|
3555
|
+
setMessages((prev) => [...prev, { id: Date.now(), role: "system", text: "\u{1F3E0} [STORAGE RESET] Flux Flow will return to default ~/.fluxflow after restart." }]);
|
|
3556
|
+
setActiveView("chat");
|
|
3557
|
+
}
|
|
3288
3558
|
} else if (item.value === "updateManager") {
|
|
3289
3559
|
setActiveView("updateManager");
|
|
3290
3560
|
} else if (item.value === "Cancel") setActiveView("chat");
|
|
@@ -3377,6 +3647,11 @@ Selection: ${val}`,
|
|
|
3377
3647
|
} else if (key === "janitorModel") {
|
|
3378
3648
|
setJanitorModel(val);
|
|
3379
3649
|
newSettings.janitorModel = val;
|
|
3650
|
+
} else if (key === "externalDataPath") {
|
|
3651
|
+
const newSysSettings = { ...systemSettings, useExternalData: true, externalDataPath: val.trim() };
|
|
3652
|
+
setSystemSettings(newSysSettings);
|
|
3653
|
+
newSettings.systemSettings = newSysSettings;
|
|
3654
|
+
setMessages((prev) => [...prev, { id: Date.now(), role: "system", text: "\u{1F4C1} [EXTERNAL STORAGE] Flux Flow will use " + val.trim() + " for data after restart." }]);
|
|
3380
3655
|
}
|
|
3381
3656
|
if (next) {
|
|
3382
3657
|
setInputConfig(next(key === "quotas" ? newQuotas : val));
|
|
@@ -3795,10 +4070,11 @@ var init_app = __esm({
|
|
|
3795
4070
|
init_paths();
|
|
3796
4071
|
init_terminal();
|
|
3797
4072
|
init_exec_command();
|
|
4073
|
+
init_setup();
|
|
3798
4074
|
SESSION_START_TIME = Date.now();
|
|
3799
4075
|
CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
|
|
3800
|
-
versionFluxflow = "1.
|
|
3801
|
-
updatedOn = "2026-
|
|
4076
|
+
versionFluxflow = "1.5.1";
|
|
4077
|
+
updatedOn = "2026-05-01";
|
|
3802
4078
|
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(
|
|
3803
4079
|
CommandMenu,
|
|
3804
4080
|
{
|