@hashimkarim/usagestat-linux-x64-gnu 2.0.0-alpha.1 → 2.0.0-alpha.3
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 +0 -0
- package/native-manifest.json +365 -77
- package/package.json +49 -1
- package/plugins/UPSTREAM-LICENSES.md +109 -0
- package/plugins/abacus-ai/icon.svg +0 -0
- package/plugins/abacus-ai/plugin.js +0 -0
- package/plugins/abacus-ai/plugin.json +0 -0
- package/plugins/aiand/icon.svg +6 -0
- package/plugins/aiand/plugin.js +108 -0
- package/plugins/aiand/plugin.json +22 -0
- package/plugins/alibaba/icon-color.svg +0 -0
- package/plugins/alibaba/icon.svg +0 -0
- package/plugins/alibaba/plugin.js +0 -0
- package/plugins/alibaba/plugin.json +0 -0
- package/plugins/alibaba-token-plan/icon-color.svg +0 -0
- package/plugins/alibaba-token-plan/icon.svg +0 -0
- package/plugins/alibaba-token-plan/plugin.js +0 -0
- package/plugins/alibaba-token-plan/plugin.json +0 -0
- package/plugins/amp/icon-color.svg +0 -0
- package/plugins/amp/icon-conflict.svg +0 -0
- package/plugins/amp/icon.svg +0 -0
- package/plugins/amp/plugin.js +0 -0
- package/plugins/amp/plugin.json +0 -0
- package/plugins/antigravity/icon-color.svg +0 -0
- package/plugins/antigravity/icon-conflict.svg +0 -0
- package/plugins/antigravity/icon.svg +0 -0
- package/plugins/antigravity/plugin.js +0 -0
- package/plugins/antigravity/plugin.json +0 -0
- package/plugins/antigravity-cli/icon.svg +0 -0
- package/plugins/antigravity-cli/plugin.js +0 -0
- package/plugins/antigravity-cli/plugin.json +0 -0
- package/plugins/antigravity-ide/icon.svg +0 -0
- package/plugins/antigravity-ide/plugin.js +0 -0
- package/plugins/antigravity-ide/plugin.json +0 -0
- package/plugins/augment/icon.svg +0 -0
- package/plugins/augment/plugin.js +0 -0
- package/plugins/augment/plugin.json +0 -0
- package/plugins/aws-bedrock/icon-color.svg +0 -0
- package/plugins/aws-bedrock/icon.svg +0 -0
- package/plugins/aws-bedrock/plugin.js +0 -0
- package/plugins/aws-bedrock/plugin.json +0 -0
- package/plugins/azure-openai/icon-color.svg +0 -0
- package/plugins/azure-openai/icon.svg +0 -0
- package/plugins/azure-openai/plugin.js +0 -0
- package/plugins/azure-openai/plugin.json +0 -0
- package/plugins/chutes/icon.svg +0 -0
- package/plugins/chutes/plugin.js +0 -0
- package/plugins/chutes/plugin.json +0 -0
- package/plugins/claude/icon-color.svg +0 -0
- package/plugins/claude/icon.svg +0 -0
- package/plugins/claude/plugin.js +70 -23
- package/plugins/claude/plugin.json +0 -0
- package/plugins/clawrouter/icon.svg +7 -0
- package/plugins/clawrouter/plugin.js +45 -0
- package/plugins/clawrouter/plugin.json +22 -0
- package/plugins/clinepass/icon.svg +4 -0
- package/plugins/clinepass/plugin.js +71 -0
- package/plugins/clinepass/plugin.json +23 -0
- package/plugins/codebuddy/plugin.js +331 -0
- package/plugins/codebuddy/plugin.json +30 -0
- package/plugins/codebuff/icon.svg +0 -0
- package/plugins/codebuff/plugin.js +0 -0
- package/plugins/codebuff/plugin.json +0 -0
- package/plugins/codex/icon-color.svg +0 -0
- package/plugins/codex/icon.svg +0 -0
- package/plugins/codex/plugin.js +7 -2
- package/plugins/codex/plugin.json +0 -0
- package/plugins/command-code/icon.svg +0 -0
- package/plugins/command-code/plugin.js +7 -0
- package/plugins/command-code/plugin.json +0 -0
- package/plugins/copilot/icon-color.svg +0 -0
- package/plugins/copilot/icon.svg +0 -0
- package/plugins/copilot/plugin.js +61 -9
- package/plugins/copilot/plugin.json +4 -1
- package/plugins/crof/icon.svg +0 -0
- package/plugins/crof/plugin.js +0 -0
- package/plugins/crof/plugin.json +0 -0
- package/plugins/crofai/icon.svg +0 -0
- package/plugins/crofai/plugin.js +0 -0
- package/plugins/crofai/plugin.json +0 -0
- package/plugins/cursor/icon-conflict.svg +0 -0
- package/plugins/cursor/icon.svg +0 -0
- package/plugins/cursor/plugin.js +46 -9
- package/plugins/cursor/plugin.json +3 -2
- package/plugins/cursor-nightly/icon.png +0 -0
- package/plugins/cursor-nightly/plugin.js +45 -8
- package/plugins/cursor-nightly/plugin.json +3 -2
- package/plugins/deepgram/icon.svg +0 -0
- package/plugins/deepgram/plugin.js +0 -0
- package/plugins/deepgram/plugin.json +0 -0
- package/plugins/deepinfra/icon.svg +4 -0
- package/plugins/deepinfra/plugin.js +97 -0
- package/plugins/deepinfra/plugin.json +28 -0
- package/plugins/deepseek/icon-color.svg +0 -0
- package/plugins/deepseek/icon.svg +0 -0
- package/plugins/deepseek/plugin.js +0 -0
- package/plugins/deepseek/plugin.json +0 -0
- package/plugins/devin/icon.svg +0 -0
- package/plugins/devin/plugin.js +111 -0
- package/plugins/devin/plugin.json +2 -1
- package/plugins/doubao/icon-color.svg +0 -0
- package/plugins/doubao/icon.svg +0 -0
- package/plugins/doubao/plugin.js +0 -0
- package/plugins/doubao/plugin.json +0 -0
- package/plugins/droid/icon.svg +0 -0
- package/plugins/droid/plugin.json +2 -1
- package/plugins/elevenlabs/icon.svg +0 -0
- package/plugins/elevenlabs/plugin.js +17 -2
- package/plugins/elevenlabs/plugin.json +0 -0
- package/plugins/factory/icon-conflict.svg +0 -0
- package/plugins/factory/icon.svg +0 -0
- package/plugins/factory/plugin.js +38 -14
- package/plugins/factory/plugin.json +2 -1
- package/plugins/fireworks-ai/icon.svg +0 -0
- package/plugins/fireworks-ai/plugin.js +0 -0
- package/plugins/fireworks-ai/plugin.json +0 -0
- package/plugins/gemini/icon-color.svg +0 -0
- package/plugins/gemini/icon.svg +0 -0
- package/plugins/gemini/plugin.js +0 -0
- package/plugins/gemini/plugin.json +0 -0
- package/plugins/grok/icon.svg +0 -0
- package/plugins/grok/plugin.js +30 -5
- package/plugins/grok/plugin.json +3 -1
- package/plugins/groqcloud/icon.svg +0 -0
- package/plugins/groqcloud/plugin.js +0 -0
- package/plugins/groqcloud/plugin.json +0 -0
- package/plugins/ibmbob/icon.svg +23 -0
- package/plugins/ibmbob/plugin.js +61 -0
- package/plugins/ibmbob/plugin.json +22 -0
- package/plugins/jetbrains-ai-assistant/icon-color.svg +0 -0
- package/plugins/jetbrains-ai-assistant/icon-conflict.svg +0 -0
- package/plugins/jetbrains-ai-assistant/icon.svg +0 -0
- package/plugins/jetbrains-ai-assistant/plugin.js +0 -0
- package/plugins/jetbrains-ai-assistant/plugin.json +0 -0
- package/plugins/kilo/icon.svg +0 -0
- package/plugins/kilo/plugin.js +0 -0
- package/plugins/kilo/plugin.json +0 -0
- package/plugins/kimi/icon-color.svg +0 -0
- package/plugins/kimi/icon-conflict.svg +0 -0
- package/plugins/kimi/icon.svg +0 -0
- package/plugins/kimi/plugin.js +122 -141
- package/plugins/kimi/plugin.json +5 -2
- package/plugins/kimi-k2/icon-color.svg +0 -0
- package/plugins/kimi-k2/icon.svg +0 -0
- package/plugins/kimi-k2/plugin.js +0 -0
- package/plugins/kimi-k2/plugin.json +0 -0
- package/plugins/kiro/icon-color.svg +0 -0
- package/plugins/kiro/icon-conflict.svg +0 -0
- package/plugins/kiro/icon.svg +0 -0
- package/plugins/kiro/plugin.js +49 -10
- package/plugins/kiro/plugin.json +3 -1
- package/plugins/litellm/icon.svg +0 -0
- package/plugins/litellm/plugin.js +0 -0
- package/plugins/litellm/plugin.json +0 -0
- package/plugins/llm-proxy/icon.svg +0 -0
- package/plugins/llm-proxy/plugin.js +0 -0
- package/plugins/llm-proxy/plugin.json +0 -0
- package/plugins/longcat/icon.svg +4 -0
- package/plugins/longcat/plugin.js +182 -0
- package/plugins/longcat/plugin.json +23 -0
- package/plugins/manus/icon.svg +0 -0
- package/plugins/manus/plugin.js +0 -0
- package/plugins/manus/plugin.json +0 -0
- package/plugins/mimo/icon.svg +0 -0
- package/plugins/mimo/plugin.js +0 -0
- package/plugins/mimo/plugin.json +0 -0
- package/plugins/minimax/icon-color.svg +0 -0
- package/plugins/minimax/icon-conflict.svg +0 -0
- package/plugins/minimax/icon.svg +0 -0
- package/plugins/minimax/plugin.js +25 -31
- package/plugins/minimax/plugin.json +0 -0
- package/plugins/mistral/icon-color.svg +0 -0
- package/plugins/mistral/icon.svg +0 -0
- package/plugins/mistral/plugin.js +0 -0
- package/plugins/mistral/plugin.json +0 -0
- package/plugins/moonshot/icon.svg +0 -0
- package/plugins/moonshot/plugin.js +8 -6
- package/plugins/moonshot/plugin.json +0 -0
- package/plugins/nanogpt/icon-color.svg +0 -0
- package/plugins/nanogpt/icon.svg +0 -0
- package/plugins/nanogpt/plugin.js +0 -0
- package/plugins/nanogpt/plugin.json +0 -0
- package/plugins/neuralwatt/icon.svg +0 -0
- package/plugins/neuralwatt/plugin.js +0 -0
- package/plugins/neuralwatt/plugin.json +0 -0
- package/plugins/notion/icon.svg +1 -0
- package/plugins/notion/plugin.js +282 -0
- package/plugins/notion/plugin.json +29 -0
- package/plugins/ollama/icon.svg +0 -0
- package/plugins/ollama/plugin.js +257 -56
- package/plugins/ollama/plugin.json +6 -3
- package/plugins/openai-api/icon.svg +0 -0
- package/plugins/openai-api/plugin.js +0 -0
- package/plugins/openai-api/plugin.json +0 -0
- package/plugins/opencode/icon.svg +0 -0
- package/plugins/opencode/plugin.js +0 -0
- package/plugins/opencode/plugin.json +0 -0
- package/plugins/opencode-go/icon-conflict.svg +0 -0
- package/plugins/opencode-go/icon.svg +0 -0
- package/plugins/opencode-go/plugin.js +78 -10
- package/plugins/opencode-go/plugin.json +5 -2
- package/plugins/openrouter/icon.svg +0 -0
- package/plugins/openrouter/plugin.js +371 -114
- package/plugins/openrouter/plugin.json +6 -0
- package/plugins/perplexity/icon-color.svg +0 -0
- package/plugins/perplexity/icon-conflict.svg +0 -0
- package/plugins/perplexity/icon.svg +0 -0
- package/plugins/perplexity/plugin.js +0 -0
- package/plugins/perplexity/plugin.json +0 -0
- package/plugins/poe/icon.svg +0 -0
- package/plugins/poe/plugin.js +24 -18
- package/plugins/poe/plugin.json +2 -0
- package/plugins/qoder/icon.svg +3 -0
- package/plugins/qoder/plugin.js +227 -0
- package/plugins/qoder/plugin.json +23 -0
- package/plugins/qwencloud/icon.svg +4 -0
- package/plugins/qwencloud/plugin.js +677 -0
- package/plugins/qwencloud/plugin.json +29 -0
- package/plugins/sakana/icon.svg +5 -0
- package/plugins/sakana/plugin.js +156 -0
- package/plugins/sakana/plugin.json +23 -0
- package/plugins/stepfun/icon-color.svg +0 -0
- package/plugins/stepfun/icon.svg +0 -0
- package/plugins/stepfun/plugin.js +0 -0
- package/plugins/stepfun/plugin.json +0 -0
- package/plugins/sub2api/icon.svg +4 -0
- package/plugins/sub2api/plugin.js +191 -0
- package/plugins/sub2api/plugin.json +16 -0
- package/plugins/synthetic/icon-conflict.svg +0 -0
- package/plugins/synthetic/icon.svg +0 -0
- package/plugins/synthetic/plugin.js +0 -0
- package/plugins/synthetic/plugin.json +0 -0
- package/plugins/t3chat/icon-color.svg +0 -0
- package/plugins/t3chat/icon.svg +0 -0
- package/plugins/t3chat/plugin.js +0 -0
- package/plugins/t3chat/plugin.json +0 -0
- package/plugins/venice/icon-color.svg +0 -0
- package/plugins/venice/icon.svg +0 -0
- package/plugins/venice/plugin.js +0 -0
- package/plugins/venice/plugin.json +0 -0
- package/plugins/vertex-ai/icon-color.svg +0 -0
- package/plugins/vertex-ai/icon.svg +0 -0
- package/plugins/vertex-ai/plugin.js +0 -0
- package/plugins/vertex-ai/plugin.json +0 -0
- package/plugins/warp/icon-color.svg +0 -0
- package/plugins/warp/icon.svg +0 -0
- package/plugins/warp/plugin.js +0 -0
- package/plugins/warp/plugin.json +0 -0
- package/plugins/wayfinder/icon.svg +5 -0
- package/plugins/wayfinder/plugin.js +47 -0
- package/plugins/wayfinder/plugin.json +16 -0
- package/plugins/windsurf/icon-conflict.svg +0 -0
- package/plugins/windsurf/icon.svg +0 -0
- package/plugins/windsurf/plugin.js +0 -0
- package/plugins/windsurf/plugin.json +0 -0
- package/plugins/xai/icon.svg +7 -0
- package/plugins/xai/plugin.js +162 -0
- package/plugins/xai/plugin.json +28 -0
- package/plugins/zai/icon-conflict.svg +0 -0
- package/plugins/zai/icon.svg +0 -0
- package/plugins/zai/plugin.js +111 -161
- package/plugins/zai/plugin.json +4 -1
- package/plugins/zed/icon.svg +0 -0
- package/plugins/zed/plugin.js +0 -0
- package/plugins/zed/plugin.json +0 -0
- package/plugins/zenmux/icon.svg +3 -0
- package/plugins/zenmux/plugin.js +102 -0
- package/plugins/zenmux/plugin.json +23 -0
- package/plugins/zoommate/icon.svg +1 -0
- package/plugins/zoommate/plugin.js +244 -0
- package/plugins/zoommate/plugin.json +29 -0
- package/usagestat +0 -0
- package/usagestatd +0 -0
package/plugins/cursor/plugin.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor
|
|
1
|
+
globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
|
|
2
2
|
(function () {
|
|
3
3
|
const KEYCHAIN_ACCESS_TOKEN_SERVICE = "cursor-access-token"
|
|
4
4
|
const KEYCHAIN_REFRESH_TOKEN_SERVICE = "cursor-refresh-token"
|
|
5
5
|
const BASE_URL = "https://api2.cursor.sh"
|
|
6
6
|
const USAGE_URL = BASE_URL + "/aiserver.v1.DashboardService/GetCurrentPeriodUsage"
|
|
7
|
+
const GROK_BOT_USAGE_URL = BASE_URL + "/aiserver.v1.DashboardService/GetSandUsageStatus"
|
|
7
8
|
const PLAN_URL = BASE_URL + "/aiserver.v1.DashboardService/GetPlanInfo"
|
|
8
9
|
const REFRESH_URL = BASE_URL + "/oauth/token"
|
|
9
10
|
const CREDITS_URL = BASE_URL + "/aiserver.v1.DashboardService/GetCreditGrantsBalance"
|
|
@@ -282,13 +283,13 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
282
283
|
format: { kind: "count", suffix: " req" },
|
|
283
284
|
}),
|
|
284
285
|
ctx.line.progress({
|
|
285
|
-
label: "
|
|
286
|
+
label: "Cursor Models",
|
|
286
287
|
used: 18,
|
|
287
288
|
limit: 100,
|
|
288
289
|
format: { kind: "percent" },
|
|
289
290
|
}),
|
|
290
291
|
ctx.line.progress({
|
|
291
|
-
label: "
|
|
292
|
+
label: "Other Models",
|
|
292
293
|
used: 7,
|
|
293
294
|
limit: 50,
|
|
294
295
|
format: { kind: "percent" },
|
|
@@ -414,7 +415,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
414
415
|
}
|
|
415
416
|
}
|
|
416
417
|
|
|
417
|
-
function connectPost(ctx, url, token) {
|
|
418
|
+
function connectPost(ctx, url, token, timeoutMs) {
|
|
418
419
|
return ctx.util.request({
|
|
419
420
|
method: "POST",
|
|
420
421
|
url: url,
|
|
@@ -426,7 +427,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
426
427
|
"User-Agent": CONNECT_CLIENT_USER_AGENT,
|
|
427
428
|
},
|
|
428
429
|
bodyText: "{}",
|
|
429
|
-
timeoutMs: 10000,
|
|
430
|
+
timeoutMs: timeoutMs || 10000,
|
|
430
431
|
})
|
|
431
432
|
}
|
|
432
433
|
|
|
@@ -762,7 +763,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
762
763
|
|
|
763
764
|
if (typeof pu.autoPercentUsed === "number" && Number.isFinite(pu.autoPercentUsed)) {
|
|
764
765
|
lines.push(ctx.line.progress({
|
|
765
|
-
label: "
|
|
766
|
+
label: "Cursor Models",
|
|
766
767
|
used: pu.autoPercentUsed,
|
|
767
768
|
limit: 100,
|
|
768
769
|
format: { kind: "percent" },
|
|
@@ -773,7 +774,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
773
774
|
|
|
774
775
|
if (typeof pu.apiPercentUsed === "number" && Number.isFinite(pu.apiPercentUsed)) {
|
|
775
776
|
lines.push(ctx.line.progress({
|
|
776
|
-
label: "
|
|
777
|
+
label: "Other Models",
|
|
777
778
|
used: pu.apiPercentUsed,
|
|
778
779
|
limit: 100,
|
|
779
780
|
format: { kind: "percent" },
|
|
@@ -1106,7 +1107,40 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
1106
1107
|
return result
|
|
1107
1108
|
}
|
|
1108
1109
|
|
|
1109
|
-
function
|
|
1110
|
+
function attachGrokBotUsage(ctx, result, accessToken) {
|
|
1111
|
+
if (!result || !Array.isArray(result.lines) || !accessToken) return result
|
|
1112
|
+
try {
|
|
1113
|
+
const resp = connectPost(ctx, GROK_BOT_USAGE_URL, accessToken, 3000)
|
|
1114
|
+
if (resp.status < 200 || resp.status >= 300) return result
|
|
1115
|
+
const usage = ctx.util.tryParseJson(resp.bodyText)
|
|
1116
|
+
if (!usage || usage.usesPooledEnterpriseAllowance === true ||
|
|
1117
|
+
usage.hasNonZeroIncludedLimit === false || usage.includedLimitZero === true) return result
|
|
1118
|
+
const percent = readFiniteNumber(usage.usagePercent)
|
|
1119
|
+
if (!Number.isFinite(percent) || percent < 0) return result
|
|
1120
|
+
const resetMs = ctx.util.parseDateMs(usage.nextResetTimestampUtc)
|
|
1121
|
+
const startMs = ctx.util.parseDateMs(usage.currentPeriodStart)
|
|
1122
|
+
const line = ctx.line.progress({
|
|
1123
|
+
label: "Grok Bot usage",
|
|
1124
|
+
used: Math.min(100, percent),
|
|
1125
|
+
limit: 100,
|
|
1126
|
+
format: { kind: "percent" },
|
|
1127
|
+
resetsAt: Number.isFinite(resetMs) ? ctx.util.toIso(resetMs) : undefined,
|
|
1128
|
+
periodDurationMs: Number.isFinite(startMs) && Number.isFinite(resetMs) && resetMs > startMs
|
|
1129
|
+
? resetMs - startMs : 7 * 24 * 60 * 60 * 1000,
|
|
1130
|
+
})
|
|
1131
|
+
let insertAt = result.lines.length
|
|
1132
|
+
for (const label of ["Other Models", "Cursor Models", "Total usage"]) {
|
|
1133
|
+
const index = result.lines.findIndex((item) => item.label === label)
|
|
1134
|
+
if (index >= 0) { insertAt = index + 1; break }
|
|
1135
|
+
}
|
|
1136
|
+
result.lines.splice(insertAt, 0, line)
|
|
1137
|
+
} catch (_) {
|
|
1138
|
+
ctx.host.log.warn("Cursor Grok Bot quota unavailable")
|
|
1139
|
+
}
|
|
1140
|
+
return result
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
function probeImpl(ctx, state) {
|
|
1110
1144
|
const authState = loadAuthState(ctx)
|
|
1111
1145
|
let accessToken = authState.accessToken
|
|
1112
1146
|
const refreshTokenValue = authState.refreshToken
|
|
@@ -1176,6 +1210,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
1176
1210
|
throw "Usage request failed. Check your connection."
|
|
1177
1211
|
}
|
|
1178
1212
|
|
|
1213
|
+
state.accessToken = accessToken
|
|
1179
1214
|
if (ctx.util.isAuthStatus(usageResp.status)) {
|
|
1180
1215
|
ctx.host.log.error("usage returned auth error after all retries: status=" + usageResp.status)
|
|
1181
1216
|
throw "Token expired. " + LOGIN_HINT
|
|
@@ -1368,7 +1403,9 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
1368
1403
|
}
|
|
1369
1404
|
|
|
1370
1405
|
function probe(ctx) {
|
|
1371
|
-
var
|
|
1406
|
+
var state = {}
|
|
1407
|
+
var result = probeImpl(ctx, state)
|
|
1408
|
+
result = attachGrokBotUsage(ctx, result, state.accessToken)
|
|
1372
1409
|
result = attachCursorTranscriptActivity(ctx, result)
|
|
1373
1410
|
result = attachCursorBillingDaily(ctx, result)
|
|
1374
1411
|
return attachCursorMtdUsage(ctx, result)
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
{ "type": "progress", "label": "Credits", "scope": "overview", "primaryOrder": 2 },
|
|
17
17
|
{ "type": "progress", "label": "Total usage", "scope": "overview", "primaryOrder": 1 },
|
|
18
18
|
{ "type": "progress", "label": "Requests", "scope": "overview", "primaryOrder": 3 },
|
|
19
|
-
{ "type": "progress", "label": "
|
|
20
|
-
{ "type": "progress", "label": "
|
|
19
|
+
{ "type": "progress", "label": "Cursor Models", "scope": "detail" },
|
|
20
|
+
{ "type": "progress", "label": "Other Models", "scope": "detail" },
|
|
21
|
+
{ "type": "progress", "label": "Grok Bot usage", "scope": "detail" },
|
|
21
22
|
{ "type": "progress", "label": "On-demand", "scope": "detail" }
|
|
22
23
|
]
|
|
23
24
|
}
|
|
File without changes
|
|
@@ -4,6 +4,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
4
4
|
const KEYCHAIN_REFRESH_TOKEN_SERVICE = "cursor-refresh-token"
|
|
5
5
|
const BASE_URL = "https://api2.cursor.sh"
|
|
6
6
|
const USAGE_URL = BASE_URL + "/aiserver.v1.DashboardService/GetCurrentPeriodUsage"
|
|
7
|
+
const GROK_BOT_USAGE_URL = BASE_URL + "/aiserver.v1.DashboardService/GetSandUsageStatus"
|
|
7
8
|
const PLAN_URL = BASE_URL + "/aiserver.v1.DashboardService/GetPlanInfo"
|
|
8
9
|
const REFRESH_URL = BASE_URL + "/oauth/token"
|
|
9
10
|
const CREDITS_URL = BASE_URL + "/aiserver.v1.DashboardService/GetCreditGrantsBalance"
|
|
@@ -282,13 +283,13 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
282
283
|
format: { kind: "count", suffix: " req" },
|
|
283
284
|
}),
|
|
284
285
|
ctx.line.progress({
|
|
285
|
-
label: "
|
|
286
|
+
label: "Cursor Models",
|
|
286
287
|
used: 18,
|
|
287
288
|
limit: 100,
|
|
288
289
|
format: { kind: "percent" },
|
|
289
290
|
}),
|
|
290
291
|
ctx.line.progress({
|
|
291
|
-
label: "
|
|
292
|
+
label: "Other Models",
|
|
292
293
|
used: 7,
|
|
293
294
|
limit: 50,
|
|
294
295
|
format: { kind: "percent" },
|
|
@@ -414,7 +415,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
414
415
|
}
|
|
415
416
|
}
|
|
416
417
|
|
|
417
|
-
function connectPost(ctx, url, token) {
|
|
418
|
+
function connectPost(ctx, url, token, timeoutMs) {
|
|
418
419
|
return ctx.util.request({
|
|
419
420
|
method: "POST",
|
|
420
421
|
url: url,
|
|
@@ -426,7 +427,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
426
427
|
"User-Agent": CONNECT_CLIENT_USER_AGENT,
|
|
427
428
|
},
|
|
428
429
|
bodyText: "{}",
|
|
429
|
-
timeoutMs: 10000,
|
|
430
|
+
timeoutMs: timeoutMs || 10000,
|
|
430
431
|
})
|
|
431
432
|
}
|
|
432
433
|
|
|
@@ -762,7 +763,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
762
763
|
|
|
763
764
|
if (typeof pu.autoPercentUsed === "number" && Number.isFinite(pu.autoPercentUsed)) {
|
|
764
765
|
lines.push(ctx.line.progress({
|
|
765
|
-
label: "
|
|
766
|
+
label: "Cursor Models",
|
|
766
767
|
used: pu.autoPercentUsed,
|
|
767
768
|
limit: 100,
|
|
768
769
|
format: { kind: "percent" },
|
|
@@ -773,7 +774,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
773
774
|
|
|
774
775
|
if (typeof pu.apiPercentUsed === "number" && Number.isFinite(pu.apiPercentUsed)) {
|
|
775
776
|
lines.push(ctx.line.progress({
|
|
776
|
-
label: "
|
|
777
|
+
label: "Other Models",
|
|
777
778
|
used: pu.apiPercentUsed,
|
|
778
779
|
limit: 100,
|
|
779
780
|
format: { kind: "percent" },
|
|
@@ -1106,7 +1107,40 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
1106
1107
|
return result
|
|
1107
1108
|
}
|
|
1108
1109
|
|
|
1109
|
-
function
|
|
1110
|
+
function attachGrokBotUsage(ctx, result, accessToken) {
|
|
1111
|
+
if (!result || !Array.isArray(result.lines) || !accessToken) return result
|
|
1112
|
+
try {
|
|
1113
|
+
const resp = connectPost(ctx, GROK_BOT_USAGE_URL, accessToken, 3000)
|
|
1114
|
+
if (resp.status < 200 || resp.status >= 300) return result
|
|
1115
|
+
const usage = ctx.util.tryParseJson(resp.bodyText)
|
|
1116
|
+
if (!usage || usage.usesPooledEnterpriseAllowance === true ||
|
|
1117
|
+
usage.hasNonZeroIncludedLimit === false || usage.includedLimitZero === true) return result
|
|
1118
|
+
const percent = readFiniteNumber(usage.usagePercent)
|
|
1119
|
+
if (!Number.isFinite(percent) || percent < 0) return result
|
|
1120
|
+
const resetMs = ctx.util.parseDateMs(usage.nextResetTimestampUtc)
|
|
1121
|
+
const startMs = ctx.util.parseDateMs(usage.currentPeriodStart)
|
|
1122
|
+
const line = ctx.line.progress({
|
|
1123
|
+
label: "Grok Bot usage",
|
|
1124
|
+
used: Math.min(100, percent),
|
|
1125
|
+
limit: 100,
|
|
1126
|
+
format: { kind: "percent" },
|
|
1127
|
+
resetsAt: Number.isFinite(resetMs) ? ctx.util.toIso(resetMs) : undefined,
|
|
1128
|
+
periodDurationMs: Number.isFinite(startMs) && Number.isFinite(resetMs) && resetMs > startMs
|
|
1129
|
+
? resetMs - startMs : 7 * 24 * 60 * 60 * 1000,
|
|
1130
|
+
})
|
|
1131
|
+
let insertAt = result.lines.length
|
|
1132
|
+
for (const label of ["Other Models", "Cursor Models", "Total usage"]) {
|
|
1133
|
+
const index = result.lines.findIndex((item) => item.label === label)
|
|
1134
|
+
if (index >= 0) { insertAt = index + 1; break }
|
|
1135
|
+
}
|
|
1136
|
+
result.lines.splice(insertAt, 0, line)
|
|
1137
|
+
} catch (_) {
|
|
1138
|
+
ctx.host.log.warn("Cursor Grok Bot quota unavailable")
|
|
1139
|
+
}
|
|
1140
|
+
return result
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
function probeImpl(ctx, state) {
|
|
1110
1144
|
const authState = loadAuthState(ctx)
|
|
1111
1145
|
let accessToken = authState.accessToken
|
|
1112
1146
|
const refreshTokenValue = authState.refreshToken
|
|
@@ -1176,6 +1210,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
1176
1210
|
throw "Usage request failed. Check your connection."
|
|
1177
1211
|
}
|
|
1178
1212
|
|
|
1213
|
+
state.accessToken = accessToken
|
|
1179
1214
|
if (ctx.util.isAuthStatus(usageResp.status)) {
|
|
1180
1215
|
ctx.host.log.error("usage returned auth error after all retries: status=" + usageResp.status)
|
|
1181
1216
|
throw "Token expired. " + LOGIN_HINT
|
|
@@ -1368,7 +1403,9 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
|
|
|
1368
1403
|
}
|
|
1369
1404
|
|
|
1370
1405
|
function probe(ctx) {
|
|
1371
|
-
var
|
|
1406
|
+
var state = {}
|
|
1407
|
+
var result = probeImpl(ctx, state)
|
|
1408
|
+
result = attachGrokBotUsage(ctx, result, state.accessToken)
|
|
1372
1409
|
result = attachCursorTranscriptActivity(ctx, result)
|
|
1373
1410
|
result = attachCursorBillingDaily(ctx, result)
|
|
1374
1411
|
return attachCursorMtdUsage(ctx, result)
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
{ "type": "progress", "label": "Credits", "scope": "overview", "primaryOrder": 2 },
|
|
17
17
|
{ "type": "progress", "label": "Total usage", "scope": "overview", "primaryOrder": 1 },
|
|
18
18
|
{ "type": "progress", "label": "Requests", "scope": "overview", "primaryOrder": 3 },
|
|
19
|
-
{ "type": "progress", "label": "
|
|
20
|
-
{ "type": "progress", "label": "
|
|
19
|
+
{ "type": "progress", "label": "Cursor Models", "scope": "detail" },
|
|
20
|
+
{ "type": "progress", "label": "Other Models", "scope": "detail" },
|
|
21
|
+
{ "type": "progress", "label": "Grok Bot usage", "scope": "detail" },
|
|
21
22
|
{ "type": "progress", "label": "On-demand", "scope": "detail" }
|
|
22
23
|
]
|
|
23
24
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 32">
|
|
2
|
+
<title>DeepInfra</title>
|
|
3
|
+
<path fill="currentColor" d="M2.833 10.66A2.837 2.837 0 0 1 0 7.825a2.837 2.837 0 0 1 2.833-2.834 2.837 2.837 0 0 1 2.834 2.834 2.837 2.837 0 0 1-2.834 2.833m0-4.555a1.723 1.723 0 0 0 0 3.445 1.724 1.724 0 0 0 0-3.445m0 12.552A2.837 2.837 0 0 1 0 15.822a2.837 2.837 0 0 1 2.833-2.834 2.837 2.837 0 0 1 2.834 2.834 2.837 2.837 0 0 1-2.834 2.833m0-4.554a1.722 1.722 0 0 0 0 3.443 1.722 1.722 0 1 0 0-3.443m0 12.539A2.837 2.837 0 0 1 0 23.806a2.837 2.837 0 0 1 2.833-2.833 2.837 2.837 0 0 1 2.834 2.833 2.837 2.837 0 0 1-2.834 2.834m0-4.555a1.723 1.723 0 0 0 0 3.445c.95 0 1.723-.772 1.723-1.722s-.772-1.723-1.723-1.723M24.376 10.66a2.837 2.837 0 0 1-2.833-2.834 2.837 2.837 0 0 1 2.833-2.834 2.837 2.837 0 0 1 2.834 2.834 2.837 2.837 0 0 1-2.834 2.833m0-4.555c-.948 0-1.722.772-1.722 1.722a1.724 1.724 0 0 0 3.445 0c0-.95-.772-1.722-1.723-1.722m0 12.551a2.837 2.837 0 0 1-2.833-2.833 2.837 2.837 0 0 1 2.833-2.834 2.837 2.837 0 0 1 2.834 2.834 2.837 2.837 0 0 1-2.834 2.833m0-4.554c-.948 0-1.722.772-1.722 1.72 0 .95.772 1.723 1.722 1.723s1.723-.772 1.723-1.722-.772-1.721-1.723-1.721m0 12.539a2.837 2.837 0 0 1-2.833-2.834 2.837 2.837 0 0 1 2.833-2.833 2.837 2.837 0 0 1 2.834 2.833 2.837 2.837 0 0 1-2.834 2.834m0-4.555a1.724 1.724 0 1 0 1.723 1.723c0-.95-.772-1.723-1.723-1.723m-10.771-7.43a2.837 2.837 0 0 1-2.834-2.833 2.837 2.837 0 0 1 2.834-2.834 2.837 2.837 0 0 1 2.833 2.834 2.837 2.837 0 0 1-2.833 2.833m0-4.554c-.949 0-1.723.772-1.723 1.722s.773 1.723 1.723 1.723 1.723-.772 1.723-1.723c0-.95-.773-1.722-1.723-1.722m0-3.434a2.837 2.837 0 0 1-2.834-2.834A2.837 2.837 0 0 1 13.606 1a2.837 2.837 0 0 1 2.833 2.833 2.837 2.837 0 0 1-2.833 2.834m0-4.554c-.949 0-1.723.772-1.723 1.722a1.724 1.724 0 0 0 3.446 0c0-.95-.773-1.722-1.723-1.722m-.001 20.534a2.837 2.837 0 0 1-2.834-2.833 2.837 2.837 0 0 1 2.834-2.834 2.837 2.837 0 0 1 2.833 2.834 2.837 2.837 0 0 1-2.833 2.833m0-4.554a1.724 1.724 0 0 0 0 3.445 1.724 1.724 0 0 0 0-3.445m0 12.547a2.837 2.837 0 0 1-2.834-2.834 2.837 2.837 0 0 1 2.834-2.833 2.837 2.837 0 0 1 2.833 2.833 2.837 2.837 0 0 1-2.833 2.834m0-4.555a1.724 1.724 0 1 0 1.723 1.723c0-.95-.773-1.723-1.723-1.723M9.094 11.154a.9.9 0 0 1-.342-.066l-1.845-.735a.926.926 0 1 1 .687-1.72l1.845.735a.926.926 0 0 1-.344 1.786zm0 7.835a.9.9 0 0 1-.342-.066l-1.845-.735a.925.925 0 1 1 .687-1.72l1.845.736a.925.925 0 0 1-.344 1.785zm10.82-3.914a.9.9 0 0 1-.342-.066l-1.844-.735a.926.926 0 0 1 .685-1.72l1.845.736a.926.926 0 0 1-.344 1.785m0-7.835a.9.9 0 0 1-.342-.066l-1.844-.735a.926.926 0 0 1 .685-1.72l1.845.735a.926.926 0 0 1-.344 1.786m0 15.668a.9.9 0 0 1-.342-.066l-1.844-.735a.926.926 0 0 1 .685-1.72l1.845.735a.926.926 0 0 1-.344 1.785m-12.616.281a.926.926 0 0 1-.344-1.785l1.845-.735a.925.925 0 1 1 .687 1.72l-1.845.734a.9.9 0 0 1-.342.066zm0-7.793a.926.926 0 0 1-.344-1.785l1.845-.735a.925.925 0 1 1 .687 1.72l-1.845.734a.9.9 0 0 1-.342.066zm0-8.18a.926.926 0 0 1-.344-1.785L8.8 4.695a.925.925 0 1 1 .687 1.72L7.64 7.15a.9.9 0 0 1-.342.065zm1.796 20.169a.9.9 0 0 1-.342-.066l-1.845-.735a.925.925 0 0 1 .686-1.72l1.844.736a.926.926 0 0 1-.343 1.785m8.976-16.229a.926.926 0 0 1-.344-1.787l1.844-.735a.927.927 0 0 1 .687 1.72l-1.844.736a.9.9 0 0 1-.342.066zm0 7.835a.926.926 0 0 1-.344-1.786l1.844-.736a.927.927 0 0 1 .687 1.721l-1.844.736a.9.9 0 0 1-.342.066zm0 8.393a.925.925 0 0 1-.343-1.785l1.843-.735a.925.925 0 1 1 .686 1.72l-1.845.735a.9.9 0 0 1-.342.065"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
var CHECKLIST_URL = "https://api.deepinfra.com/payment/checklist?compute_owed=true";
|
|
3
|
+
var USAGE_URL = "https://api.deepinfra.com/payment/usage?from=current";
|
|
4
|
+
|
|
5
|
+
function clean(value) {
|
|
6
|
+
if (typeof value !== "string") return null;
|
|
7
|
+
var v = value.trim();
|
|
8
|
+
if (!v) return null;
|
|
9
|
+
if ((v[0] === "\"" && v[v.length - 1] === "\"") || (v[0] === "'" && v[v.length - 1] === "'")) v = v.slice(1, -1).trim();
|
|
10
|
+
if (v.slice(0, 7).toLowerCase() === "bearer ") v = v.slice(7).trim();
|
|
11
|
+
return v || null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function env(ctx, name) {
|
|
15
|
+
try {
|
|
16
|
+
return clean(ctx.host.env.get(name));
|
|
17
|
+
} catch (_) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function apiKey(ctx) {
|
|
23
|
+
return clean(ctx.provider && ctx.provider.apiKey) || env(ctx, "DEEPINFRA_API_KEY") || env(ctx, "DEEPINFRA_TOKEN");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function requestJson(ctx, url, key, label, timeoutMs) {
|
|
27
|
+
var result = ctx.util.requestJson({
|
|
28
|
+
method: "GET",
|
|
29
|
+
url: url,
|
|
30
|
+
headers: { Authorization: "Bearer " + key, Accept: "application/json" },
|
|
31
|
+
timeoutMs: timeoutMs || 15000,
|
|
32
|
+
});
|
|
33
|
+
if (result.resp.status === 401) throw "DeepInfra API key rejected.";
|
|
34
|
+
if (result.resp.status === 403) throw "DeepInfra API key cannot access billing data.";
|
|
35
|
+
if (result.resp.status < 200 || result.resp.status >= 300) throw "DeepInfra " + label + " API error: HTTP " + result.resp.status + ".";
|
|
36
|
+
if (!result.json || typeof result.json !== "object") throw "Failed to parse DeepInfra " + label + " response.";
|
|
37
|
+
return result.json;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function number(value) {
|
|
41
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
42
|
+
if (typeof value === "string" && value.trim()) {
|
|
43
|
+
var parsed = Number(value.replace(/,/g, ""));
|
|
44
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function probe(ctx) {
|
|
50
|
+
var key = apiKey(ctx);
|
|
51
|
+
if (!key) throw "DeepInfra API key not found. Set DEEPINFRA_API_KEY, DEEPINFRA_TOKEN, or provider apiKey.";
|
|
52
|
+
|
|
53
|
+
var checklist = requestJson(ctx, CHECKLIST_URL, key, "billing");
|
|
54
|
+
var recent = number(checklist.recent);
|
|
55
|
+
var stripeBalance = number(checklist.stripe_balance);
|
|
56
|
+
if (recent === null || recent < 0 || stripeBalance === null) throw "DeepInfra returned invalid billing amounts.";
|
|
57
|
+
var currentMonth = null;
|
|
58
|
+
try {
|
|
59
|
+
var usage = requestJson(ctx, USAGE_URL, key, "usage", 3000);
|
|
60
|
+
if (Array.isArray(usage.months) && usage.months.length) {
|
|
61
|
+
var cents = number(usage.months[usage.months.length - 1].total_cost);
|
|
62
|
+
if (cents !== null && cents >= 0) currentMonth = cents / 100;
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
ctx.host.log.warn("DeepInfra monthly usage unavailable: " + String(error));
|
|
66
|
+
}
|
|
67
|
+
var netBalance = stripeBalance + recent;
|
|
68
|
+
var available = Math.max(0, -netBalance);
|
|
69
|
+
var owed = Math.max(0, netBalance);
|
|
70
|
+
var limit = number(checklist.limit);
|
|
71
|
+
var suspended = checklist.suspended === true;
|
|
72
|
+
var balanceText = owed > 0 ? "$" + owed.toFixed(2) + " owed" : "$" + available.toFixed(2) + " available";
|
|
73
|
+
var detail = balanceText + (currentMonth === null ? "" : " · $" + currentMonth.toFixed(2) + " spent this month");
|
|
74
|
+
if (suspended) detail = "Suspended" + (checklist.suspend_reason ? ": " + checklist.suspend_reason : "") + " · " + detail;
|
|
75
|
+
|
|
76
|
+
var metrics = [];
|
|
77
|
+
if (limit > 0 && Number.isFinite(limit)) {
|
|
78
|
+
metrics.push(ctx.line.progress({
|
|
79
|
+
label: "Billing Cycle",
|
|
80
|
+
used: Math.max(0, recent),
|
|
81
|
+
limit: limit,
|
|
82
|
+
format: { kind: "dollars" },
|
|
83
|
+
detail: detail,
|
|
84
|
+
}));
|
|
85
|
+
} else {
|
|
86
|
+
metrics.push(ctx.line.text({ label: "Balance", value: balanceText }));
|
|
87
|
+
}
|
|
88
|
+
if (currentMonth !== null) metrics.push(ctx.line.text({ label: "Month Cost", value: "$" + currentMonth.toFixed(2) }));
|
|
89
|
+
if (suspended || owed > 0 || available <= 0) {
|
|
90
|
+
metrics.push(ctx.line.badge({ label: "Status", text: suspended ? "Suspended" : "No prepaid balance", color: "#ef4444" }));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { displayName: "DeepInfra", source: "api", lines: metrics };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
globalThis.__openusage_plugin = { id: "deepinfra", probe: probe };
|
|
97
|
+
})();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "deepinfra",
|
|
4
|
+
"name": "DeepInfra",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"entry": "plugin.js",
|
|
7
|
+
"enabledByDefault": false,
|
|
8
|
+
"supportedModes": [
|
|
9
|
+
"api"
|
|
10
|
+
],
|
|
11
|
+
"autoMode": "api",
|
|
12
|
+
"usageDashboardUrl": "https://deepinfra.com/dash",
|
|
13
|
+
"statusPageUrl": "https://status.deepinfra.com",
|
|
14
|
+
"links": [
|
|
15
|
+
{
|
|
16
|
+
"label": "Dashboard",
|
|
17
|
+
"url": "https://deepinfra.com/dash"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"label": "Status",
|
|
21
|
+
"url": "https://status.deepinfra.com"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"envVars": [
|
|
25
|
+
"DEEPINFRA_API_KEY",
|
|
26
|
+
"DEEPINFRA_TOKEN"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/plugins/devin/icon.svg
CHANGED
|
File without changes
|
package/plugins/devin/plugin.js
CHANGED
|
@@ -305,8 +305,119 @@
|
|
|
305
305
|
return auth.apiKey + "\n" + effectiveApiServerUrl(auth)
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
+
function firstPresent(values) {
|
|
309
|
+
for (var i = 0; i < values.length; i++) if (values[i] !== null && values[i] !== undefined) return values[i]
|
|
310
|
+
return null
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function manualOrganization(raw) {
|
|
314
|
+
if (typeof raw !== "string") throw {code: "missing-auth", message: "Set Devin workspace_id or DEVIN_ORGANIZATION to the intended organization."}
|
|
315
|
+
var value = raw.trim().replace(/^\/+|\/+$/g, "")
|
|
316
|
+
if (/^https:\/\//i.test(value)) {
|
|
317
|
+
var url = /^https:\/\/(?:[a-z0-9-]+\.)*devin\.ai\/(org|organizations)\/([^/?#]+)(?:[/?#].*)?$/i.exec(value)
|
|
318
|
+
if (!url) throw {code: "failed", message: "Use an app.devin.ai organization URL, organization ID, or slug."}
|
|
319
|
+
value = url[1].toLowerCase() + "/" + url[2]
|
|
320
|
+
}
|
|
321
|
+
var parts = value.split("/")
|
|
322
|
+
var id = parts.length === 1 ? parts[0] : parts[1]
|
|
323
|
+
if (parts.length > 2 || (parts.length === 2 && !/^(org|organizations)$/.test(parts[0])) || !/^[a-z0-9][a-z0-9_.-]*$/i.test(id)) {
|
|
324
|
+
throw {code: "failed", message: "Invalid Devin organization; use an organization ID, slug, or app.devin.ai organization URL."}
|
|
325
|
+
}
|
|
326
|
+
var internal = parts.length === 2 ? parts[0] === "organizations" : /^org[-_]/.test(id)
|
|
327
|
+
return { id: id, internal: internal, path: (internal ? "organizations/" : "org/") + id }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function manualQuotaPercent(value) {
|
|
331
|
+
var number = readFiniteNumber(value)
|
|
332
|
+
if (number !== null) return number <= 1 ? number * 100 : number
|
|
333
|
+
if (!value || typeof value !== "object") return null
|
|
334
|
+
var usedPercent = firstPresent([value.used_percent, value.usedPercent, value.usage_percent, value.usagePercent, value.percent_used, value.percentUsed, value.percent])
|
|
335
|
+
if (usedPercent !== null) return manualQuotaPercent(usedPercent)
|
|
336
|
+
var remainingPercent = firstPresent([value.remaining_percent, value.remainingPercent, value.percent_remaining, value.percentRemaining])
|
|
337
|
+
if (remainingPercent !== null) {
|
|
338
|
+
var remaining = manualQuotaPercent(remainingPercent)
|
|
339
|
+
return remaining === null ? null : 100 - remaining
|
|
340
|
+
}
|
|
341
|
+
var limit = readFiniteNumber(firstPresent([value.limit, value.quota, value.total, value.max]))
|
|
342
|
+
var used = readFiniteNumber(firstPresent([value.used, value.usage, value.used_count, value.usedCount, value.consumed]))
|
|
343
|
+
if (limit !== null && limit > 0 && used !== null) return used / limit * 100
|
|
344
|
+
remaining = readFiniteNumber(firstPresent([value.remaining, value.left]))
|
|
345
|
+
return limit !== null && limit > 0 && remaining !== null ? (limit - remaining) / limit * 100 : null
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function manualWindow(ctx, data, cadence, depth) {
|
|
349
|
+
if (!data || typeof data !== "object" || depth > 8) return null
|
|
350
|
+
var current = readFiniteNumber(data[cadence + "_percentage"])
|
|
351
|
+
if (current !== null) {
|
|
352
|
+
var currentPercent = current < 1 ? current * 100 : current
|
|
353
|
+
if (Number.isFinite(currentPercent)) return {used: currentPercent, reset: ctx.util.toIso(data[cadence + "_reset_at"])}
|
|
354
|
+
}
|
|
355
|
+
var singular = cadence === "daily" ? "day" : "week"
|
|
356
|
+
var window = firstPresent([data[cadence], data[singular], data[cadence + "_quota"], data[cadence + "Quota"], data[cadence + "_usage"], data[cadence + "Usage"]])
|
|
357
|
+
var percent = manualQuotaPercent(window)
|
|
358
|
+
if (percent !== null && Number.isFinite(percent)) {
|
|
359
|
+
return {used: percent, reset: ctx.util.toIso(window && typeof window === "object" ? firstPresent([window.resetsAt, window.reset_at, window.resetAt, window.reset_time]) : null)}
|
|
360
|
+
}
|
|
361
|
+
var keys = Object.keys(data)
|
|
362
|
+
for (var i = 0; i < keys.length; i++) {
|
|
363
|
+
var nested = manualWindow(ctx, data[keys[i]], cadence, depth + 1)
|
|
364
|
+
if (nested) return nested
|
|
365
|
+
}
|
|
366
|
+
return null
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function manualOutput(ctx, data) {
|
|
370
|
+
var daily = manualWindow(ctx, data, "daily", 0)
|
|
371
|
+
var weekly = manualWindow(ctx, data, "weekly", 0)
|
|
372
|
+
var lines = []
|
|
373
|
+
if (daily) lines.push(buildUsedQuotaLine(ctx, "Daily quota", daily.used, daily.reset, DAY_MS))
|
|
374
|
+
if (weekly) lines.push(buildUsedQuotaLine(ctx, "Weekly quota", weekly.used, weekly.reset, WEEK_MS))
|
|
375
|
+
if (!lines.length) throw {code: "no-data", message: "Devin web response did not contain measurable daily or weekly quotas."}
|
|
376
|
+
var balance = readFiniteNumber(data.overage_balance)
|
|
377
|
+
if (balance === null) {
|
|
378
|
+
var cents = readFiniteNumber(data.overage_balance_cents)
|
|
379
|
+
if (cents !== null) balance = cents / 100
|
|
380
|
+
}
|
|
381
|
+
if (balance !== null && balance >= 0) lines.push(ctx.line.text({label: "Extra usage balance", value: "$" + balance.toFixed(2)}))
|
|
382
|
+
var plan = firstPresent([data.plan_name, data.planName, data.plan, data.tier, data.subscription_tier])
|
|
383
|
+
return {source: "web", plan: typeof plan === "string" && plan.trim() ? plan.trim() : null, lines: lines}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function probeManual(ctx, opts) {
|
|
387
|
+
if (opts.cookieSource === "off") throw {code: "missing-auth", message: "Devin web authentication is disabled in settings.cookieSource."}
|
|
388
|
+
if (opts.credentialsPath || opts.ideVariant || opts.userDataDir || (opts.authSource && ["auto", "manual"].indexOf(opts.authSource) < 0)) {
|
|
389
|
+
throw {code: "failed", message: "Select either Devin manual web authentication or a CLI/IDE credential source."}
|
|
390
|
+
}
|
|
391
|
+
var raw = firstPresent([ctx.host.env.get("DEVIN_BEARER_TOKEN"), ctx.host.env.get("DEVIN_AUTHORIZATION"), ctx.provider.cookieHeader, opts.bearerToken])
|
|
392
|
+
var token = typeof raw === "string" ? raw.trim().replace(/^Authorization:\s*/i, "").replace(/^Bearer(?:\s+|$)/i, "").trim() : ""
|
|
393
|
+
if (!token || /[^\x21-\x7e]/.test(token)) throw {code: "missing-auth", message: "Set a valid Devin bearer token in cookie_header or DEVIN_BEARER_TOKEN."}
|
|
394
|
+
var org = manualOrganization(firstPresent([ctx.host.env.get("DEVIN_ORGANIZATION"), ctx.host.env.get("DEVIN_ORG"), ctx.provider.workspaceId, opts.organization]))
|
|
395
|
+
var paths = org.internal ? [org.id, org.path] : [org.path, org.id]
|
|
396
|
+
for (var i = 0; i < paths.length; i++) {
|
|
397
|
+
var headers = {Authorization: "Bearer " + token, Accept: "application/json", "Accept-Language": "en-US,en;q=0.9", "User-Agent": "usagestat/" + ctx.app.version}
|
|
398
|
+
if (org.internal) headers["x-cog-org-id"] = org.id
|
|
399
|
+
var response = ctx.util.request({method: "GET", url: "https://app.devin.ai/api/" + paths[i] + "/billing/quota/usage", headers: headers, timeoutMs: 10000})
|
|
400
|
+
if (response.status === 401 || response.status === 403) throw {code: "credential-denied", message: "Devin web token was rejected. Reauthenticate the selected organization."}
|
|
401
|
+
// Alternate routes refer to the same organization; only missing routes
|
|
402
|
+
// permit retry. A rejected token never selects a CLI or IDE account.
|
|
403
|
+
if (response.status === 404 || response.status === 405) continue
|
|
404
|
+
if (response.status !== 200) throw {code: "failed", message: "Devin quota request failed (HTTP " + response.status + ")."}
|
|
405
|
+
var data = ctx.util.tryParseJson(response.bodyText)
|
|
406
|
+
if (!data || typeof data !== "object") throw {code: "no-data", message: "Invalid Devin quota response."}
|
|
407
|
+
return manualOutput(ctx, data)
|
|
408
|
+
}
|
|
409
|
+
throw {code: "no-data", message: "Devin quota endpoint was not found for the selected organization."}
|
|
410
|
+
}
|
|
411
|
+
|
|
308
412
|
function probe(ctx) {
|
|
309
413
|
var opts = settings(ctx)
|
|
414
|
+
var mode = ctx.sourceMode || "auto"
|
|
415
|
+
var manual = mode === "web" || opts.authSource === "manual" || opts.cookieSource === "manual" ||
|
|
416
|
+
(mode === "auto" && !opts.authSource && opts.cookieSource == null && (ctx.provider.cookieHeader != null || opts.bearerToken != null))
|
|
417
|
+
if (manual) {
|
|
418
|
+
if (mode === "local") throw {code: "failed", message: "Select web or auto mode to use Devin manual web authentication."}
|
|
419
|
+
return probeManual(ctx, opts)
|
|
420
|
+
}
|
|
310
421
|
var source = opts.authSource || (opts.credentialsPath ? "cli" : opts.ideVariant || opts.userDataDir ? "ide" : "auto")
|
|
311
422
|
if (["auto", "cli", "ide"].indexOf(source) < 0) throw {code: "failed", message: "Set Devin settings.authSource to auto, cli or ide."}
|
|
312
423
|
if ((opts.credentialsPath && source !== "cli") || ((opts.ideVariant || opts.userDataDir) && source !== "ide")) {
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
"entry": "plugin.js",
|
|
7
7
|
"icon": "icon.svg",
|
|
8
8
|
"brandColor": "#000000",
|
|
9
|
-
"supportedModes": ["local"],
|
|
9
|
+
"supportedModes": ["local", "web"],
|
|
10
10
|
"autoMode": "local",
|
|
11
|
+
"envVars": ["DEVIN_BEARER_TOKEN", "DEVIN_AUTHORIZATION", "DEVIN_ORGANIZATION", "DEVIN_ORG"],
|
|
11
12
|
"lines": [
|
|
12
13
|
{ "type": "progress", "label": "Weekly quota", "scope": "overview", "primaryOrder": 1 },
|
|
13
14
|
{ "type": "progress", "label": "Daily quota", "scope": "overview" },
|
|
File without changes
|
package/plugins/doubao/icon.svg
CHANGED
|
File without changes
|
package/plugins/doubao/plugin.js
CHANGED
|
File without changes
|
|
File without changes
|
package/plugins/droid/icon.svg
CHANGED
|
File without changes
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
{ "label": "Factory", "url": "https://app.factory.ai" },
|
|
11
11
|
{ "label": "Status", "url": "https://status.factory.ai" }
|
|
12
12
|
],
|
|
13
|
-
"supportedModes": ["oauth", "local"],
|
|
13
|
+
"supportedModes": ["api", "oauth", "local", "web"],
|
|
14
|
+
"envVars": ["FACTORY_API_KEY", "FACTORY_COOKIE", "DROID_COOKIE"],
|
|
14
15
|
"autoMode": "oauth",
|
|
15
16
|
"webUrl": "https://app.factory.ai",
|
|
16
17
|
"lines": [
|
|
File without changes
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
function apiKey(ctx) {
|
|
3
|
-
return ctx.host.env.get("ELEVENLABS_API_KEY") || ctx.host.env.get("XI_API_KEY");
|
|
3
|
+
return (ctx.provider && ctx.provider.apiKey) || ctx.host.env.get("ELEVENLABS_API_KEY") || ctx.host.env.get("XI_API_KEY");
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
function apiBase(ctx) {
|
|
7
7
|
return ctx.host.env.get("ELEVENLABS_API_URL") || "https://api.elevenlabs.io";
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
function authError(status, json) {
|
|
11
|
+
var detail = json && json.detail;
|
|
12
|
+
var codes = detail && typeof detail === "object" ? [detail.code, detail.status] : [];
|
|
13
|
+
for (var i = 0; i < codes.length; i++) {
|
|
14
|
+
var code = typeof codes[i] === "string" ? codes[i].trim().toLowerCase() : "";
|
|
15
|
+
if (code === "invalid_api_key") return "ElevenLabs rejected the API key. Check whether it is valid or revoked.";
|
|
16
|
+
if (code === "missing_permissions" || code === "insufficient_permissions") {
|
|
17
|
+
return "ElevenLabs API key needs the user_read permission to fetch subscription usage.";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return status === 403
|
|
21
|
+
? "ElevenLabs denied access. Check the API key's endpoint permissions and IP allowlist."
|
|
22
|
+
: "ElevenLabs could not authenticate the API key. Check the key and its permissions.";
|
|
23
|
+
}
|
|
24
|
+
|
|
10
25
|
function probe(ctx) {
|
|
11
26
|
var key = apiKey(ctx);
|
|
12
27
|
if (!key) throw "ElevenLabs API key not found. Set ELEVENLABS_API_KEY or XI_API_KEY.";
|
|
@@ -17,7 +32,7 @@
|
|
|
17
32
|
headers: { "xi-api-key": key, Accept: "application/json" },
|
|
18
33
|
timeoutMs: 15000,
|
|
19
34
|
});
|
|
20
|
-
if (ctx.util.isAuthStatus(result.resp.status)) throw
|
|
35
|
+
if (ctx.util.isAuthStatus(result.resp.status)) throw authError(result.resp.status, result.json);
|
|
21
36
|
if (result.resp.status < 200 || result.resp.status >= 300) throw "ElevenLabs API error (HTTP " + result.resp.status + ").";
|
|
22
37
|
if (!result.json) throw "Could not parse ElevenLabs subscription response.";
|
|
23
38
|
|
|
File without changes
|
|
File without changes
|
package/plugins/factory/icon.svg
CHANGED
|
File without changes
|