fluxflow-cli 2.14.1 → 2.15.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.
Files changed (2) hide show
  1. package/dist/fluxflow.js +460 -128
  2. package/package.json +2 -2
package/dist/fluxflow.js CHANGED
@@ -305,7 +305,13 @@ var init_settings = __esm({
305
305
  backgroundLimit: 999999,
306
306
  searchLimit: 100,
307
307
  customModelId: "",
308
- customLimit: 0
308
+ customLimit: 0,
309
+ providerTiers: {
310
+ Google: "Free",
311
+ DeepSeek: "Free",
312
+ NVIDIA: "Free",
313
+ OpenRouter: "Free"
314
+ }
309
315
  },
310
316
  systemSettings: {
311
317
  memory: true,
@@ -345,7 +351,14 @@ var init_settings = __esm({
345
351
  settingsObj = {
346
352
  ...DEFAULT_SETTINGS,
347
353
  ...saved,
348
- quotas: { ...DEFAULT_SETTINGS.quotas, ...saved.quotas },
354
+ quotas: {
355
+ ...DEFAULT_SETTINGS.quotas,
356
+ ...saved.quotas,
357
+ providerTiers: {
358
+ ...DEFAULT_SETTINGS.quotas.providerTiers,
359
+ ...saved.quotas?.providerTiers || {}
360
+ }
361
+ },
349
362
  systemSettings: { ...DEFAULT_SETTINGS.systemSettings, ...saved.systemSettings },
350
363
  profileData: { ...DEFAULT_SETTINGS.profileData, ...saved.profileData },
351
364
  imageSettings: { ...DEFAULT_SETTINGS.imageSettings, ...saved.imageSettings }
@@ -5746,7 +5759,7 @@ function SettingsMenu({
5746
5759
  case "other":
5747
5760
  return [
5748
5761
  { label: "Current Provider", value: "aiProvider", status: aiProvider },
5749
- { label: "Budgets", value: "apiTier", status: apiTier === "Free" ? "Provider Limits" : "Custom Budget" },
5762
+ { label: "Key Strategy", value: "apiTier", status: apiTier === "Free" ? "Free" : quotas?.providerBudgets?.__useProvider ? "Paid" : "Paid" },
5750
5763
  { label: "Download Language Parsers", value: "parserDownload", status: "ACTION" }
5751
5764
  ];
5752
5765
  default:
@@ -6256,9 +6269,9 @@ ${userMemories}` : "";
6256
6269
  ${parts.join("\n\n")}
6257
6270
  ` : "";
6258
6271
  };
6259
- getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, isMemoryEnabled = true, isFirstPrompt = false, aiProvider = "Google", isMultiModal = false) => {
6272
+ getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, isMemoryEnabled = true, isFirstPrompt = false, aiProvider = "Google", isMultiModal = false, isGemini) => {
6260
6273
  let thinkingConfig = "";
6261
- if (thinkingLevel !== "GEM") {
6274
+ if (!isGemini && aiProvider === "Google") {
6262
6275
  let levelKey = thinkingLevel;
6263
6276
  if (thinkingLevel === "Fast") levelKey = "Off";
6264
6277
  if (thinkingLevel === "Low") levelKey = "Minimal";
@@ -6266,6 +6279,23 @@ ${parts.join("\n\n")}
6266
6279
  if (thinkingLevel === "xHigh" || thinkingLevel === "Max") levelKey = "xHigh";
6267
6280
  thinkingConfig = thinking_prompts_default[levelKey] || thinking_prompts_default["Medium"];
6268
6281
  }
6282
+ if (isGemini || aiProvider !== "Google") {
6283
+ const MAP_FOR_NON_GOOGLE_OR_GEMINI = {
6284
+ "Fast": "LOWEST",
6285
+ "Low": "LOW",
6286
+ "Medium": "MEDIUM",
6287
+ "Standard": "MEDIUM",
6288
+ "High": "HIGH",
6289
+ "xHigh": "HIGH",
6290
+ "Max": "HIGH"
6291
+ };
6292
+ thinkingConfig = thinking_prompts_default["xHigh"];
6293
+ thinkingConfig = thinkingConfig.replace("EFFORT LEVEL: HIGH\nThink in a continuous, relentless analytical monologue. ", `EFFORT LEVEL: ${MAP_FOR_NON_GOOGLE_OR_GEMINI[thinkingLevel]}
6294
+ `).replace("- MANDATORY THINKING: Full reasoning required for ALL requests/greetings", "");
6295
+ if (thinkingLevel === "Fast") {
6296
+ thinkingConfig = "EFFORT LEVEL: LOWEST\nNo thinking. Immediate response\nRULES:\n- Verify ALL imports and system stability, AVOID ANY Syntax errors, re-read TOOL RESULTS/files to verify\n";
6297
+ }
6298
+ }
6269
6299
  const osDetected = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
6270
6300
  const userInstrStr = profile.instructions && profile.instructions?.length > 0 ? `User Instructions: ${profile.instructions}
6271
6301
 
@@ -6311,12 +6341,13 @@ Mode: ${mode}${thinkingLevel !== "Fast" ? " (Thinking)" : ""}. ${mode === "Flux"
6311
6341
  -- MARKERS --
6312
6342
  - TOOL SYSTEM: [TOOL RESULT]
6313
6343
  - SYSTEM NOTIFICATION: [SYSTEM] in user turn
6314
- ${aiProvider === "Google" ? `${thinkingLevel !== "GEM" ? `
6344
+
6315
6345
  -- THINKING RULES --
6316
- ${thinkingConfig}
6317
- ${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" ? `
6346
+ ${aiProvider === "Google" && !isGemini ? `${thinkingConfig}
6347
+ ${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" && !isGemini ? `
6318
6348
  CRITICAL THINKING POLICY
6319
- - ALWAYS use <think> ... </think> before responding, even with simple queries/greetings` : ""}` : ""}` : ``}
6349
+ - ALWAYS use <think> ... </think> before responding, even with simple queries/greetings
6350
+ ` : ""}` : `${thinkingConfig}`}
6320
6351
  ${TOOL_PROTOCOL(mode, osDetected, aiProvider.toLowerCase() === "deepseek" ? false : isMultiModal, aiProvider)}
6321
6352
  ${projectContextBlock}
6322
6353
  -- MEMORY RULES --
@@ -7063,6 +7094,26 @@ var init_usage = __esm({
7063
7094
  cachedUsage.stats[key] = Array.from(uniqueMap.values());
7064
7095
  } else if (typeof cachedUsage.stats[key] === "number") {
7065
7096
  cachedUsage.stats[key] = Math.max(cachedUsage.stats[key], Number(diskData.stats[key]) || 0);
7097
+ } else if (cachedUsage.stats[key] && typeof cachedUsage.stats[key] === "object") {
7098
+ const diskObj = diskData.stats[key] || {};
7099
+ const memObj = cachedUsage.stats[key];
7100
+ for (const subKey in diskObj) {
7101
+ if (typeof diskObj[subKey] === "number") {
7102
+ memObj[subKey] = Math.max(memObj[subKey] || 0, diskObj[subKey]);
7103
+ } else if (diskObj[subKey] && typeof diskObj[subKey] === "object") {
7104
+ if (!memObj[subKey]) memObj[subKey] = {};
7105
+ for (const mKey in diskObj[subKey]) {
7106
+ if (typeof diskObj[subKey][mKey] === "number") {
7107
+ memObj[subKey][mKey] = Math.max(memObj[subKey][mKey] || 0, diskObj[subKey][mKey]);
7108
+ } else if (diskObj[subKey][mKey] && typeof diskObj[subKey][mKey] === "object") {
7109
+ if (!memObj[subKey][mKey]) memObj[subKey][mKey] = {};
7110
+ for (const valKey in diskObj[subKey][mKey]) {
7111
+ memObj[subKey][mKey][valKey] = Math.max(memObj[subKey][mKey][valKey] || 0, diskObj[subKey][mKey][valKey]);
7112
+ }
7113
+ }
7114
+ }
7115
+ }
7116
+ }
7066
7117
  }
7067
7118
  }
7068
7119
  }
@@ -7206,12 +7257,18 @@ var init_usage = __esm({
7206
7257
  }
7207
7258
  return summed;
7208
7259
  };
7209
- incrementUsage = async (key) => {
7260
+ incrementUsage = async (key, provider) => {
7210
7261
  const stats = await getDailyUsage();
7211
7262
  if (stats[key] !== void 0) {
7212
7263
  stats[key]++;
7213
- queueFlush();
7214
7264
  }
7265
+ if (provider && key === "agent") {
7266
+ if (!stats.providerRequests) {
7267
+ stats.providerRequests = {};
7268
+ }
7269
+ stats.providerRequests[provider] = (stats.providerRequests[provider] || 0) + 1;
7270
+ }
7271
+ queueFlush();
7215
7272
  };
7216
7273
  addToUsage = async (key, amount, provider, model) => {
7217
7274
  const stats = await getDailyUsage();
@@ -7306,14 +7363,30 @@ var init_usage = __esm({
7306
7363
  checkQuota = async (key, settings) => {
7307
7364
  const tier = settings.apiTier || "Free";
7308
7365
  const quotas = settings.quotas || {};
7366
+ const resolveAgentLimits = () => {
7367
+ const providerBudgets = quotas.providerBudgets || {};
7368
+ const useProvider = !!providerBudgets.__useProvider;
7369
+ const currentProvider = settings.aiProvider || "Google";
7370
+ if (useProvider && providerBudgets[currentProvider]) {
7371
+ const pb = providerBudgets[currentProvider];
7372
+ return {
7373
+ agentLimit: typeof pb.agentLimit === "number" && pb.agentLimit > 0 ? pb.agentLimit : 99999999,
7374
+ tokenLimit: typeof pb.tokenLimit === "number" && pb.tokenLimit > 0 ? pb.tokenLimit : 99999999999999,
7375
+ monthlyTokenLimit: typeof pb.monthlyTokenLimit === "number" && pb.monthlyTokenLimit > 0 ? pb.monthlyTokenLimit : 99999999999999
7376
+ };
7377
+ }
7378
+ return {
7379
+ agentLimit: quotas.agentLimit || 99999999,
7380
+ tokenLimit: quotas.tokenLimit || 99999999999999,
7381
+ monthlyTokenLimit: quotas.monthlyTokenLimit || 99999999999999
7382
+ };
7383
+ };
7309
7384
  if (tier === "Free") {
7310
7385
  if (key === "agent") {
7311
- const reqLimit = quotas.agentLimit || 99999999;
7312
- const tokenLimit = quotas.tokenLimit || 99999999999999;
7313
- const monthlyTokenLimit = quotas.monthlyTokenLimit || 99999999999999;
7386
+ const { agentLimit, tokenLimit, monthlyTokenLimit } = resolveAgentLimits();
7314
7387
  const dailyUsage = await getDailyUsage();
7315
7388
  if (dailyUsage.agent + dailyUsage.background >= 999999) return false;
7316
- const dailyOk = dailyUsage.agent < reqLimit && (dailyUsage.tokens || 0) < tokenLimit;
7389
+ const dailyOk = dailyUsage.agent < agentLimit && (dailyUsage.tokens || 0) < tokenLimit;
7317
7390
  if (!dailyOk) return false;
7318
7391
  let monthlyUsage;
7319
7392
  if (quotas.resetMode === "Custom") {
@@ -7335,11 +7408,9 @@ var init_usage = __esm({
7335
7408
  }
7336
7409
  if (tier === "Paid" || tier === "Custom") {
7337
7410
  if (key === "agent") {
7338
- const reqLimit = quotas.agentLimit || 99999999;
7339
- const tokenLimit = quotas.tokenLimit || 99999999999999;
7340
- const monthlyTokenLimit = quotas.monthlyTokenLimit || 99999999999999;
7411
+ const { agentLimit, tokenLimit, monthlyTokenLimit } = resolveAgentLimits();
7341
7412
  const dailyUsage = await getDailyUsage();
7342
- const dailyOk = dailyUsage.agent < reqLimit && (dailyUsage.tokens || 0) < tokenLimit;
7413
+ const dailyOk = dailyUsage.agent < agentLimit && (dailyUsage.tokens || 0) < tokenLimit;
7343
7414
  if (!dailyOk) return false;
7344
7415
  let monthlyUsage;
7345
7416
  if (quotas.resetMode === "Custom") {
@@ -8382,6 +8453,37 @@ async function getFilesRecursively(dir, excludes, baseDir = dir, depth = 1) {
8382
8453
  }
8383
8454
  return results;
8384
8455
  }
8456
+ function normStr(s) {
8457
+ return s.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
8458
+ }
8459
+ function levenshtein(a, b) {
8460
+ if (a === b) return 0;
8461
+ if (a.length === 0) return b.length;
8462
+ if (b.length === 0) return a.length;
8463
+ const cap = Math.floor(Math.max(a.length, b.length) / 2) + 1;
8464
+ const dp = Array.from({ length: a.length + 1 }, (_, i) => i);
8465
+ for (let j = 1; j <= b.length; j++) {
8466
+ let prev = dp[0];
8467
+ dp[0] = j;
8468
+ for (let i = 1; i <= a.length; i++) {
8469
+ const tmp = dp[i];
8470
+ dp[i] = b[j - 1] === a[i - 1] ? prev : 1 + Math.min(prev, dp[i], dp[i - 1]);
8471
+ prev = tmp;
8472
+ }
8473
+ if (Math.min(...dp) > cap) return cap + 1;
8474
+ }
8475
+ return dp[a.length];
8476
+ }
8477
+ function fuzzyMatch(line, keyword) {
8478
+ const normLine = normStr(line);
8479
+ const lineWords = normLine.split(" ");
8480
+ const kwTokens = normStr(keyword).split(" ").filter(Boolean);
8481
+ if (normLine.includes(normStr(keyword))) return true;
8482
+ return kwTokens.every((token) => {
8483
+ const maxDist = token.length <= 2 ? 0 : token.length <= 5 ? 1 : 2;
8484
+ return lineWords.some((word) => levenshtein(token, word) <= maxDist);
8485
+ });
8486
+ }
8385
8487
  var search_keyword;
8386
8488
  var init_search_keyword = __esm({
8387
8489
  "src/tools/search_keyword.js"() {
@@ -8389,7 +8491,7 @@ var init_search_keyword = __esm({
8389
8491
  search_keyword = async (args) => {
8390
8492
  const { keyword, file, subString } = parseArgs(args);
8391
8493
  if (!keyword) return 'ERROR: Missing "keyword" argument.';
8392
- const matchSubstring = subString === true || subString === "true" || subString === 1 || subString === "1" || subString === "true";
8494
+ const matchSubstring = subString === true || subString === "true" || subString === 1 || subString === "1" || subString === "true" || subString === "yes" || subString === "yes" || false;
8393
8495
  const wordRegex = new RegExp(`(?<![\\w])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w])`, "i");
8394
8496
  const excludes = [
8395
8497
  "node_modules",
@@ -8430,30 +8532,50 @@ var init_search_keyword = __esm({
8430
8532
  const lines = content.split(/\r?\n/);
8431
8533
  const fileMatches = [];
8432
8534
  for (let i = 0; i < lines.length; i++) {
8433
- const matched = matchSubstring ? lines[i].toLowerCase().includes(keyword.toLowerCase()) : wordRegex.test(lines[i]);
8535
+ const matched = matchSubstring ? lines[i].toLowerCase().includes(keyword.toLowerCase()) || fuzzyMatch(lines[i], keyword) : wordRegex.test(lines[i]);
8434
8536
  if (matched) {
8435
- const displayPath = fileObj.relativePath.replace(/\\/g, "/");
8436
- fileMatches.push(`${displayPath} \u2192 ${i + 1}`);
8537
+ fileMatches.push({ line: i + 1, content: lines[i].trim() });
8437
8538
  }
8438
8539
  }
8439
- return fileMatches;
8540
+ if (fileMatches.length === 0) return null;
8541
+ const displayPath = fileObj.relativePath.replace(/\\/g, "/");
8542
+ return { path: displayPath, matches: fileMatches };
8440
8543
  } catch {
8441
8544
  return [];
8442
8545
  }
8443
8546
  });
8444
8547
  const settledResults = await Promise.all(searchPromises);
8445
- const matches = settledResults.flat().slice(0, maxMatches);
8548
+ const fileGroups = [];
8549
+ let totalMatches = 0;
8550
+ for (const result of settledResults) {
8551
+ if (!result) continue;
8552
+ if (totalMatches >= maxMatches) break;
8553
+ const remaining = maxMatches - totalMatches;
8554
+ const trimmedMatches = result.matches.slice(0, remaining);
8555
+ fileGroups.push({ path: result.path, matches: trimmedMatches });
8556
+ totalMatches += trimmedMatches.length;
8557
+ }
8446
8558
  if (typeof global.gc === "function") {
8447
8559
  global.gc();
8448
8560
  }
8449
- if (matches.length === 0) {
8561
+ if (fileGroups.length === 0) {
8450
8562
  return `Found 0 matches for keyword: "${keyword}"${file ? ` in file: ${file}` : ". Try to specify files"} ${matchSubstring ? "(subString mode)" : ""}`;
8451
8563
  }
8452
- let output = `Found ${matches.length} matches ${matchSubstring ? "(subString mode)" : ""}:
8564
+ let output = `Found ${totalMatches} match${totalMatches === 1 ? "" : "es"} across ${fileGroups.length} file${fileGroups.length === 1 ? "" : "s"} ${matchSubstring ? "(subString mode)" : ""}:
8453
8565
 
8454
8566
  `;
8455
- output += matches.join("\n");
8456
- return output;
8567
+ for (const group of fileGroups) {
8568
+ output += `${group.path}
8569
+ `;
8570
+ for (let i = 0; i < group.matches.length; i++) {
8571
+ const isLast = i === group.matches.length - 1;
8572
+ const prefix = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
8573
+ output += `${prefix} ${group.matches[i].line}: ${group.matches[i].content}
8574
+ `;
8575
+ }
8576
+ output += "\n";
8577
+ }
8578
+ return output.trimEnd();
8457
8579
  } catch (error) {
8458
8580
  return `ERROR: ${error.message}`;
8459
8581
  }
@@ -11579,7 +11701,7 @@ ${activeSummaryBlock}${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" &&
11579
11701
  } else if (retryCount > 0) {
11580
11702
  yield { type: "model_update", content: null };
11581
11703
  }
11582
- currentSystemInstruction = getSystemInstruction(profile, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? "GEM" : thinkingLevel, mode, systemSettings, isMemoryEnabled, isFirstPrompt, aiProvider, aiProvider === "Google" ? true : isMultiModal);
11704
+ currentSystemInstruction = getSystemInstruction(profile, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? thinkingLevel : thinkingLevel, mode, systemSettings, isMemoryEnabled, isFirstPrompt, aiProvider, aiProvider === "Google" ? true : isMultiModal, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? true : false);
11583
11705
  const lastUserMsg = contents[contents.length - 1];
11584
11706
  if (isBridgeConnected() & loop > 0) {
11585
11707
  yield { type: "status", content: "Checking Code..." };
@@ -12851,7 +12973,7 @@ ${boxBottom}`}`) };
12851
12973
  const { keyword, file } = parseArgs(toolCall.args);
12852
12974
  let matchCount = 0;
12853
12975
  if (result) {
12854
- const m = result.match(/Found (\d+) matches/i);
12976
+ const m = result.match(/Found (\d+) match/i);
12855
12977
  if (m) {
12856
12978
  matchCount = parseInt(m[1]);
12857
12979
  }
@@ -13016,7 +13138,7 @@ ${colorMainWords(output)}` };
13016
13138
  if (!hasFinish2 && !hasContinue2 && !didCallTool && signalSafeText2.length > 0 && !endsNormally && !isThinkingLoop && !isStutteringLoop && !isGeneralLoop) {
13017
13139
  }
13018
13140
  success = true;
13019
- await incrementUsage("agent");
13141
+ await incrementUsage("agent", aiProvider);
13020
13142
  } catch (err) {
13021
13143
  if (TERMINATION_SIGNAL) {
13022
13144
  yield { type: "status", content: "Request Cancelled" };
@@ -13034,7 +13156,7 @@ ${colorMainWords(output)}` };
13034
13156
  }
13035
13157
  }
13036
13158
  success = true;
13037
- await incrementUsage("agent");
13159
+ await incrementUsage("agent", aiProvider);
13038
13160
  break;
13039
13161
  }
