@hashimkarim/usagestat-linux-x64-gnu 2.1.0 → 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 +197 -89
- package/package.json +19 -1
- package/plugins/abliteration/plugin.json +1 -1
- package/plugins/aerostack/plugin.json +1 -1
- package/plugins/aixy/plugin.json +1 -1
- package/plugins/atlascloud/plugin.json +1 -1
- package/plugins/bifrost/plugin.json +1 -1
- package/plugins/chatbox/plugin.json +1 -1
- package/plugins/claudeplatform/plugin.json +1 -1
- package/plugins/codex/plugin.js +11 -6
- package/plugins/codex/plugin.json +10 -1
- package/plugins/cosmic/plugin.json +1 -1
- package/plugins/cursor/plugin.js +18 -6
- package/plugins/devpass/plugin.json +1 -1
- package/plugins/exa/plugin.json +1 -1
- package/plugins/fal/plugin.json +1 -1
- package/plugins/flora/plugin.json +1 -1
- package/plugins/futureagi/plugin.json +1 -1
- package/plugins/gemini-apps/plugin.json +1 -1
- package/plugins/gitkraken/plugin.json +1 -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/plugin.js +80 -23
- package/plugins/helmcode/plugin.json +2 -2
- package/plugins/huggingface/plugin.json +1 -1
- package/plugins/hyper/plugin.json +1 -1
- 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 +16 -1
- package/plugins/kimi/plugin.json +46 -6
- package/plugins/langdock/plugin.json +1 -1
- package/plugins/linkup/plugin.json +1 -1
- package/plugins/lithosai/plugin.json +1 -1
- package/plugins/llmman/plugin.json +1 -1
- package/plugins/munito/plugin.json +1 -1
- package/plugins/muse/plugin.json +1 -1
- package/plugins/museai/plugin.js +5 -3
- package/plugins/museai/plugin.json +2 -2
- package/plugins/nous/plugin.json +1 -1
- package/plugins/opencode-go/plugin.js +4 -3
- 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/plugin.json +1 -1
- package/plugins/perplexity/LICENSE +22 -0
- package/plugins/perplexity/plugin.js +122 -0
- package/plugins/perplexity/plugin.json +36 -7
- package/plugins/qwencloud/plugin.json +1 -1
- package/plugins/raycast/plugin.json +1 -1
- package/plugins/replicate/plugin.json +1 -1
- package/plugins/sailresearch/plugin.json +1 -1
- 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/plugin.json +1 -1
- package/plugins/soniox/plugin.json +1 -1
- package/plugins/synthetic/plugin.js +12 -0
- package/plugins/synthetic/plugin.json +51 -9
- package/plugins/tavily/plugin.json +1 -1
- package/plugins/tinyapi/plugin.json +1 -1
- package/plugins/traecn/plugin.js +10 -11
- package/plugins/traecn/plugin.json +2 -2
- 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/plugin.js +63 -112
- package/plugins/typesafe/plugin.json +2 -2
- package/plugins/v0/plugin.json +1 -1
- package/plugins/vercel/plugin.json +2 -2
- package/plugins/workbuddy/plugin.js +103 -17
- package/plugins/workbuddy/plugin.json +14 -2
- package/plugins/xapi/plugin.json +1 -1
- package/plugins/xkiro/plugin.json +1 -1
- package/usagestat +0 -0
- package/usagestatd +0 -0
|
@@ -24,19 +24,17 @@ function _optionalChain(ops) {
|
|
|
24
24
|
return value;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const
|
|
28
|
-
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.";
|
|
29
30
|
// Cloudflare blocks the Linux default UA (it contains "curl/"), so send a browser-style one.
|
|
30
31
|
const userAgent =
|
|
31
32
|
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36";
|
|
32
|
-
const actionCacheKey = "typesafe.billing.next-action";
|
|
33
|
-
const actionCacheTTL = 12 * 60 * 60;
|
|
34
|
-
const maximumChunkCount = 60;
|
|
35
33
|
|
|
36
34
|
defineProvider({
|
|
37
35
|
id: "typesafe",
|
|
38
36
|
name: "TypeSafe",
|
|
39
|
-
endpoints: [
|
|
37
|
+
endpoints: ["https://console.typesafe.ai"],
|
|
40
38
|
settings: [],
|
|
41
39
|
capabilities: ["browser-cookies", "http-status"],
|
|
42
40
|
cookieDomains: ["console.typesafe.ai", "typesafe.ai"],
|
|
@@ -51,9 +49,27 @@ defineProvider({
|
|
|
51
49
|
const value = raw && /^\d+(?:\.\d+)?$/.test(raw.trim()) ? Number(raw) : 1;
|
|
52
50
|
return Number.isFinite(value) ? Math.min(10, value) : 1;
|
|
53
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.
|
|
54
65
|
const validate = (response) => {
|
|
55
|
-
if (response.status ===
|
|
56
|
-
|
|
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
|
+
}
|
|
57
73
|
if (response.status === 429)
|
|
58
74
|
throw ctx.fail.rateLimited("TypeSafe rate limit reached.", { retryAfterSeconds: retryAfter(response) });
|
|
59
75
|
if (response.status === 408 || response.status >= 500)
|
|
@@ -63,105 +79,40 @@ defineProvider({
|
|
|
63
79
|
if (response.status < 200 || response.status >= 300)
|
|
64
80
|
throw ctx.fail.apiFailure(`TypeSafe returned HTTP ${response.status}.`);
|
|
65
81
|
};
|
|
66
|
-
// Login pages can also arrive directly with HTTP 200, without an HTTP redirect.
|
|
67
|
-
const validateBody = (response) => {
|
|
68
|
-
validate(response);
|
|
69
|
-
if (/\\?"\(auth\)\\?",\{\\?"children\\?":\[\\?"login\\?"/.test(response.bodyText)) expired();
|
|
70
|
-
};
|
|
71
|
-
const discoverActionID = async (cookie) => {
|
|
72
|
-
const page = await ctx.http.get(billingURL, {
|
|
73
|
-
headers: { Cookie: cookie, Accept: "text/html", "User-Agent": userAgent },
|
|
74
|
-
timeoutSeconds: 6,
|
|
75
|
-
});
|
|
76
|
-
validateBody(page);
|
|
77
|
-
const scriptPattern = /<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/gi;
|
|
78
|
-
const chunkURLs = [];
|
|
79
|
-
let match;
|
|
80
|
-
while ((match = scriptPattern.exec(page.bodyText)) && chunkURLs.length < maximumChunkCount) {
|
|
81
|
-
const source = match[1];
|
|
82
|
-
const normalized = source.startsWith("/") ? `${billingOrigin}${source}` : source;
|
|
83
|
-
if (!normalized.startsWith(`${billingOrigin}/`) || !/\.js(?:$|\?)/i.test(normalized)) continue;
|
|
84
|
-
if (!chunkURLs.includes(normalized)) chunkURLs.push(normalized);
|
|
85
|
-
}
|
|
86
|
-
let discoveryFailure;
|
|
87
|
-
for (const url of chunkURLs) {
|
|
88
|
-
try {
|
|
89
|
-
const chunk = await ctx.http.get(url, { headers: { "User-Agent": userAgent }, timeoutSeconds: 2 });
|
|
90
|
-
if (chunk.status === 429 || chunk.status === 408 || chunk.status >= 500) validate(chunk);
|
|
91
|
-
if (chunk.status < 200 || chunk.status >= 300) continue;
|
|
92
|
-
const action = _optionalChain([
|
|
93
|
-
/"([0-9a-f]{40,})"[^)]{0,150}"getBillingOverviewResult"/i,
|
|
94
|
-
"access",
|
|
95
|
-
(_) => _.exec,
|
|
96
|
-
"call",
|
|
97
|
-
(_2) => _2(chunk.bodyText),
|
|
98
|
-
"optionalAccess",
|
|
99
|
-
(_3) => _3[1],
|
|
100
|
-
]);
|
|
101
|
-
if (action) return action;
|
|
102
|
-
} catch (error) {
|
|
103
|
-
discoveryFailure ??= error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (discoveryFailure !== undefined) throw discoveryFailure;
|
|
107
|
-
throw ctx.fail.parseFailure("TypeSafe billing page format changed: action id not found.");
|
|
108
|
-
};
|
|
109
82
|
const cookie = await ctx.browser.cookieHeader("typesafe.ai");
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (response.status === 404 && response.headers["x-nextjs-action-not-found"] === "1") {
|
|
130
|
-
ctx.cache.set(actionCacheKey, "", 1);
|
|
131
|
-
actionID = await discoverActionID(cookie);
|
|
132
|
-
ctx.cache.set(actionCacheKey, actionID, actionCacheTTL);
|
|
133
|
-
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);
|
|
134
102
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
let candidate;
|
|
142
|
-
try {
|
|
143
|
-
candidate = JSON.parse(line.slice(separator + 1));
|
|
144
|
-
} catch (error) {
|
|
145
|
-
void error;
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
const object = record(candidate);
|
|
149
|
-
if (object && Object.prototype.hasOwnProperty.call(object, "ok")) {
|
|
150
|
-
result = object;
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
103
|
+
let payload;
|
|
104
|
+
try {
|
|
105
|
+
payload = JSON.parse(body);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
void error;
|
|
108
|
+
throw ctx.fail.parseFailure(overviewFailure);
|
|
153
109
|
}
|
|
154
|
-
|
|
155
|
-
if (
|
|
156
|
-
const billing = record(
|
|
157
|
-
_optionalChain([record, "call", (_4) => _4(result.data), "optionalAccess", (_5) => _5.billing]),
|
|
158
|
-
);
|
|
159
|
-
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);
|
|
160
112
|
const number = (value) => (typeof value === "number" && Number.isFinite(value) ? value : undefined);
|
|
161
113
|
const spent = number(billing.spent);
|
|
162
114
|
const balance = number(billing.balance);
|
|
163
|
-
if (spent === undefined || balance === undefined)
|
|
164
|
-
throw ctx.fail.parseFailure("TypeSafe billing response format changed: missing billing numbers.");
|
|
115
|
+
if (spent === undefined || balance === undefined) throw ctx.fail.parseFailure(overviewFailure);
|
|
165
116
|
const cycleLabel =
|
|
166
117
|
typeof billing.cycleLabel === "string" && billing.cycleLabel.trim() ? billing.cycleLabel.trim() : undefined;
|
|
167
118
|
const plan = typeof billing.plan === "string" && billing.plan.trim() ? billing.plan.trim() : undefined;
|
|
@@ -171,21 +122,21 @@ defineProvider({
|
|
|
171
122
|
: _optionalChain([
|
|
172
123
|
plan,
|
|
173
124
|
"optionalAccess",
|
|
174
|
-
(
|
|
125
|
+
(_3) => _3.split,
|
|
175
126
|
"call",
|
|
176
|
-
(
|
|
127
|
+
(_4) => _4(/[_-]+/),
|
|
177
128
|
"access",
|
|
178
|
-
(
|
|
129
|
+
(_5) => _5.filter,
|
|
179
130
|
"call",
|
|
180
|
-
(
|
|
131
|
+
(_6) => _6(Boolean),
|
|
181
132
|
"access",
|
|
182
|
-
(
|
|
133
|
+
(_7) => _7.map,
|
|
183
134
|
"call",
|
|
184
|
-
(
|
|
135
|
+
(_8) => _8((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()),
|
|
185
136
|
"access",
|
|
186
|
-
(
|
|
137
|
+
(_9) => _9.join,
|
|
187
138
|
"call",
|
|
188
|
-
(
|
|
139
|
+
(_10) => _10(" "),
|
|
189
140
|
]);
|
|
190
141
|
const rows = [{ label: cycleLabel ? `Spent (${cycleLabel})` : "Spent", value: ctx.format.usd(spent) }];
|
|
191
142
|
if (planLabel) rows.push({ label: "Plan", value: planLabel });
|
|
@@ -194,10 +145,10 @@ defineProvider({
|
|
|
194
145
|
let creditCount = 0;
|
|
195
146
|
for (const item of Array.isArray(billing.credits) ? billing.credits : []) {
|
|
196
147
|
const credit = record(item);
|
|
197
|
-
const amount = number(_optionalChain([credit, "optionalAccess", (
|
|
198
|
-
const remaining = number(_optionalChain([credit, "optionalAccess", (
|
|
148
|
+
const amount = number(_optionalChain([credit, "optionalAccess", (_11) => _11.amount]));
|
|
149
|
+
const remaining = number(_optionalChain([credit, "optionalAccess", (_12) => _12.remaining]));
|
|
199
150
|
const expiresAt =
|
|
200
|
-
typeof _optionalChain([credit, "optionalAccess", (
|
|
151
|
+
typeof _optionalChain([credit, "optionalAccess", (_13) => _13.expiresAt]) === "string"
|
|
201
152
|
? credit.expiresAt
|
|
202
153
|
: undefined;
|
|
203
154
|
if (amount === undefined || remaining === undefined || remaining <= 0 || !expiresAt) continue;
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"setupFields": [],
|
|
18
18
|
"upstream": {
|
|
19
19
|
"repository": "https://github.com/steipete/CodexBar",
|
|
20
|
-
"revision": "
|
|
20
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
21
21
|
"path": "Sources/CodexBarCore/Resources/Plugins/typesafe.js",
|
|
22
|
-
"sha256": "
|
|
22
|
+
"sha256": "da91db172678133393d38836fcb4028342edade8aa7994d2bc5e95290498192f",
|
|
23
23
|
"license": "MIT"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/plugins/v0/plugin.json
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"upstream": {
|
|
26
26
|
"repository": "https://github.com/steipete/CodexBar",
|
|
27
|
-
"revision": "
|
|
27
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
28
28
|
"path": "Sources/CodexBarCore/Resources/Plugins/v0.js",
|
|
29
29
|
"sha256": "5f54a9329dca35d59d76e427c34d4d9db8d3400d0b38c5fcee8899928a900019",
|
|
30
30
|
"license": "MIT"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"upstream": {
|
|
39
39
|
"repository": "https://github.com/steipete/CodexBar",
|
|
40
|
-
"revision": "
|
|
40
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
41
41
|
"path": "Sources/CodexBarCore/Resources/Plugins/vercel.js",
|
|
42
42
|
"sha256": "27b39f42709e93a2cfbf850171f198069553c99967f9b7ba6d16614862cb084a",
|
|
43
43
|
"license": "MIT"
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"upstreamSources": [
|
|
46
46
|
{
|
|
47
47
|
"repository": "https://github.com/steipete/CodexBar",
|
|
48
|
-
"revision": "
|
|
48
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
49
49
|
"path": "Sources/CodexBarCore/Resources/Plugins/vercel-budget.js",
|
|
50
50
|
"sha256": "a65d3affe808517590edc33e4d15b813ba1fa870b4435ef18cb7c7195ab376ad",
|
|
51
51
|
"license": "MIT"
|
|
@@ -17,6 +17,8 @@ defineProvider({
|
|
|
17
17
|
{ key: "webTimeoutSeconds", title: "Web timeout", type: "plain" },
|
|
18
18
|
{ key: "resetDeadlineMillis", title: "Reset lookup deadline", type: "plain" },
|
|
19
19
|
{ key: "chromeMajorVersion", title: "Chrome major version", type: "plain" },
|
|
20
|
+
{ key: "manualUserAgent", title: "User-Agent", type: "plain" },
|
|
21
|
+
{ key: "manualUserAgentError", title: "Invalid User-Agent message", type: "plain" },
|
|
20
22
|
],
|
|
21
23
|
capabilities: ["browser-cookies", "http-status"],
|
|
22
24
|
cookieDomains: ["workbuddy.cn", "www.workbuddy.cn"],
|
|
@@ -30,6 +32,16 @@ defineProvider({
|
|
|
30
32
|
const domain = "www.workbuddy.cn";
|
|
31
33
|
const policy = ctx.browser.availability(domain);
|
|
32
34
|
if (policy === "off") throw ctx.fail.missingCredential("WorkBuddy cookies are disabled.");
|
|
35
|
+
const manualUserAgent = policy === "manual" ? _nullishCoalesce(ctx.settings.get("manualUserAgent"), () => "") : "";
|
|
36
|
+
if (/[^\x20-\x7e]/.test(manualUserAgent)) {
|
|
37
|
+
throw ctx.fail.missingCredential(
|
|
38
|
+
_nullishCoalesce(
|
|
39
|
+
ctx.settings.get("manualUserAgentError"),
|
|
40
|
+
() => "Paste the WorkBuddy User-Agent as a single request header value.",
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
const override = manualUserAgent.trim();
|
|
33
45
|
const timeoutRaw = Number(_nullishCoalesce(ctx.settings.get("webTimeoutSeconds"), () => "15"));
|
|
34
46
|
const timeoutSeconds = Number.isFinite(timeoutRaw) ? Math.min(30, Math.max(1, Math.round(timeoutRaw))) : 15;
|
|
35
47
|
// WorkBuddy binds the session to the browser User-Agent, and Chrome's reduced UA varies only by major version.
|
|
@@ -37,8 +49,9 @@ defineProvider({
|
|
|
37
49
|
const major = Number(_nullishCoalesce(ctx.settings.get("chromeMajorVersion"), () => ""));
|
|
38
50
|
const userAgent = (version) =>
|
|
39
51
|
`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version}.0.0.0 Safari/537.36`;
|
|
40
|
-
const
|
|
52
|
+
const chromeUserAgents =
|
|
41
53
|
Number.isSafeInteger(major) && major > 1 ? [userAgent(major), userAgent(major - 1)] : [undefined];
|
|
54
|
+
const userAgents = override ? [override] : chromeUserAgents;
|
|
42
55
|
|
|
43
56
|
const fail = (field) => {
|
|
44
57
|
throw ctx.fail.parseFailure(`Invalid WorkBuddy billing response: ${field}`);
|
|
@@ -105,6 +118,16 @@ defineProvider({
|
|
|
105
118
|
// The dashboard refreshes one second after the inclusive end time.
|
|
106
119
|
return new Date(end.getTime() + 1000);
|
|
107
120
|
};
|
|
121
|
+
// Spent grants stay in the listing until their cycle ends. The dashboard reports the lapse of the
|
|
122
|
+
// grants that still carry credits, so a grant with nothing left must not bound the reward window.
|
|
123
|
+
const creditsLeft = (value) => {
|
|
124
|
+
try {
|
|
125
|
+
return amount(value, "CycleCapacityRemain");
|
|
126
|
+
} catch (error) {
|
|
127
|
+
void error;
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
108
131
|
// Package codes the WorkBuddy client sends for its paid and free listings (WorkBuddy 5.6.2).
|
|
109
132
|
const paidPackageCodes = [
|
|
110
133
|
"TCACA_code_002_AkiJS3ZHF5",
|
|
@@ -130,16 +153,21 @@ defineProvider({
|
|
|
130
153
|
"TCACA_code_029_6wCGEWquYy",
|
|
131
154
|
"TCACA_code_030_BjSt89qTvr",
|
|
132
155
|
];
|
|
133
|
-
//
|
|
134
|
-
|
|
156
|
+
// Platform reward grants are aggregated under one package code in the summary. Only the free listing
|
|
157
|
+
// carries each grant's own cycle, so the reward expiry has to be read from there.
|
|
158
|
+
const benefitPackageCode = "TCACA_code_007_nzdH5h4Nl0";
|
|
159
|
+
// Reset and reward expiry are optional: a failed or unexpected package listing must not discard the balance.
|
|
160
|
+
const listings = async (session, agent, wantsRewards) => {
|
|
135
161
|
const now = ctx.date.now().getTime();
|
|
136
162
|
// Share five seconds across listings, retaining the host's margin for publishing the balance.
|
|
137
163
|
const hostDeadline = Number(ctx.settings.get("resetDeadlineMillis"));
|
|
138
164
|
const deadline = Math.min(Date.now() + 5000, hostDeadline > 0 ? hostDeadline : Infinity);
|
|
139
|
-
let
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
165
|
+
let paidReset;
|
|
166
|
+
let freeReset;
|
|
167
|
+
let rewardExpiry;
|
|
168
|
+
for (const [path, codes, isFree] of [
|
|
169
|
+
["/billing/meter/get-user-resource-paid-packages", paidPackageCodes, false],
|
|
170
|
+
["/billing/meter/get-user-resource-free-packages", freePackageCodes, true],
|
|
143
171
|
]) {
|
|
144
172
|
const budget = (deadline - Date.now()) / 1000;
|
|
145
173
|
if (budget < 1) break;
|
|
@@ -152,20 +180,41 @@ defineProvider({
|
|
|
152
180
|
{ PageNumber: 1, PageSize: 100, PackageCodes: codes, Status: [0, 3] },
|
|
153
181
|
Math.min(timeoutSeconds, budget),
|
|
154
182
|
);
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
183
|
+
if (Array.isArray(page.Accounts)) {
|
|
184
|
+
for (const raw of page.Accounts) {
|
|
185
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
186
|
+
const account = raw;
|
|
187
|
+
if (account.CapacityUnit !== "credits") continue;
|
|
188
|
+
const end = cycleEnd(account.CycleEndTime);
|
|
189
|
+
if (!end || end.getTime() <= now) continue;
|
|
190
|
+
if (isFree) {
|
|
191
|
+
// Reward grants expire piecewise; the earliest grant that still holds credits bounds the window.
|
|
192
|
+
const left = creditsLeft(account.CycleCapacityRemain);
|
|
193
|
+
if (
|
|
194
|
+
account.PackageCode === benefitPackageCode &&
|
|
195
|
+
left !== undefined &&
|
|
196
|
+
left > 0 &&
|
|
197
|
+
(!rewardExpiry || end.getTime() < rewardExpiry.getTime())
|
|
198
|
+
) {
|
|
199
|
+
rewardExpiry = end;
|
|
200
|
+
}
|
|
201
|
+
// Reward grants expire; they never supply the allowance's fallback reset.
|
|
202
|
+
if (account.PackageCode !== benefitPackageCode && (!freeReset || end.getTime() < freeReset.getTime())) {
|
|
203
|
+
freeReset = end;
|
|
204
|
+
}
|
|
205
|
+
} else if (!paidReset || end.getTime() < paidReset.getTime()) {
|
|
206
|
+
paidReset = end;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
162
209
|
}
|
|
163
210
|
} catch (error) {
|
|
164
211
|
if (error.transportClass === "cancelled") throw error;
|
|
165
212
|
}
|
|
166
|
-
// Paid cycles refill the allowance; free grants may expire earlier.
|
|
167
|
-
|
|
213
|
+
// Paid cycles refill the allowance; free grants may expire earlier. The free listing is only
|
|
214
|
+
// worth a request when it answers the reset or bounds the reward window.
|
|
215
|
+
if (!isFree && paidReset && !wantsRewards) break;
|
|
168
216
|
}
|
|
217
|
+
return { reset: _nullishCoalesce(paidReset, () => freeReset), rewardExpiry };
|
|
169
218
|
};
|
|
170
219
|
|
|
171
220
|
let rejected = false;
|
|
@@ -201,21 +250,58 @@ defineProvider({
|
|
|
201
250
|
remaining += amount(item.CycleRemainCapacity, "CycleRemainCapacity");
|
|
202
251
|
if (item.CycleFrozenCapacity !== undefined) frozen += amount(item.CycleFrozenCapacity, "CycleFrozenCapacity");
|
|
203
252
|
}
|
|
253
|
+
// Credit groups split the same allowance by origin. Rewards are optional, so an unexpected group
|
|
254
|
+
// shape must not discard the balance the dashboard shows.
|
|
255
|
+
let rewardTotal = 0;
|
|
256
|
+
let rewardRemaining = 0;
|
|
257
|
+
for (const raw of Array.isArray(summary.CreditGroups) ? summary.CreditGroups : []) {
|
|
258
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
259
|
+
const group = raw;
|
|
260
|
+
if (group.Kind !== "benefit" || group.CapacityUnit !== "credits") continue;
|
|
261
|
+
try {
|
|
262
|
+
const groupTotal = amount(group.CycleTotalCapacity, "CycleTotalCapacity");
|
|
263
|
+
const groupRemaining = amount(group.CycleRemainCapacity, "CycleRemainCapacity");
|
|
264
|
+
rewardTotal += groupTotal;
|
|
265
|
+
rewardRemaining += groupRemaining;
|
|
266
|
+
} catch (error) {
|
|
267
|
+
void error;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
204
270
|
const plan = text(summary.SubscriptionPackageName);
|
|
205
271
|
// Frozen credits are listed as reported; the meter uses the remaining amount the dashboard shows.
|
|
206
272
|
const rows = frozen > 0 ? [{ label: "Reserved", value: format(frozen) }] : [];
|
|
207
273
|
if (total <= 0) {
|
|
208
274
|
rows.unshift({ label: "Left", value: format(remaining) }, { label: "Total", value: format(total) });
|
|
209
275
|
}
|
|
276
|
+
const wantsRewards = rewardTotal > 0;
|
|
277
|
+
const cycle = await listings(session, agent, wantsRewards);
|
|
278
|
+
// The window carries the balance; it cannot carry a countdown, because reward credits lapse
|
|
279
|
+
// grant by grant instead of resetting, so the expiry stays a detail row.
|
|
280
|
+
if (wantsRewards && cycle.rewardExpiry) {
|
|
281
|
+
rows.push({ label: "Reward expiry", value: ctx.format.monthDay(cycle.rewardExpiry.getTime()) });
|
|
282
|
+
}
|
|
283
|
+
const extraWindows = wantsRewards
|
|
284
|
+
? [
|
|
285
|
+
{
|
|
286
|
+
id: "rewards",
|
|
287
|
+
title: "Rewards",
|
|
288
|
+
window: {
|
|
289
|
+
usedPercent: ctx.pct(Math.max(0, rewardTotal - rewardRemaining), rewardTotal),
|
|
290
|
+
resetDescription: `${format(rewardRemaining)} / ${format(rewardTotal)} credits left`,
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
]
|
|
294
|
+
: [];
|
|
210
295
|
return {
|
|
211
296
|
primary:
|
|
212
297
|
total > 0
|
|
213
298
|
? {
|
|
214
299
|
usedPercent: ctx.pct(Math.max(0, total - remaining), total),
|
|
215
|
-
resetsAt:
|
|
300
|
+
resetsAt: cycle.reset,
|
|
216
301
|
resetDescription: `${format(remaining)} / ${format(total)} credits left`,
|
|
217
302
|
}
|
|
218
303
|
: undefined,
|
|
304
|
+
extraWindows: extraWindows.length ? extraWindows : undefined,
|
|
219
305
|
details: rows.length ? [{ title: "Credits", rows }] : undefined,
|
|
220
306
|
identity: plan ? { loginMethod: plan } : undefined,
|
|
221
307
|
dataConfidence: "exact",
|
|
@@ -34,6 +34,18 @@
|
|
|
34
34
|
"type": "string",
|
|
35
35
|
"description": "Provider-specific account or source selection."
|
|
36
36
|
},
|
|
37
|
+
{
|
|
38
|
+
"key": "manualUserAgent",
|
|
39
|
+
"title": "User-Agent",
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "Provider-specific account or source selection."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"key": "manualUserAgentError",
|
|
45
|
+
"title": "Invalid User-Agent message",
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Provider-specific account or source selection."
|
|
48
|
+
},
|
|
37
49
|
{
|
|
38
50
|
"key": "browserUserAgent",
|
|
39
51
|
"title": "Browser User-Agent",
|
|
@@ -43,9 +55,9 @@
|
|
|
43
55
|
],
|
|
44
56
|
"upstream": {
|
|
45
57
|
"repository": "https://github.com/steipete/CodexBar",
|
|
46
|
-
"revision": "
|
|
58
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
47
59
|
"path": "Sources/CodexBarCore/Resources/Plugins/workbuddy.js",
|
|
48
|
-
"sha256": "
|
|
60
|
+
"sha256": "4a440637dc7a8ec77592edf5d0fe50181eff9919be08fefa7007bc50476a4301",
|
|
49
61
|
"license": "MIT"
|
|
50
62
|
}
|
|
51
63
|
}
|
package/plugins/xapi/plugin.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"setupFields": [],
|
|
18
18
|
"upstream": {
|
|
19
19
|
"repository": "https://github.com/steipete/CodexBar",
|
|
20
|
-
"revision": "
|
|
20
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
21
21
|
"path": "Sources/CodexBarCore/Resources/Plugins/xapi.js",
|
|
22
22
|
"sha256": "c8aff7628f2a67a8726486d847747dd8121123da6526b2793b4b7b36ec37930f",
|
|
23
23
|
"license": "MIT"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"setupFields": [],
|
|
17
17
|
"upstream": {
|
|
18
18
|
"repository": "https://github.com/steipete/CodexBar",
|
|
19
|
-
"revision": "
|
|
19
|
+
"revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
|
|
20
20
|
"path": "Sources/CodexBarCore/Resources/Plugins/xkiro.js",
|
|
21
21
|
"sha256": "03bfcb8e518577ee18dd335e4e1e1f7d629d3e5abdf2216b187b1cba8b683853",
|
|
22
22
|
"license": "MIT"
|
package/usagestat
CHANGED
|
Binary file
|
package/usagestatd
CHANGED
|
Binary file
|