pi-provider-qoder-cn 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -8
- package/dist/index.js +101 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,14 +64,18 @@ export QODERCN_PERSONAL_ACCESS_TOKEN="<your PAT>"
|
|
|
64
64
|
|
|
65
65
|
### `/qoder-cn.usage`
|
|
66
66
|
|
|
67
|
-
Shows the plan allowance and any purchased add-on credits.
|
|
67
|
+
Shows the plan allowance and any purchased add-on credits as one aligned grid.
|
|
68
|
+
In a terminal the gauge is coloured by how much of the allowance is gone —
|
|
69
|
+
green under 60%, yellow to 85%, red above that, and bold red once the quota is
|
|
70
|
+
exceeded.
|
|
68
71
|
|
|
69
72
|
```text
|
|
70
73
|
Qoder CN Plan (personal_standard)
|
|
71
|
-
[
|
|
72
|
-
[
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
Overall [███░░░░░░░░░░░░░░░░░░] 12.5% used
|
|
75
|
+
Plan quota [███░░░░░░░░░░░░░░░░░░] 250 / 2000 credits 12.5% 1750 left
|
|
76
|
+
Add-on [░░░░░░░░░░░░░░░░░░░░░] 0 / 700 credits 0% 700 left
|
|
77
|
+
Resets never
|
|
78
|
+
Manage https://qoder.com.cn/account/usage
|
|
75
79
|
```
|
|
76
80
|
|
|
77
81
|
Append `json` to print the untouched API payload — useful when a field you need
|
|
@@ -81,13 +85,24 @@ is not in the formatted view:
|
|
|
81
85
|
/qoder-cn.usage json
|
|
82
86
|
```
|
|
83
87
|
|
|
88
|
+
Append `plain` for the same grid without the colour codes, e.g. when pasting it
|
|
89
|
+
into an issue:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
/qoder-cn.usage plain
|
|
93
|
+
```
|
|
94
|
+
|
|
84
95
|
Notes:
|
|
85
96
|
|
|
86
|
-
- The plan allowance (`Plan quota`) and top-up credits (`Add-on
|
|
97
|
+
- The plan allowance (`Plan quota`) and top-up credits (`Add-on`) are
|
|
87
98
|
independent buckets with independent expiry, so both are listed when present.
|
|
88
|
-
|
|
99
|
+
Columns are sized to the rows that came back, so a single-bucket plan does
|
|
100
|
+
not reserve space for one it does not have.
|
|
101
|
+
- Colour is suppressed automatically for a `NO_COLOR` environment, a dumb
|
|
102
|
+
terminal, and any non-TTY output that is not the interactive UI.
|
|
103
|
+
- `Resets never` means the API reported the year-9999 sentinel, i.e. the
|
|
89
104
|
allowance does not reset on a schedule.
|
|
90
|
-
- `
|
|
105
|
+
- `Note plan quota is prorated` appears after a mid-cycle plan change.
|
|
91
106
|
- The command uses the same stored token as chat requests, refreshing it first,
|
|
92
107
|
so it reports the same quota the model is actually billed against.
|
|
93
108
|
|
package/dist/index.js
CHANGED
|
@@ -1719,11 +1719,37 @@ To reset: /qoder-endpoint default`,
|
|
|
1719
1719
|
|
|
1720
1720
|
// src/commands/usage.ts
|
|
1721
1721
|
init_region();
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1722
|
+
var ANSI = {
|
|
1723
|
+
reset: "\x1B[0m",
|
|
1724
|
+
bold: "\x1B[1m",
|
|
1725
|
+
dim: "\x1B[2m",
|
|
1726
|
+
red: "\x1B[31m",
|
|
1727
|
+
green: "\x1B[32m",
|
|
1728
|
+
yellow: "\x1B[33m"
|
|
1729
|
+
};
|
|
1730
|
+
var FILLED_CHAR = "\u2588";
|
|
1731
|
+
var EMPTY_CHAR = "\u2591";
|
|
1732
|
+
var UNKNOWN_CHAR = "?";
|
|
1733
|
+
var BAR_WIDTH = 20;
|
|
1734
|
+
var COLUMN_GAP = " ";
|
|
1735
|
+
function paint(text, code) {
|
|
1736
|
+
return code && text ? `${code}${text}${ANSI.reset}` : text;
|
|
1737
|
+
}
|
|
1738
|
+
function usageColor(percentage, danger = false) {
|
|
1739
|
+
if (danger) return `${ANSI.red}${ANSI.bold}`;
|
|
1740
|
+
if (typeof percentage !== "number" || !Number.isFinite(percentage)) return void 0;
|
|
1741
|
+
if (percentage >= 85) return `${ANSI.red}${ANSI.bold}`;
|
|
1742
|
+
if (percentage >= 60) return ANSI.yellow;
|
|
1743
|
+
return ANSI.green;
|
|
1744
|
+
}
|
|
1745
|
+
function usageBar(percentage, options = {}) {
|
|
1746
|
+
const width = options.width ?? BAR_WIDTH;
|
|
1747
|
+
const known = typeof percentage === "number" && Number.isFinite(percentage);
|
|
1748
|
+
const filled = known ? Math.max(0, Math.min(width, Math.round(percentage / 100 * width))) : 0;
|
|
1749
|
+
const fill = known ? FILLED_CHAR.repeat(filled) : "";
|
|
1750
|
+
const rest = known ? EMPTY_CHAR.repeat(width - filled) : UNKNOWN_CHAR.repeat(width);
|
|
1751
|
+
if (!options.color) return `[${fill}${rest}]`;
|
|
1752
|
+
return paint("[", ANSI.dim) + paint(fill, usageColor(percentage, options.danger)) + paint(rest, ANSI.dim) + paint("]", ANSI.dim);
|
|
1727
1753
|
}
|
|
1728
1754
|
function formatAmount(value, unit) {
|
|
1729
1755
|
if (typeof value !== "number" || !Number.isFinite(value)) return "?";
|
|
@@ -1748,41 +1774,82 @@ function formatResetTime(expiresAt, now = Date.now()) {
|
|
|
1748
1774
|
const relative = days > 0 ? `${days}d ${hours}h` : hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
|
|
1749
1775
|
return `${new Date(timestamp).toISOString().slice(0, 10)} (in ${relative})`;
|
|
1750
1776
|
}
|
|
1751
|
-
function
|
|
1752
|
-
|
|
1777
|
+
function isBucketRow(row) {
|
|
1778
|
+
return typeof row.used === "number" || typeof row.total === "number";
|
|
1779
|
+
}
|
|
1780
|
+
function rightCell(text, width, code) {
|
|
1781
|
+
return " ".repeat(Math.max(0, width - text.length)) + paint(text, code);
|
|
1782
|
+
}
|
|
1783
|
+
function renderUsageRows(rows, notes, color) {
|
|
1784
|
+
const buckets = rows.filter(isBucketRow);
|
|
1785
|
+
const usedWidth = Math.max(0, ...buckets.map((row) => formatAmount(row.used).length));
|
|
1786
|
+
const totalWidth = Math.max(0, ...buckets.map((row) => formatAmount(row.total).length));
|
|
1787
|
+
const unitWidth = Math.max(0, ...buckets.map((row) => (row.unit ?? "").length));
|
|
1788
|
+
const remainWidth = Math.max(0, ...buckets.map((row) => formatAmount(row.remaining).length));
|
|
1789
|
+
const labelWidth = Math.max(0, ...rows.map((row) => row.label.length), ...notes.map((note) => note.label.length));
|
|
1790
|
+
const percentWidth = Math.max(0, ...rows.map((row) => row.percent === void 0 ? 1 : `${row.percent}%`.length));
|
|
1791
|
+
const amountWidth = buckets.length > 0 ? usedWidth + 3 + totalWidth + 1 + unitWidth : 0;
|
|
1792
|
+
const remainder = (row) => isBucketRow(row) ? `${formatAmount(row.remaining).padStart(remainWidth)} left` : row.tail ?? "";
|
|
1793
|
+
const remainderWidth = Math.max(0, ...rows.map((row) => remainder(row).length));
|
|
1794
|
+
const lines = rows.map((row) => {
|
|
1795
|
+
const amount = isBucketRow(row) ? `${formatAmount(row.used).padStart(usedWidth)} / ${formatAmount(row.total).padStart(totalWidth)} ${(row.unit ?? "").padEnd(unitWidth)}` : " ".repeat(amountWidth);
|
|
1796
|
+
const percent = row.percent === void 0 ? UNKNOWN_CHAR : `${row.percent}%`;
|
|
1797
|
+
return [
|
|
1798
|
+
row.label.padEnd(labelWidth),
|
|
1799
|
+
usageBar(row.percent, { color, danger: row.danger }),
|
|
1800
|
+
amount,
|
|
1801
|
+
rightCell(percent, percentWidth, color ? usageColor(row.percent, row.danger) : void 0),
|
|
1802
|
+
remainder(row).padEnd(remainderWidth)
|
|
1803
|
+
].join(COLUMN_GAP);
|
|
1804
|
+
});
|
|
1805
|
+
for (const note of notes) {
|
|
1806
|
+
lines.push(`${note.label.padEnd(labelWidth)}${COLUMN_GAP}${color ? paint(note.value, note.color) : note.value}`);
|
|
1807
|
+
}
|
|
1808
|
+
return lines.map((line) => line.replace(/ +$/, ""));
|
|
1809
|
+
}
|
|
1810
|
+
function bucketRow(label, bucket, danger) {
|
|
1753
1811
|
const rawPercent = bucket?.percentage;
|
|
1754
1812
|
const derivedPercent = typeof bucket?.used === "number" && typeof bucket?.total === "number" && bucket.total > 0 ? bucket.used / bucket.total * 100 : void 0;
|
|
1755
1813
|
const percent = formatPercent(
|
|
1756
1814
|
typeof rawPercent === "number" && Number.isFinite(rawPercent) ? rawPercent : derivedPercent
|
|
1757
1815
|
);
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1816
|
+
return {
|
|
1817
|
+
label,
|
|
1818
|
+
percent,
|
|
1819
|
+
used: bucket?.used,
|
|
1820
|
+
total: bucket?.total,
|
|
1821
|
+
remaining: bucket?.remaining,
|
|
1822
|
+
unit: bucket?.unit || "credits",
|
|
1823
|
+
danger
|
|
1824
|
+
};
|
|
1763
1825
|
}
|
|
1764
1826
|
function hasBucket(bucket) {
|
|
1765
1827
|
if (!bucket) return false;
|
|
1766
1828
|
return (bucket.total ?? 0) > 0 || (bucket.used ?? 0) > 0;
|
|
1767
1829
|
}
|
|
1768
|
-
function formatQoderUsage(raw, mode, now = Date.now()) {
|
|
1830
|
+
function formatQoderUsage(raw, mode, now = Date.now(), options = {}) {
|
|
1831
|
+
const color = options.color ?? false;
|
|
1769
1832
|
const region = getQoderRegionConfig(mode);
|
|
1770
|
-
const
|
|
1833
|
+
const danger = Boolean(raw?.isQuotaExceeded);
|
|
1771
1834
|
const userType = raw?.userType ? ` (${raw.userType})` : "";
|
|
1772
1835
|
const overallPercent = formatPercent(raw?.totalUsagePercentage);
|
|
1773
|
-
|
|
1774
|
-
lines.push(`${usageBar(overallPercent)} ${overallPercent === void 0 ? "?" : `${overallPercent}%`} used`);
|
|
1775
|
-
if (raw?.isQuotaExceeded) lines.push(" ! quota exceeded");
|
|
1836
|
+
const rows = [{ label: "Overall", percent: overallPercent, tail: "used", danger }];
|
|
1776
1837
|
const showUserQuota = hasBucket(raw?.userQuota);
|
|
1777
1838
|
const showAddOnQuota = hasBucket(raw?.addOnQuota);
|
|
1778
|
-
if (showUserQuota)
|
|
1779
|
-
if (showAddOnQuota)
|
|
1780
|
-
|
|
1781
|
-
if (
|
|
1782
|
-
|
|
1839
|
+
if (showUserQuota) rows.push(bucketRow("Plan quota", raw.userQuota, danger));
|
|
1840
|
+
if (showAddOnQuota) rows.push(bucketRow("Add-on", raw.addOnQuota, danger));
|
|
1841
|
+
const notes = [];
|
|
1842
|
+
if (danger) notes.push({ label: "Status", value: "quota exceeded", color: ANSI.red });
|
|
1843
|
+
if (!showUserQuota && !showAddOnQuota) notes.push({ label: "Buckets", value: "none returned" });
|
|
1844
|
+
if (raw?.isPlanQuotaProrated) notes.push({ label: "Note", value: "plan quota is prorated" });
|
|
1845
|
+
notes.push({ label: "Resets", value: formatResetTime(raw?.expiresAt, now) });
|
|
1783
1846
|
const manageUrl = raw?.addOnQuota?.detailUrl || raw?.upgradeUrl || region.manageUrl;
|
|
1784
|
-
if (manageUrl)
|
|
1785
|
-
|
|
1847
|
+
if (manageUrl) notes.push({ label: "Manage", value: manageUrl });
|
|
1848
|
+
const title = `${region.usageTitle}${userType}`;
|
|
1849
|
+
return {
|
|
1850
|
+
title: region.usageTitle,
|
|
1851
|
+
lines: [paint(title, color ? ANSI.bold : void 0), ...renderUsageRows(rows, notes, color)]
|
|
1852
|
+
};
|
|
1786
1853
|
}
|
|
1787
1854
|
async function fetchQoderQuota(accessToken, mode) {
|
|
1788
1855
|
const response = await fetch(getQoderUsageURL(mode), {
|
|
@@ -1819,6 +1886,14 @@ async function resolveAccessToken(providerID, ctx) {
|
|
|
1819
1886
|
return getCachedCredentials2("", providerID)?.access || void 0;
|
|
1820
1887
|
}
|
|
1821
1888
|
var FORMAT_ARGS = /* @__PURE__ */ new Set(["json", "raw", "debug"]);
|
|
1889
|
+
var PLAIN_ARGS = /* @__PURE__ */ new Set(["plain", "no-color", "nocolor"]);
|
|
1890
|
+
function shouldColorize(args, options = {}) {
|
|
1891
|
+
if (PLAIN_ARGS.has((args || "").trim().toLowerCase())) return false;
|
|
1892
|
+
const env = options.env ?? process.env;
|
|
1893
|
+
if (env.NO_COLOR || env.TERM === "dumb" || env.FORCE_COLOR === "0") return false;
|
|
1894
|
+
if (options.hasUI) return true;
|
|
1895
|
+
return options.isTTY ?? Boolean(process.stdout?.isTTY);
|
|
1896
|
+
}
|
|
1822
1897
|
async function runUsageCommand(mode, args, ctx) {
|
|
1823
1898
|
const region = getQoderRegionConfig(mode);
|
|
1824
1899
|
const providerID = region.providerID;
|
|
@@ -1830,7 +1905,8 @@ async function runUsageCommand(mode, args, ctx) {
|
|
|
1830
1905
|
return;
|
|
1831
1906
|
}
|
|
1832
1907
|
const raw = await fetchQoderQuota(accessToken, mode);
|
|
1833
|
-
const
|
|
1908
|
+
const color = !wantsRaw && shouldColorize(args, { hasUI: Boolean(ctx?.ui) });
|
|
1909
|
+
const output = wantsRaw ? JSON.stringify(raw, null, 2) : formatQoderUsage(raw, mode, Date.now(), { color }).lines.join("\n");
|
|
1834
1910
|
ctx?.ui?.notify(output, "info");
|
|
1835
1911
|
if (!ctx?.ui) console.log(output);
|
|
1836
1912
|
} catch (error) {
|
package/package.json
CHANGED