fluxflow-cli 2.14.2 → 2.15.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.
Files changed (2) hide show
  1. package/dist/fluxflow.js +423 -116
  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 }
@@ -4778,9 +4791,9 @@ var init_StatusBar = __esm({
4778
4791
  paddingX: 1,
4779
4792
  width: "100%"
4780
4793
  },
4781
- /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Box4, { marginRight: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white", bold: true }, modeIcon, " ", mode.toUpperCase())), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white", bold: true }, thinkingLevel.toUpperCase())), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "MEM: "), /* @__PURE__ */ React5.createElement(Text5, { color: "white", bold: true }, isMemoryEnabled ? "ON" : "OFF"))),
4782
- /* @__PURE__ */ React5.createElement(Box4, { flexGrow: 1, justifyContent: "center", paddingX: 2 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white", italic: true }, " ", truncatePath(process.cwd(), 35))),
4783
- /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white" }, " ", formatTokens(tokensTotal), " ", /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, (tokens / maxLimit * 100).toFixed(0), "%"))), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "grey", bold: true }, memoryUsage, "/", memoryLimit, " ", memoryUnit)), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginLeft: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", italic: true }, " ", chatId), (apiTier === "Custom" || apiTier === "Paid") && /* @__PURE__ */ React5.createElement(Text5, { color: "white" }, " | ", /* @__PURE__ */ React5.createElement(Text5, { color: "gray", bold: true }, "CUSTOM"))))
4794
+ /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Box4, { marginRight: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white", bold: true }, mode.toUpperCase())), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white", bold: true }, thinkingLevel.toUpperCase())), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", bold: true }, "MEM: "), /* @__PURE__ */ React5.createElement(Text5, { color: "white", bold: true }, isMemoryEnabled ? "ON" : "OFF"))),
4795
+ /* @__PURE__ */ React5.createElement(Box4, { flexGrow: 1, justifyContent: "center", paddingX: 2 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white", italic: true }, truncatePath(process.cwd(), 35))),
4796
+ /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white" }, formatTokens(tokensTotal), " ", /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, (tokens / maxLimit * 100).toFixed(0), "%"))), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "grey", bold: true }, memoryUsage, "/", memoryLimit, " ", memoryUnit)), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginLeft: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", bold: true }, chatId), (apiTier === "Custom" || apiTier === "Paid") && /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, " \u2503 "), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", bold: true }, "PAID"))))
4784
4797
  );
4785
4798
  });
4786
4799
  StatusBar_default = StatusBar;
@@ -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:
@@ -5895,7 +5908,7 @@ function SettingsMenu({
5895
5908
  setActiveView("parserDownload");
5896
5909
  }
5897
5910
  };
5898
- return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "white", padding: 0, width: "100%", minHeight: 32 }, /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, paddingY: 0, marginBottom: 0, borderStyle: "single", borderColor: "gray", width: "100%" }, /* @__PURE__ */ React7.createElement(Text7, { color: "gray", bold: true }, "SYSTEM CONFIGURATION")), /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "row", width: "100%", minHeight: 26 }, /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", width: "30%", borderStyle: "round", borderColor: activeColumn === "categories" ? "white" : "grey", padding: 1, paddingY: 0 }, /* @__PURE__ */ React7.createElement(Box6, { marginBottom: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: activeColumn === "categories" ? "white" : "grey", bold: true, underline: true }, "CATEGORIES")), CATEGORIES.map((cat, index) => {
5911
+ return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "white", padding: 0, width: "100%", minHeight: 32 }, /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, paddingY: 0, marginBottom: 0, borderStyle: "single", borderColor: "gray", width: "100%" }, /* @__PURE__ */ React7.createElement(Text7, { color: "white", bold: true }, "SYSTEM CONFIGURATION")), /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "row", width: "100%", minHeight: 26 }, /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", width: "30%", borderStyle: "round", borderColor: activeColumn === "categories" ? "white" : "grey", padding: 1, paddingY: 0 }, /* @__PURE__ */ React7.createElement(Box6, { marginBottom: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: activeColumn === "categories" ? "white" : "grey", bold: true, underline: true }, "CATEGORIES")), CATEGORIES.map((cat, index) => {
5899
5912
  const isSelected = selectedCategoryIndex === index;
