codex-lb 0.3.0__py3-none-any.whl → 0.3.1__py3-none-any.whl
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.
- app/static/index.css +1014 -338
- app/static/index.html +459 -422
- app/static/index.js +101 -29
- {codex_lb-0.3.0.dist-info → codex_lb-0.3.1.dist-info}/METADATA +32 -6
- {codex_lb-0.3.0.dist-info → codex_lb-0.3.1.dist-info}/RECORD +8 -9
- app/static/7.css +0 -1409
- {codex_lb-0.3.0.dist-info → codex_lb-0.3.1.dist-info}/WHEEL +0 -0
- {codex_lb-0.3.0.dist-info → codex_lb-0.3.1.dist-info}/entry_points.txt +0 -0
- {codex_lb-0.3.0.dist-info → codex_lb-0.3.1.dist-info}/licenses/LICENSE +0 -0
app/static/index.js
CHANGED
|
@@ -506,6 +506,33 @@
|
|
|
506
506
|
};
|
|
507
507
|
const routingLabel = (strategy) => ROUTING_LABELS[strategy] || "unknown";
|
|
508
508
|
const errorLabel = (code) => ERROR_LABELS[code] || "--";
|
|
509
|
+
const calculateProgressClass = (status, remainingPercent) => {
|
|
510
|
+
if (status === "exceeded") return "error";
|
|
511
|
+
if (status === "paused" || status === "deactivated") return "";
|
|
512
|
+
const percent = toNumber(remainingPercent) || 0;
|
|
513
|
+
if (percent <= 20) return "error";
|
|
514
|
+
if (percent <= 50) return "limited";
|
|
515
|
+
return "success";
|
|
516
|
+
};
|
|
517
|
+
const calculateProgressTextClass = (status, remainingPercent) => {
|
|
518
|
+
const cls = calculateProgressClass(status, remainingPercent);
|
|
519
|
+
return cls ? `text-${cls}` : "";
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
const calculateTextUsageClass = (status, remainingPercent) => {
|
|
523
|
+
if (status === "exceeded") return "error";
|
|
524
|
+
// For text, we show usage color even if paused. Only deactivated is plain.
|
|
525
|
+
if (status === "deactivated") return "";
|
|
526
|
+
const percent = toNumber(remainingPercent) || 0;
|
|
527
|
+
if (percent <= 20) return "error";
|
|
528
|
+
if (percent <= 50) return "limited";
|
|
529
|
+
return "success";
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
const calculateTextUsageTextClass = (status, remainingPercent) => {
|
|
533
|
+
const cls = calculateTextUsageClass(status, remainingPercent);
|
|
534
|
+
return cls ? `text-${cls}` : "";
|
|
535
|
+
};
|
|
509
536
|
const progressClass = (status) => PROGRESS_CLASS_BY_STATUS[status] || "";
|
|
510
537
|
|
|
511
538
|
const normalizeSearchInput = (value) =>
|
|
@@ -937,9 +964,9 @@
|
|
|
937
964
|
const entries =
|
|
938
965
|
window.key === "primary"
|
|
939
966
|
? applySecondaryExhaustedToPrimary(
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
967
|
+
rawEntries,
|
|
968
|
+
secondaryExhaustedAccounts,
|
|
969
|
+
)
|
|
943
970
|
: rawEntries;
|
|
944
971
|
const remaining =
|
|
945
972
|
hasPrimaryAdjustments
|
|
@@ -954,21 +981,40 @@
|
|
|
954
981
|
window.key,
|
|
955
982
|
);
|
|
956
983
|
const gradient = buildDonutGradient(items, capacity);
|
|
957
|
-
const legendItems = items.map((item) =>
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
984
|
+
const legendItems = items.map((item) => {
|
|
985
|
+
const percent = item.remainingPercent;
|
|
986
|
+
let valueClass = "success";
|
|
987
|
+
if (percent <= 20) {
|
|
988
|
+
valueClass = "error";
|
|
989
|
+
} else if (percent <= 50) {
|
|
990
|
+
valueClass = "limited";
|
|
991
|
+
}
|
|
992
|
+
return {
|
|
993
|
+
label: truncateText(item.label, 28),
|
|
994
|
+
fullLabel: item.label,
|
|
995
|
+
detailLabel: "Remaining",
|
|
996
|
+
detailValue: formatPercent(item.remainingPercent),
|
|
997
|
+
valueClass,
|
|
998
|
+
color: item.color,
|
|
999
|
+
};
|
|
1000
|
+
});
|
|
1001
|
+
if (capacity > 0) {
|
|
964
1002
|
const consumedPercent = Math.min(
|
|
965
1003
|
100,
|
|
966
1004
|
Math.max(0, (consumed / capacity) * 100),
|
|
967
1005
|
);
|
|
1006
|
+
let consumedClass = "success";
|
|
1007
|
+
if (consumedPercent >= 80) {
|
|
1008
|
+
consumedClass = "error";
|
|
1009
|
+
} else if (consumedPercent >= 50) {
|
|
1010
|
+
consumedClass = "limited";
|
|
1011
|
+
}
|
|
968
1012
|
legendItems.push({
|
|
969
1013
|
label: "Consumed",
|
|
1014
|
+
fullLabel: "Consumed",
|
|
970
1015
|
detailLabel: "",
|
|
971
1016
|
detailValue: formatPercent(consumedPercent),
|
|
1017
|
+
valueClass: consumedClass,
|
|
972
1018
|
color: CONSUMED_COLOR,
|
|
973
1019
|
});
|
|
974
1020
|
}
|
|
@@ -995,7 +1041,7 @@
|
|
|
995
1041
|
},
|
|
996
1042
|
remaining: remainingRounded,
|
|
997
1043
|
remainingText: formatPercent(secondaryRemaining),
|
|
998
|
-
progressClass:
|
|
1044
|
+
progressClass: calculateProgressClass(account.status, secondaryRemaining),
|
|
999
1045
|
marquee: account.status === "deactivated",
|
|
1000
1046
|
meta: formatQuotaResetMeta(
|
|
1001
1047
|
account.resetAtSecondary,
|
|
@@ -1700,12 +1746,33 @@
|
|
|
1700
1746
|
window.open(this.authDialog.verificationUrl, "_blank", "noopener");
|
|
1701
1747
|
}
|
|
1702
1748
|
},
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1749
|
+
calculateProgressClass(status, remainingPercent) {
|
|
1750
|
+
return calculateProgressClass(status, remainingPercent);
|
|
1751
|
+
},
|
|
1752
|
+
calculateProgressTextClass(status, remainingPercent) {
|
|
1753
|
+
return calculateProgressTextClass(status, remainingPercent);
|
|
1754
|
+
},
|
|
1755
|
+
async copyToClipboard(value, label, e) {
|
|
1756
|
+
if (!value) return;
|
|
1757
|
+
|
|
1758
|
+
// Localized feedback in the button
|
|
1759
|
+
let btn = null;
|
|
1760
|
+
let originalText = "";
|
|
1761
|
+
if (e && e.target) {
|
|
1762
|
+
btn = e.target.tagName === "BUTTON" ? e.target : e.target.closest("button");
|
|
1763
|
+
if (btn) {
|
|
1764
|
+
originalText = btn.textContent;
|
|
1765
|
+
btn.textContent = "Copied!";
|
|
1766
|
+
btn.classList.add("copy-success");
|
|
1767
|
+
window.setTimeout(() => {
|
|
1768
|
+
btn.textContent = originalText;
|
|
1769
|
+
btn.classList.remove("copy-success");
|
|
1770
|
+
}, 4000);
|
|
1771
|
+
}
|
|
1706
1772
|
}
|
|
1773
|
+
|
|
1707
1774
|
try {
|
|
1708
|
-
if (navigator.clipboard
|
|
1775
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1709
1776
|
await navigator.clipboard.writeText(value);
|
|
1710
1777
|
} else {
|
|
1711
1778
|
const textarea = document.createElement("textarea");
|
|
@@ -1718,29 +1785,26 @@
|
|
|
1718
1785
|
document.execCommand("copy");
|
|
1719
1786
|
document.body.removeChild(textarea);
|
|
1720
1787
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
window.setTimeout(() => {
|
|
1725
|
-
if (this.authDialog.statusLabel === `${label} copied.`) {
|
|
1726
|
-
this.authDialog.statusLabel = previousLabel;
|
|
1727
|
-
}
|
|
1728
|
-
}, 2000);
|
|
1729
|
-
} else {
|
|
1788
|
+
|
|
1789
|
+
// Only show message box if auth dialog is not open and button feedback wasn't possible
|
|
1790
|
+
if (!this.authDialog.open && !btn) {
|
|
1730
1791
|
this.openMessageBox({
|
|
1731
1792
|
tone: "success",
|
|
1732
1793
|
title: "Copied",
|
|
1733
1794
|
message: `${label} copied to clipboard.`,
|
|
1734
1795
|
});
|
|
1735
1796
|
}
|
|
1736
|
-
} catch (
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1797
|
+
} catch (err) {
|
|
1798
|
+
console.error("Clipboard error:", err);
|
|
1799
|
+
if (btn) {
|
|
1800
|
+
btn.textContent = "Failed";
|
|
1801
|
+
btn.classList.remove("copy-success");
|
|
1802
|
+
window.setTimeout(() => { btn.textContent = originalText; }, 2000);
|
|
1803
|
+
} else if (!this.authDialog.open) {
|
|
1740
1804
|
this.openMessageBox({
|
|
1741
1805
|
tone: "error",
|
|
1742
1806
|
title: "Copy failed",
|
|
1743
|
-
message:
|
|
1807
|
+
message: `Could not copy ${label}.`,
|
|
1744
1808
|
});
|
|
1745
1809
|
}
|
|
1746
1810
|
}
|
|
@@ -2084,6 +2148,7 @@
|
|
|
2084
2148
|
statusLabel,
|
|
2085
2149
|
requestStatusLabel,
|
|
2086
2150
|
requestStatusClass,
|
|
2151
|
+
calculateTextUsageTextClass,
|
|
2087
2152
|
progressClass,
|
|
2088
2153
|
planLabel,
|
|
2089
2154
|
routingLabel,
|
|
@@ -2100,7 +2165,14 @@
|
|
|
2100
2165
|
formatQuotaResetLabel,
|
|
2101
2166
|
formatAccessTokenLabel,
|
|
2102
2167
|
formatRefreshTokenLabel,
|
|
2168
|
+
formatAccessTokenLabel,
|
|
2169
|
+
formatRefreshTokenLabel,
|
|
2103
2170
|
formatIdTokenLabel,
|
|
2171
|
+
theme: localStorage.getItem('theme') || 'dark',
|
|
2172
|
+
toggleTheme() {
|
|
2173
|
+
this.theme = this.theme === 'dark' ? 'light' : 'dark';
|
|
2174
|
+
localStorage.setItem('theme', this.theme);
|
|
2175
|
+
},
|
|
2104
2176
|
}));
|
|
2105
2177
|
};
|
|
2106
2178
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-lb
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Codex load balancer and proxy for ChatGPT accounts with usage dashboard
|
|
5
5
|
Author-email: Soju06 <qlskssk@gmail.com>
|
|
6
6
|
Maintainer-email: Soju06 <qlskssk@gmail.com>
|
|
@@ -55,9 +55,13 @@ Description-Content-Type: text/markdown
|
|
|
55
55
|
|
|
56
56
|
Load balancer for ChatGPT accounts. Pool multiple accounts, track usage, view everything in a dashboard.
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
### Main Dashboard View
|
|
59
|
+
|
|
60
|
+