13040
13162
  if (inThinkingState) {
@@ -14550,8 +14672,13 @@ function App({ args = [] }) {
14550
14672
  };
14551
14673
  const [activeView, setActiveView] = useState14("chat");
14552
14674
  const [apiTier, setApiTier] = useState14("Free");
14553
- const [quotas, setQuotas] = useState14({ limitMode: "Daily", agentLimit: 99999999, tokenLimit: 99999999999999, backgroundLimit: 999999, searchLimit: 100, customModelId: "", customLimit: 0 });
14675
+ const [quotas, setQuotas] = useState14({ limitMode: "Daily", agentLimit: 99999999, tokenLimit: 99999999999999, backgroundLimit: 999999, searchLimit: 100, customModelId: "", customLimit: 0, providerBudgets: {} });
14554
14676
  const [inputConfig, setInputConfig] = useState14(null);
14677
+ const [budgetReturnView, setBudgetReturnView] = useState14("chat");
14678
+ const [providerBudgetQueue, setProviderBudgetQueue] = useState14([]);
14679
+ const [providerBudgetCursor, setProviderBudgetCursor] = useState14(0);
14680
+ const [pbsCursor, setPbsCursor] = useState14(0);
14681
+ const [pbsSelected, setPbsSelected] = useState14({});
14555
14682
  const [systemSettings, setSystemSettings] = useState14({ memory: true, compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
14556
14683
  const [profileData, setProfileData] = useState14({ name: null, nickname: null, instructions: null });
14557
14684
  const [imageSettings, setImageSettings] = useState14({ keyType: "Default", quality: "Low-High", apiKey: "" });
@@ -15007,6 +15134,34 @@ function App({ args = [] }) {
15007
15134
  }
15008
15135
  return;
15009
15136
  }
15137
+ if (activeView === "providerBudgetSelect") {
15138
+ const PBS_PROVIDERS = ["Google", "DeepSeek", "NVIDIA", "OpenRouter"];
15139
+ if (key.upArrow) {
15140
+ setPbsCursor((c) => (c - 1 + PBS_PROVIDERS.length) % PBS_PROVIDERS.length);
15141
+ return;
15142
+ } else if (key.downArrow) {
15143
+ setPbsCursor((c) => (c + 1) % PBS_PROVIDERS.length);
15144
+ return;
15145
+ } else if (inputText === " ") {
15146
+ const prov = PBS_PROVIDERS[pbsCursor];
15147
+ setPbsSelected((s) => ({ ...s, [prov]: !s[prov] }));
15148
+ return;
15149
+ } else if (key.return) {
15150
+ const chosenProviders = PBS_PROVIDERS.filter((p) => pbsSelected[p]);
15151
+ if (chosenProviders.length === 0) return;
15152
+ const updatedQuotas = { ...quotas, providerBudgets: { ...quotas.providerBudgets || {}, __useProvider: true } };
15153
+ setQuotas(updatedQuotas);
15154
+ setProviderBudgetQueue(chosenProviders);
15155
+ setProviderBudgetCursor(0);
15156
+ setPbsCursor(0);
15157
+ setActiveView("providerBudgetFlow");
15158
+ return;
15159
+ } else if (key.escape) {
15160
+ setActiveView("budgetTypeSelect");
15161
+ return;
15162
+ }
15163
+ return;
15164
+ }
15010
15165
  if (key.escape) {
15011
15166
  if (showBtwBox) {
15012
15167
  setShowBtwBox(false);
@@ -15165,7 +15320,8 @@ function App({ args = [] }) {
15165
15320
  }
15166
15321
  const startupProvider = parsedArgs.provider || saved.aiProvider || "Google";
15167
15322
  setAiProvider(startupProvider);
15168
- const currentTier = saved.apiTier || "Free";
15323
+ const providerTiers = saved.quotas?.providerTiers || {};
15324
+ const currentTier = providerTiers[startupProvider] || saved.apiTier || "Free";
15169
15325
  persistedModelRef.current = saved.activeModel;
15170
15326
  if (parsedArgs.model) {
15171
15327
  setActiveModel(parsedArgs.model);
@@ -15197,8 +15353,8 @@ function App({ args = [] }) {
15197
15353
  setActiveModel(saved.activeModel);
15198
15354
  }
15199
15355
  setShowFullThinking(saved.showFullThinking);
15200
- setApiTier(saved.apiTier || "Free");
15201
- setQuotas(saved.quotas || { limitMode: "Daily", agentLimit: 99999999, tokenLimit: 99999999999999, backgroundLimit: 999999, searchLimit: 100, customModelId: "", customLimit: 0 });
15356
+ setApiTier(currentTier);
15357
+ setQuotas(saved.quotas || { limitMode: "Daily", agentLimit: 99999999, tokenLimit: 99999999999999, backgroundLimit: 999999, searchLimit: 100, customModelId: "", customLimit: 0, providerBudgets: {} });
15202
15358
  const freshSettings = {
15203
15359
  memory: true,
15204
15360
  compression: 0,
@@ -15458,14 +15614,6 @@ function App({ args = [] }) {
15458
15614
  { cmd: "/export", desc: "Export current chat in a .txt file" },
15459
15615
  { cmd: "/chats", desc: "List all chat sessions" },
15460
15616
  { cmd: "/btw", desc: "Ask a question without intefering with ongoing tasks" },
15461
- {
15462
- cmd: "/mode",
15463
- desc: "Toggle Flux/Flow modes",
15464
- subs: [
15465
- { cmd: "flux", desc: "Enable Dev toolset" },
15466
- { cmd: "flow", desc: "Enable Chat mode" }
15467
- ]
15468
- },
15469
15617
  {
15470
15618
  cmd: "/thinking",
15471
15619
  desc: "Set AI reasoning depth",
@@ -15696,6 +15844,14 @@ function App({ args = [] }) {
15696
15844
  }
15697
15845
  ]
15698
15846
  },
15847
+ {
15848
+ cmd: "/mode",
15849
+ desc: "Toggle Flux/Flow modes",
15850
+ subs: [
15851
+ { cmd: "flux", desc: "Enable Dev toolset" },
15852
+ { cmd: "flow", desc: "Enable Chat mode" }
15853
+ ]
15854
+ },
15699
15855
  { cmd: "/settings", desc: "Configure system prefs" },
15700
15856
  { cmd: "/key", desc: "Manage API keys" },
15701
15857
  { cmd: "/profile", desc: "Edit developer persona" },
@@ -15717,7 +15873,8 @@ function App({ args = [] }) {
15717
15873
  desc: "Set or View budget limits",
15718
15874
  subs: [
15719
15875
  { cmd: "view", desc: "View current usage budget bars" },
15720
- { cmd: "set", desc: "Configure budgets (Daily/Monthly limits)" }
15876
+ { cmd: "set", desc: "Configure budgets (Daily/Monthly limits)" },
15877
+ { cmd: "reset", desc: "Reset budgets to default limits" }
15721
15878
  ]
15722
15879
  },
15723
15880
  {
@@ -16368,28 +16525,8 @@ ${list || "No saved chats found."}`, isMeta: true }];
16368
16525
  case "/budget": {
16369
16526
  const sub = parts[1]?.toLowerCase();
16370
16527
  if (sub === "set") {
16371
- setInputConfig({
16372
- label: "Enter Agent daily budget (requests made):",
16373
- key: "quotas",
16374
- subKey: "agentLimit",
16375
- value: getPrefilledValue(quotas.agentLimit),
16376
- returnView: "chat",
16377
- next: (newQuotas) => ({
16378
- label: "Enter Agent daily budget (tokens used):",
16379
- key: "quotas",
16380
- subKey: "tokenLimit",
16381
- value: getPrefilledValue(newQuotas.tokenLimit),
16382
- returnView: "chat",
16383
- next: (q2) => ({
16384
- label: "Enter Agent monthly budget (tokens used):",
16385
- key: "quotas",
16386
- subKey: "monthlyTokenLimit",
16387
- value: getPrefilledValue(q2.monthlyTokenLimit),
16388
- returnView: "budgetResetMode"
16389
- })
16390
- })
16391
- });
16392
- setActiveView("input");
16528
+ setBudgetReturnView("chat");
16529
+ setActiveView("budgetTypeSelect");
16393
16530
  } else if (sub === "view") {
16394
16531
  const run = async () => {
16395
16532
  const usage = await getDailyUsage();
@@ -16401,10 +16538,34 @@ ${list || "No saved chats found."}`, isMeta: true }];
16401
16538
  setActiveView("budgetView");
16402
16539
  };
16403
16540
  run();
16541
+ } else if (sub === "reset") {
16542
+ const defaultQuotas = {
16543
+ limitMode: "Daily",
16544
+ agentLimit: 99999999,
16545
+ tokenLimit: 99999999999999,
16546
+ backgroundLimit: 999999,
16547
+ searchLimit: 100,
16548
+ customModelId: "",
16549
+ customLimit: 0,
16550
+ providerBudgets: {},
16551
+ providerTiers: {
16552
+ Google: "Free",
16553
+ DeepSeek: "Free",
16554
+ NVIDIA: "Free",
16555
+ OpenRouter: "Free"
16556
+ }
16557
+ };
16558
+ setQuotas(defaultQuotas);
16559
+ setApiTier("Free");
16560
+ saveSettings({ apiTier: "Free", quotas: defaultQuotas });
16561
+ setMessages((prev) => {
16562
+ setCompletedIndex(prev.length + 1);
16563
+ return [...prev, { id: Date.now(), role: "system", text: `\u2705 [BUDGET] Budgets and limits reset to default values successfully.`, isMeta: true }];
16564
+ });
16404
16565
  } else {
16405
16566
  setMessages((prev) => {
16406
16567
  setCompletedIndex(prev.length + 1);
16407
- return [...prev, { id: Date.now(), role: "system", text: `Usage: /budget <Set|View>`, isMeta: true }];
16568
+ return [...prev, { id: Date.now(), role: "system", text: `Usage: /budget <Set|View|Reset>`, isMeta: true }];
16408
16569
  });
16409
16570
  }
16410
16571
  break;
@@ -17212,6 +17373,87 @@ Selection: ${val}`,
17212
17373
  useEffect11(() => {
17213
17374
  setSelectedIndex(0);
17214
17375
  }, [suggestions]);
17376
+ useEffect11(() => {
17377
+ if (activeView !== "providerBudgetSelect") return;
17378
+ const PBS_PROVIDERS = ["Google", "DeepSeek", "NVIDIA", "OpenRouter"];
17379
+ const existingBudgets = quotas.providerBudgets || {};
17380
+ const initialSelected = PBS_PROVIDERS.reduce((acc, p) => {
17381
+ acc[p] = !!(existingBudgets[p] && (existingBudgets[p].agentLimit || existingBudgets[p].tokenLimit));
17382
+ return acc;
17383
+ }, {});
17384
+ setPbsSelected(initialSelected);
17385
+ setPbsCursor(0);
17386
+ }, [activeView]);
17387
+ useEffect11(() => {
17388
+ if (activeView !== "providerBudgetFlow") return;
17389
+ const currentProvider = providerBudgetQueue[providerBudgetCursor];
17390
+ if (!currentProvider) {
17391
+ const returnMode = budgetReturnView === "settings" ? "resetMode" : "budgetResetMode";
17392
+ const rawPB = quotas.providerBudgets || {};
17393
+ const cleaned = { __useProvider: true };
17394
+ for (const prov of providerBudgetQueue) {
17395
+ if (rawPB[prov]) cleaned[prov] = rawPB[prov];
17396
+ }
17397
+ const finalCleanedQuotas = { ...quotas, providerBudgets: cleaned };
17398
+ setQuotas(finalCleanedQuotas);
17399
+ saveSettings({ apiTier, quotas: finalCleanedQuotas });
17400
+ setActiveView(returnMode);
17401
+ return;
17402
+ }
17403
+ const existingPB = (quotas.providerBudgets || {})[currentProvider] || {};
17404
+ const totalProviders = providerBudgetQueue.length;
17405
+ const currentStep = providerBudgetCursor + 1;
17406
+ const providerLabel = `[${currentStep}/${totalProviders}] ${currentProvider}`;
17407
+ const advanceToNext = (finalQuotas) => {
17408
+ if (providerBudgetCursor + 1 < providerBudgetQueue.length) {
17409
+ setProviderBudgetCursor((c) => c + 1);
17410
+ setActiveView("providerBudgetFlow");
17411
+ } else {
17412
+ const rawPB = finalQuotas.providerBudgets || {};
17413
+ const cleaned = { __useProvider: true };
17414
+ for (const prov of providerBudgetQueue) {
17415
+ if (rawPB[prov]) cleaned[prov] = rawPB[prov];
17416
+ }
17417
+ const finalCleanedQuotas = { ...finalQuotas, providerBudgets: cleaned };
17418
+ setQuotas(finalCleanedQuotas);
17419
+ const rm = budgetReturnView === "settings" ? "resetMode" : "budgetResetMode";
17420
+ saveSettings({ apiTier, quotas: finalCleanedQuotas });
17421
+ setActiveView(rm);
17422
+ }
17423
+ };
17424
+ setInputConfig({
17425
+ label: `${providerLabel} \u2014 Daily budget (requests/day):`,
17426
+ key: "providerBudgets",
17427
+ providerKey: currentProvider,
17428
+ subKey: "agentLimit",
17429
+ value: getPrefilledValue(existingPB.agentLimit),
17430
+ returnView: "providerBudgetSelect",
17431
+ next: (newQuotas) => {
17432
+ const updatedPB = (newQuotas.providerBudgets || {})[currentProvider] || {};
17433
+ return {
17434
+ label: `${providerLabel} \u2014 Daily budget (tokens/day):`,
17435
+ key: "providerBudgets",
17436
+ providerKey: currentProvider,
17437
+ subKey: "tokenLimit",
17438
+ value: getPrefilledValue(updatedPB.tokenLimit),
17439
+ returnView: "providerBudgetSelect",
17440
+ next: (q2) => {
17441
+ const pb2 = (q2.providerBudgets || {})[currentProvider] || {};
17442
+ return {
17443
+ label: `${providerLabel} \u2014 Monthly budget (tokens/month):`,
17444
+ key: "providerBudgets",
17445
+ providerKey: currentProvider,
17446
+ subKey: "monthlyTokenLimit",
17447
+ value: getPrefilledValue(pb2.monthlyTokenLimit),
17448
+ returnView: "providerBudgetFlow",
17449
+ onDone: advanceToNext
17450
+ };
17451
+ }
17452
+ };
17453
+ }
17454
+ });
17455
+ setActiveView("input");
17456
+ }, [activeView, providerBudgetCursor]);
17215
17457
  const CustomMenuItem = ({ label: label2, isSelected }) => {
17216
17458
  const isCancel = label2 === "Cancel" || label2 === "Back" || label2.toLowerCase().includes("exit") || label2.toLowerCase().includes("back");
17217
17459
  return /* @__PURE__ */ React15.createElement(
@@ -17290,7 +17532,9 @@ Selection: ${val}`,
17290
17532
  defaultModel = "moonshotai/kimi-k2.6";
17291
17533
  }
17292
17534
  setActiveModel(defaultModel);
17293
- saveSettings({ aiProvider: selectedProvider, activeModel: defaultModel, apiTier, quotas });
17535
+ const targetTier = (quotas.providerTiers || {})[selectedProvider] || "Free";
17536
+ setApiTier(targetTier);
17537
+ saveSettings({ aiProvider: selectedProvider, activeModel: defaultModel, apiTier: targetTier, quotas });
17294
17538
  setMessages((prev) => [
17295
17539
  ...prev,
17296
17540
  {
@@ -17315,30 +17559,12 @@ Selection: ${val}`,
17315
17559
  }
17316
17560
  );
17317
17561
  case "apiTier": {
17318
- const reqCurrent = dailyUsage?.agent || 0;
17319
- const reqLimit = quotas.agentLimit || 99999999;
17320
- const tokenCurrent = dailyUsage?.tokens || 0;
17321
- const tokenLimit = quotas.tokenLimit || 99999999999999;
17322
- const monthlyCurrent = quotas.resetMode === "Custom" ? customPeriodUsage?.tokens || 0 : monthlyUsage?.tokens || 0;
17323
- const monthlyLimit = quotas.monthlyTokenLimit || 99999999999999;
17324
- let resetInfo = "";
17325
- if (quotas.resetMode === "Custom") {
17326
- const today = /* @__PURE__ */ new Date();
17327
- const resetDay = quotas.resetDay || 1;
17328
- let resetMonth = today.getMonth();
17329
- if (today.getDate() >= resetDay) {
17330
- resetMonth += 1;
17331
- }
17332
- const resetDate = new Date(today.getFullYear(), resetMonth, resetDay);
17333
- const monthName = resetDate.toLocaleString("default", { month: "short" });
17334
- resetInfo = `Resets on: ${resetDay}-${monthName}`;
17335
- }
17336
- return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "white", padding: 0, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, "SELECT YOUR CURRENT API TIER BASED ON YOUR PROVIDER. (Provider: ", aiProvider, ")")), /* @__PURE__ */ React15.createElement(
17562
+ return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "white", padding: 0, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, "SET API KEY STRATEGY FOR ", aiProvider, ".")), /* @__PURE__ */ React15.createElement(
17337
17563
  SelectInput2,
17338
17564
  {
17339
17565
  items: [
17340
- { label: "Provider Limits", value: "Free" },
17341
- { label: `Set Budgets (API with Billing Account) ${apiTier === "Paid" ? "\u25CF" : ""}`, value: "Paid" },
17566
+ { label: "Free Key [Basic set of Models]", value: "Free" },
17567
+ { label: `Billing Key [Premium Models Available] ${apiTier === "Paid" ? "\u25CF" : ""}`, value: "Paid" },
17342
17568
  { label: "Back", value: "settings" }
17343
17569
  ],
17344
17570
  onSelect: (item) => {
@@ -17348,40 +17574,22 @@ Selection: ${val}`,
17348
17574
  }
17349
17575
  const newTier = item.value;
17350
17576
  setApiTier(newTier);
17351
- if (newTier === "Paid") {
17352
- setInputConfig({
17353
- label: "Enter Agent daily budget (requests made):",
17354
- key: "quotas",
17355
- subKey: "agentLimit",
17356
- value: getPrefilledValue(quotas.agentLimit),
17357
- returnView: "settings",
17358
- next: (newQuotas) => ({
17359
- label: "Enter Agent daily budget (tokens used):",
17360
- key: "quotas",
17361
- subKey: "tokenLimit",
17362
- value: getPrefilledValue(newQuotas.tokenLimit),
17363
- returnView: "settings",
17364
- next: (q2) => ({
17365
- label: "Enter Agent monthly budget (tokens used):",
17366
- key: "quotas",
17367
- subKey: "monthlyTokenLimit",
17368
- value: getPrefilledValue(q2.monthlyTokenLimit),
17369
- returnView: "resetMode"
17370
- })
17371
- })
17372
- });
17373
- setActiveView("input");
17374
- } else {
17375
- const newQuotas = { ...quotas, agentLimit: 99999999, tokenLimit: 99999999999999, monthlyTokenLimit: 99999999999999 };
17376
- setQuotas(newQuotas);
17377
- saveSettings({ apiTier: newTier, quotas: newQuotas });
17378
- setActiveView("settings");
17379
- }
17577
+ const updatedProviderTiers = {
17578
+ ...quotas.providerTiers || {},
17579
+ [aiProvider]: newTier
17580
+ };
17581
+ const newQuotas = {
17582
+ ...quotas,
17583
+ providerTiers: updatedProviderTiers
17584
+ };
17585
+ setQuotas(newQuotas);
17586
+ saveSettings({ apiTier: newTier, quotas: newQuotas });
17587
+ setActiveView("settings");
17380
17588
  },
17381
17589
  itemComponent: CustomMenuItem,
17382
17590
  indicatorComponent: () => null
17383
17591
  }
17384
- ), apiTier === "Paid" && /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "white", bold: true }, "USAGE BUDGET STATUS")), renderProgressBar("Daily Requests", reqCurrent, reqLimit, "cyan"), renderProgressBar("Daily Tokens", tokenCurrent, tokenLimit, "green"), renderProgressBar("Monthly Tokens", monthlyCurrent, monthlyLimit, "yellow"), resetInfo ? /* @__PURE__ */ React15.createElement(Box15, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "Monthly Reset : "), /* @__PURE__ */ React15.createElement(Text16, { color: "magenta", bold: true }, resetInfo)) : /* @__PURE__ */ React15.createElement(Box15, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "Monthly Reset : "), /* @__PURE__ */ React15.createElement(Text16, { color: "blue", bold: true }, "Rolling 30-Day Window"))), /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", italic: true }, "(Arrows to select \u2022 Enter to confirm)")));
17592
+ ), /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", italic: true }, "(Arrows to select \u2022 Enter to confirm)")));
17385
17593
  }
