tokentracker-cli 0.84.5 → 0.84.7

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 (120) hide show
  1. package/README.de.md +10 -8
  2. package/README.ja.md +9 -7
  3. package/README.ko.md +9 -7
  4. package/README.md +12 -10
  5. package/README.zh-CN.md +9 -7
  6. package/dashboard/dist/assets/{AchievementBadge-DJvRfvu1.js → AchievementBadge-DAPsTMsx.js} +1 -1
  7. package/dashboard/dist/assets/{AchievementsPage-Cr8WToxS.js → AchievementsPage-B9oXfXvm.js} +2 -2
  8. package/dashboard/dist/assets/{AchievementsSection-BCPYB46e.js → AchievementsSection-DI_LTZb3.js} +1 -1
  9. package/dashboard/dist/assets/{BadgeDetailModal-D-12Ek-H.js → BadgeDetailModal--WCrJWVa.js} +1 -1
  10. package/dashboard/dist/assets/Card-BzbSN3EC.js +1 -0
  11. package/dashboard/dist/assets/{ClawdAnimated-DoGgAppT.js → ClawdAnimated-Dgxae1At.js} +1 -1
  12. package/dashboard/dist/assets/{CommandPalette-DIKocEAD.js → CommandPalette-BiZBYV5A.js} +1 -1
  13. package/dashboard/dist/assets/{CommunityStatsModal-BNuMbEWy.js → CommunityStatsModal-C6jZx10_.js} +1 -1
  14. package/dashboard/dist/assets/{ConfirmModal-F2gPIIS9.js → ConfirmModal-lD-mHMvO.js} +1 -1
  15. package/dashboard/dist/assets/{DashboardPage-YvoCEGG9.js → DashboardPage-DCPNoiRG.js} +1 -1
  16. package/dashboard/dist/assets/{DevicePage-CbC_J22s.js → DevicePage-DiF2IOjJ.js} +1 -1
  17. package/dashboard/dist/assets/{DialogClose-VEynQCYg.js → DialogClose-Crx2EFnI.js} +1 -1
  18. package/dashboard/dist/assets/DialogDescription-CHsHo924.js +1 -0
  19. package/dashboard/dist/assets/{DialogTitle-BbAVHI_v.js → DialogTitle-amHXttsp.js} +1 -1
  20. package/dashboard/dist/assets/{FadeIn-CZVqSnis.js → FadeIn-CIhF_9PP.js} +1 -1
  21. package/dashboard/dist/assets/{HeaderGithubStar-Cs_h332Y.js → HeaderGithubStar-D6Ph2GuY.js} +1 -1
  22. package/dashboard/dist/assets/{HoverTooltip-DvvHTphK.js → HoverTooltip-BS8eO6tj.js} +1 -1
  23. package/dashboard/dist/assets/{IpCheckPage-DPVmqSS_.js → IpCheckPage-Cz1Je6WS.js} +1 -1
  24. package/dashboard/dist/assets/LandingPage-DqeXJfwn.js +2 -0
  25. package/dashboard/dist/assets/{LeaderboardAvatar-BAPZ2ROX.js → LeaderboardAvatar-D09yLoVA.js} +1 -1
  26. package/dashboard/dist/assets/LeaderboardPage-BetpFetS.js +2 -0
  27. package/dashboard/dist/assets/{LeaderboardProfileModal-r03akfe2.js → LeaderboardProfileModal-UW3KnANZ.js} +4 -4
  28. package/dashboard/dist/assets/{LeaderboardProfilePage-CU63YW2M.js → LeaderboardProfilePage-Chd_2AVY.js} +1 -1
  29. package/dashboard/dist/assets/LimitsPage-uhpNteXJ.js +4 -0
  30. package/dashboard/dist/assets/{LocalOnlyNotice-BHlcPCZk.js → LocalOnlyNotice-CpFFX8ro.js} +1 -1
  31. package/dashboard/dist/assets/{LoginCard-C-dqUumR.js → LoginCard-DkZpGnVu.js} +1 -1
  32. package/dashboard/dist/assets/{LoginModal-BFzRg4Iy.js → LoginModal-CALv6jkD.js} +1 -1
  33. package/dashboard/dist/assets/{LoginPage-QtlnGF1S.js → LoginPage-BZuXHYAT.js} +1 -1
  34. package/dashboard/dist/assets/PetPage-C1LuDyYr.js +1 -0
  35. package/dashboard/dist/assets/{PopoverPopup-CltmqcrT.js → PopoverPopup-CluVhrMr.js} +1 -1
  36. package/dashboard/dist/assets/{ProviderIcon-BqOI9EiD.js → ProviderIcon-D3YKjVKb.js} +1 -1
  37. package/dashboard/dist/assets/{ResetPasswordPage-BsfCBUZD.js → ResetPasswordPage-CzN50nX5.js} +1 -1
  38. package/dashboard/dist/assets/{SegmentedControl-D4biXw_Q.js → SegmentedControl-DFOAcEKU.js} +1 -1
  39. package/dashboard/dist/assets/{Select-DTw-14iE.js → Select-B3W_ihs_.js} +1 -1
  40. package/dashboard/dist/assets/{SelectItemText-3tUrnu3f.js → SelectItemText-FXRtXnFV.js} +1 -1
  41. package/dashboard/dist/assets/ServiceStatusPage-BItZqH7q.js +1 -0
  42. package/dashboard/dist/assets/{SessionsPage-DYYlNqHg.js → SessionsPage-DVQU-hTH.js} +2 -2
  43. package/dashboard/dist/assets/SettingsPage-ByvrwOi6.js +1 -0
  44. package/dashboard/dist/assets/SkillsPage-DrGr5Qij.js +1 -0
  45. package/dashboard/dist/assets/{TokenGalaxy-DU77e8sM.js → TokenGalaxy-ClF_n9od.js} +1 -1
  46. package/dashboard/dist/assets/{ToolbarRootContext-DV3KGf-a.js → ToolbarRootContext-B82hGhAf.js} +1 -1
  47. package/dashboard/dist/assets/{TrendMonitor-D6QYKmbl.js → TrendMonitor-DkWm7_1z.js} +1 -1
  48. package/dashboard/dist/assets/{WidgetsPage-DSP4jCsH.js → WidgetsPage-DCY4pyR2.js} +1 -1
  49. package/dashboard/dist/assets/{WrappedPage-B0x6O_DU.js → WrappedPage-yGQ4DLqj.js} +1 -1
  50. package/dashboard/dist/assets/__vite-browser-external-BdRrPLk6.js +1483 -0
  51. package/dashboard/dist/assets/__vite-browser-external-hXV_ZYTG.css +1 -0
  52. package/dashboard/dist/assets/{agent-logos-B2S_cdOL.js → agent-logos-D5SHGOxB.js} +1 -1
  53. package/dashboard/dist/assets/{arrow-up-right-DNUVuhx2.js → arrow-up-right-CbMcAcss.js} +1 -1
  54. package/dashboard/dist/assets/{check-C3ARiiE_.js → check-Dyzh7lTz.js} +1 -1
  55. package/dashboard/dist/assets/{chevron-right-3bY7H8jO.js → chevron-right-DT06ZIKC.js} +1 -1
  56. package/dashboard/dist/assets/{copy-C-UIMKoO.js → copy-B3Kt3Wrf.js} +1 -1
  57. package/dashboard/dist/assets/{download-DtRzJfcr.js → download-B3rOytqI.js} +1 -1
  58. package/dashboard/dist/assets/external-link-BamTP0Ks.js +1 -0
  59. package/dashboard/dist/assets/{flame-Bas3ry-g.js → flame-msy2h1ms.js} +1 -1
  60. package/dashboard/dist/assets/{format-tokens-DFtRUIH8.js → format-tokens-8b1OUM_8.js} +1 -1
  61. package/dashboard/dist/assets/{icons-CHBt4Tmo.js → icons-C2NR2_Tf.js} +1 -1
  62. package/dashboard/dist/assets/{index-CZRlrK7w.js → index-C8sDJzUY.js} +1 -1
  63. package/dashboard/dist/assets/{index-C1ADnvzm.js → index-CvTfpiCF.js} +1 -1
  64. package/dashboard/dist/assets/{index-4unZlm2w.js → index-x3vFJQFt.js} +1 -1
  65. package/dashboard/dist/assets/{info-BkrO-fg3.js → info-C9j23aGq.js} +1 -1
  66. package/dashboard/dist/assets/limits-providers-ySB9Ci5l.js +1 -0
  67. package/dashboard/dist/assets/{link-2-DTLFz8uG.js → link-2-rrcyTT6S.js} +1 -1
  68. package/dashboard/dist/assets/{loader-circle-BqQW_Ipq.js → loader-circle-x9x9I8Hv.js} +1 -1
  69. package/dashboard/dist/assets/{maximize-2-YdpeE4dq.js → maximize-2-7TWFb2k7.js} +1 -1
  70. package/dashboard/dist/assets/{provider-display-CgBOpjq0.js → provider-display-JbSwpvxU.js} +1 -1
  71. package/dashboard/dist/assets/{react-DD9jpv--.js → react-DK5clHtY.js} +1 -1
  72. package/dashboard/dist/assets/{search--TvX-fig.js → search-DukXRvEV.js} +1 -1
  73. package/dashboard/dist/assets/{skills-api-C5C8a1at.js → skills-api-BUFJuwiR.js} +1 -1
  74. package/dashboard/dist/assets/{store-CRA8gPnP.js → store-BTh7bv09.js} +1 -1
  75. package/dashboard/dist/assets/{terminal-BXZyl2ek.js → terminal-CcUqLtn1.js} +1 -1
  76. package/dashboard/dist/assets/{trash-2-IFdBQ65v.js → trash-2-Bq7hnvNq.js} +1 -1
  77. package/dashboard/dist/assets/{use-community-stats-LyJ5aMpX.js → use-community-stats-qTpx6ugG.js} +1 -1
  78. package/dashboard/dist/assets/{use-limits-display-prefs-CKvFS1Nn.js → use-limits-display-prefs-Di_J4eVv.js} +1 -1
  79. package/dashboard/dist/assets/{use-native-settings-Cg-4j_0z.js → use-native-settings-CIqpv9Z3.js} +1 -1
  80. package/dashboard/dist/assets/{use-ordered-list-2pX5PXZO.js → use-ordered-list-OwogSXT7.js} +1 -1
  81. package/dashboard/dist/assets/use-reduced-motion-5QGGPoom.js +1 -0
  82. package/dashboard/dist/assets/{use-session-efficiency-pref-DilJOODA.js → use-session-efficiency-pref-CjAvMh7u.js} +1 -1
  83. package/dashboard/dist/assets/{use-transform-DiyYTLpI.js → use-transform-KZLdt-NZ.js} +1 -1
  84. package/dashboard/dist/assets/{use-usage-limits-DkUmp_nW.js → use-usage-limits-Tbifa037.js} +1 -1
  85. package/dashboard/dist/assets/{useCurrency-lm7KjGoD.js → useCurrency-D2td-bkn.js} +1 -1
  86. package/dashboard/dist/assets/{useScrollLock-DVSyJ-GE.js → useScrollLock-Cvyfyezc.js} +1 -1
  87. package/dashboard/dist/assets/useTokenFormat-DYIUS6IH.js +1 -0
  88. package/dashboard/dist/assets/{zap-CKZt1_i5.js → zap-B19mEed5.js} +1 -1
  89. package/dashboard/dist/brand-logos/qoder.svg +5 -0
  90. package/dashboard/dist/index.html +18 -15
  91. package/dashboard/dist/ip-check.html +2 -2
  92. package/dashboard/dist/leaderboard.html +2 -2
  93. package/dashboard/dist/llms.txt +3 -2
  94. package/dashboard/dist/share.html +2 -2
  95. package/package.json +2 -2
  96. package/src/commands/init.js +1 -0
  97. package/src/commands/status.js +34 -7
  98. package/src/commands/sync.js +353 -5
  99. package/src/lib/codex-rollout-parser.js +17 -39
  100. package/src/lib/codex-token-usage.js +195 -0
  101. package/src/lib/local-api.js +4 -1
  102. package/src/lib/pet-packages.js +44 -2
  103. package/src/lib/pricing/seed-snapshot.json +1 -1
  104. package/src/lib/provider-status.js +104 -0
  105. package/src/lib/qoder-limits.js +985 -0
  106. package/src/lib/rollout.js +744 -91
  107. package/src/lib/usage-limits.js +243 -43
  108. package/dashboard/dist/assets/Card-Ha3x9zIj.js +0 -1
  109. package/dashboard/dist/assets/DialogDescription-B9bb9Ar0.js +0 -1
  110. package/dashboard/dist/assets/LandingPage-BCvNh3zE.js +0 -2
  111. package/dashboard/dist/assets/LeaderboardPage-DZSxazHP.js +0 -2
  112. package/dashboard/dist/assets/LimitsPage-JWZDa5_Y.js +0 -4
  113. package/dashboard/dist/assets/PetPage-C6UMY2qD.js +0 -1
  114. package/dashboard/dist/assets/SettingsPage-CYlEboSl.js +0 -1
  115. package/dashboard/dist/assets/SkillsPage-Bzq8V5Pd.js +0 -1
  116. package/dashboard/dist/assets/__vite-browser-external-D3llGq6p.js +0 -1465
  117. package/dashboard/dist/assets/__vite-browser-external-WKilYZnq.css +0 -1
  118. package/dashboard/dist/assets/limits-providers-BtwYmYHA.js +0 -1
  119. package/dashboard/dist/assets/use-reduced-motion-BCR01kRg.js +0 -1
  120. package/dashboard/dist/assets/useTokenFormat-Li8BANKO.js +0 -1