5900
5913
  const isExit = cat.id === "exit";
5901
5914
  return /* @__PURE__ */ React7.createElement(
@@ -7081,6 +7094,26 @@ var init_usage = __esm({
7081
7094
  cachedUsage.stats[key] = Array.from(uniqueMap.values());
7082
7095
  } else if (typeof cachedUsage.stats[key] === "number") {
7083
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
+ }
7084
7117
  }
7085
7118
  }
7086
7119
  }
@@ -7224,12 +7257,18 @@ var init_usage = __esm({
7224
7257
  }
7225
7258
  return summed;
7226
7259
  };
7227
- incrementUsage = async (key) => {
7260
+ incrementUsage = async (key, provider) => {
7228
7261
  const stats = await getDailyUsage();
7229
7262
  if (stats[key] !== void 0) {
7230
7263
  stats[key]++;
7231
- queueFlush();
7232
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();
7233
7272
  };
7234
7273
  addToUsage = async (key, amount, provider, model) => {
7235
7274
  const stats = await getDailyUsage();
@@ -7324,14 +7363,30 @@ var init_usage = __esm({
7324
7363
  checkQuota = async (key, settings) => {
7325
7364
  const tier = settings.apiTier || "Free";
7326
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
+ };
7327
7384
  if (tier === "Free") {
7328
7385
  if (key === "agent") {
7329
- const reqLimit = quotas.agentLimit || 99999999;
7330
- const tokenLimit = quotas.tokenLimit || 99999999999999;
7331
- const monthlyTokenLimit = quotas.monthlyTokenLimit || 99999999999999;
7386
+ const { agentLimit, tokenLimit, monthlyTokenLimit } = resolveAgentLimits();
7332
7387
  const dailyUsage = await getDailyUsage();
7333
7388
  if (dailyUsage.agent + dailyUsage.background >= 999999) return false;
7334
- const dailyOk = dailyUsage.agent < reqLimit && (dailyUsage.tokens || 0) < tokenLimit;
7389
+ const dailyOk = dailyUsage.agent < agentLimit && (dailyUsage.tokens || 0) < tokenLimit;
7335
7390
  if (!dailyOk) return false;
7336
7391
  let monthlyUsage;
7337
7392
  if (quotas.resetMode === "Custom") {
@@ -7353,11 +7408,9 @@ var init_usage = __esm({
7353
7408
  }
7354
7409
  if (tier === "Paid" || tier === "Custom") {
7355
7410
  if (key === "agent") {
7356
- const reqLimit = quotas.agentLimit || 99999999;
7357
- const tokenLimit = quotas.tokenLimit || 99999999999999;
7358
- const monthlyTokenLimit = quotas.monthlyTokenLimit || 99999999999999;
7411
+ const { agentLimit, tokenLimit, monthlyTokenLimit } = resolveAgentLimits();
7359
7412
  const dailyUsage = await getDailyUsage();
7360
- const dailyOk = dailyUsage.agent < reqLimit && (dailyUsage.tokens || 0) < tokenLimit;
7413
+ const dailyOk = dailyUsage.agent < agentLimit && (dailyUsage.tokens || 0) < tokenLimit;
7361
7414
  if (!dailyOk) return false;
7362
7415
  let monthlyUsage;
7363
7416
  if (quotas.resetMode === "Custom") {
@@ -8400,6 +8453,37 @@ async function getFilesRecursively(dir, excludes, baseDir = dir, depth = 1) {
8400
8453
  }
8401
8454
  return results;
8402
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
+ }
8403
8487
  var search_keyword;
8404
8488
  var init_search_keyword = __esm({
8405
8489
  "src/tools/search_keyword.js"() {
@@ -8407,7 +8491,7 @@ var init_search_keyword = __esm({
8407
8491
  search_keyword = async (args) => {
8408
8492
  const { keyword, file, subString } = parseArgs(args);
8409
8493
  if (!keyword) return 'ERROR: Missing "keyword" argument.';
8410
- 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;
8411
8495
  const wordRegex = new RegExp(`(?<![\\w])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w])`, "i");
8412
8496
  const excludes = [
8413
8497
  "node_modules",
@@ -8448,30 +8532,50 @@ var init_search_keyword = __esm({
8448
8532
  const lines = content.split(/\r?\n/);
8449
8533
  const fileMatches = [];
8450
8534
  for (let i = 0; i < lines.length; i++) {
8451
- 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]);
8452
8536
  if (matched) {
8453
- const displayPath = fileObj.relativePath.replace(/\\/g, "/");
8454
- fileMatches.push(`${displayPath} \u2192 ${i + 1}`);
8537
+ fileMatches.push({ line: i + 1, content: lines[i].trim() });
8455
8538
  }
8456
8539
  }
8457
- return fileMatches;
8540
+ if (fileMatches.length === 0) return null;
8541
+ const displayPath = fileObj.relativePath.replace(/\\/g, "/");
8542
+ return { path: displayPath, matches: fileMatches };
8458
8543
  } catch {
8459
8544
  return [];
8460
8545
  }
8461
8546
  });
8462
8547
  const settledResults = await Promise.all(searchPromises);
8463
- 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
+ }
8464
8558
  if (typeof global.gc === "function") {
8465
8559
  global.gc();
8466
8560
  }
8467
- if (matches.length === 0) {
8561
+ if (fileGroups.length === 0) {
8468
8562
  return `Found 0 matches for keyword: "${keyword}"${file ? ` in file: ${file}` : ". Try to specify files"} ${matchSubstring ? "(subString mode)" : ""}`;
8469
8563
  }
8470
- 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)" : ""}:
8471
8565
 
8472
8566
  `;
8473
- output += matches.join("\n");
8474
- 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();
8475
8579
  } catch (error) {
8476
8580
  return `ERROR: ${error.message}`;
8477
8581
  }
@@ -12869,7 +12973,7 @@ ${boxBottom}`}`) };
12869
12973
  const { keyword, file } = parseArgs(toolCall.args);
12870
12974
  let matchCount = 0;
12871
12975
  if (result) {
12872
- const m = result.match(/Found (\d+) matches/i);
12976
+ const m = result.match(/Found (\d+) match/i);
12873
12977
  if (m) {
12874
12978
  matchCount = parseInt(m[1]);
12875
12979
  }
@@ -13034,7 +13138,7 @@ ${colorMainWords(output)}` };
13034
13138
  if (!hasFinish2 && !hasContinue2 && !didCallTool && signalSafeText2.length > 0 && !endsNormally && !isThinkingLoop && !isStutteringLoop && !isGeneralLoop) {
13035
13139
  }
13036
13140
  success = true;
13037
- await incrementUsage("agent");
13141
+ await incrementUsage("agent", aiProvider);
13038
13142
  } catch (err) {
13039
13143
  if (TERMINATION_SIGNAL) {
13040
13144
  yield { type: "status", content: "Request Cancelled" };
@@ -13052,7 +13156,7 @@ ${colorMainWords(output)}` };
13052
13156
  }
13053
13157
  }
13054
13158
  success = true;
13055
- await incrementUsage("agent");
13159
+ await incrementUsage("agent", aiProvider);
13056
13160
  break;
13057
13161
  }
13058
13162
  if (inThinkingState) {
@@ -14568,8 +14672,13 @@ function App({ args = [] }) {
14568
14672
  };
14569
14673
  const [activeView, setActiveView] = useState14("chat");
14570
14674
  const [apiTier, setApiTier] = useState14("Free");
14571
- 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: {} });
14572
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({});
14573
14682
  const [systemSettings, setSystemSettings] = useState14({ memory: true, compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
14574
14683
  const [profileData, setProfileData] = useState14({ name: null, nickname: null, instructions: null });
14575
14684
  const [imageSettings, setImageSettings] = useState14({ keyType: "Default", quality: "Low-High", apiKey: "" });
@@ -15025,6 +15134,34 @@ function App({ args = [] }) {
15025
15134
  }
15026
15135
  return;
15027
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
+ }
15028
15165
  if (key.escape) {
15029
15166
  if (showBtwBox) {
15030
15167
  setShowBtwBox(false);
@@ -15183,7 +15320,8 @@ function App({ args = [] }) {
15183
15320
  }
15184
15321
  const startupProvider = parsedArgs.provider || saved.aiProvider || "Google";
15185
15322
  setAiProvider(startupProvider);
15186
- const currentTier = saved.apiTier || "Free";
15323
+ const providerTiers = saved.quotas?.providerTiers || {};
15324
+ const currentTier = providerTiers[startupProvider] || saved.apiTier || "Free";
15187
15325
  persistedModelRef.current = saved.activeModel;
15188
15326
  if (parsedArgs.model) {
15189
15327
  setActiveModel(parsedArgs.model);
@@ -15215,8 +15353,8 @@ function App({ args = [] }) {
15215
15353
  setActiveModel(saved.activeModel);
15216
15354
  }
15217
15355
  setShowFullThinking(saved.showFullThinking);
15218
- setApiTier(saved.apiTier || "Free");
15219
- 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: {} });
15220
15358
  const freshSettings = {
15221
15359
  memory: true,
15222
15360
  compression: 0,
@@ -15735,7 +15873,8 @@ function App({ args = [] }) {
15735
15873
  desc: "Set or View budget limits",
15736
15874
  subs: [
15737
15875
  { cmd: "view", desc: "View current usage budget bars" },
15738
- { 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" }
15739
15878
  ]
15740
15879
  },
15741
15880
  {
@@ -16386,28 +16525,8 @@ ${list || "No saved chats found."}`, isMeta: true }];
16386
16525
  case "/budget": {
16387
16526
  const sub = parts[1]?.toLowerCase();
16388
16527
  if (sub === "set") {
16389
- setInputConfig({
16390
- label: "Enter Agent daily budget (requests made):",
16391
- key: "quotas",
16392
- subKey: "agentLimit",
16393
- value: getPrefilledValue(quotas.agentLimit),
16394
- returnView: "chat",
16395
- next: (newQuotas) => ({
16396
- label: "Enter Agent daily budget (tokens used):",
16397
- key: "quotas",
16398
- subKey: "tokenLimit",
16399
- value: getPrefilledValue(newQuotas.tokenLimit),
16400
- returnView: "chat",
16401
- next: (q2) => ({
16402
- label: "Enter Agent monthly budget (tokens used):",
16403
- key: "quotas",
16404
- subKey: "monthlyTokenLimit",
16405
- value: getPrefilledValue(q2.monthlyTokenLimit),
16406
- returnView: "budgetResetMode"
16407
- })
16408
- })
16409
- });
16410
- setActiveView("input");
16528
+ setBudgetReturnView("chat");
16529
+ setActiveView("budgetTypeSelect");
16411
16530
  } else if (sub === "view") {
16412
16531
  const run = async () => {
16413
16532
  const usage = await getDailyUsage();
@@ -16419,10 +16538,34 @@ ${list || "No saved chats found."}`, isMeta: true }];
16419
16538
  setActiveView("budgetView");
16420
16539
  };
16421
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
+ });
16422
16565
  } else {
16423
16566
  setMessages((prev) => {
16424
16567
  setCompletedIndex(prev.length + 1);
16425
- 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 }];
16426
16569
  });
16427
16570
  }
16428
16571
  break;
@@ -17230,6 +17373,87 @@ Selection: ${val}`,
17230
17373
  useEffect11(() => {
17231
17374
  setSelectedIndex(0);
17232
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]);
17233
17457
  const CustomMenuItem = ({ label: label2, isSelected }) => {
17234
17458
  const isCancel = label2 === "Cancel" || label2 === "Back" || label2.toLowerCase().includes("exit") || label2.toLowerCase().includes("back");
17235
17459
  return /* @__PURE__ */ React15.createElement(
@@ -17308,7 +17532,9 @@ Selection: ${val}`,
17308
17532
  defaultModel = "moonshotai/kimi-k2.6";
17309
17533
  }
17310
17534
  setActiveModel(defaultModel);
17311
- 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 });
17312
17538
  setMessages((prev) => [
17313
17539
  ...prev,
17314
17540
  {
@@ -17333,30 +17559,12 @@ Selection: ${val}`,
17333
17559
  }
17334
17560
  );
17335
17561
  case "apiTier": {
17336
- const reqCurrent = dailyUsage?.agent || 0;
17337
- const reqLimit = quotas.agentLimit || 99999999;
17338
- const tokenCurrent = dailyUsage?.tokens || 0;
17339
- const tokenLimit = quotas.tokenLimit || 99999999999999;
17340
- const monthlyCurrent = quotas.resetMode === "Custom" ? customPeriodUsage?.tokens || 0 : monthlyUsage?.tokens || 0;
17341
- const monthlyLimit = quotas.monthlyTokenLimit || 99999999999999;
17342
- let resetInfo = "";
17343
- if (quotas.resetMode === "Custom") {
17344
- const today = /* @__PURE__ */ new Date();
17345
- const resetDay = quotas.resetDay || 1;
17346
- let resetMonth = today.getMonth();
17347
- if (today.getDate() >= resetDay) {
17348
- resetMonth += 1;
17349
- }
17350
- const resetDate = new Date(today.getFullYear(), resetMonth, resetDay);
17351
- const monthName = resetDate.toLocaleString("default", { month: "short" });
17352
- resetInfo = `Resets on: ${resetDay}-${monthName}`;
17353
- }
17354
- 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: "white", bold: true }, "SELECT API MODE FOR ", aiProvider.toUpperCase())), /* @__PURE__ */ React15.createElement(
17355
17563
  SelectInput2,
17356
17564
  {
17357
17565
  items: [
17358
- { label: "Provider Limits", value: "Free" },
17359
- { label: `Set Budgets (API with Billing Account) ${apiTier === "Paid" ? "\u25CF" : ""}`, value: "Paid" },
17566
+ { label: "Free Mode (For Free APIs) [Free Models Only]", value: "Free" },
17567
+ { label: `Paid Mode (For Billing APIs) [Premium Models Unlocked] ${apiTier === "Paid" ? "\u25CF" : ""}`, value: "Paid" },
17360
17568
  { label: "Back", value: "settings" }
17361
17569
  ],
17362
17570
  onSelect: (item) => {
@@ -17366,40 +17574,22 @@ Selection: ${val}`,
17366
17574
  }
17367
17575
  const newTier = item.value;
17368
17576
  setApiTier(newTier);
17369
- if (newTier === "Paid") {
17370
- setInputConfig({
17371
- label: "Enter Agent daily budget (requests made):",
17372
- key: "quotas",
17373
- subKey: "agentLimit",
17374
- value: getPrefilledValue(quotas.agentLimit),
17375
- returnView: "settings",
17376
- next: (newQuotas) => ({
17377
- label: "Enter Agent daily budget (tokens used):",
17378
- key: "quotas",
17379
- subKey: "tokenLimit",
17380
- value: getPrefilledValue(newQuotas.tokenLimit),
17381
- returnView: "settings",
17382
- next: (q2) => ({
17383
- label: "Enter Agent monthly budget (tokens used):",
17384
- key: "quotas",
17385
- subKey: "monthlyTokenLimit",
17386
- value: getPrefilledValue(q2.monthlyTokenLimit),
17387
- returnView: "resetMode"
17388
- })
17389
- })
17390
- });
17391
- setActiveView("input");
17392
- } else {
17393
- const newQuotas = { ...quotas, agentLimit: 99999999, tokenLimit: 99999999999999, monthlyTokenLimit: 99999999999999 };
17394
- setQuotas(newQuotas);
17395
- saveSettings({ apiTier: newTier, quotas: newQuotas });
17396
- setActiveView("settings");
17397
- }
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");
17398
17588
  },
17399
17589
  itemComponent: CustomMenuItem,
17400
17590
  indicatorComponent: () => null
17401
17591
  }
17402
- ), 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)")));
17403
17593
  }
17404
17594
  case "resetMode":
17405
17595
  return /* @__PURE__ */ React15.createElement(
@@ -17436,6 +17626,82 @@ Selection: ${val}`,
17436
17626
  onClose: () => setActiveView("apiTier")
17437
17627
  }
17438
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;
17439
17705
  case "budgetResetMode":
17440
17706
  return /* @__PURE__ */ React15.createElement(
17441
17707
  CommandMenu,
@@ -17479,7 +17745,12 @@ Selection: ${val}`,
17479
17745
  const monthlyCurrent = quotas.resetMode === "Custom" ? customPeriodUsage?.tokens || 0 : monthlyUsage?.tokens || 0;
17480
17746
  const monthlyLimit = quotas.monthlyTokenLimit || 99999999999999;
17481
17747
  const isFreeTier = apiTier !== "Paid";
17482
- 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));
17483
17754
  let resetInfo = "";
17484
17755
  if (quotas.resetMode === "Custom") {
17485
17756
  const today = /* @__PURE__ */ new Date();
@@ -17492,7 +17763,22 @@ Selection: ${val}`,
17492
17763
  const monthName = resetDate.toLocaleString("default", { month: "short" });
17493
17764
  resetInfo = `Resets on: ${resetDay}-${monthName}`;
17494
17765
  }
17495
- 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"), /* @__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: "white", 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"))));
17496
17782
  }
17497
17783
  case "input":
17498
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(
@@ -17501,7 +17787,7 @@ Selection: ${val}`,
17501
17787
  value: inputConfig?.value || "",
17502
17788
  onChange: (val) => setInputConfig((prev) => ({ ...prev, value: val })),
17503
17789
  onSubmit: async (val) => {
17504
- const { key, subKey, next } = inputConfig;
17790
+ const { key, subKey, next, onDone } = inputConfig;
17505
17791
  let newQuotas = { ...quotas };
17506
17792
  let newSettings = {};
17507
17793
  if (key === "quotas") {
@@ -17512,6 +17798,19 @@ Selection: ${val}`,
17512
17798
  newQuotas[subKey] = parsedValue;
17513
17799
  setQuotas(newQuotas);
17514
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;
17515
17814
  } else if (key === "activeModel") {
17516
17815
  setActiveModel(val);
17517
17816
  newSettings.activeModel = val;
@@ -17561,15 +17860,23 @@ Selection: ${val}`,
17561
17860
  defaultModel = "moonshotai/kimi-k2.6";
17562
17861
  }
17563
17862
  setActiveModel(defaultModel);
17863
+ const targetTier = (quotas.providerTiers || {})[prov] || "Free";
17864
+ setApiTier(targetTier);
17564
17865
  newSettings.aiProvider = prov;
17565
17866
  newSettings.activeModel = defaultModel;
17867
+ newSettings.apiTier = targetTier;
17566
17868
  setMessages((prev) => {
17567
17869
  setCompletedIndex(prev.length + 1);
17568
17870
  return [...prev, { id: Date.now(), role: "system", text: `\u2705 ${prov} API Key saved successfully! Model set to ${defaultModel}.`, isMeta: true }];
17569
17871
  });
17570
17872
  }
17571
17873
  if (next) {
17572
- 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);
17573
17880
  } else {
17574
17881
  saveSettings({ ...newSettings, apiTier, quotas: newQuotas, imageSettings: newSettings.imageSettings || imageSettings });
17575
17882
  setInputConfig(null);
@@ -18063,7 +18370,7 @@ Selection: ${val}`,
18063
18370
  setSetupStep(1);
18064
18371
  }
18065
18372
  }
18066
- ))) : /* @__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(
18067
18374
  TextInput4,
18068
18375
  {
18069
18376
  value: tempKey,
@@ -18103,7 +18410,7 @@ Selection: ${val}`,
18103
18410
  url = "https://build.nvidia.com/settings/api-keys";
18104
18411
  label2 = "billing";
18105
18412
  }
18106
- return /* @__PURE__ */ React15.createElement(Text16, { color: "gray", dimColor: true, italic: true }, "Paid API has more models. Configure ", /* @__PURE__ */ React15.createElement(Text16, { color: "cyan", underline: true }, `\x1B]8;;${url}\x07${label2}\x1B]8;;\x07`), " & /settings");
18413
+ return /* @__PURE__ */ React15.createElement(Text16, { color: "gray", dimColor: true, italic: true }, "Paid API Strategy has more models. Configure ", /* @__PURE__ */ React15.createElement(Text16, { color: "cyan", underline: true }, `\x1B]8;;${url}\x07${label2}\x1B]8;;\x07`), " & /settings");
18107
18414
  })() : null),
18108
18415
  visible.map((s, i) => {
18109
18416
  const actualIdx = startIdx + i;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "2.14.2",
4
- "date": "2026-06-29",
3
+ "version": "2.15.1",
4
+ "date": "2026-06-30",
5
5
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",