17386
17594
  case "resetMode":
17387
17595
  return /* @__PURE__ */ React15.createElement(
@@ -17418,6 +17626,82 @@ Selection: ${val}`,
17418
17626
  onClose: () => setActiveView("apiTier")
17419
17627
  }
17420
17628
  );
17629
+ case "budgetTypeSelect":
17630
+ return /* @__PURE__ */ React15.createElement(
17631
+ CommandMenu,
17632
+ {
17633
+ title: "SELECT BUDGET TYPE",
17634
+ items: [
17635
+ { label: `Global Budget (single limit for all providers) ${apiTier === "Paid" && !quotas.providerBudgets?.["__useProvider"] ? "\u25CF" : ""}`, value: "global" },
17636
+ { label: `Provider Budgets (set limits per provider individually) ${quotas.providerBudgets?.["__useProvider"] ? "\u25CF" : ""}`, value: "provider" },
17637
+ { label: "Back", value: budgetReturnView }
17638
+ ],
17639
+ onSelect: (item) => {
17640
+ if (item.value === budgetReturnView || item.value === "Back") {
17641
+ setActiveView(budgetReturnView);
17642
+ return;
17643
+ }
17644
+ if (item.value === "global") {
17645
+ const updatedQuotas = {
17646
+ ...quotas,
17647
+ agentLimit: 99999999,
17648
+ tokenLimit: 99999999999999,
17649
+ monthlyTokenLimit: 99999999999999,
17650
+ providerBudgets: { __useProvider: false }
17651
+ };
17652
+ setQuotas(updatedQuotas);
17653
+ const returnMode = budgetReturnView === "settings" ? "resetMode" : "budgetResetMode";
17654
+ setInputConfig({
17655
+ label: "Enter Agent daily budget (requests made):",
17656
+ key: "quotas",
17657
+ subKey: "agentLimit",
17658
+ value: getPrefilledValue(updatedQuotas.agentLimit),
17659
+ returnView: budgetReturnView,
17660
+ next: (newQuotas) => ({
17661
+ label: "Enter Agent daily budget (tokens used):",
17662
+ key: "quotas",
17663
+ subKey: "tokenLimit",
17664
+ value: getPrefilledValue(newQuotas.tokenLimit),
17665
+ returnView: budgetReturnView,
17666
+ next: (q2) => ({
17667
+ label: "Enter Agent monthly budget (tokens used):",
17668
+ key: "quotas",
17669
+ subKey: "monthlyTokenLimit",
17670
+ value: getPrefilledValue(q2.monthlyTokenLimit),
17671
+ returnView: returnMode
17672
+ })
17673
+ })
17674
+ });
17675
+ setActiveView("input");
17676
+ } else if (item.value === "provider") {
17677
+ const updatedQuotas = {
17678
+ ...quotas,
17679
+ agentLimit: 99999999,
17680
+ tokenLimit: 99999999999999,
17681
+ monthlyTokenLimit: 99999999999999,
17682
+ providerBudgets: {
17683
+ ...quotas.providerBudgets || {},
17684
+ __useProvider: true
17685
+ }
17686
+ };
17687
+ setQuotas(updatedQuotas);
17688
+ setActiveView("providerBudgetSelect");
17689
+ }
17690
+ },
17691
+ onClose: () => setActiveView(budgetReturnView)
17692
+ }
17693
+ );
17694
+ case "providerBudgetSelect": {
17695
+ const PROVIDERS_LIST = ["Google", "DeepSeek", "NVIDIA", "OpenRouter"];
17696
+ const anySelected = PROVIDERS_LIST.some((p) => pbsSelected[p]);
17697
+ return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "white", padding: 0, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, "SELECT PROVIDERS TO SET BUDGETS FOR")), PROVIDERS_LIST.map((prov, i) => {
17698
+ const isActive = i === pbsCursor;
17699
+ const isChecked = !!pbsSelected[prov];
17700
+ return /* @__PURE__ */ React15.createElement(Box15, { key: prov, backgroundColor: isActive ? "#2a2a2a" : void 0, paddingX: 1, width: "100%" }, /* @__PURE__ */ React15.createElement(Text16, { color: isActive ? "white" : "gray", bold: isActive }, isActive ? "\u276F " : " ", /* @__PURE__ */ React15.createElement(Text16, { color: isChecked ? "green" : "gray" }, isChecked ? "\u2611" : "\u2610"), " ", prov, isChecked && quotas.providerBudgets?.[prov]?.agentLimit ? /* @__PURE__ */ React15.createElement(Text16, { color: "cyan" }, " (budget set)") : null));
17701
+ }), /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", italic: true }, "\u2191\u2193 Navigate \u2022 Space to toggle \u2022 Enter to confirm \u2022 ESC to go back"), !anySelected && /* @__PURE__ */ React15.createElement(Text16, { color: "yellow", italic: true }, " Select at least one provider to continue")));
17702
+ }
17703
+ case "providerBudgetFlow":
17704
+ return null;
17421
17705
  case "budgetResetMode":
17422
17706
  return /* @__PURE__ */ React15.createElement(
17423
17707
  CommandMenu,
@@ -17461,7 +17745,12 @@ Selection: ${val}`,
17461
17745
  const monthlyCurrent = quotas.resetMode === "Custom" ? customPeriodUsage?.tokens || 0 : monthlyUsage?.tokens || 0;
17462
17746
  const monthlyLimit = quotas.monthlyTokenLimit || 99999999999999;
17463
17747
  const isFreeTier = apiTier !== "Paid";
17464
- const limitsNotSet = isFreeTier && (shouldClearValue(reqLimit) || shouldClearValue(tokenLimit) || shouldClearValue(monthlyLimit));
17748
+ const usingProviderBudgets = !!quotas.providerBudgets?.__useProvider;
17749
+ const providerBudgetsMap = quotas.providerBudgets || {};
17750
+ const configuredProviders = ["Google", "DeepSeek", "NVIDIA", "OpenRouter"].filter(
17751
+ (p) => providerBudgetsMap[p] && (providerBudgetsMap[p].agentLimit || providerBudgetsMap[p].tokenLimit || providerBudgetsMap[p].monthlyTokenLimit)
17752
+ );
17753
+ const limitsNotSet = !usingProviderBudgets && (shouldClearValue(reqLimit) || shouldClearValue(tokenLimit) || shouldClearValue(monthlyLimit));
17465
17754
  let resetInfo = "";
17466
17755
  if (quotas.resetMode === "Custom") {
17467
17756
  const today = /* @__PURE__ */ new Date();
@@ -17474,7 +17763,22 @@ Selection: ${val}`,
17474
17763
  const monthName = resetDate.toLocaleString("default", { month: "short" });
17475
17764
  resetInfo = `Resets on: ${resetDay}-${monthName}`;
17476
17765
  }
17477
- return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "white", padding: 1, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { marginBottom: 1, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React15.createElement(Text16, { color: "white", bold: true, underline: true }, "BUDGET LIMIT STATUS", isFreeTier ? " (Isn't it fun to see numbers go BRRRR)" : ""), /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "[ ESC to Close ]")), limitsNotSet ? /* @__PURE__ */ React15.createElement(Box15, { padding: 1, justifyContent: "center", alignItems: "center", width: "100%" }, /* @__PURE__ */ React15.createElement(Text16, { color: "yellow", bold: true }, "LIMITS NOT SET")) : /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, width: "100%" }, renderProgressBar("Daily Requests", reqCurrent, reqLimit, "cyan"), renderProgressBar("Daily Tokens", tokenCurrent, tokenLimit, "green"), renderProgressBar("Monthly Tokens", monthlyCurrent, monthlyLimit, "yellow"), resetInfo ? /* @__PURE__ */ React15.createElement(Box15, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "Monthly Reset : "), /* @__PURE__ */ React15.createElement(Text16, { color: "magenta", bold: true }, resetInfo)) : /* @__PURE__ */ React15.createElement(Box15, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "Monthly Reset : "), /* @__PURE__ */ React15.createElement(Text16, { color: "blue", bold: true }, "Rolling 30-Day Window"))));
17766
+ return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "white", padding: 1, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { marginBottom: 1, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React15.createElement(Text16, { color: "white", bold: true, underline: true }, "BUDGET LIMIT STATUS", isFreeTier && !usingProviderBudgets ? " (Isn't it fun to see numbers go BRRRR)" : ""), /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "[ ESC to Close ]")), limitsNotSet ? /* @__PURE__ */ React15.createElement(Box15, { padding: 1, justifyContent: "center", alignItems: "center", width: "100%" }, /* @__PURE__ */ React15.createElement(Text16, { color: "yellow", bold: true }, "LIMITS NOT SET")) : usingProviderBudgets && configuredProviders.length > 0 ? /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", gap: 1, width: "100%" }, configuredProviders.map((prov) => {
17767
+ const pb = providerBudgetsMap[prov];
17768
+ const provReqCurrent = dailyUsage?.providerRequests?.[prov] || 0;
17769
+ let provTokenCurrent = 0;
17770
+ const dailyModels = dailyUsage?.models?.[prov] || {};
17771
+ for (const m in dailyModels) {
17772
+ provTokenCurrent += dailyModels[m]?.tokens || 0;
17773
+ }
17774
+ let provMonthlyCurrent = 0;
17775
+ const monthlySource = quotas.resetMode === "Custom" ? customPeriodUsage : monthlyUsage;
17776
+ const monthlyModels = monthlySource?.models?.[prov] || {};
17777
+ for (const m in monthlyModels) {
17778
+ provMonthlyCurrent += monthlyModels[m]?.tokens || 0;
17779
+ }
17780
+ return /* @__PURE__ */ React15.createElement(Box15, { key: prov, flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { marginBottom: 0 }, /* @__PURE__ */ React15.createElement(Text16, { color: "cyan", bold: true }, "\u25C6 ", prov)), renderProgressBar("Daily Requests", provReqCurrent, pb.agentLimit || 99999999, "cyan"), renderProgressBar("Daily Tokens", provTokenCurrent, pb.tokenLimit || 99999999999999, "green"), renderProgressBar("Monthly Tokens", provMonthlyCurrent, pb.monthlyTokenLimit || 99999999999999, "yellow"));
17781
+ }), resetInfo ? /* @__PURE__ */ React15.createElement(Box15, { marginLeft: 4 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "Monthly Reset : "), /* @__PURE__ */ React15.createElement(Text16, { color: "magenta", bold: true }, resetInfo)) : /* @__PURE__ */ React15.createElement(Box15, { marginLeft: 4 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "Monthly Reset : "), /* @__PURE__ */ React15.createElement(Text16, { color: "blue", bold: true }, "Rolling 30-Day Window"))) : /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, width: "100%" }, renderProgressBar("Daily Requests", reqCurrent, reqLimit, "cyan"), renderProgressBar("Daily Tokens", tokenCurrent, tokenLimit, "green"), renderProgressBar("Monthly Tokens", monthlyCurrent, monthlyLimit, "yellow"), resetInfo ? /* @__PURE__ */ React15.createElement(Box15, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "Monthly Reset : "), /* @__PURE__ */ React15.createElement(Text16, { color: "magenta", bold: true }, resetInfo)) : /* @__PURE__ */ React15.createElement(Box15, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "Monthly Reset : "), /* @__PURE__ */ React15.createElement(Text16, { color: "blue", bold: true }, "Rolling 30-Day Window"))));
17478
17782
  }
17479
17783
  case "input":
17480
17784
  return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "white", padding: 0, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "white", bold: true }, "DATA CONFIGURATION")), inputConfig?.note && /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", italic: true }, inputConfig.note)), /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, flexDirection: "row" }, /* @__PURE__ */ React15.createElement(Text16, { color: "white", bold: true }, inputConfig?.label, " "), /* @__PURE__ */ React15.createElement(
@@ -17483,7 +17787,7 @@ Selection: ${val}`,
17483
17787
  value: inputConfig?.value || "",
17484
17788
  onChange: (val) => setInputConfig((prev) => ({ ...prev, value: val })),
17485
17789
  onSubmit: async (val) => {
17486
- const { key, subKey, next } = inputConfig;
17790
+ const { key, subKey, next, onDone } = inputConfig;
17487
17791
  let newQuotas = { ...quotas };
17488
17792
  let newSettings = {};
17489
17793
  if (key === "quotas") {
@@ -17494,6 +17798,19 @@ Selection: ${val}`,
17494
17798
  newQuotas[subKey] = parsedValue;
17495
17799
  setQuotas(newQuotas);
17496
17800
  newSettings.quotas = newQuotas;
17801
+ } else if (key === "providerBudgets") {
17802
+ const prov = inputConfig.providerKey;
17803
+ const parsedValue = subKey.toLowerCase().includes("limit") ? parseInt(val) || 0 : val;
17804
+ const existingPBudgets = newQuotas.providerBudgets || {};
17805
+ newQuotas.providerBudgets = {
17806
+ ...existingPBudgets,
17807
+ [prov]: {
17808
+ ...existingPBudgets[prov] || {},
17809
+ [subKey]: parsedValue
17810
+ }
17811
+ };
17812
+ setQuotas(newQuotas);
17813
+ newSettings.quotas = newQuotas;
17497
17814
  } else if (key === "activeModel") {
17498
17815
  setActiveModel(val);
17499
17816
  newSettings.activeModel = val;
@@ -17543,15 +17860,23 @@ Selection: ${val}`,
17543
17860
  defaultModel = "moonshotai/kimi-k2.6";
17544
17861
  }
17545
17862
  setActiveModel(defaultModel);
17863
+ const targetTier = (quotas.providerTiers || {})[prov] || "Free";
17864
+ setApiTier(targetTier);
17546
17865
  newSettings.aiProvider = prov;
17547
17866
  newSettings.activeModel = defaultModel;
17867
+ newSettings.apiTier = targetTier;
17548
17868
  setMessages((prev) => {
17549
17869
  setCompletedIndex(prev.length + 1);
17550
17870
  return [...prev, { id: Date.now(), role: "system", text: `\u2705 ${prov} API Key saved successfully! Model set to ${defaultModel}.`, isMeta: true }];
17551
17871
  });
17552
17872
  }
17553
17873
  if (next) {
17554
- setInputConfig(next(key === "quotas" ? newQuotas : val));
17874
+ const nextConfig = next(key === "quotas" || key === "providerBudgets" ? newQuotas : val);
17875
+ setInputConfig(nextConfig);
17876
+ } else if (onDone) {
17877
+ saveSettings({ ...newSettings, apiTier, quotas: newQuotas, imageSettings: newSettings.imageSettings || imageSettings });
17878
+ setInputConfig(null);
17879
+ onDone(newQuotas);
17555
17880
  } else {
17556
17881
  saveSettings({ ...newSettings, apiTier, quotas: newQuotas, imageSettings: newSettings.imageSettings || imageSettings });
17557
17882
  setInputConfig(null);
@@ -18045,7 +18370,7 @@ Selection: ${val}`,
18045
18370
  setSetupStep(1);
18046
18371
  }
18047
18372
  }
18048
- ))) : /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Text16, { color: "white" }, "Please enter your ", aiProvider, " API Key to initialize the agent (If billing is enabled set budgets in /settings \u2192 Others \u2192 Budgets to avoid runaway spending)."), /* @__PURE__ */ React15.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, " ", ">", " "), /* @__PURE__ */ React15.createElement(
18373
+ ))) : /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Text16, { color: "white" }, "Please enter your ", aiProvider, " API Key to initialize the agent (If billing is enabled set /settings \u2192 Others \u2192 API Strategy to use premium models. Set budget limit at /budgets.)."), /* @__PURE__ */ React15.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, " ", ">", " "), /* @__PURE__ */ React15.createElement(
18049
18374
  TextInput4,
18050
18375
  {
18051
18376
  value: tempKey,
@@ -18409,13 +18734,20 @@ var calculatedLimit = Math.floor(totalSystemRamMB * SAFETY_MARGIN);
18409
18734
  var _rawArgs = process.argv.slice(2);
18410
18735
  var _allocIdx = _rawArgs.indexOf("--allocation");
18411
18736
  var _allocValue = _allocIdx !== -1 ? parseInt(_rawArgs[_allocIdx + 1], 10) : NaN;
18737
+ if (!isNaN(_allocValue) && _allocValue < 64) {
18738
+ console.error(`
18739
+ [ERROR] Allocation value '${_allocValue} MB' is too low. Minimum: 64 MB, Recommended: 4096 MB.
18740
+ `);
18741
+ process.exit(1);
18742
+ }
18412
18743
  var _maxAllowed = Math.floor(totalSystemRamMB * 0.75);
18413
18744
  var HEAP_LIMIT = !isNaN(_allocValue) && _allocValue > 0 ? Math.min(_allocValue, _maxAllowed) : Math.max(1536, Math.min(32768, calculatedLimit));
18414
18745
  var isBundled = fileURLToPath2(import.meta.url).endsWith(".js");
18415
18746
  if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-size"))) {
18416
18747
  if (!Number.isNaN(_allocValue)) {
18417
- console.log("\n[MEMORY] Using custom memory allocation: " + _allocValue + " MB" + (_allocValue > _maxAllowed ? " (Max allowed: " + _maxAllowed + "MB)" : ""));
18418
- await new Promise((resolve) => setTimeout(resolve, 2e3));
18748
+ console.log(`
18749
+ [MEMORY] Starting with: '${_allocValue > _maxAllowed ? _maxAllowed : _allocValue} MB' Allocation ${_allocValue > _maxAllowed ? "(Max allowed: '" + _maxAllowed + " MB')" : ""}. Please Wait...`);
18750
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
18419
18751
  }
18420
18752
  const cp = spawn3(process.execPath, [
18421
18753
  `--max-old-space-size=${HEAP_LIMIT}`,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "2.14.1",
4
- "date": "2026-06-29",
3
+ "version": "2.15.0",
4
+ "date": "2026-06-30",
5
5
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",