@@ -27,6 +27,8 @@ const {
27
27
  const { fetchGrokLimits } = require("./grok-limits");
28
28
  const { fetchZcodeLimits } = require("./zcode-limits");
29
29
  const { fetchOpencodeGoLimits } = require("./opencode-go-limits");
30
+ const { fetchQoderLimits } = require("./qoder-limits");
31
+ const { fetchProviderServiceStatus } = require("./provider-status");
30
32
  const { readSqliteJsonRows, readSqliteJsonRowsAsync } = require("./sqlite-reader");
31
33
 
32
34
  const execFileAsync = promisify(cp.execFile);
@@ -1222,11 +1224,24 @@ function runCommand(commandRunner, command, args, options = {}) {
1222
1224
  return Promise.resolve(commandRunner(command, args, merged));
1223
1225
  }
1224
1226
 
1225
- const { timeout, maxBuffer, ...spawnOptions } = merged;
1227
+ const {
1228
+ timeout,
1229
+ maxBuffer,
1230
+ completeWhen,
1231
+ completionGraceMs = 250,
1232
+ killProcessGroup = false,
1233
+ ...spawnOptions
1234
+ } = merged;
1226
1235
  return new Promise((resolve) => {
1227
1236
  let child;
1237
+ const useProcessGroup =
1238
+ killProcessGroup && process.platform !== "win32";
1228
1239
  try {
1229
- child = cp.spawn(command, args, { ...spawnOptions, stdio: ["ignore", "pipe", "pipe"] });
1240
+ child = cp.spawn(command, args, {
1241
+ ...spawnOptions,
1242
+ detached: useProcessGroup || spawnOptions.detached,
1243
+ stdio: ["ignore", "pipe", "pipe"],
1244
+ });
1230
1245
  } catch (error) {
1231
1246
  resolve({ status: null, stdout: "", stderr: "", error });
1232
1247
  return;
@@ -1238,12 +1253,14 @@ function runCommand(commandRunner, command, args, options = {}) {
1238
1253
  let timedOut = false;
1239
1254
  let timer = null;
1240
1255
  let hardTimer = null;
1256
+ let completionTimer = null;
1241
1257
 
1242
1258
  const settle = ({ status = null, error = null } = {}) => {
1243
1259
  if (settled) return;
1244
1260
  settled = true;
1245
1261
  if (timer) clearTimeout(timer);
1246
1262
  if (hardTimer) clearTimeout(hardTimer);
1263
+ if (completionTimer) clearTimeout(completionTimer);
1247
1264
  let finalError = error;
1248
1265
  if (!finalError && timedOut) {
1249
1266
  finalError = new Error(`spawn ${command} ETIMEDOUT`);
@@ -1254,19 +1271,49 @@ function runCommand(commandRunner, command, args, options = {}) {
1254
1271
  resolve(result);
1255
1272
  };
1256
1273
 
1274
+ const signalChild = (signal) => {
1275
+ try {
1276
+ if (useProcessGroup && Number.isInteger(child.pid)) {
1277
+ process.kill(-child.pid, signal);
1278
+ } else {
1279
+ child.kill(signal);
1280
+ }
1281
+ } catch (_error) {}
1282
+ };
1283
+
1284
+ const stopChild = ({ timeoutExpired = false } = {}) => {
1285
+ if (settled) return;
1286
+ if (timeoutExpired) timedOut = true;
1287
+ signalChild("SIGTERM");
1288
+ // Guarantee settlement even if the process group ignores SIGTERM or
1289
+ // keeps inherited stdio open.
1290
+ hardTimer = setTimeout(() => {
1291
+ signalChild("SIGKILL");
1292
+ settle({ status: null });
1293
+ }, 1000);
1294
+ if (typeof hardTimer.unref === "function") hardTimer.unref();
1295
+ };
1296
+
1257
1297
  if (Number.isFinite(timeout) && timeout > 0) {
1258
1298
  timer = setTimeout(() => {
1259
- timedOut = true;
1260
- try { child.kill("SIGTERM"); } catch (_error) {}
1261
- // Guarantee settlement even if the child ignores SIGTERM or keeps stdio open.
1262
- hardTimer = setTimeout(() => {
1263
- try { child.kill("SIGKILL"); } catch (_error) {}
1264
- settle({ status: null });
1265
- }, 1000);
1266
- if (typeof hardTimer.unref === "function") hardTimer.unref();
1299
+ stopChild({ timeoutExpired: true });
1267
1300
  }, timeout);
1268
1301
  }
1269
1302
 
1303
+ const scheduleCompletion = () => {
1304
+ if (typeof completeWhen !== "function" || settled) return;
1305
+ let complete = false;
1306
+ try {
1307
+ complete = Boolean(completeWhen(stdout, stderr));
1308
+ } catch (_error) {}
1309
+ if (!complete) return;
1310
+ if (completionTimer) clearTimeout(completionTimer);
1311
+ completionTimer = setTimeout(
1312
+ () => stopChild(),
1313
+ Math.max(0, Number(completionGraceMs) || 0),
1314
+ );
1315
+ };
1316
+
1270
1317
  const collect = (stream, append) => {
1271
1318
  if (!stream) return;
1272
1319
  stream.setEncoding("utf8");
@@ -1275,9 +1322,11 @@ function runCommand(commandRunner, command, args, options = {}) {
1275
1322
  if (stdout.length + stderr.length > maxBuffer) {
1276
1323
  const error = new Error(`spawn ${command} maxBuffer length exceeded`);
1277
1324
  error.code = "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
1278
- try { child.kill("SIGKILL"); } catch (_error) {}
1325
+ signalChild("SIGKILL");
1279
1326
  settle({ status: null, error });
1327
+ return;
1280
1328
  }
1329
+ scheduleCompletion();
1281
1330
  });
1282
1331
  };
1283
1332
  collect(child.stdout, (chunk) => { stdout += chunk; });
@@ -1324,14 +1373,94 @@ function parseMonthDayResetDate(dateStr, now = new Date()) {
1324
1373
  return candidate.toISOString();
1325
1374
  }
1326
1375
 
1376
+ function parseKiroResetDate(dateStr, now = new Date()) {
1377
+ if (typeof dateStr !== "string") return null;
1378
+ if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
1379
+ const candidate = new Date(`${dateStr}T00:00:00.000Z`);
1380
+ if (
1381
+ Number.isFinite(candidate.getTime()) &&
1382
+ candidate.toISOString().slice(0, 10) === dateStr
1383
+ ) {
1384
+ return candidate.toISOString();
1385
+ }
1386
+ return null;
1387
+ }
1388
+ return parseMonthDayResetDate(dateStr, now);
1389
+ }
1390
+
1327
1391
  function isKiroUsageOutputComplete(output) {
1328
- const lowered = stripAnsi(output).toLowerCase();
1329
- return lowered.includes("covered in plan")
1330
- || lowered.includes("resets on")
1331
- || lowered.includes("bonus credits")
1332
- || lowered.includes("plan:")
1333
- || lowered.includes("managed by admin")
1334
- || lowered.includes("managed by organization");
1392
+ try {
1393
+ parseKiroUsageOutput(output);
1394
+ return true;
1395
+ } catch (_error) {
1396
+ return false;
1397
+ }
1398
+ }
1399
+
1400
+ function parseKiroCliVersion(output) {
1401
+ const match = String(output || "").match(
1402
+ /(?:kiro-cli\s+)?(\d+)\.(\d+)\.(\d+)/i,
1403
+ );
1404
+ if (!match) return null;
1405
+ return {
1406
+ major: Number(match[1]),
1407
+ minor: Number(match[2]),
1408
+ patch: Number(match[3]),
1409
+ };
1410
+ }
1411
+
1412
+ function kiroCliRequiresPty(version) {
1413
+ if (!version) return false;
1414
+ if (version.major !== 2) return version.major > 2;
1415
+ return version.minor >= 13;
1416
+ }
1417
+
1418
+ function quotePosixShellArg(value) {
1419
+ return `'${String(value).replace(/'/g, `'"'"'`)}'`;
1420
+ }
1421
+
1422
+ function kiroPtyInvocation(binaryPath, args, platform = process.platform) {
1423
+ if (platform === "darwin") {
1424
+ return {
1425
+ command: "/usr/bin/script",
1426
+ args: ["-q", "/dev/null", binaryPath, ...args],
1427
+ };
1428
+ }
1429
+ if (platform === "linux") {
1430
+ const commandLine = [binaryPath, ...args]
1431
+ .map(quotePosixShellArg)
1432
+ .join(" ");
1433
+ return {
1434
+ command: "script",
1435
+ args: ["-q", "-c", commandLine, "/dev/null"],
1436
+ };
1437
+ }
1438
+ return null;
1439
+ }
1440
+
1441
+ function combineKiroCommandOutput(result) {
1442
+ const stdout =
1443
+ typeof result?.stdout === "string" ? result.stdout.trim() : "";
1444
+ const stderr =
1445
+ typeof result?.stderr === "string" ? result.stderr.trim() : "";
1446
+ return [stdout, stderr].filter(Boolean).join("\n");
1447
+ }
1448
+
1449
+ function parseKiroCommandResult(result, { now = new Date() } = {}) {
1450
+ const output = combineKiroCommandOutput(result);
1451
+ if (
1452
+ result?.error?.code === "ETIMEDOUT" &&
1453
+ !isKiroUsageOutputComplete(output)
1454
+ ) {
1455
+ throw new Error("Kiro CLI timed out.");
1456
+ }
1457
+ if (!output && result?.status !== 0) {
1458
+ const detail = result?.error?.message
1459
+ ? `: ${result.error.message}`
1460
+ : ".";
1461
+ throw new Error(`Kiro CLI failed with status ${result?.status ?? "unknown"}${detail}`);
1462
+ }
1463
+ return parseKiroUsageOutput(output, { now });
1335
1464
  }
1336
1465
 
1337
1466
  function parseKiroUsageOutput(output, { now = new Date() } = {}) {
@@ -1364,8 +1493,12 @@ function parseKiroUsageOutput(output, { now = new Date() } = {}) {
1364
1493
  planName = modernPlan[1].split("\n")[0].trim() || planName;
1365
1494
  }
1366
1495
 
1367
- const resetMatch = stripped.match(/resets on (\d{2}\/\d{2})/i);
1368
- const primaryReset = resetMatch ? parseMonthDayResetDate(resetMatch[1], now) : null;
1496
+ const resetMatch = stripped.match(
1497
+ /resets on (\d{4}-\d{2}-\d{2}|\d{2}\/\d{2})/i,
1498
+ );
1499
+ const primaryReset = resetMatch
1500
+ ? parseKiroResetDate(resetMatch[1], now)
1501
+ : null;
1369
1502
 
1370
1503
  let creditsPercent = null;
1371
1504
  const percentMatch = stripped.match(/█+\s*(\d+)%/);
@@ -1845,38 +1978,76 @@ async function fetchCopilotLimits({
1845
1978
  }
1846
1979
  }
1847
1980
 
1848
- async function fetchKiroLimits({ commandRunner, now = new Date() } = {}) {
1849
- if (!(await isBinaryAvailable("kiro-cli", { commandRunner }))) {
1981
+ async function fetchKiroLimits({
1982
+ commandRunner,
1983
+ now = new Date(),
1984
+ platform = process.platform,
1985
+ } = {}) {
1986
+ const binaryPath = await whichBinary("kiro-cli", { commandRunner });
1987
+ if (!binaryPath) {
1850
1988
  return { configured: false };
1851
1989
  }
1852
1990
 
1853
- const result = await runCommand(
1991
+ const versionResult = await runCommand(
1854
1992
  commandRunner,
1855
- "kiro-cli",
1856
- ["chat", "--no-interactive", "/usage"],
1993
+ binaryPath,
1994
+ ["--version"],
1857
1995
  {
1858
- timeout: 20_000,
1996
+ timeout: 2_000,
1859
1997
  env: { ...process.env, TERM: "xterm-256color" },
1860
1998
  },
1861
1999
  );
2000
+ const version = parseKiroCliVersion(
2001
+ combineKiroCommandOutput(versionResult),
2002
+ );
2003
+ const args = ["chat", "--no-interactive", "/usage"];
2004
+ const pty = kiroPtyInvocation(binaryPath, args, platform);
2005
+
2006
+ // Kiro CLI 2.13 changed pipe behavior: /usage is treated as a normal model
2007
+ // prompt without a terminal. Go straight to a PTY so a refresh does not
2008
+ // accidentally spend credits on an assistant response. Older/unknown builds
2009
+ // retain the existing pipe-first behavior, with a bounded PTY fallback if
2010
+ // the output is not a recognizable usage panel.
2011
+ const attempts = [];
2012
+ if (kiroCliRequiresPty(version)) {
2013
+ if (pty) attempts.push(pty);
2014
+ } else {
2015
+ attempts.push({ command: binaryPath, args });
2016
+ if (pty) attempts.push(pty);
2017
+ }
1862
2018
 
1863
- const stdout = typeof result?.stdout === "string" ? result.stdout : "";
1864
- const stderr = typeof result?.stderr === "string" ? result.stderr : "";
1865
- const output = stderr.trim() || stdout.trim();
1866
-
2019
+ let lastError = null;
1867
2020
  try {
1868
- if (result?.error?.code === "ETIMEDOUT" && !isKiroUsageOutputComplete(output)) {
1869
- throw new Error("Kiro CLI timed out.");
2021
+ if (attempts.length === 0) {
2022
+ throw new Error(
2023
+ `Kiro CLI ${version ? `${version.major}.${version.minor}.${version.patch}` : ""} requires a pseudo-terminal on ${platform}.`,
2024
+ );
1870
2025
  }
1871
- if (!output && result?.status !== 0) {
1872
- throw new Error(`Kiro CLI failed with status ${result.status}.`);
2026
+ for (const attempt of attempts) {
2027
+ const result = await runCommand(
2028
+ commandRunner,
2029
+ attempt.command,
2030
+ attempt.args,
2031
+ {
2032
+ timeout: 20_000,
2033
+ env: { ...process.env, TERM: "xterm-256color" },
2034
+ completeWhen: (stdout, stderr) =>
2035
+ isKiroUsageOutputComplete(`${stdout}\n${stderr}`),
2036
+ completionGraceMs: 750,
2037
+ killProcessGroup: Boolean(pty),
2038
+ },
2039
+ );
2040
+ try {
2041
+ return {
2042
+ configured: true,
2043
+ error: null,
2044
+ ...parseKiroCommandResult(result, { now }),
2045
+ };
2046
+ } catch (error) {
2047
+ lastError = error;
2048
+ }
1873
2049
  }
1874
-
1875
- return {
1876
- configured: true,
1877
- error: null,
1878
- ...parseKiroUsageOutput(output, { now }),
1879
- };
2050
+ throw lastError || new Error("Failed to read Kiro usage.");
1880
2051
  } catch (error) {
1881
2052
  return {
1882
2053
  configured: true,
@@ -2740,7 +2911,7 @@ function toTitleCase(s) {
2740
2911
  // leading brand word and Title Case the rest.
2741
2912
  function normalizePlanLabel(raw, brand) {
2742
2913
  if (raw == null) return null;
2743
- let s = String(raw).trim();
2914
+ let s = String(raw).replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
2744
2915
  if (!s) return null;
2745
2916
  const lower = s.toLowerCase();
2746
2917
  if (["free", "none", "unknown"].includes(lower)) return null;
@@ -2883,7 +3054,7 @@ async function fetchUsageLimitsUncached({
2883
3054
  : null;
2884
3055
 
2885
3056
  const providerFetch = withFetchTimeout(fetchImpl, providerTimeoutMs);
2886
- const [claudeResult, codexResult, cursor, kimi, gemini, kiro, antigravity, copilot, grok, zcode, opencodeGo] = await Promise.all([
3057
+ const [claudeResult, codexResult, cursor, kimi, gemini, kiro, antigravity, copilot, grok, zcode, opencodeGo, qoder, claudeServiceStatus] = await Promise.all([
2887
3058
  claudeToken && !freshClaudeCache && !claudeRetryAtMs
2888
3059
  ? withProviderTimeout(fetchClaudeUsageLimits(claudeToken, { fetchImpl: providerFetch, maxAttempts: 1 }), "Claude", providerTimeoutMs).then(
2889
3060
  (value) => ({ status: "fulfilled", value }),
@@ -2906,7 +3077,7 @@ async function fetchUsageLimitsUncached({
2906
3077
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
2907
3078
  withProviderTimeout(fetchGeminiLimits({ home, env, fetchImpl: providerFetch, commandRunner }), "Gemini", providerTimeoutMs)
2908
3079
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
2909
- fetchKiroLimits({ commandRunner, now }),
3080
+ fetchKiroLimits({ commandRunner, now, platform }),
2910
3081
  fetchAntigravityLimits({ home, commandRunner, requestFn, fetchImpl: providerFetch, nowMs }),
2911
3082
  withProviderTimeout(fetchCopilotLimits({ home, env, fetchImpl: providerFetch, platform, securityRunner }), "GitHub Copilot", providerTimeoutMs)
2912
3083
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
@@ -2919,6 +3090,23 @@ async function fetchUsageLimitsUncached({
2919
3090
  // OPENCODE_GO_AUTH_COOKIE is set. See src/lib/opencode-go-limits.js.
2920
3091
  withProviderTimeout(fetchOpencodeGoLimits({ home, env, fetchImpl: providerFetch }), "OpenCode Go", providerTimeoutMs)
2921
3092
  .catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
3093
+ withProviderTimeout(
3094
+ fetchQoderLimits({
3095
+ home,
3096
+ env,
3097
+ platform,
3098
+ securityRunner,
3099
+ fetchImpl: providerFetch,
3100
+ }),
3101
+ "Qoder",
3102
+ providerTimeoutMs,
3103
+ ).catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
3104
+ // Public status-page probe (fail-soft, own 5-min cache in provider-status.js).
3105
+ // Only probed for configured accounts — without a token the Claude section
3106
+ // never renders, so the reading would have nowhere to go.
3107
+ claudeToken
3108
+ ? fetchProviderServiceStatus("claude", { fetchImpl, nowMs })
3109
+ : Promise.resolve(null),
2922
3110
  ]);
2923
3111
 
2924
3112
  let claude;
@@ -2985,6 +3173,15 @@ async function fetchUsageLimitsUncached({
2985
3173
  }
2986
3174
  }
2987
3175
 
3176
+ // Attach the service-status reading AFTER assembly so it rides on every path
3177
+ // (live, fresh-cache, stale-cache, error) but never gets persisted by
3178
+ // writeClaudeLimitsCache above — a disk cache must not resurrect an incident
3179
+ // banner hours later. Only active incidents ship; "none" is omitted so the
3180
+ // client renders nothing in the happy path.
3181
+ if (claude.configured && claudeServiceStatus && claudeServiceStatus.indicator !== "none") {
3182
+ claude.service_status = claudeServiceStatus;
3183
+ }
3184
+
2988
3185
  let codex;
2989
3186
  if (!codexToken) {
2990
3187
  codex = { configured: false };
@@ -3043,6 +3240,7 @@ async function fetchUsageLimitsUncached({
3043
3240
  grok: withPlanLabel(grok, null, "Grok"),
3044
3241
  zcode: withPlanLabel(zcode, zcode.plan_label, "ZCode"),
3045
3242
  opencodeGo: withPlanLabel(opencodeGo, opencodeGo?.plan_label, "OpenCode Go"),
3243
+ qoder: withPlanLabel(qoder, qoder?.plan_label, "Qoder"),
3046
3244
  };
3047
3245
 
3048
3246
  for (const [providerName, provider] of Object.entries(data)) {
@@ -3086,6 +3284,7 @@ module.exports = {
3086
3284
  normalizeGeminiQuotaResponse,
3087
3285
  normalizeKimiUsageResponse,
3088
3286
  parseKiroUsageOutput,
3287
+ fetchKiroLimits,
3089
3288
  normalizeAntigravityResponse,
3090
3289
  parseListeningPorts,
3091
3290
  detectAntigravityProcess,
@@ -3100,4 +3299,5 @@ module.exports = {
3100
3299
  fetchGrokLimits,
3101
3300
  fetchZcodeLimits,
3102
3301
  fetchOpencodeGoLimits,
3302
+ fetchQoderLimits,
3103
3303
  };
@@ -1 +0,0 @@
1
- import{j as r,d as e}from"./__vite-browser-external-D3llGq6p.js";function n({children:i,title:a,subtitle:o,className:s="",bodyClassName:d=""}){return r.jsxs("div",{className:e("rounded-xl border border-oai-gray-200 dark:border-oai-gray-800 bg-white dark:bg-oai-gray-900 transition-colors duration-200",s),children:[(a||o)&&r.jsxs("div",{className:"px-5 py-4 border-b border-oai-gray-200 dark:border-oai-gray-800 transition-colors duration-200",children:[a&&r.jsx("h3",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide transition-colors duration-200",children:a}),o&&r.jsx("p",{className:"text-sm text-oai-gray-500 dark:text-oai-gray-300 mt-1 transition-colors duration-200",children:o})]}),r.jsx("div",{className:`p-5 ${d}`,children:i})]})}export{n as C};
@@ -1 +0,0 @@
1
- import{r as n,z as a}from"./__vite-browser-external-D3llGq6p.js";import{u as p}from"./DialogTitle-BbAVHI_v.js";import{u as c}from"./useScrollLock-DVSyJ-GE.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,{store:i}=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};