|
|
61
|
+
|
|
62
|
+
### Accounts View
|
|
63
|
+
|
|
64
|
+

|
|
61
65
|
|
|
62
66
|
## Quick Start
|
|
63
67
|
|
|
@@ -78,9 +82,7 @@ uvx codex-lb
|
|
|
78
82
|
|
|
79
83
|
Open [localhost:2455](http://localhost:2455) → Add account → Done.
|
|
80
84
|
|
|
81
|
-
## Accounts view
|
|
82
85
|
|
|
83
|
-

|
|
84
86
|
|
|
85
87
|
## Codex CLI & Extension Setup
|
|
86
88
|
|
|
@@ -102,7 +104,31 @@ requires_openai_auth = true # Required: enables model selection in Codex IDE ex
|
|
|
102
104
|
## Data
|
|
103
105
|
|
|
104
106
|
All data stored in `~/.codex-lb/`:
|
|
107
|
+
|
|
105
108
|
- `store.db` – accounts, usage logs
|
|
106
109
|
- `encryption.key` – encrypts tokens (auto-generated)
|
|
107
110
|
|
|
108
111
|
Backup this directory to preserve your accounts.
|
|
112
|
+
|
|
113
|
+
## Contributors ✨
|
|
114
|
+
|
|
115
|
+
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
116
|
+
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
117
|
+
<!-- prettier-ignore-start -->
|
|
118
|
+
<!-- markdownlint-disable -->
|
|
119
|
+
<table>
|
|
120
|
+
<tbody>
|
|
121
|
+
<tr>
|
|
122
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Soju06"><img src="https://avatars.githubusercontent.com/u/34199905?v=4?s=100" width="100px;" alt="Soju06"/><br /><sub><b>Soju06</b></sub></a><br /><a href="https://github.com/Soju06/codex-lb/commits?author=Soju06" title="Code">💻</a> <a href="https://github.com/Soju06/codex-lb/commits?author=Soju06" title="Tests">⚠️</a> <a href="#maintenance-Soju06" title="Maintenance">🚧</a> <a href="#infra-Soju06" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
|
123
|
+
<td align="center" valign="top" width="14.28%"><a href="http://jonas.kamsker.at/"><img src="https://avatars.githubusercontent.com/u/11245306?v=4?s=100" width="100px;" alt="Jonas Kamsker"/><br /><sub><b>Jonas Kamsker</b></sub></a><br /><a href="https://github.com/Soju06/codex-lb/commits?author=JKamsker" title="Code">💻</a> <a href="https://github.com/Soju06/codex-lb/issues?q=author%3AJKamsker" title="Bug reports">🐛</a> <a href="#maintenance-JKamsker" title="Maintenance">🚧</a></td>
|
|
124
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Quack6765"><img src="https://avatars.githubusercontent.com/u/5446230?v=4?s=100" width="100px;" alt="Quack"/><br /><sub><b>Quack</b></sub></a><br /><a href="https://github.com/Soju06/codex-lb/commits?author=Quack6765" title="Code">💻</a> <a href="https://github.com/Soju06/codex-lb/issues?q=author%3AQuack6765" title="Bug reports">🐛</a> <a href="#maintenance-Quack6765" title="Maintenance">🚧</a> <a href="#design-Quack6765" title="Design">🎨</a></td>
|
|
125
|
+
</tr>
|
|
126
|
+
</tbody>
|
|
127
|
+
</table>
|
|
128
|
+
|
|
129
|
+
<!-- markdownlint-restore -->
|
|
130
|
+
<!-- prettier-ignore-end -->
|
|
131
|
+
|
|
132
|
+
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
133
|
+
|
|
134
|
+
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
|
@@ -86,12 +86,11 @@ app/modules/usage/repository.py,sha256=DtJI4kgajW7YUJ0JKJjdNCPBXT_fdBwqDoepi9azn
|
|
|
86
86
|
app/modules/usage/schemas.py,sha256=eCgunQOvQeYEqv9IecjunONPVLpg2MPn_YGzsnBTcpQ,1633
|
|
87
87
|
app/modules/usage/service.py,sha256=8-XX8m4TgQteum-a53l0DS-EL2NnC4r9artehfFltvM,10315
|
|
88
88
|
app/modules/usage/updater.py,sha256=VAeRFvgfpZLCTGxfikefw35ecs8Ja7EoWldIldXDjmE,6783
|
|
89
|
-
app/static/
|
|
90
|
-
app/static/index.
|
|
91
|
-
app/static/index.
|
|
92
|
-
|
|
93
|
-
codex_lb-0.3.
|
|
94
|
-
codex_lb-0.3.
|
|
95
|
-
codex_lb-0.3.
|
|
96
|
-
codex_lb-0.3.
|
|
97
|
-
codex_lb-0.3.0.dist-info/RECORD,,
|
|
89
|
+
app/static/index.css,sha256=k75jd3sqISoTjl-cqyrMo72EUlwr65FQEX9SEFSAaRA,23223
|
|
90
|
+
app/static/index.html,sha256=K79S0wMIla6i3oXFwVpSiG2jNlE8x5XxKzsxu177yyo,29131
|
|
91
|
+
app/static/index.js,sha256=odwrhEiSlwq3WW7WcnO4-AGVh_N-TnD5AMuzagOtg4M,61544
|
|
92
|
+
codex_lb-0.3.1.dist-info/METADATA,sha256=KyjxuKYrjCvIXQYcDQ1M5kASLBR6Vu6u3RplrzT6Znk,5840
|
|
93
|
+
codex_lb-0.3.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
94
|
+
codex_lb-0.3.1.dist-info/entry_points.txt,sha256=SEa5T6Uz2Fhy574No6Y0XyGmYi3PXLrhu2xStJTqyI8,42
|
|
95
|
+
codex_lb-0.3.1.dist-info/licenses/LICENSE,sha256=cHPibxiL0TXwrUX_kNY6ym544EX1UCzKhxdaca5cFuk,1062
|
|
96
|
+
codex_lb-0.3.1.dist-info/RECORD,,
|