@hashimkarim/usagestat-linux-x64-gnu 2.0.2 → 2.2.0
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/native-manifest.json +808 -148
- package/package.json +112 -2
- package/plugins/abliteration/LICENSE +21 -0
- package/plugins/abliteration/icon.svg +5 -0
- package/plugins/abliteration/plugin.js +89 -0
- package/plugins/abliteration/plugin.json +24 -0
- package/plugins/aerostack/LICENSE +21 -0
- package/plugins/aerostack/icon.svg +1 -0
- package/plugins/aerostack/plugin.js +94 -0
- package/plugins/aerostack/plugin.json +24 -0
- package/plugins/aixy/icon.svg +1 -0
- package/plugins/aixy/plugin.js +5 -0
- package/plugins/aixy/plugin.json +9 -1
- package/plugins/antigravity/plugin.js +19 -10
- package/plugins/antigravity-cli/plugin.js +8 -3
- package/plugins/antigravity-ide/plugin.js +8 -5
- package/plugins/atlascloud/plugin.js +5 -0
- package/plugins/atlascloud/plugin.json +2 -1
- package/plugins/bifrost/icon.svg +3 -0
- package/plugins/bifrost/plugin.js +5 -0
- package/plugins/bifrost/plugin.json +9 -1
- package/plugins/chatbox/LICENSE +21 -0
- package/plugins/chatbox/icon.svg +4 -0
- package/plugins/chatbox/plugin.js +208 -0
- package/plugins/chatbox/plugin.json +24 -0
- package/plugins/claude/plugin.js +39 -2
- package/plugins/claudeplatform/LICENSE +21 -0
- package/plugins/claudeplatform/icon.svg +3 -0
- package/plugins/claudeplatform/plugin.js +244 -0
- package/plugins/claudeplatform/plugin.json +25 -0
- package/plugins/codex/plugin.js +14 -8
- package/plugins/codex/plugin.json +11 -2
- package/plugins/cosmic/LICENSE +21 -0
- package/plugins/cosmic/icon.svg +1 -0
- package/plugins/cosmic/plugin.js +134 -0
- package/plugins/cosmic/plugin.json +32 -0
- package/plugins/cursor/plugin.js +27 -8
- package/plugins/cursor-nightly/plugin.json +1 -1
- package/plugins/devpass/icon.svg +3 -0
- package/plugins/devpass/plugin.js +5 -0
- package/plugins/devpass/plugin.json +2 -1
- package/plugins/exa/LICENSE +21 -0
- package/plugins/exa/icon-color.svg +1 -0
- package/plugins/exa/icon.svg +1 -0
- package/plugins/exa/plugin.js +75 -0
- package/plugins/exa/plugin.json +32 -0
- package/plugins/fal/LICENSE +21 -0
- package/plugins/fal/icon-color.svg +1 -0
- package/plugins/fal/icon.svg +1 -0
- package/plugins/fal/plugin.js +74 -0
- package/plugins/fal/plugin.json +24 -0
- package/plugins/flora/LICENSE +21 -0
- package/plugins/flora/icon.svg +4 -0
- package/plugins/flora/plugin.js +93 -0
- package/plugins/flora/plugin.json +32 -0
- package/plugins/futureagi/LICENSE +21 -0
- package/plugins/futureagi/icon.svg +24 -0
- package/plugins/futureagi/plugin.js +139 -0
- package/plugins/futureagi/plugin.json +32 -0
- package/plugins/gemini-apps/LICENSE +1 -2
- package/plugins/gemini-apps/plugin.js +136 -465
- package/plugins/gemini-apps/plugin.json +13 -7
- package/plugins/gitkraken/icon.svg +2 -0
- package/plugins/gitkraken/plugin.js +5 -0
- package/plugins/gitkraken/plugin.json +9 -1
- package/plugins/googledeveloper/LICENSE +21 -0
- package/plugins/googledeveloper/icon.svg +3 -0
- package/plugins/googledeveloper/plugin.js +204 -0
- package/plugins/googledeveloper/plugin.json +25 -0
- package/plugins/helmcode/icon.svg +5 -0
- package/plugins/helmcode/plugin.js +120 -55
- package/plugins/helmcode/plugin.json +10 -2
- package/plugins/huggingface/plugin.js +6 -1
- package/plugins/huggingface/plugin.json +4 -3
- package/plugins/hyper/plugin.js +53 -47
- package/plugins/hyper/plugin.json +3 -2
- package/plugins/jetbrains-ai-assistant/LICENSE +22 -0
- package/plugins/jetbrains-ai-assistant/plugin.js +502 -16
- package/plugins/jetbrains-ai-assistant/plugin.json +27 -4
- package/plugins/kimi/plugin.js +54 -14
- package/plugins/kimi/plugin.json +46 -6
- package/plugins/langdock/LICENSE +21 -0
- package/plugins/langdock/icon.svg +4 -0
- package/plugins/langdock/plugin.js +106 -0
- package/plugins/langdock/plugin.json +25 -0
- package/plugins/linkup/LICENSE +21 -0
- package/plugins/linkup/icon.svg +10 -0
- package/plugins/linkup/plugin.js +46 -0
- package/plugins/linkup/plugin.json +24 -0
- package/plugins/lithosai/plugin.js +5 -0
- package/plugins/lithosai/plugin.json +2 -1
- package/plugins/llmman/icon.svg +4 -0
- package/plugins/llmman/plugin.js +5 -0
- package/plugins/llmman/plugin.json +9 -1
- package/plugins/munito/LICENSE +21 -0
- package/plugins/munito/icon.svg +7 -0
- package/plugins/munito/plugin.js +171 -0
- package/plugins/munito/plugin.json +24 -0
- package/plugins/muse/icon.svg +3 -0
- package/plugins/muse/plugin.js +5 -0
- package/plugins/muse/plugin.json +9 -1
- package/plugins/museai/plugin.js +10 -3
- package/plugins/museai/plugin.json +3 -2
- package/plugins/nous/plugin.js +5 -0
- package/plugins/nous/plugin.json +9 -1
- package/plugins/ollama/plugin.js +58 -0
- package/plugins/opencode-go/plugin.js +21 -9
- package/plugins/opendesign/LICENSE +21 -0
- package/plugins/opendesign/icon.svg +4 -0
- package/plugins/opendesign/plugin.js +108 -0
- package/plugins/opendesign/plugin.json +25 -0
- package/plugins/parallel/LICENSE +21 -0
- package/plugins/parallel/icon.svg +11 -0
- package/plugins/parallel/plugin.js +97 -0
- package/plugins/parallel/plugin.json +25 -0
- package/plugins/perplexity/LICENSE +22 -0
- package/plugins/perplexity/plugin.js +122 -0
- package/plugins/perplexity/plugin.json +36 -7
- package/plugins/qwencloud/LICENSE +21 -0
- package/plugins/qwencloud/plugin.js +197 -0
- package/plugins/qwencloud/plugin.json +17 -0
- package/plugins/raycast/icon.svg +4 -0
- package/plugins/raycast/plugin.js +5 -0
- package/plugins/raycast/plugin.json +9 -1
- package/plugins/replicate/plugin.js +5 -0
- package/plugins/replicate/plugin.json +2 -1
- package/plugins/sailresearch/LICENSE +21 -0
- package/plugins/sailresearch/icon.svg +4 -0
- package/plugins/sailresearch/plugin.js +104 -0
- package/plugins/sailresearch/plugin.json +24 -0
- package/plugins/siliconflow/LICENSE +21 -0
- package/plugins/siliconflow/icon.svg +3 -0
- package/plugins/siliconflow/plugin.js +87 -0
- package/plugins/siliconflow/plugin.json +32 -0
- package/plugins/sofya/LICENSE +21 -0
- package/plugins/sofya/icon.svg +3 -0
- package/plugins/sofya/plugin.js +86 -0
- package/plugins/sofya/plugin.json +24 -0
- package/plugins/soniox/LICENSE +21 -0
- package/plugins/soniox/icon.svg +3 -0
- package/plugins/soniox/plugin.js +103 -0
- package/plugins/soniox/plugin.json +24 -0
- package/plugins/synthetic/plugin.js +12 -0
- package/plugins/synthetic/plugin.json +51 -9
- package/plugins/tavily/LICENSE +21 -0
- package/plugins/tavily/icon.svg +3 -0
- package/plugins/tavily/plugin.js +61 -0
- package/plugins/tavily/plugin.json +24 -0
- package/plugins/tinyapi/LICENSE +21 -0
- package/plugins/tinyapi/icon.svg +4 -0
- package/plugins/tinyapi/plugin.js +73 -0
- package/plugins/tinyapi/plugin.json +25 -0
- package/plugins/traecn/LICENSE +21 -0
- package/plugins/traecn/icon.svg +3 -0
- package/plugins/traecn/plugin.js +103 -0
- package/plugins/traecn/plugin.json +25 -0
- package/plugins/tripo/LICENSE +21 -0
- package/plugins/tripo/icon.svg +4 -0
- package/plugins/tripo/plugin.js +60 -0
- package/plugins/tripo/plugin.json +24 -0
- package/plugins/typesafe/icon.svg +4 -0
- package/plugins/typesafe/plugin.js +85 -129
- package/plugins/typesafe/plugin.json +3 -2
- package/plugins/v0/plugin.js +5 -0
- package/plugins/v0/plugin.json +9 -1
- package/plugins/vercel/plugin.js +147 -0
- package/plugins/vercel/plugin.json +34 -3
- package/plugins/workbuddy/icon.svg +1 -1
- package/plugins/workbuddy/plugin.js +110 -19
- package/plugins/workbuddy/plugin.json +32 -2
- package/plugins/xai/plugin.js +9 -0
- package/plugins/xapi/LICENSE +21 -0
- package/plugins/xapi/icon.svg +1 -0
- package/plugins/xapi/plugin.js +90 -0
- package/plugins/xapi/plugin.json +25 -0
- package/plugins/xkiro/icon.svg +1 -0
- package/plugins/xkiro/plugin.js +5 -0
- package/plugins/xkiro/plugin.json +2 -1
- package/usagestat +0 -0
- package/usagestatd +0 -0
- package/plugins/cursor-nightly/plugin.js +0 -1416
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
|
|
2
2
|
globalThis.__usagestat_bundled_options = {"labels":["Spend","Spend","Additional"],"defaults":{},"autoMode":"web"};
|
|
3
|
+
(function(defineProvider) {
|
|
4
|
+
// BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/typesafe.js
|
|
3
5
|
function _optionalChain(ops) {
|
|
4
6
|
let lastAccessLHS = undefined;
|
|
5
7
|
let value = ops[0];
|
|
@@ -22,16 +24,17 @@ function _optionalChain(ops) {
|
|
|
22
24
|
return value;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
27
|
+
const billingOverviewURL = "https://console.typesafe.ai/api/billing/overview";
|
|
28
|
+
const overviewFailure =
|
|
29
|
+
"TypeSafe billing overview could not be read. Open https://console.typesafe.ai/billing to check access; if the page opens, report the integration failure.";
|
|
30
|
+
// Cloudflare blocks the Linux default UA (it contains "curl/"), so send a browser-style one.
|
|
31
|
+
const userAgent =
|
|
32
|
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36";
|
|
30
33
|
|
|
31
34
|
defineProvider({
|
|
32
35
|
id: "typesafe",
|
|
33
36
|
name: "TypeSafe",
|
|
34
|
-
endpoints: [
|
|
37
|
+
endpoints: ["https://console.typesafe.ai"],
|
|
35
38
|
settings: [],
|
|
36
39
|
capabilities: ["browser-cookies", "http-status"],
|
|
37
40
|
cookieDomains: ["console.typesafe.ai", "typesafe.ai"],
|
|
@@ -46,9 +49,27 @@ defineProvider({
|
|
|
46
49
|
const value = raw && /^\d+(?:\.\d+)?$/.test(raw.trim()) ? Number(raw) : 1;
|
|
47
50
|
return Number.isFinite(value) ? Math.min(10, value) : 1;
|
|
48
51
|
};
|
|
52
|
+
// A redirect only means the session is gone when it targets a login/SSO route. The billing overview
|
|
53
|
+
// itself may redirect for benign route moves (for example /settings/billing -> /billing).
|
|
54
|
+
const loginRedirect = (location) => {
|
|
55
|
+
if (typeof location !== "string" || !location.trim()) return false;
|
|
56
|
+
const target = location.trim();
|
|
57
|
+
const loginPath = /(?:^|[/?#])(?:login|sign-?in|signin|auth|oauth|sso)(?:[/?#]|$)/i;
|
|
58
|
+
if (!/^(?:https?:)?\/\//i.test(target)) return loginPath.test(target);
|
|
59
|
+
const hostMatch = /^(?:https?:)?\/\/([^/?#]+)/i.exec(target);
|
|
60
|
+
const hostname = hostMatch ? hostMatch[1].toLowerCase().replace(/:\d+$/, "") : "";
|
|
61
|
+
const knownHost = hostname === "typesafe.ai" || hostname.endsWith(".typesafe.ai");
|
|
62
|
+
return !knownHost || loginPath.test(target);
|
|
63
|
+
};
|
|
64
|
+
// The billing overview API refuses requests that do not look same-origin, even with a valid session.
|
|
49
65
|
const validate = (response) => {
|
|
50
|
-
if (response.status ===
|
|
51
|
-
|
|
66
|
+
if (response.status === 403 && /cross-origin request refused/i.test(response.bodyText))
|
|
67
|
+
throw ctx.fail.apiFailure(overviewFailure);
|
|
68
|
+
if (response.status === 401 || response.status === 403) expired();
|
|
69
|
+
if (response.status >= 300 && response.status < 400) {
|
|
70
|
+
if (loginRedirect(response.headers["location"])) expired();
|
|
71
|
+
throw ctx.fail.apiFailure(overviewFailure);
|
|
72
|
+
}
|
|
52
73
|
if (response.status === 429)
|
|
53
74
|
throw ctx.fail.rateLimited("TypeSafe rate limit reached.", { retryAfterSeconds: retryAfter(response) });
|
|
54
75
|
if (response.status === 408 || response.status >= 500)
|
|
@@ -58,106 +79,40 @@ defineProvider({
|
|
|
58
79
|
if (response.status < 200 || response.status >= 300)
|
|
59
80
|
throw ctx.fail.apiFailure(`TypeSafe returned HTTP ${response.status}.`);
|
|
60
81
|
};
|
|
61
|
-
// Login pages can also arrive directly with HTTP 200, without an HTTP redirect.
|
|
62
|
-
const isLoginLanding = (body) => /\\?"\(auth\)\\?",\{\\?"children\\?":\[\\?"login\\?"/.test(body);
|
|
63
|
-
const validateBody = (response) => {
|
|
64
|
-
validate(response);
|
|
65
|
-
if (isLoginLanding(response.bodyText)) expired();
|
|
66
|
-
};
|
|
67
|
-
const actionIDPattern = /"([0-9a-f]{40,})"[^)]{0,150}"getBillingOverviewResult"/i;
|
|
68
|
-
const discoverActionID = async (cookie) => {
|
|
69
|
-
const page = await ctx.http.get(billingURL, {
|
|
70
|
-
headers: { Cookie: cookie, Accept: "text/html" },
|
|
71
|
-
timeoutSeconds: 6,
|
|
72
|
-
});
|
|
73
|
-
validateBody(page);
|
|
74
|
-
const scriptPattern = /<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/gi;
|
|
75
|
-
const chunkURLs = [];
|
|
76
|
-
let match;
|
|
77
|
-
while ((match = scriptPattern.exec(page.bodyText)) && chunkURLs.length < maximumChunkCount) {
|
|
78
|
-
const source = match[1];
|
|
79
|
-
const normalized = source.startsWith("/") ? `${billingOrigin}${source}` : source;
|
|
80
|
-
if (!normalized.startsWith(`${billingOrigin}/`) || !/\.js(?:$|\?)/i.test(normalized)) continue;
|
|
81
|
-
if (!chunkURLs.includes(normalized)) chunkURLs.push(normalized);
|
|
82
|
-
}
|
|
83
|
-
let discoveryFailure;
|
|
84
|
-
for (const url of chunkURLs) {
|
|
85
|
-
try {
|
|
86
|
-
const chunk = await ctx.http.get(url, { timeoutSeconds: 2 });
|
|
87
|
-
if (chunk.status === 429 || chunk.status === 408 || chunk.status >= 500) validate(chunk);
|
|
88
|
-
if (chunk.status < 200 || chunk.status >= 300) continue;
|
|
89
|
-
const action = _optionalChain([
|
|
90
|
-
actionIDPattern,
|
|
91
|
-
"access",
|
|
92
|
-
(_) => _.exec,
|
|
93
|
-
"call",
|
|
94
|
-
(_2) => _2(chunk.bodyText),
|
|
95
|
-
"optionalAccess",
|
|
96
|
-
(_3) => _3[1],
|
|
97
|
-
]);
|
|
98
|
-
if (action) return action;
|
|
99
|
-
} catch (error) {
|
|
100
|
-
discoveryFailure ??= error;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (discoveryFailure !== undefined) throw discoveryFailure;
|
|
104
|
-
throw ctx.fail.parseFailure("TypeSafe billing page format changed: action id not found.");
|
|
105
|
-
};
|
|
106
82
|
const cookie = await ctx.browser.cookieHeader("typesafe.ai");
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
ctx.cache.set(actionCacheKey, "", 1);
|
|
127
|
-
actionID = await discoverActionID(cookie);
|
|
128
|
-
ctx.cache.set(actionCacheKey, actionID, actionCacheTTL);
|
|
129
|
-
response = await post(actionID);
|
|
83
|
+
const response = await ctx.http.get(billingOverviewURL, {
|
|
84
|
+
headers: {
|
|
85
|
+
Cookie: cookie,
|
|
86
|
+
Accept: "application/json",
|
|
87
|
+
Origin: "https://console.typesafe.ai",
|
|
88
|
+
Referer: "https://console.typesafe.ai/billing",
|
|
89
|
+
"Sec-Fetch-Site": "same-origin",
|
|
90
|
+
"Sec-Fetch-Mode": "cors",
|
|
91
|
+
"Sec-Fetch-Dest": "empty",
|
|
92
|
+
"User-Agent": userAgent,
|
|
93
|
+
},
|
|
94
|
+
timeoutSeconds: 10,
|
|
95
|
+
});
|
|
96
|
+
validate(response);
|
|
97
|
+
// A signed-out console can answer with an HTML login page instead of JSON.
|
|
98
|
+
const body = response.bodyText.trim();
|
|
99
|
+
if (body.startsWith("<")) {
|
|
100
|
+
if (/(?:login|sign[-\s]?in|\(auth\))/i.test(body)) expired();
|
|
101
|
+
throw ctx.fail.apiFailure(overviewFailure);
|
|
130
102
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
let candidate;
|
|
138
|
-
try {
|
|
139
|
-
candidate = JSON.parse(line.slice(separator + 1));
|
|
140
|
-
} catch (error) {
|
|
141
|
-
void error;
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
const object = record(candidate);
|
|
145
|
-
if (object && Object.prototype.hasOwnProperty.call(object, "ok")) {
|
|
146
|
-
result = object;
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
103
|
+
let payload;
|
|
104
|
+
try {
|
|
105
|
+
payload = JSON.parse(body);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
void error;
|
|
108
|
+
throw ctx.fail.parseFailure(overviewFailure);
|
|
149
109
|
}
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
const billing = record(
|
|
153
|
-
_optionalChain([record, "call", (_4) => _4(result.data), "optionalAccess", (_5) => _5.billing]),
|
|
154
|
-
);
|
|
155
|
-
if (!billing) throw ctx.fail.parseFailure("TypeSafe billing response format changed: missing billing.");
|
|
110
|
+
const billing = record(_optionalChain([record, "call", (_) => _(payload), "optionalAccess", (_2) => _2.billing]));
|
|
111
|
+
if (!billing) throw ctx.fail.parseFailure(overviewFailure);
|
|
156
112
|
const number = (value) => (typeof value === "number" && Number.isFinite(value) ? value : undefined);
|
|
157
113
|
const spent = number(billing.spent);
|
|
158
114
|
const balance = number(billing.balance);
|
|
159
|
-
if (spent === undefined || balance === undefined)
|
|
160
|
-
throw ctx.fail.parseFailure("TypeSafe billing response format changed: missing billing numbers.");
|
|
115
|
+
if (spent === undefined || balance === undefined) throw ctx.fail.parseFailure(overviewFailure);
|
|
161
116
|
const cycleLabel =
|
|
162
117
|
typeof billing.cycleLabel === "string" && billing.cycleLabel.trim() ? billing.cycleLabel.trim() : undefined;
|
|
163
118
|
const plan = typeof billing.plan === "string" && billing.plan.trim() ? billing.plan.trim() : undefined;
|
|
@@ -167,46 +122,44 @@ defineProvider({
|
|
|
167
122
|
: _optionalChain([
|
|
168
123
|
plan,
|
|
169
124
|
"optionalAccess",
|
|
170
|
-
(
|
|
125
|
+
(_3) => _3.split,
|
|
171
126
|
"call",
|
|
172
|
-
(
|
|
127
|
+
(_4) => _4(/[_-]+/),
|
|
173
128
|
"access",
|
|
174
|
-
(
|
|
129
|
+
(_5) => _5.filter,
|
|
175
130
|
"call",
|
|
176
|
-
(
|
|
131
|
+
(_6) => _6(Boolean),
|
|
177
132
|
"access",
|
|
178
|
-
(
|
|
133
|
+
(_7) => _7.map,
|
|
179
134
|
"call",
|
|
180
|
-
(
|
|
135
|
+
(_8) => _8((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()),
|
|
181
136
|
"access",
|
|
182
|
-
(
|
|
137
|
+
(_9) => _9.join,
|
|
183
138
|
"call",
|
|
184
|
-
(
|
|
139
|
+
(_10) => _10(" "),
|
|
185
140
|
]);
|
|
186
141
|
const rows = [{ label: cycleLabel ? `Spent (${cycleLabel})` : "Spent", value: ctx.format.usd(spent) }];
|
|
187
142
|
if (planLabel) rows.push({ label: "Plan", value: planLabel });
|
|
188
143
|
const availableRows = 24 - rows.length;
|
|
189
144
|
const creditRows = [];
|
|
190
145
|
let creditCount = 0;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
});
|
|
209
|
-
}
|
|
146
|
+
for (const item of Array.isArray(billing.credits) ? billing.credits : []) {
|
|
147
|
+
const credit = record(item);
|
|
148
|
+
const amount = number(_optionalChain([credit, "optionalAccess", (_11) => _11.amount]));
|
|
149
|
+
const remaining = number(_optionalChain([credit, "optionalAccess", (_12) => _12.remaining]));
|
|
150
|
+
const expiresAt =
|
|
151
|
+
typeof _optionalChain([credit, "optionalAccess", (_13) => _13.expiresAt]) === "string"
|
|
152
|
+
? credit.expiresAt
|
|
153
|
+
: undefined;
|
|
154
|
+
if (amount === undefined || remaining === undefined || remaining <= 0 || !expiresAt) continue;
|
|
155
|
+
const expires = Date.parse(expiresAt);
|
|
156
|
+
if (!Number.isFinite(expires)) continue;
|
|
157
|
+
creditCount++;
|
|
158
|
+
if (creditRows.length >= availableRows) continue;
|
|
159
|
+
creditRows.push({
|
|
160
|
+
label: "Credit",
|
|
161
|
+
value: `${ctx.format.number(remaining)} of ${ctx.format.number(amount)}, expires ${ctx.format.monthDay(expires)}`,
|
|
162
|
+
});
|
|
210
163
|
}
|
|
211
164
|
if (creditCount > availableRows) {
|
|
212
165
|
const shown = availableRows - 1;
|
|
@@ -223,3 +176,6 @@ defineProvider({
|
|
|
223
176
|
};
|
|
224
177
|
},
|
|
225
178
|
});
|
|
179
|
+
|
|
180
|
+
// END UPSTREAM
|
|
181
|
+
})(globalThis.defineProvider);
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
"envVars": [
|
|
15
15
|
"TYPESAFE_COOKIE"
|
|
16
16
|
],
|
|
17
|
+
"setupFields": [],
|
|
17
18
|
"upstream": {
|
|
18
19
|
"repository": "https://github.com/steipete/CodexBar",
|
|
19
|
-
"revision": "
|
|
20
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
20
21
|
"path": "Sources/CodexBarCore/Resources/Plugins/typesafe.js",
|
|
21
|
-
"sha256": "
|
|
22
|
+
"sha256": "da91db172678133393d38836fcb4028342edade8aa7994d2bc5e95290498192f",
|
|
22
23
|
"license": "MIT"
|
|
23
24
|
}
|
|
24
25
|
}
|
package/plugins/v0/plugin.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
|
|
2
2
|
globalThis.__usagestat_bundled_options = {"labels":["Billing","Rate limit","Additional"],"defaults":{},"autoMode":"api"};
|
|
3
|
+
(function(defineProvider) {
|
|
4
|
+
// BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/v0.js
|
|
3
5
|
function _nullishCoalesce(lhs, rhsFn) {
|
|
4
6
|
if (lhs != null) {
|
|
5
7
|
return lhs;
|
|
@@ -212,3 +214,6 @@ defineProvider({
|
|
|
212
214
|
};
|
|
213
215
|
},
|
|
214
216
|
});
|
|
217
|
+
|
|
218
|
+
// END UPSTREAM
|
|
219
|
+
})(globalThis.defineProvider);
|
package/plugins/v0/plugin.json
CHANGED
|
@@ -14,9 +14,17 @@
|
|
|
14
14
|
"V0_API_KEY",
|
|
15
15
|
"V0_SCOPE"
|
|
16
16
|
],
|
|
17
|
+
"setupFields": [
|
|
18
|
+
{
|
|
19
|
+
"key": "V0_SCOPE",
|
|
20
|
+
"title": "Scope",
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Provider-specific account or source selection."
|
|
23
|
+
}
|
|
24
|
+
],
|
|
17
25
|
"upstream": {
|
|
18
26
|
"repository": "https://github.com/steipete/CodexBar",
|
|
19
|
-
"revision": "
|
|
27
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
20
28
|
"path": "Sources/CodexBarCore/Resources/Plugins/v0.js",
|
|
21
29
|
"sha256": "5f54a9329dca35d59d76e427c34d4d9db8d3400d0b38c5fcee8899928a900019",
|
|
22
30
|
"license": "MIT"
|
package/plugins/vercel/plugin.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
|
|
2
2
|
globalThis.__usagestat_bundled_options = {"labels":["Balance","Balance","Additional"],"defaults":{},"autoMode":"api"};
|
|
3
|
+
(function(defineProvider) {
|
|
4
|
+
// BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/vercel.js
|
|
3
5
|
defineProvider({
|
|
4
6
|
id: "vercel",
|
|
5
7
|
name: "Vercel AI Gateway",
|
|
@@ -53,3 +55,148 @@ defineProvider({
|
|
|
53
55
|
};
|
|
54
56
|
},
|
|
55
57
|
});
|
|
58
|
+
|
|
59
|
+
// END UPSTREAM
|
|
60
|
+
})(globalThis.defineProvider);
|
|
61
|
+
(function() { const credits=globalThis.__usagestat_plugin; let budget;
|
|
62
|
+
(function(defineProvider) {
|
|
63
|
+
// BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/vercel-budget.js
|
|
64
|
+
defineProvider({
|
|
65
|
+
id: "vercel",
|
|
66
|
+
name: "Vercel AI Gateway",
|
|
67
|
+
endpoints: ["https://api.vercel.com"],
|
|
68
|
+
auth: { type: "bearer", secret: "VERCEL_ACCESS_TOKEN" },
|
|
69
|
+
settings: [
|
|
70
|
+
{ key: "VERCEL_ACCESS_TOKEN", title: "Vercel access token", type: "secure" },
|
|
71
|
+
{ key: "VERCEL_TEAM_ID", title: "Team ID", type: "plain" },
|
|
72
|
+
],
|
|
73
|
+
capabilities: ["http-status"],
|
|
74
|
+
async fetchUsage(ctx) {
|
|
75
|
+
const team = ctx.settings.get("VERCEL_TEAM_ID")?.trim();
|
|
76
|
+
if (!team) throw ctx.fail.missingCredential("Set the Vercel Team ID to read your personal budget.");
|
|
77
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(team)) {
|
|
78
|
+
throw ctx.fail.permissionDenied("Invalid Vercel Team ID. Enter the team ID or slug without spaces.");
|
|
79
|
+
}
|
|
80
|
+
function fail() {
|
|
81
|
+
throw ctx.fail.parseFailure("Vercel returned an unrecognized personal budget.");
|
|
82
|
+
}
|
|
83
|
+
async function read(url) {
|
|
84
|
+
const response = await ctx.http.get(url);
|
|
85
|
+
const message = `Vercel API returned HTTP ${response.status}.`;
|
|
86
|
+
if (response.status === 401) throw ctx.fail.authenticationExpired(message);
|
|
87
|
+
if (response.status === 403) throw ctx.fail.permissionDenied(message);
|
|
88
|
+
if (response.status === 429) {
|
|
89
|
+
const delay = Number(response.headers["retry-after"] ?? 1);
|
|
90
|
+
throw ctx.fail.rateLimited(message, {
|
|
91
|
+
retryAfterSeconds: Number.isFinite(delay) && delay >= 0 ? Math.min(delay, 10) : 1,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (response.status >= 500) throw ctx.fail.providerUnavailable(message);
|
|
95
|
+
if (response.status !== 200) throw ctx.fail.apiFailure(message);
|
|
96
|
+
try {
|
|
97
|
+
return JSON.parse(response.bodyText);
|
|
98
|
+
} catch {
|
|
99
|
+
return fail();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const user = (await read("https://api.vercel.com/v2/user"))?.user;
|
|
104
|
+
if (typeof user?.id !== "string" || !user.id) return fail();
|
|
105
|
+
|
|
106
|
+
// At most 20 pages; the plugin deadline bounds slower teams.
|
|
107
|
+
const matches = [];
|
|
108
|
+
const cursors = new Set();
|
|
109
|
+
let cursor = null;
|
|
110
|
+
for (let page = 0; ; page += 1) {
|
|
111
|
+
const query =
|
|
112
|
+
`teamId=${encodeURIComponent(team)}&scopeType=user&limit=200` +
|
|
113
|
+
(cursor === null ? "" : `&cursor=${encodeURIComponent(cursor)}`);
|
|
114
|
+
const body = await read(`https://api.vercel.com/ai-gateway/budgets/list?${query}`);
|
|
115
|
+
if (!Array.isArray(body?.budgets)) return fail();
|
|
116
|
+
for (const budget of body.budgets) {
|
|
117
|
+
if (budget?.scopeType !== "user" || ![user.id, `usr_${user.id}`].includes(budget.scopeId)) continue;
|
|
118
|
+
if (typeof budget.active !== "boolean" || typeof budget.archived !== "boolean") return fail();
|
|
119
|
+
if (budget.source !== undefined && budget.source !== "default") return fail();
|
|
120
|
+
if (budget.active && !budget.archived) matches.push(budget);
|
|
121
|
+
}
|
|
122
|
+
const next = body.nextCursor;
|
|
123
|
+
if (next === undefined || next === null) break;
|
|
124
|
+
if (typeof next !== "string" || !next || cursors.has(next) || page >= 19) return fail();
|
|
125
|
+
cursors.add(next);
|
|
126
|
+
cursor = next;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const identity = {
|
|
130
|
+
email: typeof user.email === "string" ? user.email : null,
|
|
131
|
+
organization: team,
|
|
132
|
+
loginMethod: "Access token",
|
|
133
|
+
accountID: user.id,
|
|
134
|
+
};
|
|
135
|
+
const custom = matches.filter((budget) => budget.source !== "default");
|
|
136
|
+
const effective = custom.length > 0 ? custom : matches;
|
|
137
|
+
if (effective.length > 1) return fail();
|
|
138
|
+
const budget = effective[0];
|
|
139
|
+
if (!budget) {
|
|
140
|
+
return {
|
|
141
|
+
details: [{ title: "Personal budget", rows: [{ label: "No personal budget configured", value: "—" }] }],
|
|
142
|
+
identity,
|
|
143
|
+
dataConfidence: "exact",
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const limit = budget.limitAmount;
|
|
147
|
+
const spent = budget.currentSpend;
|
|
148
|
+
if (typeof limit !== "number" || !Number.isFinite(limit) || limit <= 0) return fail();
|
|
149
|
+
if (typeof spent !== "number" || !Number.isFinite(spent) || spent < 0) return fail();
|
|
150
|
+
|
|
151
|
+
// Documented UTC boundaries: daily midnight, weekly Monday, monthly the 1st; `none` never resets.
|
|
152
|
+
const now = ctx.date.now();
|
|
153
|
+
const year = now.getUTCFullYear();
|
|
154
|
+
const month = now.getUTCMonth();
|
|
155
|
+
const day = now.getUTCDate();
|
|
156
|
+
let start = null;
|
|
157
|
+
let end = null;
|
|
158
|
+
if (budget.refreshPeriod === "daily") {
|
|
159
|
+
start = Date.UTC(year, month, day);
|
|
160
|
+
end = Date.UTC(year, month, day + 1);
|
|
161
|
+
} else if (budget.refreshPeriod === "weekly") {
|
|
162
|
+
start = Date.UTC(year, month, day - ((now.getUTCDay() + 6) % 7));
|
|
163
|
+
end = start + 7 * 24 * 60 * 60 * 1000;
|
|
164
|
+
} else if (budget.refreshPeriod === "monthly") {
|
|
165
|
+
start = Date.UTC(year, month, 1);
|
|
166
|
+
end = Date.UTC(year, month + 1, 1);
|
|
167
|
+
} else if (budget.refreshPeriod !== "none") {
|
|
168
|
+
return fail();
|
|
169
|
+
}
|
|
170
|
+
const spentText = ctx.format.usd(spent);
|
|
171
|
+
const limitText = ctx.format.usd(limit);
|
|
172
|
+
return {
|
|
173
|
+
primary: {
|
|
174
|
+
usedPercent: (spent / limit) * 100,
|
|
175
|
+
windowMinutes: start === null ? null : (end - start) / 60000,
|
|
176
|
+
resetsAt: end === null ? null : new Date(end),
|
|
177
|
+
resetDescription: `${spentText} / ${limitText}`,
|
|
178
|
+
},
|
|
179
|
+
details: [
|
|
180
|
+
{
|
|
181
|
+
title: "Personal budget",
|
|
182
|
+
rows: [
|
|
183
|
+
{ label: "Spend", value: spentText },
|
|
184
|
+
{ label: "Limit", value: limitText },
|
|
185
|
+
{ label: "Remaining", value: ctx.format.usd(Math.max(0, limit - spent)) },
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
identity,
|
|
190
|
+
dataConfidence: "exact",
|
|
191
|
+
};
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// END UPSTREAM
|
|
196
|
+
})(definition => { budget=globalThis.__usagestat_bundledProvider(definition,{"labels":["Personal budget","Additional","Additional"],"autoMode":"api","genericApiKey":false}); });
|
|
197
|
+
globalThis.__usagestat_plugin = {id:"vercel",probe(ctx) {
|
|
198
|
+
const selected=ctx.provider?.settings?.personalBudget;
|
|
199
|
+
if(selected!==undefined && typeof selected!=='boolean') throw new Error('personalBudget must be a boolean.');
|
|
200
|
+
return (selected===true ? budget : credits).probe(ctx);
|
|
201
|
+
}};
|
|
202
|
+
})();
|
|
@@ -11,13 +11,44 @@
|
|
|
11
11
|
"autoMode": "api",
|
|
12
12
|
"usageDashboardUrl": "https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai-gateway",
|
|
13
13
|
"envVars": [
|
|
14
|
-
"AI_GATEWAY_API_KEY"
|
|
14
|
+
"AI_GATEWAY_API_KEY",
|
|
15
|
+
"VERCEL_ACCESS_TOKEN",
|
|
16
|
+
"VERCEL_TEAM_ID"
|
|
17
|
+
],
|
|
18
|
+
"setupFields": [
|
|
19
|
+
{
|
|
20
|
+
"key": "personalBudget",
|
|
21
|
+
"title": "Read personal budget",
|
|
22
|
+
"type": "boolean",
|
|
23
|
+
"description": "Use a Vercel access token and Team ID instead of AI Gateway key credits."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"key": "VERCEL_ACCESS_TOKEN",
|
|
27
|
+
"title": "Vercel access token",
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Separate account token; the AI Gateway API key is not used for budgets."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"key": "VERCEL_TEAM_ID",
|
|
33
|
+
"title": "Team ID",
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Team ID or slug whose personal budget belongs to the token user."
|
|
36
|
+
}
|
|
15
37
|
],
|
|
16
38
|
"upstream": {
|
|
17
39
|
"repository": "https://github.com/steipete/CodexBar",
|
|
18
|
-
"revision": "
|
|
40
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
19
41
|
"path": "Sources/CodexBarCore/Resources/Plugins/vercel.js",
|
|
20
42
|
"sha256": "27b39f42709e93a2cfbf850171f198069553c99967f9b7ba6d16614862cb084a",
|
|
21
43
|
"license": "MIT"
|
|
22
|
-
}
|
|
44
|
+
},
|
|
45
|
+
"upstreamSources": [
|
|
46
|
+
{
|
|
47
|
+
"repository": "https://github.com/steipete/CodexBar",
|
|
48
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
49
|
+
"path": "Sources/CodexBarCore/Resources/Plugins/vercel-budget.js",
|
|
50
|
+
"sha256": "a65d3affe808517590edc33e4d15b813ba1fa870b4435ef18cb7c7195ab376ad",
|
|
51
|
+
"license": "MIT"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
23
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" fill-rule="evenodd"><path d="M18.777 1.647c.28-.02.536.114.972.51 1.018.926 2.437 2.828 3.318 4.452l.34.631.482.24.11.06v3.638a5.206 5.206 0 00-5.32-1.23c-.491.166-1.021.471-2.08 1.082l-6.09 3.516c-1.057.61-1.586.916-1.975 1.259a5.208 5.208 0 00-1.493 5.572c.165.49.471 1.02 1.082 2.08l.315.543h-3.26c-.685 0-1.34-.135-1.939-.377-.169-.956-.009-1.789.469-2.335.158-.18.164-.189.13-.493a11.846 11.846 0 01-.057-1.711l.02-.444-.667-1.18C2.1 15.622 1.445 14.078 1.192 12.9c-.133-.647-.125-.934.04-1.146.1-.128.427-.261.822-.334.994-.175 3.162-.017 5.575.41l.25.043.551-.487c.915-.81 1.522-1.264 2.641-1.962 1.167-.73 2.484-1.331 3.967-1.807l.476-.152.261-.688c.937-2.471 1.896-4.293 2.58-4.9.235-.21.25-.22.422-.23z"/><path d="M12.139 18.2a1.203 1.203 0 011.642.44l1.296 2.243a1.204 1.204 0 01-2.083 1.203l-1.296-2.243a1.203 1.203 0 01.44-1.644zM18.629 14.452a1.203 1.203 0 011.642.44l1.295 2.244a1.203 1.203 0 11-2.083 1.203l-1.295-2.243a1.203 1.203 0 01.44-1.644z"/></svg>
|