@hashimkarim/usagestat-linux-x64-gnu 2.0.0-alpha.1 → 2.0.0-alpha.4
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 +1088 -134
- package/package.json +160 -1
- package/plugins/UPSTREAM-LICENSES.md +113 -0
- package/plugins/_provider-icons/LICENSE +21 -0
- package/plugins/_provider-icons/NOTICE +12 -0
- package/plugins/_provider-icons/licenses/CodexBar-MIT.txt +21 -0
- package/plugins/_provider-icons/licenses/LobeHub-MIT.txt +21 -0
- package/plugins/_provider-icons/licenses/UsageStat-Bar-MIT.txt +21 -0
- package/plugins/_provider-icons/manifest.json +1219 -0
- package/plugins/_provider-icons/provenance.json +269 -0
- package/plugins/_provider-icons/source.json +4 -0
- package/plugins/abacus-ai/icon-color.svg +18 -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/aixy/LICENSE +21 -0
- package/plugins/aixy/plugin.js +261 -0
- package/plugins/aixy/plugin.json +24 -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 +136 -46
- package/plugins/antigravity/plugin.json +0 -0
- package/plugins/antigravity-cli/icon-color.svg +1 -0
- package/plugins/antigravity-cli/icon.svg +1 -3
- package/plugins/antigravity-cli/plugin.js +11 -15
- package/plugins/antigravity-cli/plugin.json +0 -0
- package/plugins/antigravity-ide/icon-color.svg +1 -0
- package/plugins/antigravity-ide/icon.svg +1 -3
- package/plugins/antigravity-ide/plugin.js +0 -0
- package/plugins/antigravity-ide/plugin.json +0 -0
- package/plugins/atlascloud/LICENSE +21 -0
- package/plugins/atlascloud/icon-color.svg +1 -0
- package/plugins/atlascloud/icon.svg +1 -0
- package/plugins/atlascloud/plugin.js +49 -0
- package/plugins/atlascloud/plugin.json +23 -0
- package/plugins/augment/icon-color.svg +14 -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/bifrost/LICENSE +21 -0
- package/plugins/bifrost/plugin.js +361 -0
- package/plugins/bifrost/plugin.json +23 -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 +358 -161
- package/plugins/claude/plugin.json +1 -1
- 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 +87 -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-color.svg +92 -0
- package/plugins/codebuff/icon.svg +0 -0
- package/plugins/codebuff/plugin.js +0 -0
- package/plugins/codebuff/plugin.json +0 -0
- package/plugins/coderabbit/plugin.js +25 -0
- package/plugins/coderabbit/plugin.json +12 -0
- package/plugins/codex/icon-color.svg +0 -0
- package/plugins/codex/icon.svg +0 -0
- package/plugins/codex/plugin.js +8 -2
- package/plugins/codex/plugin.json +0 -0
- package/plugins/command-code/icon-color.svg +3 -0
- package/plugins/command-code/icon.svg +0 -0
- package/plugins/command-code/plugin.js +14 -3
- package/plugins/command-code/plugin.json +0 -0
- package/plugins/copilot/icon-color.svg +0 -0
- package/plugins/copilot/icon.svg +1 -1
- package/plugins/copilot/plugin.js +61 -9
- package/plugins/copilot/plugin.json +4 -1
- package/plugins/crof/icon-color.svg +3 -0
- 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-color.svg +3 -0
- package/plugins/crofai/icon.svg +2 -15
- package/plugins/crofai/plugin.js +0 -0
- package/plugins/crofai/plugin.json +0 -0
- package/plugins/cursor/icon-color.svg +1 -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-color.svg +1 -0
- package/plugins/deepinfra/icon.svg +1 -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 +150 -59
- package/plugins/deepseek/plugin.json +3 -1
- package/plugins/devin/icon.svg +0 -0
- package/plugins/devin/plugin.js +114 -0
- package/plugins/devin/plugin.json +2 -1
- package/plugins/devpass/LICENSE +21 -0
- package/plugins/devpass/plugin.js +115 -0
- package/plugins/devpass/plugin.json +23 -0
- 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-color.svg +4 -0
- 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-color.svg +1 -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-color.svg +1 -0
- package/plugins/fireworks-ai/icon.svg +1 -6
- 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/gemini-apps/LICENSE +22 -0
- package/plugins/gemini-apps/icon-color.svg +1 -0
- package/plugins/gemini-apps/icon.svg +1 -0
- package/plugins/gemini-apps/plugin.js +470 -0
- package/plugins/gemini-apps/plugin.json +19 -0
- package/plugins/gitkraken/LICENSE +21 -0
- package/plugins/gitkraken/plugin.js +153 -0
- package/plugins/gitkraken/plugin.json +24 -0
- package/plugins/grok/icon-color.svg +1 -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-color.svg +1 -0
- package/plugins/groqcloud/icon.svg +0 -0
- package/plugins/groqcloud/plugin.js +0 -0
- package/plugins/groqcloud/plugin.json +0 -0
- package/plugins/helmcode/LICENSE +21 -0
- package/plugins/helmcode/plugin.js +188 -0
- package/plugins/helmcode/plugin.json +24 -0
- package/plugins/huggingface/LICENSE +21 -0
- package/plugins/huggingface/icon-color.svg +1 -0
- package/plugins/huggingface/icon.svg +1 -0
- package/plugins/huggingface/plugin.js +293 -0
- package/plugins/huggingface/plugin.json +25 -0
- package/plugins/hyper/LICENSE +21 -0
- package/plugins/hyper/plugin.js +131 -0
- package/plugins/hyper/plugin.json +26 -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-color.svg +1 -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 +54 -3
- 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/llmman/LICENSE +21 -0
- package/plugins/llmman/plugin.js +133 -0
- package/plugins/llmman/plugin.json +23 -0
- package/plugins/longcat/icon-color.svg +1 -0
- package/plugins/longcat/icon.svg +1 -0
- package/plugins/longcat/plugin.js +182 -0
- package/plugins/longcat/plugin.json +23 -0
- package/plugins/manus/icon-color.svg +6 -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-color.svg +4 -0
- package/plugins/mimo/icon.svg +4 -1
- 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 +110 -5
- package/plugins/mistral/plugin.json +0 -0
- package/plugins/moonshot/icon-color.svg +1 -0
- package/plugins/moonshot/icon.svg +0 -0
- package/plugins/moonshot/plugin.js +8 -6
- package/plugins/moonshot/plugin.json +0 -0
- package/plugins/muse/LICENSE +21 -0
- package/plugins/muse/plugin.js +319 -0
- package/plugins/muse/plugin.json +26 -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/nous/LICENSE +21 -0
- package/plugins/nous/icon-color.svg +1 -0
- package/plugins/nous/icon.svg +1 -0
- package/plugins/nous/plugin.js +124 -0
- package/plugins/nous/plugin.json +24 -0
- package/plugins/ollama/icon-color.svg +1 -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-color.svg +1 -0
- 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-color.svg +1 -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-color.svg +1 -0
- package/plugins/opencode-go/icon-conflict.svg +0 -0
- package/plugins/opencode-go/icon.svg +0 -0
- package/plugins/opencode-go/plugin.js +103 -17
- package/plugins/opencode-go/plugin.json +5 -2
- package/plugins/openrouter/icon-color.svg +1 -0
- 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/pi/plugin.js +113 -0
- package/plugins/pi/plugin.json +11 -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-color.svg +1 -0
- package/plugins/qwencloud/icon.svg +1 -0
- package/plugins/qwencloud/plugin.js +677 -0
- package/plugins/qwencloud/plugin.json +29 -0
- package/plugins/raycast/LICENSE +21 -0
- package/plugins/raycast/plugin.js +174 -0
- package/plugins/raycast/plugin.json +24 -0
- package/plugins/replicate/LICENSE +21 -0
- package/plugins/replicate/icon-color.svg +1 -0
- package/plugins/replicate/icon.svg +1 -0
- package/plugins/replicate/plugin.js +165 -0
- package/plugins/replicate/plugin.json +24 -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-color.svg +14 -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/typesafe/LICENSE +21 -0
- package/plugins/typesafe/plugin.js +225 -0
- package/plugins/typesafe/plugin.json +24 -0
- package/plugins/v0/LICENSE +21 -0
- package/plugins/v0/icon-color.svg +1 -0
- package/plugins/v0/icon.svg +1 -0
- package/plugins/v0/plugin.js +214 -0
- package/plugins/v0/plugin.json +24 -0
- package/plugins/venice/icon-color.svg +0 -0
- package/plugins/venice/icon.svg +0 -0
- package/plugins/venice/plugin.js +50 -1
- package/plugins/venice/plugin.json +2 -1
- package/plugins/vercel/LICENSE +21 -0
- package/plugins/vercel/icon-color.svg +1 -0
- package/plugins/vercel/icon.svg +1 -0
- package/plugins/vercel/plugin.js +55 -0
- package/plugins/vercel/plugin.json +23 -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-color.svg +1 -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-color.svg +1 -0
- package/plugins/xai/icon.svg +1 -0
- package/plugins/xai/plugin.js +162 -0
- package/plugins/xai/plugin.json +28 -0
- package/plugins/xkiro/LICENSE +21 -0
- package/plugins/xkiro/plugin.js +99 -0
- package/plugins/xkiro/plugin.json +23 -0
- package/plugins/zai/icon-color.svg +1 -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-color.svg +1 -0
- package/plugins/zenmux/icon.svg +1 -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/ollama/plugin.js
CHANGED
|
@@ -1,92 +1,293 @@
|
|
|
1
1
|
(function () {
|
|
2
|
-
var SETTINGS_URL = "https://ollama.com/settings"
|
|
3
|
-
var
|
|
2
|
+
var SETTINGS_URL = "https://ollama.com/settings";
|
|
3
|
+
var TAGS_URL = "https://ollama.com/api/tags";
|
|
4
|
+
var VALIDATION_URL = "https://ollama.com/api/web_search";
|
|
5
|
+
var SESSION_COOKIE = "__Secure-session";
|
|
6
|
+
var SESSION_MS = 5 * 60 * 60 * 1000;
|
|
7
|
+
var WEEK_MS = 7 * 24 * 60 * 60 * 1000;
|
|
4
8
|
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
function clean(value) {
|
|
10
|
+
if (typeof value !== "string") return null;
|
|
11
|
+
var v = value.trim();
|
|
12
|
+
if (!v) return null;
|
|
13
|
+
if (
|
|
14
|
+
v.length >= 2 &&
|
|
15
|
+
((v.charAt(0) === '"' && v.charAt(v.length - 1) === '"') ||
|
|
16
|
+
(v.charAt(0) === "'" && v.charAt(v.length - 1) === "'"))
|
|
17
|
+
) {
|
|
18
|
+
v = v.slice(1, -1).trim();
|
|
19
|
+
}
|
|
20
|
+
return v || null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function env(ctx, name) {
|
|
24
|
+
try {
|
|
25
|
+
return clean(ctx.host.env.get(name));
|
|
26
|
+
} catch (_) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function setting(ctx, names) {
|
|
32
|
+
var settings = ctx.provider && ctx.provider.settings ? ctx.provider.settings : {};
|
|
33
|
+
for (var i = 0; i < names.length; i++) {
|
|
34
|
+
var value = clean(settings[names[i]]);
|
|
35
|
+
if (value) return value;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getApiKey(ctx) {
|
|
41
|
+
return clean(ctx.provider && ctx.provider.apiKey) ||
|
|
42
|
+
setting(ctx, ["apiKey", "ollamaApiKey"]) ||
|
|
43
|
+
env(ctx, "OLLAMA_API_KEY") ||
|
|
44
|
+
env(ctx, "OLLAMA_KEY");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getCookieHeader(ctx, optional) {
|
|
48
|
+
var raw = clean(ctx.provider && ctx.provider.cookieHeader) ||
|
|
49
|
+
setting(ctx, ["cookieHeader", "cookie"]) ||
|
|
50
|
+
env(ctx, "OLLAMA_COOKIE");
|
|
51
|
+
if (!raw) {
|
|
52
|
+
if (optional) return null;
|
|
53
|
+
throw "Set OLLAMA_COOKIE to your " + SESSION_COOKIE + " cookie value from ollama.com/settings.";
|
|
54
|
+
}
|
|
55
|
+
var val = raw;
|
|
56
|
+
if (val.toLowerCase().slice(0, 5) === "curl ") {
|
|
57
|
+
var cookieMatch = val.match(/(?:^|\s)-H\s+['"]Cookie:\s*([^'"]+)['"]/i) ||
|
|
58
|
+
val.match(/(?:^|\s)--header\s+['"]Cookie:\s*([^'"]+)['"]/i);
|
|
59
|
+
if (cookieMatch) val = cookieMatch[1].trim();
|
|
9
60
|
}
|
|
10
|
-
var val = raw.trim()
|
|
11
|
-
// Strip optional "cookie:" prefix
|
|
12
61
|
if (val.toLowerCase().slice(0, 7) === "cookie:") {
|
|
13
|
-
val = val.slice(7).trim()
|
|
62
|
+
val = val.slice(7).trim();
|
|
14
63
|
}
|
|
15
|
-
// If just a bare token value with no key=value format, wrap it
|
|
16
64
|
if (val.indexOf("=") < 0) {
|
|
17
|
-
return SESSION_COOKIE + "=" + val
|
|
65
|
+
return SESSION_COOKIE + "=" + val;
|
|
66
|
+
}
|
|
67
|
+
return val;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function request(ctx, req) {
|
|
71
|
+
try {
|
|
72
|
+
return ctx.host.http.request(req);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
ctx.host.log.warn("Ollama request failed: " + String(e));
|
|
75
|
+
throw "Ollama request failed. Check your connection.";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function parseJson(ctx, text) {
|
|
80
|
+
var parsed = ctx.util && typeof ctx.util.tryParseJson === "function"
|
|
81
|
+
? ctx.util.tryParseJson(text)
|
|
82
|
+
: null;
|
|
83
|
+
if (!parsed && text) {
|
|
84
|
+
try {
|
|
85
|
+
parsed = JSON.parse(text);
|
|
86
|
+
} catch (_) {}
|
|
87
|
+
}
|
|
88
|
+
return parsed;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function probeApi(ctx, apiKey) {
|
|
92
|
+
var validation = request(ctx, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
url: VALIDATION_URL,
|
|
95
|
+
headers: {
|
|
96
|
+
Authorization: "Bearer " + apiKey,
|
|
97
|
+
Accept: "application/json",
|
|
98
|
+
"Content-Type": "application/json",
|
|
99
|
+
"User-Agent": "usagestat/" + String(ctx.app && ctx.app.version ? ctx.app.version : "0.0.0"),
|
|
100
|
+
},
|
|
101
|
+
bodyText: '{"query":""}',
|
|
102
|
+
timeoutMs: 15000,
|
|
103
|
+
});
|
|
104
|
+
if (validation.status === 401 || validation.status === 403) {
|
|
105
|
+
throw "Ollama API key is invalid or revoked.";
|
|
106
|
+
}
|
|
107
|
+
if (validation.status !== 200 && validation.status !== 400) {
|
|
108
|
+
throw "Ollama API validation returned HTTP " + validation.status + ".";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var tags = request(ctx, {
|
|
112
|
+
method: "GET",
|
|
113
|
+
url: TAGS_URL,
|
|
114
|
+
headers: {
|
|
115
|
+
Authorization: "Bearer " + apiKey,
|
|
116
|
+
Accept: "application/json",
|
|
117
|
+
"User-Agent": "usagestat/" + String(ctx.app && ctx.app.version ? ctx.app.version : "0.0.0"),
|
|
118
|
+
},
|
|
119
|
+
timeoutMs: 15000,
|
|
120
|
+
});
|
|
121
|
+
if (tags.status === 401 || tags.status === 403) {
|
|
122
|
+
throw "Ollama API key is invalid or revoked.";
|
|
123
|
+
}
|
|
124
|
+
if (tags.status < 200 || tags.status >= 300) {
|
|
125
|
+
throw "Ollama API returned HTTP " + tags.status + ".";
|
|
18
126
|
}
|
|
19
|
-
|
|
127
|
+
var parsed = parseJson(ctx, tags.bodyText);
|
|
128
|
+
if (!parsed || !Array.isArray(parsed.models)) {
|
|
129
|
+
throw "Ollama API tags response was not valid JSON.";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
displayName: "Ollama",
|
|
134
|
+
source: "api",
|
|
135
|
+
plan: "API key",
|
|
136
|
+
lines: [
|
|
137
|
+
ctx.line.text({
|
|
138
|
+
label: "Cloud Models",
|
|
139
|
+
value: String(parsed.models.length) + " available",
|
|
140
|
+
}),
|
|
141
|
+
ctx.line.badge({
|
|
142
|
+
label: "Status",
|
|
143
|
+
text: "API key valid; quota unavailable",
|
|
144
|
+
color: "#22c55e",
|
|
145
|
+
}),
|
|
146
|
+
],
|
|
147
|
+
};
|
|
20
148
|
}
|
|
21
149
|
|
|
22
|
-
function
|
|
150
|
+
function blockEnd(html, start, currentLabel) {
|
|
151
|
+
var labels = ["Monthly usage", "Session usage", "Hourly usage", "Weekly usage"];
|
|
152
|
+
var end = html.length;
|
|
23
153
|
for (var i = 0; i < labels.length; i++) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
154
|
+
if (labels[i] === currentLabel) continue;
|
|
155
|
+
var idx = html.indexOf(labels[i], start + currentLabel.length);
|
|
156
|
+
if (idx >= 0 && idx < end) end = idx;
|
|
157
|
+
}
|
|
158
|
+
return Math.min(end, start + 4000);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function stripHtmlEntities(value) {
|
|
162
|
+
return String(value || "")
|
|
163
|
+
.replace(/ /g, " ")
|
|
164
|
+
.replace(/&/g, "&")
|
|
165
|
+
.replace(///g, "/")
|
|
166
|
+
.replace(/\s+/g, " ")
|
|
167
|
+
.trim();
|
|
168
|
+
}
|
|
27
169
|
|
|
28
|
-
|
|
29
|
-
|
|
170
|
+
function parseIso(segment) {
|
|
171
|
+
var match = String(segment || "").match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})/);
|
|
172
|
+
if (!match) return null;
|
|
173
|
+
var ms = Date.parse(match[0]);
|
|
174
|
+
return Number.isFinite(ms) ? new Date(ms).toISOString() : null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function parseResetDescription(segment) {
|
|
178
|
+
var match = String(segment || "").match(/(resets?\s+in\s+[^<\n\r]+|reset\s+[^<\n\r]+)/i);
|
|
179
|
+
return match ? stripHtmlEntities(match[1]) : null;
|
|
180
|
+
}
|
|
30
181
|
|
|
31
|
-
|
|
32
|
-
|
|
182
|
+
function parseUsageBlock(html, labels, periodMs) {
|
|
183
|
+
for (var i = 0; i < labels.length; i++) {
|
|
184
|
+
var label = labels[i];
|
|
185
|
+
var idx = html.indexOf(label);
|
|
186
|
+
if (idx < 0) continue;
|
|
187
|
+
var segment = html.slice(idx, blockEnd(html, idx, label));
|
|
188
|
+
var usedMatch = segment.match(/(\d+(?:\.\d+)?)\s*%\s*used/i);
|
|
189
|
+
var dollars = segment.match(/\$((?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?)\s+of\s+\$((?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?)\s+used/i);
|
|
190
|
+
var widthMatch = segment.match(/width:\s*(\d+(?:\.\d+)?)%/i);
|
|
191
|
+
var value = usedMatch ? parseFloat(usedMatch[1]) : null;
|
|
192
|
+
if (value === null && dollars) {
|
|
193
|
+
var spent = Number(dollars[1].replace(/,/g, ""));
|
|
194
|
+
var cap = Number(dollars[2].replace(/,/g, ""));
|
|
195
|
+
if (Number.isFinite(spent) && Number.isFinite(cap) && cap > 0) value = spent / cap * 100;
|
|
196
|
+
}
|
|
197
|
+
if (!Number.isFinite(value) && widthMatch) value = parseFloat(widthMatch[1]);
|
|
198
|
+
if (Number.isFinite(value)) {
|
|
199
|
+
return {
|
|
200
|
+
used: Math.max(0, Math.min(100, value)),
|
|
201
|
+
resetsAt: parseIso(segment),
|
|
202
|
+
detail: parseResetDescription(segment),
|
|
203
|
+
periodMs: label === "Hourly usage" ? null : periodMs,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
33
206
|
}
|
|
34
|
-
return null
|
|
207
|
+
return null;
|
|
35
208
|
}
|
|
36
209
|
|
|
37
|
-
function
|
|
38
|
-
var
|
|
210
|
+
function progress(ctx, label, block) {
|
|
211
|
+
var line = {
|
|
212
|
+
label: label,
|
|
213
|
+
used: block.used,
|
|
214
|
+
limit: 100,
|
|
215
|
+
format: { kind: "percent" },
|
|
216
|
+
};
|
|
217
|
+
if (block.periodMs) line.periodDurationMs = block.periodMs;
|
|
218
|
+
if (block.resetsAt) line.resetsAt = block.resetsAt;
|
|
219
|
+
if (block.detail) line.detail = block.detail;
|
|
220
|
+
return ctx.line.progress(line);
|
|
221
|
+
}
|
|
39
222
|
|
|
40
|
-
|
|
223
|
+
function probeWeb(ctx) {
|
|
224
|
+
var cookieHeader = getCookieHeader(ctx);
|
|
225
|
+
var resp = request(ctx, {
|
|
41
226
|
method: "GET",
|
|
42
227
|
url: SETTINGS_URL,
|
|
43
228
|
headers: {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"User-Agent": "Mozilla/5.0 (
|
|
229
|
+
Cookie: cookieHeader,
|
|
230
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
231
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/143.0.0.0 Safari/537.36",
|
|
47
232
|
},
|
|
48
233
|
timeoutMs: 20000,
|
|
49
|
-
})
|
|
234
|
+
});
|
|
50
235
|
|
|
51
236
|
if (resp.status === 401 || resp.status === 403) {
|
|
52
|
-
throw "Session expired. Update OLLAMA_COOKIE with a fresh session token."
|
|
237
|
+
throw "Session expired. Update OLLAMA_COOKIE with a fresh session token.";
|
|
53
238
|
}
|
|
54
239
|
|
|
55
|
-
var html = resp.bodyText || ""
|
|
56
|
-
|
|
57
|
-
if (!html.includes("Session usage") && !html.includes("Weekly usage") && !html.includes("Cloud Usage")) {
|
|
240
|
+
var html = resp.bodyText || "";
|
|
241
|
+
if (!html.includes("Monthly usage") && !html.includes("Session usage") && !html.includes("Weekly usage") && !html.includes("Cloud Usage")) {
|
|
58
242
|
if (html.includes("Sign in") || resp.status !== 200) {
|
|
59
|
-
throw "Session expired. Update OLLAMA_COOKIE with a fresh session token."
|
|
243
|
+
throw "Session expired. Update OLLAMA_COOKIE with a fresh session token.";
|
|
60
244
|
}
|
|
61
245
|
}
|
|
62
246
|
|
|
63
|
-
var
|
|
64
|
-
|
|
247
|
+
var monthly = parseUsageBlock(html, ["Monthly usage"], null);
|
|
248
|
+
if (monthly && monthly.resetsAt) monthly.periodMs = ctx.util.calendarMonthDuration(monthly.resetsAt);
|
|
249
|
+
var session = parseUsageBlock(html, ["Session usage", "Hourly usage"], SESSION_MS);
|
|
250
|
+
var weekly = parseUsageBlock(html, ["Weekly usage"], WEEK_MS);
|
|
65
251
|
|
|
66
|
-
if (
|
|
67
|
-
throw "Could not find usage data on Ollama settings page."
|
|
252
|
+
if (!monthly && !session && !weekly) {
|
|
253
|
+
throw "Could not find usage data on Ollama settings page.";
|
|
68
254
|
}
|
|
69
255
|
|
|
70
|
-
var lines = []
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
256
|
+
var lines = [];
|
|
257
|
+
if (monthly) lines.push(progress(ctx, "Monthly credits", monthly));
|
|
258
|
+
if (session) lines.push(progress(ctx, "Session", session));
|
|
259
|
+
if (weekly) lines.push(progress(ctx, "Weekly", weekly));
|
|
260
|
+
|
|
261
|
+
var planMatch = html.match(/(?:Included usage|Cloud Usage)\s*<\/span>\s*<span[^>]*>([^<]+)<\/span/i);
|
|
262
|
+
return {
|
|
263
|
+
displayName: "Ollama",
|
|
264
|
+
source: "web",
|
|
265
|
+
plan: planMatch ? stripHtmlEntities(planMatch[1]) : undefined,
|
|
266
|
+
lines: lines,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function probe(ctx) {
|
|
271
|
+
var source = String(ctx.sourceMode || "auto").toLowerCase();
|
|
272
|
+
var apiKey = getApiKey(ctx);
|
|
273
|
+
if (source === "api") {
|
|
274
|
+
if (!apiKey) throw "Missing Ollama API key. Set OLLAMA_API_KEY.";
|
|
275
|
+
return probeApi(ctx, apiKey);
|
|
78
276
|
}
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
277
|
+
if (source === "web") return probeWeb(ctx);
|
|
278
|
+
if (getCookieHeader(ctx, true)) {
|
|
279
|
+
try {
|
|
280
|
+
return probeWeb(ctx);
|
|
281
|
+
} catch (e) {
|
|
282
|
+
if (!apiKey) throw e;
|
|
283
|
+
ctx.host.log.warn("Ollama settings unavailable; checking API access.");
|
|
284
|
+
}
|
|
86
285
|
}
|
|
87
|
-
|
|
88
|
-
|
|
286
|
+
if (apiKey) {
|
|
287
|
+
return probeApi(ctx, apiKey);
|
|
288
|
+
}
|
|
289
|
+
return probeWeb(ctx);
|
|
89
290
|
}
|
|
90
291
|
|
|
91
|
-
globalThis.__openusage_plugin = { id: "ollama", probe: probe }
|
|
92
|
-
})()
|
|
292
|
+
globalThis.__openusage_plugin = { id: "ollama", probe: probe };
|
|
293
|
+
})();
|
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
"links": [
|
|
9
9
|
{ "label": "Settings", "url": "https://ollama.com/settings" }
|
|
10
10
|
],
|
|
11
|
-
"envVars": ["OLLAMA_COOKIE"],
|
|
12
|
-
"supportedModes": ["web"],
|
|
11
|
+
"envVars": ["OLLAMA_COOKIE", "OLLAMA_API_KEY", "OLLAMA_KEY"],
|
|
12
|
+
"supportedModes": ["api", "web"],
|
|
13
13
|
"autoMode": "web",
|
|
14
14
|
"webUrl": "https://ollama.com",
|
|
15
15
|
"usageDashboardUrl": "https://ollama.com/settings",
|
|
16
16
|
"lines": [
|
|
17
17
|
{ "type": "progress", "label": "Session", "scope": "overview" },
|
|
18
|
-
{ "type": "progress", "label": "Weekly", "scope": "detail" }
|
|
18
|
+
{ "type": "progress", "label": "Weekly", "scope": "detail" },
|
|
19
|
+
{ "type": "progress", "label": "Monthly credits", "scope": "overview" },
|
|
20
|
+
{ "type": "text", "label": "Cloud Models", "scope": "detail" },
|
|
21
|
+
{ "type": "badge", "label": "Status", "scope": "detail" }
|
|
19
22
|
]
|
|
20
23
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>OpenAI</title><path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"></path></svg>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>opencode</title><path d="M16 6H8v12h8V6zm4 16H4V2h16v20z"></path></svg>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>opencode</title><path d="M16 6H8v12h8V6zm4 16H4V2h16v20z"></path></svg>
|
|
File without changes
|
|
File without changes
|
|
@@ -18,22 +18,27 @@
|
|
|
18
18
|
WHERE json_valid(data)
|
|
19
19
|
AND json_extract(data, '$.providerID') = 'opencode-go'
|
|
20
20
|
AND json_extract(data, '$.role') = 'assistant'
|
|
21
|
-
AND json_type(data, '$.cost') IN ('integer', 'real')
|
|
22
21
|
LIMIT 1
|
|
23
22
|
`;
|
|
24
23
|
|
|
25
24
|
const HISTORY_ROWS_SQL = `
|
|
26
25
|
SELECT
|
|
27
26
|
CAST(COALESCE(json_extract(data, '$.time.created'), time_created) AS INTEGER) AS createdMs,
|
|
28
|
-
|
|
27
|
+
json_extract(data, '$.cost') AS cost,
|
|
28
|
+
json_extract(data, '$.tokens.input') AS inputTokens,
|
|
29
|
+
json_extract(data, '$.tokens.output') AS outputTokens,
|
|
30
|
+
json_extract(data, '$.tokens.cache.read') AS cacheReadTokens,
|
|
31
|
+
json_extract(data, '$.tokens.cache.write') AS cacheCreationTokens,
|
|
32
|
+
json_extract(data, '$.tokens.reasoning') AS reasoningOutputTokens
|
|
29
33
|
FROM message
|
|
30
34
|
WHERE json_valid(data)
|
|
31
35
|
AND json_extract(data, '$.providerID') = 'opencode-go'
|
|
32
36
|
AND json_extract(data, '$.role') = 'assistant'
|
|
33
|
-
AND json_type(data, '$.cost') IN ('integer', 'real')
|
|
34
37
|
`;
|
|
35
38
|
|
|
36
39
|
function readNumber(value) {
|
|
40
|
+
if (typeof value !== "number" && typeof value !== "string") return null;
|
|
41
|
+
if (typeof value === "string" && !value.trim()) return null;
|
|
37
42
|
const n = Number(value);
|
|
38
43
|
return Number.isFinite(n) ? n : null;
|
|
39
44
|
}
|
|
@@ -318,6 +323,8 @@
|
|
|
318
323
|
}
|
|
319
324
|
|
|
320
325
|
function loadAuthKey(ctx) {
|
|
326
|
+
const configured = trim(ctx.provider && ctx.provider.apiKey) || env(ctx, "OPENCODE_GO_API_KEY");
|
|
327
|
+
if (configured) return configured;
|
|
321
328
|
const inline = ctx.host.env.get("OPENCODE_AUTH_CONTENT");
|
|
322
329
|
const path = inline ? null : dataPath(ctx, "auth.json");
|
|
323
330
|
if (!inline && !ctx.host.fs.exists(path)) return null;
|
|
@@ -357,9 +364,15 @@
|
|
|
357
364
|
if (!row || typeof row !== "object") continue;
|
|
358
365
|
const createdMs = readNumber(row.createdMs);
|
|
359
366
|
const cost = readNumber(row.cost);
|
|
360
|
-
if (createdMs === null || createdMs <= 0) continue;
|
|
361
|
-
|
|
362
|
-
|
|
367
|
+
if (createdMs === null || createdMs <= 0 || createdMs > Date.parse(ctx.nowIso)) continue;
|
|
368
|
+
const entry = { createdMs, cost: cost !== null && cost >= 0 ? cost : 0, costKnown: cost !== null && cost >= 0, tokensKnown: false };
|
|
369
|
+
for (const key of ['inputTokens','outputTokens','cacheReadTokens','cacheCreationTokens','reasoningOutputTokens']) {
|
|
370
|
+
const value = readNumber(row[key]);
|
|
371
|
+
entry[key] = value !== null && value >= 0 ? value : 0;
|
|
372
|
+
if (value !== null && value >= 0 && key !== 'reasoningOutputTokens') entry.tokensKnown = true;
|
|
373
|
+
}
|
|
374
|
+
entry.totalTokens = entry.inputTokens + entry.outputTokens + entry.cacheReadTokens + entry.cacheCreationTokens;
|
|
375
|
+
if (entry.costKnown || entry.tokensKnown) rows.push(entry);
|
|
363
376
|
}
|
|
364
377
|
|
|
365
378
|
return { ok: true, rows };
|
|
@@ -438,10 +451,9 @@
|
|
|
438
451
|
text,
|
|
439
452
|
new RegExp(name + "[^}]*?(?:resetInSec|resetInSeconds|resetSeconds|resetSec)\\s*[:=]\\s*([0-9]+)")
|
|
440
453
|
) || 0;
|
|
441
|
-
const normalized = percent <= 1 ? percent * 100 : percent;
|
|
442
454
|
return {
|
|
443
|
-
percent: Math.max(0, Math.min(100,
|
|
444
|
-
resetsAt: toIso(Date.now() +
|
|
455
|
+
percent: Math.max(0, Math.min(100, percent)),
|
|
456
|
+
resetsAt: reset > 0 ? toIso(Date.now() + reset * 1000) : null,
|
|
445
457
|
};
|
|
446
458
|
}
|
|
447
459
|
return null;
|
|
@@ -560,11 +572,52 @@
|
|
|
560
572
|
if (!parsed.lines.length) {
|
|
561
573
|
throw "OpenCode Go parse error: missing usage and balance fields.";
|
|
562
574
|
}
|
|
563
|
-
return { plan: "Go", lines: parsed.lines };
|
|
575
|
+
return { plan: "Go", source: "web", lines: parsed.lines };
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function fetchApiResult(ctx, key) {
|
|
579
|
+
const result = ctx.util.requestJson({
|
|
580
|
+
method: "GET", url: BASE_URL + "/zen/go/v1/usage",
|
|
581
|
+
headers: { Authorization: "Bearer " + key, Accept: "application/json" }, timeoutMs: 15000,
|
|
582
|
+
});
|
|
583
|
+
if (result.resp.status === 403 && result.json && result.json.error && result.json.error.type === "EntitlementError")
|
|
584
|
+
throw "No OpenCode Go subscription on this key.";
|
|
585
|
+
if (ctx.util.isAuthStatus(result.resp.status)) throw "OpenCode Go key was rejected. Sign into OpenCode Go again.";
|
|
586
|
+
if (result.resp.status < 200 || result.resp.status >= 300) throw "OpenCode Go usage API returned HTTP " + result.resp.status + ".";
|
|
587
|
+
const usage = result.json && result.json.usage;
|
|
588
|
+
const specs = [["rolling", "Session", FIVE_HOURS_MS], ["weekly", "Weekly", WEEK_MS], ["monthly", "Monthly", null]];
|
|
589
|
+
const lines = [];
|
|
590
|
+
for (const spec of specs) {
|
|
591
|
+
const window = usage && usage[spec[0]];
|
|
592
|
+
if (window == null && spec[0] !== "rolling") continue;
|
|
593
|
+
const percent = window && readNumber(window.percent);
|
|
594
|
+
if (!Number.isFinite(percent)) throw "OpenCode Go usage API returned an invalid quota.";
|
|
595
|
+
const resetInSec = readNumber(window.resetInSec);
|
|
596
|
+
const resetsAt = ctx.util.toIso(window.resetsAt) || (resetInSec !== null && resetInSec >= 0
|
|
597
|
+
? ctx.util.toIso(Date.parse(ctx.nowIso) + resetInSec * 1000) : null);
|
|
598
|
+
lines.push(ctx.line.progress({ label: spec[1], used: Math.max(0, Math.min(100, percent)), limit: 100,
|
|
599
|
+
format: { kind: "percent" }, resetsAt, periodDurationMs: spec[2] }));
|
|
600
|
+
}
|
|
601
|
+
return { plan: "Go", source: "api", lines };
|
|
564
602
|
}
|
|
565
603
|
|
|
566
604
|
function probe(ctx) {
|
|
605
|
+
const source = ctx.sourceMode || "auto";
|
|
606
|
+
if (source === "web") {
|
|
607
|
+
const cookie = cookieHeader(ctx);
|
|
608
|
+
if (!cookie) throw "OpenCode Go session cookie is missing.";
|
|
609
|
+
return fetchWebResult(ctx, cookie);
|
|
610
|
+
}
|
|
567
611
|
const authKey = loadAuthKey(ctx);
|
|
612
|
+
if (source === "api" && !authKey) throw "OpenCode Go API key missing. Set OPENCODE_GO_API_KEY.";
|
|
613
|
+
if (authKey && source !== "local") {
|
|
614
|
+
const result = fetchApiResult(ctx, authKey);
|
|
615
|
+
try {
|
|
616
|
+
const history = loadHistory(ctx);
|
|
617
|
+
if (history.ok) attachCostHistory(ctx, result.lines, history.rows);
|
|
618
|
+
} catch (_) { ctx.host.log.warn("OpenCode Go local cost history unavailable"); }
|
|
619
|
+
return result;
|
|
620
|
+
}
|
|
568
621
|
const history = hasHistory(ctx);
|
|
569
622
|
const cookie = cookieHeader(ctx);
|
|
570
623
|
const detected = !!authKey || !!cookie || (history.ok && history.present);
|
|
@@ -574,13 +627,16 @@
|
|
|
574
627
|
}
|
|
575
628
|
|
|
576
629
|
if (!history.ok && !cookie) {
|
|
577
|
-
return { plan: "Go", lines: buildSoftEmptyLines(ctx) };
|
|
630
|
+
return { plan: "Go", source: "local", lines: buildSoftEmptyLines(ctx) };
|
|
578
631
|
}
|
|
579
632
|
|
|
580
633
|
const rowsResult = loadHistory(ctx);
|
|
581
634
|
if (rowsResult.ok && rowsResult.rows.length > 0) {
|
|
582
|
-
const lines =
|
|
583
|
-
|
|
635
|
+
const lines = rowsResult.rows.every(row => row.costKnown)
|
|
636
|
+
? buildProgressLines(ctx, rowsResult.rows, readNowMs()) : [];
|
|
637
|
+
for (const line of lines) line.detail = "Estimated from local API-rate costs";
|
|
638
|
+
attachCostHistory(ctx, lines, rowsResult.rows);
|
|
639
|
+
if (cookie && source !== "local") {
|
|
584
640
|
try {
|
|
585
641
|
const webResult = fetchWebResult(ctx, cookie);
|
|
586
642
|
for (let i = 0; i < webResult.lines.length; i += 1) {
|
|
@@ -592,10 +648,10 @@
|
|
|
592
648
|
ctx.host.log.warn("OpenCode Go web enrichment failed: " + String(e));
|
|
593
649
|
}
|
|
594
650
|
}
|
|
595
|
-
return { plan: "Go", lines };
|
|
651
|
+
return { plan: "Go", source: "local-estimate", lines };
|
|
596
652
|
}
|
|
597
653
|
|
|
598
|
-
if (cookie) {
|
|
654
|
+
if (cookie && source !== "local") {
|
|
599
655
|
try {
|
|
600
656
|
return fetchWebResult(ctx, cookie);
|
|
601
657
|
} catch (e) {
|
|
@@ -604,14 +660,44 @@
|
|
|
604
660
|
}
|
|
605
661
|
|
|
606
662
|
if (!rowsResult.ok) {
|
|
607
|
-
return { plan: "Go", lines: buildSoftEmptyLines(ctx) };
|
|
663
|
+
return { plan: "Go", source: "local", lines: buildSoftEmptyLines(ctx) };
|
|
608
664
|
}
|
|
609
665
|
|
|
610
666
|
return {
|
|
611
667
|
plan: "Go",
|
|
612
|
-
|
|
668
|
+
source: "local",
|
|
669
|
+
lines: buildSoftEmptyLines(ctx),
|
|
613
670
|
};
|
|
614
671
|
}
|
|
615
672
|
|
|
673
|
+
function attachCostHistory(ctx, lines, rows) {
|
|
674
|
+
const byDay = {};
|
|
675
|
+
const now = Date.parse(ctx.nowIso);
|
|
676
|
+
const since = Date.parse(ctx.nowIso.slice(0, 10)) - 29 * 86400000;
|
|
677
|
+
for (const row of rows) {
|
|
678
|
+
if (row.createdMs < since || row.createdMs > now) continue;
|
|
679
|
+
const date = toIso(row.createdMs);
|
|
680
|
+
if (!date) continue;
|
|
681
|
+
const day = date.slice(0, 10);
|
|
682
|
+
const daily = byDay[day] || (byDay[day] = { date: day, costUsd: 0, tokensKnown: true, costKnown: true,
|
|
683
|
+
inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0, reasoningOutputTokens: 0, totalTokens: 0, requests: 0 });
|
|
684
|
+
daily.costUsd += row.cost;
|
|
685
|
+
daily.costKnown = daily.costKnown && row.costKnown;
|
|
686
|
+
daily.tokensKnown = daily.tokensKnown && row.tokensKnown;
|
|
687
|
+
daily.requests++;
|
|
688
|
+
for (const key of ['inputTokens','outputTokens','cacheReadTokens','cacheCreationTokens','reasoningOutputTokens','totalTokens']) daily[key] += row[key];
|
|
689
|
+
}
|
|
690
|
+
const daily = Object.keys(byDay).sort().map((day) => byDay[day]);
|
|
691
|
+
if (!daily.length) return;
|
|
692
|
+
const total = daily.reduce((n, day) => n + day.costUsd, 0), known = daily.every(day => day.costKnown);
|
|
693
|
+
lines.push(ctx.line.text({ label: "Local Cost", value: known ? "$" + total.toFixed(2) : total > 0 ? "$" + total.toFixed(2) + "+" : "Unpriced", subtitle: "Last 30 days; estimated API-rate cost" }));
|
|
694
|
+
lines.push(ctx.line.text({ label: "Local Tokens", value: String(daily.reduce((n, day) => n + day.totalTokens, 0)) + (daily.every(day => day.tokensKnown) ? "" : "+"), subtitle: "Last 30 days" }));
|
|
695
|
+
lines.push(ctx.line.barChart({ label: "Cost History", points: daily.map((day) => ({ label: day.date, value: day.costUsd, valueLabel: day.costKnown ? "$" + day.costUsd.toFixed(2) : day.costUsd > 0 ? "$" + day.costUsd.toFixed(2) + "+" : "Unpriced" })),
|
|
696
|
+
note: "Estimated from local OpenCode Go messages, not a subscription bill.", color: "#4d9f75" }));
|
|
697
|
+
try {
|
|
698
|
+
ctx.host.usageDaily.ingest({ displayName: "OpenCode Go", source: "opencode_go_local_estimated", daily });
|
|
699
|
+
} catch (_) { ctx.host.log.warn("Could not persist OpenCode Go daily costs"); }
|
|
700
|
+
}
|
|
701
|
+
|
|
616
702
|
globalThis.__openusage_plugin = { id: PROVIDER_ID, probe };
|
|
617
703
|
})();
|
|
@@ -17,9 +17,12 @@
|
|
|
17
17
|
}
|
|
18
18
|
],
|
|
19
19
|
"usageDashboardUrl": "https://opencode.ai/auth",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
20
|
+
"envVars": ["OPENCODE_GO_API_KEY", "OPENCODE_GO_COOKIE", "OPENCODE_GO_WORKSPACE_ID"],
|
|
21
|
+
"supportedModes": ["api", "local", "web"],
|
|
22
|
+
"autoMode": "api",
|
|
22
23
|
"lines": [
|
|
24
|
+
{ "type": "text", "label": "Local Cost", "scope": "detail" },
|
|
25
|
+
{ "type": "barChart", "label": "Cost History", "scope": "detail" },
|
|
23
26
|
{
|
|
24
27
|
"type": "progress",
|
|
25
28
|
"label": "Session",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>OpenRouter</title><path d="M16.804 1.957l7.22 4.105v.087L16.73 10.21l.017-2.117-.821-.03c-1.059-.028-1.611.002-2.268.11-1.064.175-2.038.577-3.147 1.352L8.345 11.03c-.284.195-.495.336-.68.455l-.515.322-.397.234.385.23.53.338c.476.314 1.17.796 2.701 1.866 1.11.775 2.083 1.177 3.147 1.352l.3.045c.694.091 1.375.094 2.825.033l.022-2.159 7.22 4.105v.087L16.589 22l.014-1.862-.635.022c-1.386.042-2.137.002-3.138-.162-1.694-.28-3.26-.926-4.881-2.059l-2.158-1.5a21.997 21.997 0 00-.755-.498l-.467-.28a55.927 55.927 0 00-.76-.43C2.908 14.73.563 14.116 0 14.116V9.888l.14.004c.564-.007 2.91-.622 3.809-1.124l1.016-.58.438-.274c.428-.28 1.072-.726 2.686-1.853 1.621-1.133 3.186-1.78 4.881-2.059 1.152-.19 1.974-.213 3.814-.138l.02-1.907z"></path></svg>
|
|
File without changes
|