opencode-quotas 0.0.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.
- package/README.md +344 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +42 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/constants.d.ts +9 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +15 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/defaults.d.ts +3 -0
- package/dist/src/defaults.d.ts.map +1 -0
- package/dist/src/defaults.js +52 -0
- package/dist/src/defaults.js.map +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +265 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/interfaces.d.ts +197 -0
- package/dist/src/interfaces.d.ts.map +1 -0
- package/dist/src/interfaces.js +2 -0
- package/dist/src/interfaces.js.map +1 -0
- package/dist/src/logger.d.ts +14 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +44 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/plugin-state.d.ts +20 -0
- package/dist/src/plugin-state.d.ts.map +1 -0
- package/dist/src/plugin-state.js +53 -0
- package/dist/src/plugin-state.js.map +1 -0
- package/dist/src/providers/antigravity/auth.d.ts +12 -0
- package/dist/src/providers/antigravity/auth.d.ts.map +1 -0
- package/dist/src/providers/antigravity/auth.js +109 -0
- package/dist/src/providers/antigravity/auth.js.map +1 -0
- package/dist/src/providers/antigravity/index.d.ts +2 -0
- package/dist/src/providers/antigravity/index.d.ts.map +1 -0
- package/dist/src/providers/antigravity/index.js +2 -0
- package/dist/src/providers/antigravity/index.js.map +1 -0
- package/dist/src/providers/antigravity/provider.d.ts +34 -0
- package/dist/src/providers/antigravity/provider.d.ts.map +1 -0
- package/dist/src/providers/antigravity/provider.js +216 -0
- package/dist/src/providers/antigravity/provider.js.map +1 -0
- package/dist/src/providers/codex.d.ts +4 -0
- package/dist/src/providers/codex.d.ts.map +1 -0
- package/dist/src/providers/codex.js +242 -0
- package/dist/src/providers/codex.js.map +1 -0
- package/dist/src/providers/github.d.ts +4 -0
- package/dist/src/providers/github.d.ts.map +1 -0
- package/dist/src/providers/github.js +139 -0
- package/dist/src/providers/github.js.map +1 -0
- package/dist/src/quota-cache.d.ts +26 -0
- package/dist/src/quota-cache.d.ts.map +1 -0
- package/dist/src/quota-cache.js +107 -0
- package/dist/src/quota-cache.js.map +1 -0
- package/dist/src/registry.d.ts +3 -0
- package/dist/src/registry.d.ts.map +1 -0
- package/dist/src/registry.js +23 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/services/aggregation-service.d.ts +34 -0
- package/dist/src/services/aggregation-service.d.ts.map +1 -0
- package/dist/src/services/aggregation-service.js +89 -0
- package/dist/src/services/aggregation-service.js.map +1 -0
- package/dist/src/services/config-loader.d.ts +25 -0
- package/dist/src/services/config-loader.d.ts.map +1 -0
- package/dist/src/services/config-loader.js +105 -0
- package/dist/src/services/config-loader.js.map +1 -0
- package/dist/src/services/history-service.d.ts +15 -0
- package/dist/src/services/history-service.d.ts.map +1 -0
- package/dist/src/services/history-service.js +99 -0
- package/dist/src/services/history-service.js.map +1 -0
- package/dist/src/services/prediction-engine.d.ts +47 -0
- package/dist/src/services/prediction-engine.d.ts.map +1 -0
- package/dist/src/services/prediction-engine.js +94 -0
- package/dist/src/services/prediction-engine.js.map +1 -0
- package/dist/src/services/quota-service.d.ts +41 -0
- package/dist/src/services/quota-service.d.ts.map +1 -0
- package/dist/src/services/quota-service.js +257 -0
- package/dist/src/services/quota-service.js.map +1 -0
- package/dist/src/tools/quotas.d.ts +11 -0
- package/dist/src/tools/quotas.d.ts.map +1 -0
- package/dist/src/tools/quotas.js +62 -0
- package/dist/src/tools/quotas.js.map +1 -0
- package/dist/src/ui/progress-bar.d.ts +20 -0
- package/dist/src/ui/progress-bar.d.ts.map +1 -0
- package/dist/src/ui/progress-bar.js +150 -0
- package/dist/src/ui/progress-bar.js.map +1 -0
- package/dist/src/ui/quota-table.d.ts +15 -0
- package/dist/src/ui/quota-table.d.ts.map +1 -0
- package/dist/src/ui/quota-table.js +136 -0
- package/dist/src/ui/quota-table.js.map +1 -0
- package/dist/src/utils/debug.d.ts +1 -0
- package/dist/src/utils/debug.d.ts.map +1 -0
- package/dist/src/utils/debug.js +3 -0
- package/dist/src/utils/debug.js.map +1 -0
- package/dist/src/utils/paths.d.ts +7 -0
- package/dist/src/utils/paths.d.ts.map +1 -0
- package/dist/src/utils/paths.js +37 -0
- package/dist/src/utils/paths.js.map +1 -0
- package/dist/src/utils/time.d.ts +3 -0
- package/dist/src/utils/time.d.ts.map +1 -0
- package/dist/src/utils/time.js +38 -0
- package/dist/src/utils/time.js.map +1 -0
- package/dist/src/utils/validation.d.ts +6 -0
- package/dist/src/utils/validation.d.ts.map +1 -0
- package/dist/src/utils/validation.js +66 -0
- package/dist/src/utils/validation.js.map +1 -0
- package/package.json +42 -0
- package/src/cli.ts +53 -0
- package/src/constants.ts +17 -0
- package/src/defaults.ts +53 -0
- package/src/index.ts +338 -0
- package/src/interfaces.ts +258 -0
- package/src/logger.ts +55 -0
- package/src/plugin-state.ts +65 -0
- package/src/providers/antigravity/auth.ts +163 -0
- package/src/providers/antigravity/index.ts +1 -0
- package/src/providers/antigravity/provider.ts +337 -0
- package/src/providers/codex.ts +327 -0
- package/src/providers/github.ts +161 -0
- package/src/quota-cache.ts +157 -0
- package/src/registry.ts +30 -0
- package/src/services/aggregation-service.ts +116 -0
- package/src/services/config-loader.ts +124 -0
- package/src/services/history-service.ts +116 -0
- package/src/services/prediction-engine.ts +133 -0
- package/src/services/quota-service.ts +343 -0
- package/src/tools/quotas.ts +78 -0
- package/src/ui/progress-bar.ts +204 -0
- package/src/ui/quota-table.ts +173 -0
- package/src/utils/debug.ts +1 -0
- package/src/utils/paths.ts +41 -0
- package/src/utils/time.ts +40 -0
- package/src/utils/validation.ts +63 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type IQuotaProvider, type QuotaData } from "../interfaces";
|
|
2
|
+
export declare function parseGithubUsage(data: unknown, sku: string | null, apiWarning?: string | null): QuotaData[];
|
|
3
|
+
export declare function createGithubProvider(): IQuotaProvider;
|
|
4
|
+
//# sourceMappingURL=github.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../src/providers/github.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAsDpE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,CAoD3G;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAiDrD"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { AUTH_FILE, getConfigDirectory } from "../utils/paths";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
const AUTH_PATH_LOCAL = AUTH_FILE();
|
|
5
|
+
const AUTH_PATH_CONFIG = join(getConfigDirectory(), "auth.json");
|
|
6
|
+
async function readAuthFile() {
|
|
7
|
+
for (const path of [AUTH_PATH_LOCAL, AUTH_PATH_CONFIG]) {
|
|
8
|
+
try {
|
|
9
|
+
const raw = await readFile(path, "utf8");
|
|
10
|
+
return JSON.parse(raw);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
function parseTokenSku(token) {
|
|
19
|
+
const parts = token.split(";");
|
|
20
|
+
for (const part of parts) {
|
|
21
|
+
if (part.startsWith("sku=")) {
|
|
22
|
+
return part.split("=")[1];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function getNextMonthStart() {
|
|
28
|
+
const now = new Date();
|
|
29
|
+
const nextMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1, 0, 0, 0));
|
|
30
|
+
return nextMonth;
|
|
31
|
+
}
|
|
32
|
+
function formatTimeUntil(target) {
|
|
33
|
+
const diff = target.getTime() - Date.now();
|
|
34
|
+
if (diff <= 0)
|
|
35
|
+
return "soon";
|
|
36
|
+
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
37
|
+
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
38
|
+
if (days > 0)
|
|
39
|
+
return `${days}d ${hours}h`;
|
|
40
|
+
return `${hours}h`;
|
|
41
|
+
}
|
|
42
|
+
export function parseGithubUsage(data, sku, apiWarning) {
|
|
43
|
+
// free_engaged_oss_quota is actually a "pro" equivalent for OSS maintainers
|
|
44
|
+
const isFreeLimited = sku?.includes("free") && !sku?.includes("oss");
|
|
45
|
+
const now = new Date();
|
|
46
|
+
const resetTime = getNextMonthStart();
|
|
47
|
+
const resetStr = `resets in ${formatTimeUntil(resetTime)}`;
|
|
48
|
+
let usedSuggestions = 0;
|
|
49
|
+
let limit = isFreeLimited ? 2000 : null;
|
|
50
|
+
let unit = "suggestions";
|
|
51
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
52
|
+
const currentMonthUsage = data.filter((day) => {
|
|
53
|
+
if (!day || typeof day !== "object")
|
|
54
|
+
return false;
|
|
55
|
+
const dayRecord = day;
|
|
56
|
+
const dayValue = dayRecord["day"];
|
|
57
|
+
if (typeof dayValue !== "string")
|
|
58
|
+
return false;
|
|
59
|
+
const dayDate = new Date(dayValue);
|
|
60
|
+
return dayDate.getUTCMonth() === now.getUTCMonth() && dayDate.getUTCFullYear() === now.getUTCFullYear();
|
|
61
|
+
});
|
|
62
|
+
usedSuggestions = currentMonthUsage.reduce((acc, day) => {
|
|
63
|
+
if (!day || typeof day !== "object")
|
|
64
|
+
return acc;
|
|
65
|
+
const dayRecord = day;
|
|
66
|
+
const suggestions = typeof dayRecord["total_suggestions_count"] === "number" ? dayRecord["total_suggestions_count"] : 0;
|
|
67
|
+
const chat = typeof dayRecord["total_chat_count"] === "number" ? dayRecord["total_chat_count"] : 0;
|
|
68
|
+
return acc + suggestions + chat;
|
|
69
|
+
}, 0);
|
|
70
|
+
}
|
|
71
|
+
const infoParts = [];
|
|
72
|
+
if (isFreeLimited)
|
|
73
|
+
infoParts.push("Free Plan");
|
|
74
|
+
else if (sku)
|
|
75
|
+
infoParts.push("Pro Plan");
|
|
76
|
+
if (apiWarning) {
|
|
77
|
+
infoParts.push("Service Currently Unavailable (API Deprecated)");
|
|
78
|
+
}
|
|
79
|
+
return [
|
|
80
|
+
{
|
|
81
|
+
id: "github-copilot",
|
|
82
|
+
providerName: "GitHub Copilot",
|
|
83
|
+
used: usedSuggestions,
|
|
84
|
+
limit: limit,
|
|
85
|
+
unit: unit,
|
|
86
|
+
reset: resetStr,
|
|
87
|
+
window: "Monthly",
|
|
88
|
+
info: infoParts.join(" | "),
|
|
89
|
+
}
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
export function createGithubProvider() {
|
|
93
|
+
return {
|
|
94
|
+
id: "github-copilot",
|
|
95
|
+
async fetchQuota() {
|
|
96
|
+
const auth = await readAuthFile();
|
|
97
|
+
if (!auth) {
|
|
98
|
+
throw new Error("Opencode auth.json not found");
|
|
99
|
+
}
|
|
100
|
+
const info = auth["github-copilot"] || auth["github"];
|
|
101
|
+
if (!info || !info.access) {
|
|
102
|
+
throw new Error("GitHub Copilot credentials missing");
|
|
103
|
+
}
|
|
104
|
+
const sku = parseTokenSku(info.access);
|
|
105
|
+
// Note: GitHub does not currently support a user-level Copilot usage
|
|
106
|
+
// endpoint for individual accounts. The legacy/beta endpoints were
|
|
107
|
+
// deprecated (and may return 404). We still attempt the call, but we
|
|
108
|
+
// surface failures as provider metadata so users can diagnose.
|
|
109
|
+
let data = null;
|
|
110
|
+
let apiWarning = null;
|
|
111
|
+
try {
|
|
112
|
+
const response = await fetch("https://api.github.com/user/copilot/usage", {
|
|
113
|
+
headers: {
|
|
114
|
+
Authorization: `Bearer ${info.access}`,
|
|
115
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
116
|
+
Accept: "application/vnd.github+json",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
if (response.status === 404) {
|
|
121
|
+
apiWarning = "404";
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const body = await response.text();
|
|
125
|
+
apiWarning = `${response.status}: ${body.slice(0, 50)}`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
data = (await response.json());
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
apiWarning = "Request Failed";
|
|
134
|
+
}
|
|
135
|
+
return parseGithubUsage(data, sku, apiWarning);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../../src/providers/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type IQuotaProvider, type QuotaData, type IHistoryService } from "./interfaces";
|
|
2
|
+
type CachedQuotas = {
|
|
3
|
+
data: QuotaData[];
|
|
4
|
+
fetchedAt: Date | null;
|
|
5
|
+
lastError: unknown;
|
|
6
|
+
};
|
|
7
|
+
type QuotaCacheOptions = {
|
|
8
|
+
refreshIntervalMs: number;
|
|
9
|
+
historyService?: IHistoryService;
|
|
10
|
+
debug?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare class QuotaCache {
|
|
13
|
+
private readonly providers;
|
|
14
|
+
private readonly options;
|
|
15
|
+
private state;
|
|
16
|
+
private timer;
|
|
17
|
+
private inFlight;
|
|
18
|
+
constructor(providers: IQuotaProvider[], options?: Partial<QuotaCacheOptions>);
|
|
19
|
+
start(): void;
|
|
20
|
+
stop(): void;
|
|
21
|
+
getSnapshot(): CachedQuotas;
|
|
22
|
+
refresh(): Promise<void>;
|
|
23
|
+
private doRefresh;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=quota-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota-cache.d.ts","sourceRoot":"","sources":["../../src/quota-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAKzF,KAAK,YAAY,GAAG;IAChB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAMF,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,QAAQ,CAAuB;gBAEpB,SAAS,EAAE,cAAc,EAAE,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAQ7E,KAAK,IAAI,IAAI;IAcb,IAAI,IAAI,IAAI;IAOZ,WAAW,IAAI,YAAY;IAIrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAqBvB,SAAS;CA0E1B"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { validateQuotaData } from "./utils/validation";
|
|
2
|
+
import { logger } from "./logger";
|
|
3
|
+
const DEFAULT_OPTIONS = {
|
|
4
|
+
refreshIntervalMs: 60_000,
|
|
5
|
+
};
|
|
6
|
+
export class QuotaCache {
|
|
7
|
+
providers;
|
|
8
|
+
options;
|
|
9
|
+
state;
|
|
10
|
+
timer;
|
|
11
|
+
inFlight;
|
|
12
|
+
constructor(providers, options) {
|
|
13
|
+
this.providers = providers;
|
|
14
|
+
this.options = { ...DEFAULT_OPTIONS, ...(options ?? {}) };
|
|
15
|
+
this.state = { data: [], fetchedAt: null, lastError: null };
|
|
16
|
+
this.timer = null;
|
|
17
|
+
this.inFlight = null;
|
|
18
|
+
}
|
|
19
|
+
start() {
|
|
20
|
+
if (this.timer)
|
|
21
|
+
return;
|
|
22
|
+
// Kick off an initial refresh without blocking startup.
|
|
23
|
+
void this.refresh();
|
|
24
|
+
this.timer = setInterval(() => {
|
|
25
|
+
void this.refresh();
|
|
26
|
+
}, this.options.refreshIntervalMs);
|
|
27
|
+
// Avoid keeping the process alive just for quota polling.
|
|
28
|
+
this.timer.unref?.();
|
|
29
|
+
}
|
|
30
|
+
stop() {
|
|
31
|
+
if (this.timer) {
|
|
32
|
+
clearInterval(this.timer);
|
|
33
|
+
this.timer = null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
getSnapshot() {
|
|
37
|
+
return this.state;
|
|
38
|
+
}
|
|
39
|
+
async refresh() {
|
|
40
|
+
logger.debug("cache:refresh_start", {
|
|
41
|
+
providerCount: this.providers.length,
|
|
42
|
+
refreshIntervalMs: this.options.refreshIntervalMs,
|
|
43
|
+
inFlight: !!this.inFlight,
|
|
44
|
+
});
|
|
45
|
+
if (this.inFlight) {
|
|
46
|
+
logger.debug("cache:refresh_coalesced", { inFlight: true });
|
|
47
|
+
return this.inFlight;
|
|
48
|
+
}
|
|
49
|
+
const refreshPromise = this.doRefresh();
|
|
50
|
+
this.inFlight = refreshPromise;
|
|
51
|
+
return refreshPromise;
|
|
52
|
+
}
|
|
53
|
+
async doRefresh() {
|
|
54
|
+
try {
|
|
55
|
+
const results = await Promise.all(this.providers.map(async (p) => {
|
|
56
|
+
const startedAt = Date.now();
|
|
57
|
+
try {
|
|
58
|
+
logger.debug("cache:provider_fetch_start", { id: p.id });
|
|
59
|
+
const result = await p.fetchQuota();
|
|
60
|
+
logger.debug("cache:provider_fetch_ok", {
|
|
61
|
+
id: p.id,
|
|
62
|
+
count: result.length,
|
|
63
|
+
durationMs: Date.now() - startedAt,
|
|
64
|
+
});
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
logger.error("cache:provider_fetch_error", {
|
|
69
|
+
id: p.id,
|
|
70
|
+
durationMs: Date.now() - startedAt,
|
|
71
|
+
error: e,
|
|
72
|
+
});
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}));
|
|
76
|
+
// Validate and normalize provider responses before storing
|
|
77
|
+
const flattened = results.flat();
|
|
78
|
+
const validatedData = flattened
|
|
79
|
+
.map(d => validateQuotaData(d))
|
|
80
|
+
.filter((v) => v !== null);
|
|
81
|
+
this.state = {
|
|
82
|
+
data: validatedData,
|
|
83
|
+
fetchedAt: new Date(),
|
|
84
|
+
lastError: null,
|
|
85
|
+
};
|
|
86
|
+
logger.debug("cache:refresh_ok", {
|
|
87
|
+
totalCount: this.state.data.length,
|
|
88
|
+
fetchedAt: this.state.fetchedAt?.toISOString(),
|
|
89
|
+
});
|
|
90
|
+
if (this.options.historyService) {
|
|
91
|
+
void this.options.historyService.append(this.state.data);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
this.state = {
|
|
96
|
+
...this.state,
|
|
97
|
+
lastError: e,
|
|
98
|
+
};
|
|
99
|
+
logger.error("cache:refresh_error", { error: e });
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
logger.debug("cache:refresh_end", { inFlightCleared: true });
|
|
103
|
+
this.inFlight = null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=quota-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota-cache.js","sourceRoot":"","sources":["../../src/quota-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAuB,MAAM,cAAc,CAAC;AAuBxE,wBAAgB,gBAAgB,IAAI,cAAc,CAMjD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const REGISTRY_KEY = "__OPENCODE_QUOTA_REGISTRY__";
|
|
2
|
+
function createRegistry() {
|
|
3
|
+
const providers = [];
|
|
4
|
+
return {
|
|
5
|
+
register(provider) {
|
|
6
|
+
if (providers.some((p) => p.id === provider.id)) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
providers.push(provider);
|
|
10
|
+
},
|
|
11
|
+
getAll() {
|
|
12
|
+
return [...providers];
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function getQuotaRegistry() {
|
|
17
|
+
const globalRef = globalThis;
|
|
18
|
+
if (!globalRef[REGISTRY_KEY]) {
|
|
19
|
+
globalRef[REGISTRY_KEY] = createRegistry();
|
|
20
|
+
}
|
|
21
|
+
return globalRef[REGISTRY_KEY];
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAG,6BAA6B,CAAC;AAMnD,SAAS,cAAc;IACrB,MAAM,SAAS,GAAqB,EAAE,CAAC;IACvC,OAAO;QACL,QAAQ,CAAC,QAAwB;YAC/B,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBAChD,OAAO;YACT,CAAC;YACD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM;YACJ,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,SAAS,GAAG,UAA4B,CAAC;IAC/C,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,SAAS,CAAC,YAAY,CAAC,GAAG,cAAc,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,SAAS,CAAC,YAAY,CAAmB,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type QuotaData, type IPredictionEngine, type IAggregationService } from "../interfaces";
|
|
2
|
+
/**
|
|
3
|
+
* Service for aggregating multiple quota sources into a single representative quota.
|
|
4
|
+
*
|
|
5
|
+
* Supports multiple aggregation strategies:
|
|
6
|
+
* - most_critical: Selects the quota with shortest predicted time-to-limit
|
|
7
|
+
* - max: Selects the quota with highest usage ratio
|
|
8
|
+
* - min: Selects the quota with lowest usage ratio
|
|
9
|
+
* - mean: Creates a synthetic quota with average usage ratio
|
|
10
|
+
* - median: Creates a synthetic quota with median usage ratio
|
|
11
|
+
*/
|
|
12
|
+
export declare class AggregationService implements IAggregationService {
|
|
13
|
+
private readonly predictionEngine;
|
|
14
|
+
constructor(predictionEngine: IPredictionEngine);
|
|
15
|
+
/**
|
|
16
|
+
* Aggregates quotas using the most critical (shortest time-to-limit) strategy.
|
|
17
|
+
* Falls back to max usage ratio if no predictions are available.
|
|
18
|
+
*/
|
|
19
|
+
aggregateMostCritical(quotas: QuotaData[], windowMinutes?: number, shortWindowMinutes?: number): QuotaData | null;
|
|
20
|
+
/**
|
|
21
|
+
* Aggregates quotas by selecting the one with highest usage ratio.
|
|
22
|
+
*/
|
|
23
|
+
aggregateMax(quotas: QuotaData[]): QuotaData;
|
|
24
|
+
/**
|
|
25
|
+
* Aggregates quotas by selecting the one with lowest usage ratio.
|
|
26
|
+
*/
|
|
27
|
+
aggregateMin(quotas: QuotaData[]): QuotaData;
|
|
28
|
+
/**
|
|
29
|
+
* Aggregates quotas by averaging their usage ratios.
|
|
30
|
+
* Creates a synthetic quota with percentage-based representation.
|
|
31
|
+
*/
|
|
32
|
+
aggregateAverage(quotas: QuotaData[], name: string, id: string, strategy: "mean" | "median"): QuotaData;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=aggregation-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregation-service.d.ts","sourceRoot":"","sources":["../../../src/services/aggregation-service.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAC3B,MAAM,eAAe,CAAC;AAGvB;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,YAAW,mBAAmB;IAC1D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAoB;gBAEzC,gBAAgB,EAAE,iBAAiB;IAI/C;;;OAGG;IACH,qBAAqB,CACjB,MAAM,EAAE,SAAS,EAAE,EACnB,aAAa,GAAE,MAAW,EAC1B,kBAAkB,CAAC,EAAE,MAAM,GAC5B,SAAS,GAAG,IAAI;IAgCnB;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS;IAQ5C;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS;IAQ5C;;;OAGG;IACH,gBAAgB,CACZ,MAAM,EAAE,SAAS,EAAE,EACnB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAC5B,SAAS;CAoBf"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { formatDurationMs } from "../utils/time";
|
|
2
|
+
/**
|
|
3
|
+
* Service for aggregating multiple quota sources into a single representative quota.
|
|
4
|
+
*
|
|
5
|
+
* Supports multiple aggregation strategies:
|
|
6
|
+
* - most_critical: Selects the quota with shortest predicted time-to-limit
|
|
7
|
+
* - max: Selects the quota with highest usage ratio
|
|
8
|
+
* - min: Selects the quota with lowest usage ratio
|
|
9
|
+
* - mean: Creates a synthetic quota with average usage ratio
|
|
10
|
+
* - median: Creates a synthetic quota with median usage ratio
|
|
11
|
+
*/
|
|
12
|
+
export class AggregationService {
|
|
13
|
+
predictionEngine;
|
|
14
|
+
constructor(predictionEngine) {
|
|
15
|
+
this.predictionEngine = predictionEngine;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Aggregates quotas using the most critical (shortest time-to-limit) strategy.
|
|
19
|
+
* Falls back to max usage ratio if no predictions are available.
|
|
20
|
+
*/
|
|
21
|
+
aggregateMostCritical(quotas, windowMinutes = 60, shortWindowMinutes) {
|
|
22
|
+
if (quotas.length === 0)
|
|
23
|
+
return null;
|
|
24
|
+
let minTime = Infinity;
|
|
25
|
+
let representative = null;
|
|
26
|
+
for (const q of quotas) {
|
|
27
|
+
const time = this.predictionEngine.predictTimeToLimit(q.id, windowMinutes, shortWindowMinutes);
|
|
28
|
+
if (time < minTime) {
|
|
29
|
+
minTime = time;
|
|
30
|
+
representative = q;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Fallback to max usage if no prediction is possible
|
|
34
|
+
if (!representative) {
|
|
35
|
+
return this.aggregateMax(quotas);
|
|
36
|
+
}
|
|
37
|
+
if (minTime !== Infinity) {
|
|
38
|
+
return {
|
|
39
|
+
...representative,
|
|
40
|
+
predictedReset: `in ${formatDurationMs(minTime)} (predicted)`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return representative;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Aggregates quotas by selecting the one with highest usage ratio.
|
|
47
|
+
*/
|
|
48
|
+
aggregateMax(quotas) {
|
|
49
|
+
return quotas.reduce((a, b) => {
|
|
50
|
+
const aRatio = a.limit ? a.used / a.limit : 0;
|
|
51
|
+
const bRatio = b.limit ? b.used / b.limit : 0;
|
|
52
|
+
return aRatio > bRatio ? a : b;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Aggregates quotas by selecting the one with lowest usage ratio.
|
|
57
|
+
*/
|
|
58
|
+
aggregateMin(quotas) {
|
|
59
|
+
return quotas.reduce((a, b) => {
|
|
60
|
+
const aRatio = a.limit ? a.used / a.limit : 0;
|
|
61
|
+
const bRatio = b.limit ? b.used / b.limit : 0;
|
|
62
|
+
return aRatio < bRatio ? a : b;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Aggregates quotas by averaging their usage ratios.
|
|
67
|
+
* Creates a synthetic quota with percentage-based representation.
|
|
68
|
+
*/
|
|
69
|
+
aggregateAverage(quotas, name, id, strategy) {
|
|
70
|
+
const ratios = quotas.map(q => q.limit ? q.used / q.limit : 0);
|
|
71
|
+
let avgRatio = 0;
|
|
72
|
+
if (strategy === "mean") {
|
|
73
|
+
avgRatio = ratios.reduce((a, b) => a + b, 0) / ratios.length;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
ratios.sort((a, b) => a - b);
|
|
77
|
+
avgRatio = ratios[Math.floor(ratios.length / 2)];
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
id: id,
|
|
81
|
+
providerName: name,
|
|
82
|
+
used: Math.round(avgRatio * 100),
|
|
83
|
+
limit: 100,
|
|
84
|
+
unit: "%",
|
|
85
|
+
info: "Aggregated"
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=aggregation-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregation-service.js","sourceRoot":"","sources":["../../../src/services/aggregation-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type QuotaConfig } from "../interfaces";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration loading and merging service.
|
|
4
|
+
* Handles reading config from disk and merging with defaults.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ConfigLoader {
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new configuration by merging defaults with initial config.
|
|
9
|
+
*/
|
|
10
|
+
static createConfig(initialConfig?: Partial<QuotaConfig>): QuotaConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Loads and merges user configuration from disk into the provided config.
|
|
13
|
+
* Returns the updated config.
|
|
14
|
+
*/
|
|
15
|
+
static loadFromDisk(directory: string, config: QuotaConfig): Promise<QuotaConfig>;
|
|
16
|
+
/**
|
|
17
|
+
* Merges user configuration into the target config.
|
|
18
|
+
*/
|
|
19
|
+
private static mergeUserConfig;
|
|
20
|
+
/**
|
|
21
|
+
* Validates and normalizes configuration values.
|
|
22
|
+
*/
|
|
23
|
+
private static validateConfig;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/services/config-loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAKjD;;;GAGG;AACH,qBAAa,YAAY;IACrB;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW;IAiBtE;;;OAGG;WACU,YAAY,CACrB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,GACpB,OAAO,CAAC,WAAW,CAAC;IA8BvB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAkC9B;;OAEG;mBACkB,cAAc;CAatC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { DEFAULT_CONFIG } from "../defaults";
|
|
4
|
+
import { logger } from "../logger";
|
|
5
|
+
import { validatePollingInterval } from "../utils/validation";
|
|
6
|
+
/**
|
|
7
|
+
* Configuration loading and merging service.
|
|
8
|
+
* Handles reading config from disk and merging with defaults.
|
|
9
|
+
*/
|
|
10
|
+
export class ConfigLoader {
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new configuration by merging defaults with initial config.
|
|
13
|
+
*/
|
|
14
|
+
static createConfig(initialConfig) {
|
|
15
|
+
const config = { ...DEFAULT_CONFIG, ...initialConfig };
|
|
16
|
+
// Deep clone specific nested objects to avoid mutation of the constant
|
|
17
|
+
if (DEFAULT_CONFIG.progressBar) {
|
|
18
|
+
config.progressBar = { ...DEFAULT_CONFIG.progressBar, ...initialConfig?.progressBar };
|
|
19
|
+
}
|
|
20
|
+
if (DEFAULT_CONFIG.aggregatedGroups) {
|
|
21
|
+
config.aggregatedGroups = [
|
|
22
|
+
...DEFAULT_CONFIG.aggregatedGroups,
|
|
23
|
+
...(initialConfig?.aggregatedGroups || [])
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
return config;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Loads and merges user configuration from disk into the provided config.
|
|
30
|
+
* Returns the updated config.
|
|
31
|
+
*/
|
|
32
|
+
static async loadFromDisk(directory, config) {
|
|
33
|
+
const result = { ...config };
|
|
34
|
+
try {
|
|
35
|
+
const envConfigPath = process.env.OPENCODE_QUOTAS_CONFIG_PATH;
|
|
36
|
+
const configPath = envConfigPath || join(directory, ".opencode", "quotas.json");
|
|
37
|
+
const rawConfig = await readFile(configPath, "utf-8");
|
|
38
|
+
const userConfig = JSON.parse(rawConfig);
|
|
39
|
+
ConfigLoader.mergeUserConfig(result, userConfig);
|
|
40
|
+
logger.debug("init:config_loaded", { configPath, debug: result.debug });
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
// Ignore missing config or parse errors
|
|
44
|
+
logger.error("init:config_load_failed", { error: e });
|
|
45
|
+
}
|
|
46
|
+
// Validate and normalize config values
|
|
47
|
+
await ConfigLoader.validateConfig(result);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Merges user configuration into the target config.
|
|
52
|
+
*/
|
|
53
|
+
static mergeUserConfig(target, userConfig) {
|
|
54
|
+
if (userConfig.debug !== undefined) {
|
|
55
|
+
target.debug = userConfig.debug;
|
|
56
|
+
logger.setDebug(!!target.debug);
|
|
57
|
+
}
|
|
58
|
+
if (userConfig.footer !== undefined) {
|
|
59
|
+
target.footer = userConfig.footer;
|
|
60
|
+
}
|
|
61
|
+
if (userConfig.progressBar) {
|
|
62
|
+
target.progressBar = { ...target.progressBar, ...userConfig.progressBar };
|
|
63
|
+
}
|
|
64
|
+
if (userConfig.table) {
|
|
65
|
+
target.table = { ...target.table, ...userConfig.table };
|
|
66
|
+
}
|
|
67
|
+
if (userConfig.disabled) {
|
|
68
|
+
target.disabled = userConfig.disabled;
|
|
69
|
+
}
|
|
70
|
+
if (userConfig.filterByCurrentModel !== undefined) {
|
|
71
|
+
target.filterByCurrentModel = userConfig.filterByCurrentModel;
|
|
72
|
+
}
|
|
73
|
+
if (userConfig.aggregatedGroups) {
|
|
74
|
+
target.aggregatedGroups = userConfig.aggregatedGroups;
|
|
75
|
+
}
|
|
76
|
+
if (userConfig.historyMaxAgeHours !== undefined) {
|
|
77
|
+
target.historyMaxAgeHours = userConfig.historyMaxAgeHours;
|
|
78
|
+
}
|
|
79
|
+
if (userConfig.predictionShortWindowMinutes !== undefined) {
|
|
80
|
+
target.predictionShortWindowMinutes = userConfig.predictionShortWindowMinutes;
|
|
81
|
+
}
|
|
82
|
+
if (userConfig.pollingInterval !== undefined) {
|
|
83
|
+
target.pollingInterval = userConfig.pollingInterval;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Validates and normalizes configuration values.
|
|
88
|
+
*/
|
|
89
|
+
static async validateConfig(config) {
|
|
90
|
+
// Handle pollingInterval from user config
|
|
91
|
+
const validated = validatePollingInterval(config.pollingInterval);
|
|
92
|
+
if (validated === null) {
|
|
93
|
+
console.warn('[QuotaService] pollingInterval is invalid, using default');
|
|
94
|
+
config.pollingInterval = DEFAULT_CONFIG.pollingInterval;
|
|
95
|
+
}
|
|
96
|
+
else if (validated < 10_000) {
|
|
97
|
+
console.warn('[QuotaService] pollingInterval below 10s is not recommended');
|
|
98
|
+
config.pollingInterval = Math.max(validated, 1_000);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
config.pollingInterval = validated;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=config-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../../src/services/config-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type IHistoryService, type HistoryPoint, type QuotaData } from "../interfaces";
|
|
2
|
+
export declare class HistoryService implements IHistoryService {
|
|
3
|
+
private historyPath;
|
|
4
|
+
private data;
|
|
5
|
+
private maxWindowMs;
|
|
6
|
+
private saveTimeout;
|
|
7
|
+
constructor(customPath?: string);
|
|
8
|
+
init(): Promise<void>;
|
|
9
|
+
append(snapshot: QuotaData[]): Promise<void>;
|
|
10
|
+
getHistory(quotaId: string, windowMs: number): HistoryPoint[];
|
|
11
|
+
setMaxAge(hours: number): void;
|
|
12
|
+
pruneAll(): Promise<void>;
|
|
13
|
+
private save;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=history-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-service.d.ts","sourceRoot":"","sources":["../../../src/services/history-service.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAGxF,qBAAa,cAAe,YAAW,eAAe;IAClD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,IAAI,CAAsC;IAClD,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,WAAW,CAA8C;gBAErD,UAAU,CAAC,EAAE,MAAM;IAIzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBrB,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BlD,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE;IAO7D,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIxB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB/B,OAAO,CAAC,IAAI;CAef"}
|