@howaboua/opencode-usage-plugin 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/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/hooks/command.d.ts +13 -0
- package/dist/hooks/command.d.ts.map +1 -0
- package/dist/hooks/command.js +53 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/proxy.d.ts +14 -0
- package/dist/hooks/proxy.d.ts.map +1 -0
- package/dist/hooks/proxy.js +31 -0
- package/dist/hooks/session.d.ts +8 -0
- package/dist/hooks/session.d.ts.map +1 -0
- package/dist/hooks/session.js +16 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/providers/base.d.ts +13 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +5 -0
- package/dist/providers/codex/headers.d.ts +15 -0
- package/dist/providers/codex/headers.d.ts.map +1 -0
- package/dist/providers/codex/headers.js +25 -0
- package/dist/providers/codex/index.d.ts +12 -0
- package/dist/providers/codex/index.d.ts.map +1 -0
- package/dist/providers/codex/index.js +66 -0
- package/dist/providers/codex/response.d.ts +75 -0
- package/dist/providers/codex/response.d.ts.map +1 -0
- package/dist/providers/codex/response.js +59 -0
- package/dist/providers/index.d.ts +6 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/proxy/config.d.ts +6 -0
- package/dist/providers/proxy/config.d.ts.map +1 -0
- package/dist/providers/proxy/config.js +52 -0
- package/dist/providers/proxy/fetch.d.ts +6 -0
- package/dist/providers/proxy/fetch.d.ts.map +1 -0
- package/dist/providers/proxy/fetch.js +30 -0
- package/dist/providers/proxy/format.d.ts +6 -0
- package/dist/providers/proxy/format.d.ts.map +1 -0
- package/dist/providers/proxy/format.js +102 -0
- package/dist/providers/proxy/index.d.ts +11 -0
- package/dist/providers/proxy/index.d.ts.map +1 -0
- package/dist/providers/proxy/index.js +116 -0
- package/dist/providers/proxy/types.d.ts +119 -0
- package/dist/providers/proxy/types.d.ts.map +1 -0
- package/dist/providers/proxy/types.js +4 -0
- package/dist/state.d.ts +18 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +13 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/proxy-limits.d.ts +16 -0
- package/dist/tools/proxy-limits.d.ts.map +1 -0
- package/dist/tools/proxy-limits.js +33 -0
- package/dist/tools/usage.d.ts +10 -0
- package/dist/tools/usage.d.ts.map +1 -0
- package/dist/tools/usage.js +12 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +1 -0
- package/dist/ui/status.d.ts +22 -0
- package/dist/ui/status.d.ts.map +1 -0
- package/dist/ui/status.js +142 -0
- package/dist/usage/fetch.d.ts +11 -0
- package/dist/usage/fetch.d.ts.map +1 -0
- package/dist/usage/fetch.js +80 -0
- package/dist/usage/index.d.ts +2 -0
- package/dist/usage/index.d.ts.map +1 -0
- package/dist/usage/index.js +1 -0
- package/dist/usage/registry.d.ts +19 -0
- package/dist/usage/registry.d.ts.map +1 -0
- package/dist/usage/registry.js +30 -0
- package/dist/utils/headers.d.ts +8 -0
- package/dist/utils/headers.d.ts.map +1 -0
- package/dist/utils/headers.js +33 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/paths.d.ts +7 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +24 -0
- package/package.json +28 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAI1C,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CAkD5D"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management for the proxy provider.
|
|
3
|
+
*/
|
|
4
|
+
const CONFIG_PATH = `${process.env.HOME}/.config/opencode/usage-config.jsonc`;
|
|
5
|
+
export async function loadProxyConfig() {
|
|
6
|
+
const file = Bun.file(CONFIG_PATH);
|
|
7
|
+
if (!(await file.exists())) {
|
|
8
|
+
const content = `/**
|
|
9
|
+
* Usage Plugin Configuration
|
|
10
|
+
*/
|
|
11
|
+
{
|
|
12
|
+
// Proxy endpoint
|
|
13
|
+
"endpoint": "http://localhost:8000",
|
|
14
|
+
|
|
15
|
+
// API key for authentication
|
|
16
|
+
"apiKey": "VerysecretKey",
|
|
17
|
+
|
|
18
|
+
// Request timeout in milliseconds
|
|
19
|
+
"timeout": 10000,
|
|
20
|
+
|
|
21
|
+
// Provider visibility
|
|
22
|
+
"providers": {
|
|
23
|
+
"openai": true,
|
|
24
|
+
"proxy": true
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
await Bun.write(CONFIG_PATH, content);
|
|
29
|
+
return {
|
|
30
|
+
endpoint: "http://localhost:8000",
|
|
31
|
+
apiKey: "VerysecretKey",
|
|
32
|
+
timeout: 10000,
|
|
33
|
+
providers: {
|
|
34
|
+
openai: true,
|
|
35
|
+
proxy: true,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const content = await file.text();
|
|
41
|
+
const cleanJson = content.replace(/(\".*?\"|\'.*?\')|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g1) => g1 ?? "");
|
|
42
|
+
const config = JSON.parse(cleanJson);
|
|
43
|
+
if (!config.endpoint) {
|
|
44
|
+
throw new Error('Config must contain "endpoint" field');
|
|
45
|
+
}
|
|
46
|
+
return config;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50
|
+
throw new Error(`Failed to parse config: ${message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/fetch.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAEzD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAgClF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for fetching proxy quota stats.
|
|
3
|
+
*/
|
|
4
|
+
export async function fetchProxyLimits(config) {
|
|
5
|
+
const { endpoint, apiKey, timeout = 10000 } = config;
|
|
6
|
+
const headers = {
|
|
7
|
+
"Content-Type": "application/json",
|
|
8
|
+
};
|
|
9
|
+
if (apiKey) {
|
|
10
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
11
|
+
}
|
|
12
|
+
const baseUrl = endpoint.endsWith("/v1") ? endpoint : `${endpoint}/v1`;
|
|
13
|
+
const url = `${baseUrl}/quota-stats`;
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetch(url, {
|
|
18
|
+
method: "GET",
|
|
19
|
+
headers,
|
|
20
|
+
signal: controller.signal,
|
|
21
|
+
});
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
24
|
+
}
|
|
25
|
+
return (await response.json());
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
clearTimeout(timeoutId);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA0B,MAAM,SAAS,CAAA;AAiFpE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAiC7D"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display formatting utilities for proxy limits.
|
|
3
|
+
*/
|
|
4
|
+
const GROUP_MAPPING = {
|
|
5
|
+
"claude": "claude",
|
|
6
|
+
"g3-pro": "g3-pro",
|
|
7
|
+
"g3-flash": "g3-fla",
|
|
8
|
+
"pro": "g3-pro",
|
|
9
|
+
"3-flash": "g3-fla"
|
|
10
|
+
};
|
|
11
|
+
function formatBar(remainingPercent) {
|
|
12
|
+
const clamped = Math.max(0, Math.min(100, remainingPercent));
|
|
13
|
+
const size = 20;
|
|
14
|
+
const filled = Math.round((clamped / 100) * size);
|
|
15
|
+
const empty = size - filled;
|
|
16
|
+
return `[${"=".repeat(filled)}${".".repeat(empty)}]`;
|
|
17
|
+
}
|
|
18
|
+
function normalizeTier(tier) {
|
|
19
|
+
if (!tier)
|
|
20
|
+
return "free";
|
|
21
|
+
return tier.includes("free") ? "free" : "paid";
|
|
22
|
+
}
|
|
23
|
+
function formatResetTime(isoString) {
|
|
24
|
+
if (!isoString)
|
|
25
|
+
return "";
|
|
26
|
+
try {
|
|
27
|
+
const resetAt = new Date(isoString).getTime() / 1000;
|
|
28
|
+
const now = Math.floor(Date.now() / 1000);
|
|
29
|
+
const diff = resetAt - now;
|
|
30
|
+
if (diff <= 0)
|
|
31
|
+
return "";
|
|
32
|
+
if (diff < 60)
|
|
33
|
+
return ` (resets in ${diff}s)`;
|
|
34
|
+
if (diff < 3600)
|
|
35
|
+
return ` (resets in ${Math.ceil(diff / 60)}m)`;
|
|
36
|
+
if (diff < 86400)
|
|
37
|
+
return ` (resets in ${Math.round(diff / 3600)}h)`;
|
|
38
|
+
return ` (resets in ${Math.round(diff / 86400)}d)`;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function aggregateCredentialsByTier(credentials) {
|
|
45
|
+
const result = {
|
|
46
|
+
paid: new Map(),
|
|
47
|
+
free: new Map(),
|
|
48
|
+
};
|
|
49
|
+
for (const cred of credentials) {
|
|
50
|
+
const tier = normalizeTier(cred.tier);
|
|
51
|
+
const groups = cred.model_groups ?? {};
|
|
52
|
+
for (const [name, group] of Object.entries(groups)) {
|
|
53
|
+
if (!(name in GROUP_MAPPING))
|
|
54
|
+
continue;
|
|
55
|
+
const mappedName = GROUP_MAPPING[name];
|
|
56
|
+
const existing = result[tier].get(mappedName);
|
|
57
|
+
if (existing) {
|
|
58
|
+
existing.remaining += group.requests_remaining;
|
|
59
|
+
existing.max += group.requests_max;
|
|
60
|
+
if (group.reset_time_iso) {
|
|
61
|
+
if (!existing.resetTime || new Date(group.reset_time_iso) > new Date(existing.resetTime)) {
|
|
62
|
+
existing.resetTime = group.reset_time_iso;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
result[tier].set(mappedName, {
|
|
68
|
+
remaining: group.requests_remaining,
|
|
69
|
+
max: group.requests_max,
|
|
70
|
+
resetTime: group.reset_time_iso,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
export function formatProxyLimits(data) {
|
|
78
|
+
const lines = [];
|
|
79
|
+
lines.push("[Google] Mirrowel Proxy");
|
|
80
|
+
lines.push("");
|
|
81
|
+
if (!data.providers || Object.keys(data.providers).length === 0) {
|
|
82
|
+
lines.push("No provider data available");
|
|
83
|
+
return lines.join("\n");
|
|
84
|
+
}
|
|
85
|
+
for (const [providerName, provider] of Object.entries(data.providers)) {
|
|
86
|
+
lines.push(`${providerName}:`);
|
|
87
|
+
const tierData = aggregateCredentialsByTier(provider.credentials ?? []);
|
|
88
|
+
for (const [tierName, quotas] of Object.entries(tierData)) {
|
|
89
|
+
if (quotas.size === 0)
|
|
90
|
+
continue;
|
|
91
|
+
const tierLabel = tierName === "paid" ? "Paid" : "Free";
|
|
92
|
+
lines.push(` ${tierLabel}:`);
|
|
93
|
+
for (const [groupName, quota] of quotas) {
|
|
94
|
+
const remainingPct = quota.max > 0 ? (quota.remaining / quota.max) * 100 : 0;
|
|
95
|
+
const resetSuffix = quota.remaining === 0 ? formatResetTime(quota.resetTime) : "";
|
|
96
|
+
lines.push(` ${groupName}: ${formatBar(remainingPct)} ${quota.remaining}/${quota.max}${resetSuffix}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
lines.push("");
|
|
100
|
+
}
|
|
101
|
+
return lines.join("\n");
|
|
102
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity Proxy provider for usage tracking.
|
|
3
|
+
* Fetches quota stats from a local/remote proxy server.
|
|
4
|
+
*/
|
|
5
|
+
import type { UsageProvider } from "../base";
|
|
6
|
+
export type { ProxyConfig, ProxyResponse } from "./types";
|
|
7
|
+
export { loadProxyConfig } from "./config";
|
|
8
|
+
export { fetchProxyLimits } from "./fetch";
|
|
9
|
+
export { formatProxyLimits } from "./format";
|
|
10
|
+
export declare const ProxyProvider: UsageProvider;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAM5C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AA8F5C,eAAO,MAAM,aAAa,EAAE,aAwB3B,CAAA"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity Proxy provider for usage tracking.
|
|
3
|
+
* Fetches quota stats from a local/remote proxy server.
|
|
4
|
+
*/
|
|
5
|
+
import { loadProxyConfig } from "./config";
|
|
6
|
+
import { fetchProxyLimits } from "./fetch";
|
|
7
|
+
export { loadProxyConfig } from "./config";
|
|
8
|
+
export { fetchProxyLimits } from "./fetch";
|
|
9
|
+
export { formatProxyLimits } from "./format";
|
|
10
|
+
const GROUP_MAPPING = {
|
|
11
|
+
"claude": "claude",
|
|
12
|
+
"g3-pro": "g3-pro",
|
|
13
|
+
"g3-flash": "g3-fla",
|
|
14
|
+
"pro": "g3-pro",
|
|
15
|
+
"3-flash": "g3-fla"
|
|
16
|
+
};
|
|
17
|
+
function normalizeTier(tier) {
|
|
18
|
+
if (!tier)
|
|
19
|
+
return "free";
|
|
20
|
+
return tier.includes("free") ? "free" : "paid";
|
|
21
|
+
}
|
|
22
|
+
function parseQuotaGroupsFromCredential(modelGroups) {
|
|
23
|
+
if (!modelGroups)
|
|
24
|
+
return [];
|
|
25
|
+
return Object.entries(modelGroups)
|
|
26
|
+
.filter(([name]) => name in GROUP_MAPPING)
|
|
27
|
+
.map(([name, group]) => {
|
|
28
|
+
const realRemainingPct = group.requests_max > 0
|
|
29
|
+
? Math.round((group.requests_remaining / group.requests_max) * 100)
|
|
30
|
+
: 0;
|
|
31
|
+
return {
|
|
32
|
+
name: GROUP_MAPPING[name],
|
|
33
|
+
remaining: group.requests_remaining,
|
|
34
|
+
max: group.requests_max,
|
|
35
|
+
remainingPct: realRemainingPct,
|
|
36
|
+
resetTime: group.reset_time_iso,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function aggregateByTier(credentials) {
|
|
41
|
+
const tiers = {
|
|
42
|
+
paid: new Map(),
|
|
43
|
+
free: new Map(),
|
|
44
|
+
};
|
|
45
|
+
for (const cred of credentials) {
|
|
46
|
+
const tier = normalizeTier(cred.tier);
|
|
47
|
+
const groups = parseQuotaGroupsFromCredential(cred.model_groups);
|
|
48
|
+
for (const group of groups) {
|
|
49
|
+
const existing = tiers[tier].get(group.name);
|
|
50
|
+
if (existing) {
|
|
51
|
+
existing.remaining += group.remaining;
|
|
52
|
+
existing.max += group.max;
|
|
53
|
+
if (group.resetTime && (!existing.resetTime || new Date(group.resetTime) > new Date(existing.resetTime))) {
|
|
54
|
+
existing.resetTime = group.resetTime;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
tiers[tier].set(group.name, { ...group });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const tierGroups of Object.values(tiers)) {
|
|
63
|
+
for (const group of tierGroups.values()) {
|
|
64
|
+
group.remainingPct = group.max > 0 ? Math.round((group.remaining / group.max) * 100) : 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const result = [];
|
|
68
|
+
if (tiers.paid.size > 0) {
|
|
69
|
+
result.push({ tier: "paid", quotaGroups: Array.from(tiers.paid.values()) });
|
|
70
|
+
}
|
|
71
|
+
if (tiers.free.size > 0) {
|
|
72
|
+
result.push({ tier: "free", quotaGroups: Array.from(tiers.free.values()) });
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
function parseProviders(data) {
|
|
77
|
+
if (!data.providers)
|
|
78
|
+
return [];
|
|
79
|
+
return Object.entries(data.providers).map(([name, provider]) => ({
|
|
80
|
+
name,
|
|
81
|
+
tiers: aggregateByTier(provider.credentials ?? []),
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
function parseProxyQuota(data) {
|
|
85
|
+
const summary = data.global_summary ?? data.summary;
|
|
86
|
+
return {
|
|
87
|
+
providers: parseProviders(data),
|
|
88
|
+
totalCredentials: summary?.total_credentials ?? 0,
|
|
89
|
+
activeCredentials: summary?.active_credentials ?? 0,
|
|
90
|
+
dataSource: data.data_source,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export const ProxyProvider = {
|
|
94
|
+
id: "proxy",
|
|
95
|
+
displayName: "Mirrowel Proxy",
|
|
96
|
+
async fetchUsage() {
|
|
97
|
+
try {
|
|
98
|
+
const config = await loadProxyConfig();
|
|
99
|
+
const data = await fetchProxyLimits(config);
|
|
100
|
+
return {
|
|
101
|
+
timestamp: data.timestamp * 1000,
|
|
102
|
+
provider: "proxy",
|
|
103
|
+
planType: null,
|
|
104
|
+
primary: null,
|
|
105
|
+
secondary: null,
|
|
106
|
+
codeReview: null,
|
|
107
|
+
credits: null,
|
|
108
|
+
proxyQuota: parseProxyQuota(data),
|
|
109
|
+
updatedAt: Date.now(),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the Antigravity proxy provider.
|
|
3
|
+
*/
|
|
4
|
+
/** Configuration stored in ~/.config/opencode/usage-config.jsonc */
|
|
5
|
+
export type ProxyConfig = {
|
|
6
|
+
endpoint: string;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
providers?: {
|
|
10
|
+
openai?: boolean;
|
|
11
|
+
proxy?: boolean;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/** Token statistics from the proxy */
|
|
15
|
+
export type TokenStats = {
|
|
16
|
+
input_cached: number;
|
|
17
|
+
input_uncached: number;
|
|
18
|
+
input_cache_pct: number;
|
|
19
|
+
output: number;
|
|
20
|
+
};
|
|
21
|
+
/** Quota group aggregation */
|
|
22
|
+
export type QuotaGroup = {
|
|
23
|
+
avg_remaining_pct: number;
|
|
24
|
+
credentials_exhausted: number;
|
|
25
|
+
credentials_total: number;
|
|
26
|
+
models: string[];
|
|
27
|
+
tiers: Record<string, {
|
|
28
|
+
active: number;
|
|
29
|
+
priority: number;
|
|
30
|
+
total: number;
|
|
31
|
+
}>;
|
|
32
|
+
total_remaining_pct: number;
|
|
33
|
+
total_requests_max: number;
|
|
34
|
+
total_requests_remaining: number;
|
|
35
|
+
total_requests_used: number;
|
|
36
|
+
};
|
|
37
|
+
/** Model quota information */
|
|
38
|
+
export type ModelQuota = {
|
|
39
|
+
requests: number;
|
|
40
|
+
request_count: number;
|
|
41
|
+
success_count: number;
|
|
42
|
+
failure_count: number;
|
|
43
|
+
prompt_tokens: number;
|
|
44
|
+
prompt_tokens_cached: number;
|
|
45
|
+
completion_tokens: number;
|
|
46
|
+
approx_cost: number;
|
|
47
|
+
window_start_ts: number | null;
|
|
48
|
+
quota_reset_ts: number | null;
|
|
49
|
+
baseline_remaining_fraction: number | null;
|
|
50
|
+
baseline_fetched_at: number | null;
|
|
51
|
+
quota_max_requests: number;
|
|
52
|
+
quota_display: string;
|
|
53
|
+
};
|
|
54
|
+
/** Model group information */
|
|
55
|
+
export type ModelGroup = {
|
|
56
|
+
confidence: string;
|
|
57
|
+
display: string;
|
|
58
|
+
is_exhausted: boolean;
|
|
59
|
+
models: string[];
|
|
60
|
+
remaining_pct: number;
|
|
61
|
+
requests_max: number;
|
|
62
|
+
requests_remaining: number;
|
|
63
|
+
requests_used: number;
|
|
64
|
+
reset_time_iso: string | null;
|
|
65
|
+
};
|
|
66
|
+
/** Credential information */
|
|
67
|
+
export type Credential = {
|
|
68
|
+
identifier: string;
|
|
69
|
+
full_path: string;
|
|
70
|
+
status: string;
|
|
71
|
+
last_used_ts: number;
|
|
72
|
+
tier?: string;
|
|
73
|
+
requests: number;
|
|
74
|
+
tokens: TokenStats;
|
|
75
|
+
approx_cost: number | null;
|
|
76
|
+
global: {
|
|
77
|
+
requests: number;
|
|
78
|
+
tokens: TokenStats;
|
|
79
|
+
approx_cost: number | null;
|
|
80
|
+
};
|
|
81
|
+
models: Record<string, ModelQuota>;
|
|
82
|
+
model_groups?: Record<string, ModelGroup>;
|
|
83
|
+
};
|
|
84
|
+
/** Provider information */
|
|
85
|
+
export type Provider = {
|
|
86
|
+
credential_count: number;
|
|
87
|
+
active_count: number;
|
|
88
|
+
on_cooldown_count: number;
|
|
89
|
+
exhausted_count: number;
|
|
90
|
+
total_requests: number;
|
|
91
|
+
tokens: TokenStats;
|
|
92
|
+
approx_cost: number | null;
|
|
93
|
+
credentials: Credential[];
|
|
94
|
+
quota_groups?: Record<string, QuotaGroup>;
|
|
95
|
+
global?: {
|
|
96
|
+
approx_cost: number | null;
|
|
97
|
+
tokens: TokenStats;
|
|
98
|
+
total_requests: number;
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
/** Summary statistics */
|
|
102
|
+
export type Summary = {
|
|
103
|
+
total_providers: number;
|
|
104
|
+
total_credentials: number;
|
|
105
|
+
active_credentials?: number;
|
|
106
|
+
exhausted_credentials?: number;
|
|
107
|
+
total_requests: number;
|
|
108
|
+
tokens: TokenStats;
|
|
109
|
+
approx_total_cost: number | null;
|
|
110
|
+
};
|
|
111
|
+
/** Proxy limits response structure from /v1/quota-stats */
|
|
112
|
+
export type ProxyResponse = {
|
|
113
|
+
providers: Record<string, Provider>;
|
|
114
|
+
summary: Summary;
|
|
115
|
+
global_summary?: Summary;
|
|
116
|
+
data_source: string;
|
|
117
|
+
timestamp: number;
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/types.ts"],"names":[],"mappings":"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"}
|
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps the active session context for usage notifications.
|
|
3
|
+
* Stored in a mutable object so hooks can share updates.
|
|
4
|
+
*/
|
|
5
|
+
export type UsageState = {
|
|
6
|
+
sessionID: string | null;
|
|
7
|
+
agent?: string;
|
|
8
|
+
model?: {
|
|
9
|
+
providerID: string;
|
|
10
|
+
modelID: string;
|
|
11
|
+
};
|
|
12
|
+
availableProviders: {
|
|
13
|
+
codex: boolean;
|
|
14
|
+
proxy: boolean;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare function createUsageState(): UsageState;
|
|
18
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/C,kBAAkB,EAAE;QAClB,KAAK,EAAE,OAAO,CAAA;QACd,KAAK,EAAE,OAAO,CAAA;KACf,CAAA;CACF,CAAA;AAED,wBAAgB,gBAAgB,IAAI,UAAU,CAQ7C"}
|
package/dist/state.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps the active session context for usage notifications.
|
|
3
|
+
* Stored in a mutable object so hooks can share updates.
|
|
4
|
+
*/
|
|
5
|
+
export function createUsageState() {
|
|
6
|
+
return {
|
|
7
|
+
sessionID: null,
|
|
8
|
+
availableProviders: {
|
|
9
|
+
codex: false,
|
|
10
|
+
proxy: false,
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy limits tool for checking quota stats.
|
|
3
|
+
*/
|
|
4
|
+
type SendStatusFn = (sessionID: string, text: string) => Promise<void>;
|
|
5
|
+
type MarkSilentFn = (sessionID: string, messageID: string) => void;
|
|
6
|
+
export declare function createProxyLimitsTool(sendStatus: SendStatusFn, markSilent: MarkSilentFn): {
|
|
7
|
+
description: string;
|
|
8
|
+
args: {
|
|
9
|
+
refresh: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodBoolean>>;
|
|
10
|
+
};
|
|
11
|
+
execute(args: {
|
|
12
|
+
refresh: boolean;
|
|
13
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
14
|
+
};
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=proxy-limits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-limits.d.ts","sourceRoot":"","sources":["../../src/tools/proxy-limits.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,KAAK,YAAY,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AACtE,KAAK,YAAY,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;AAElE,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY;;;;;;;;EA+BvF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy limits tool for checking quota stats.
|
|
3
|
+
*/
|
|
4
|
+
import { tool } from "@opencode-ai/plugin";
|
|
5
|
+
import { loadProxyConfig, fetchProxyLimits, formatProxyLimits } from "../providers/proxy";
|
|
6
|
+
export function createProxyLimitsTool(sendStatus, markSilent) {
|
|
7
|
+
return tool({
|
|
8
|
+
description: "Check current usage limits from the antigravity proxy server. Displays results as an inline status message.",
|
|
9
|
+
args: {
|
|
10
|
+
refresh: tool.schema
|
|
11
|
+
.boolean()
|
|
12
|
+
.optional()
|
|
13
|
+
.default(false)
|
|
14
|
+
.describe("Force refresh the limits data (default: false)"),
|
|
15
|
+
},
|
|
16
|
+
async execute(_args, context) {
|
|
17
|
+
markSilent(context.sessionID, context.messageID);
|
|
18
|
+
try {
|
|
19
|
+
const config = await loadProxyConfig();
|
|
20
|
+
const data = await fetchProxyLimits(config);
|
|
21
|
+
const message = formatProxyLimits(data);
|
|
22
|
+
await sendStatus(context.sessionID, message);
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27
|
+
const errorMessage = `Proxy Limits Error\n\n${message}`;
|
|
28
|
+
await sendStatus(context.sessionID, errorMessage);
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides a simple tool wrapper for usage info.
|
|
3
|
+
* Keeps tool definitions separate from hooks and rendering.
|
|
4
|
+
*/
|
|
5
|
+
export declare const usageTool: () => {
|
|
6
|
+
description: string;
|
|
7
|
+
args: {};
|
|
8
|
+
execute(args: Record<string, never>, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/tools/usage.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,SAAS;;;;CAOlB,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides a simple tool wrapper for usage info.
|
|
3
|
+
* Keeps tool definitions separate from hooks and rendering.
|
|
4
|
+
*/
|
|
5
|
+
import { tool } from "@opencode-ai/plugin";
|
|
6
|
+
export const usageTool = () => tool({
|
|
7
|
+
description: "Get current rate limit snapshots for all providers",
|
|
8
|
+
args: {},
|
|
9
|
+
async execute() {
|
|
10
|
+
return "Run /usage in the chat to see current limits.";
|
|
11
|
+
},
|
|
12
|
+
});
|