@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.
Files changed (273) hide show
  1. package/LICENSE +0 -0
  2. package/native-manifest.json +365 -77
  3. package/package.json +49 -1
  4. package/plugins/UPSTREAM-LICENSES.md +109 -0
  5. package/plugins/abacus-ai/icon.svg +0 -0
  6. package/plugins/abacus-ai/plugin.js +0 -0
  7. package/plugins/abacus-ai/plugin.json +0 -0
  8. package/plugins/aiand/icon.svg +6 -0
  9. package/plugins/aiand/plugin.js +108 -0
  10. package/plugins/aiand/plugin.json +22 -0
  11. package/plugins/alibaba/icon-color.svg +0 -0
  12. package/plugins/alibaba/icon.svg +0 -0
  13. package/plugins/alibaba/plugin.js +0 -0
  14. package/plugins/alibaba/plugin.json +0 -0
  15. package/plugins/alibaba-token-plan/icon-color.svg +0 -0
  16. package/plugins/alibaba-token-plan/icon.svg +0 -0
  17. package/plugins/alibaba-token-plan/plugin.js +0 -0
  18. package/plugins/alibaba-token-plan/plugin.json +0 -0
  19. package/plugins/amp/icon-color.svg +0 -0
  20. package/plugins/amp/icon-conflict.svg +0 -0
  21. package/plugins/amp/icon.svg +0 -0
  22. package/plugins/amp/plugin.js +0 -0
  23. package/plugins/amp/plugin.json +0 -0
  24. package/plugins/antigravity/icon-color.svg +0 -0
  25. package/plugins/antigravity/icon-conflict.svg +0 -0
  26. package/plugins/antigravity/icon.svg +0 -0
  27. package/plugins/antigravity/plugin.js +0 -0
  28. package/plugins/antigravity/plugin.json +0 -0
  29. package/plugins/antigravity-cli/icon.svg +0 -0
  30. package/plugins/antigravity-cli/plugin.js +0 -0
  31. package/plugins/antigravity-cli/plugin.json +0 -0
  32. package/plugins/antigravity-ide/icon.svg +0 -0
  33. package/plugins/antigravity-ide/plugin.js +0 -0
  34. package/plugins/antigravity-ide/plugin.json +0 -0
  35. package/plugins/augment/icon.svg +0 -0
  36. package/plugins/augment/plugin.js +0 -0
  37. package/plugins/augment/plugin.json +0 -0
  38. package/plugins/aws-bedrock/icon-color.svg +0 -0
  39. package/plugins/aws-bedrock/icon.svg +0 -0
  40. package/plugins/aws-bedrock/plugin.js +0 -0
  41. package/plugins/aws-bedrock/plugin.json +0 -0
  42. package/plugins/azure-openai/icon-color.svg +0 -0
  43. package/plugins/azure-openai/icon.svg +0 -0
  44. package/plugins/azure-openai/plugin.js +0 -0
  45. package/plugins/azure-openai/plugin.json +0 -0
  46. package/plugins/chutes/icon.svg +0 -0
  47. package/plugins/chutes/plugin.js +0 -0
  48. package/plugins/chutes/plugin.json +0 -0
  49. package/plugins/claude/icon-color.svg +0 -0
  50. package/plugins/claude/icon.svg +0 -0
  51. package/plugins/claude/plugin.js +70 -23
  52. package/plugins/claude/plugin.json +0 -0
  53. package/plugins/clawrouter/icon.svg +7 -0
  54. package/plugins/clawrouter/plugin.js +45 -0
  55. package/plugins/clawrouter/plugin.json +22 -0
  56. package/plugins/clinepass/icon.svg +4 -0
  57. package/plugins/clinepass/plugin.js +71 -0
  58. package/plugins/clinepass/plugin.json +23 -0
  59. package/plugins/codebuddy/plugin.js +331 -0
  60. package/plugins/codebuddy/plugin.json +30 -0
  61. package/plugins/codebuff/icon.svg +0 -0
  62. package/plugins/codebuff/plugin.js +0 -0
  63. package/plugins/codebuff/plugin.json +0 -0
  64. package/plugins/codex/icon-color.svg +0 -0
  65. package/plugins/codex/icon.svg +0 -0
  66. package/plugins/codex/plugin.js +7 -2
  67. package/plugins/codex/plugin.json +0 -0
  68. package/plugins/command-code/icon.svg +0 -0
  69. package/plugins/command-code/plugin.js +7 -0
  70. package/plugins/command-code/plugin.json +0 -0
  71. package/plugins/copilot/icon-color.svg +0 -0
  72. package/plugins/copilot/icon.svg +0 -0
  73. package/plugins/copilot/plugin.js +61 -9
  74. package/plugins/copilot/plugin.json +4 -1
  75. package/plugins/crof/icon.svg +0 -0
  76. package/plugins/crof/plugin.js +0 -0
  77. package/plugins/crof/plugin.json +0 -0
  78. package/plugins/crofai/icon.svg +0 -0
  79. package/plugins/crofai/plugin.js +0 -0
  80. package/plugins/crofai/plugin.json +0 -0
  81. package/plugins/cursor/icon-conflict.svg +0 -0
  82. package/plugins/cursor/icon.svg +0 -0
  83. package/plugins/cursor/plugin.js +46 -9
  84. package/plugins/cursor/plugin.json +3 -2
  85. package/plugins/cursor-nightly/icon.png +0 -0
  86. package/plugins/cursor-nightly/plugin.js +45 -8
  87. package/plugins/cursor-nightly/plugin.json +3 -2
  88. package/plugins/deepgram/icon.svg +0 -0
  89. package/plugins/deepgram/plugin.js +0 -0
  90. package/plugins/deepgram/plugin.json +0 -0
  91. package/plugins/deepinfra/icon.svg +4 -0
  92. package/plugins/deepinfra/plugin.js +97 -0
  93. package/plugins/deepinfra/plugin.json +28 -0
  94. package/plugins/deepseek/icon-color.svg +0 -0
  95. package/plugins/deepseek/icon.svg +0 -0
  96. package/plugins/deepseek/plugin.js +0 -0
  97. package/plugins/deepseek/plugin.json +0 -0
  98. package/plugins/devin/icon.svg +0 -0
  99. package/plugins/devin/plugin.js +111 -0
  100. package/plugins/devin/plugin.json +2 -1
  101. package/plugins/doubao/icon-color.svg +0 -0
  102. package/plugins/doubao/icon.svg +0 -0
  103. package/plugins/doubao/plugin.js +0 -0
  104. package/plugins/doubao/plugin.json +0 -0
  105. package/plugins/droid/icon.svg +0 -0
  106. package/plugins/droid/plugin.json +2 -1
  107. package/plugins/elevenlabs/icon.svg +0 -0
  108. package/plugins/elevenlabs/plugin.js +17 -2
  109. package/plugins/elevenlabs/plugin.json +0 -0
  110. package/plugins/factory/icon-conflict.svg +0 -0
  111. package/plugins/factory/icon.svg +0 -0
  112. package/plugins/factory/plugin.js +38 -14
  113. package/plugins/factory/plugin.json +2 -1
  114. package/plugins/fireworks-ai/icon.svg +0 -0
  115. package/plugins/fireworks-ai/plugin.js +0 -0
  116. package/plugins/fireworks-ai/plugin.json +0 -0
  117. package/plugins/gemini/icon-color.svg +0 -0
  118. package/plugins/gemini/icon.svg +0 -0
  119. package/plugins/gemini/plugin.js +0 -0
  120. package/plugins/gemini/plugin.json +0 -0
  121. package/plugins/grok/icon.svg +0 -0
  122. package/plugins/grok/plugin.js +30 -5
  123. package/plugins/grok/plugin.json +3 -1
  124. package/plugins/groqcloud/icon.svg +0 -0
  125. package/plugins/groqcloud/plugin.js +0 -0
  126. package/plugins/groqcloud/plugin.json +0 -0
  127. package/plugins/ibmbob/icon.svg +23 -0
  128. package/plugins/ibmbob/plugin.js +61 -0
  129. package/plugins/ibmbob/plugin.json +22 -0
  130. package/plugins/jetbrains-ai-assistant/icon-color.svg +0 -0
  131. package/plugins/jetbrains-ai-assistant/icon-conflict.svg +0 -0
  132. package/plugins/jetbrains-ai-assistant/icon.svg +0 -0
  133. package/plugins/jetbrains-ai-assistant/plugin.js +0 -0
  134. package/plugins/jetbrains-ai-assistant/plugin.json +0 -0
  135. package/plugins/kilo/icon.svg +0 -0
  136. package/plugins/kilo/plugin.js +0 -0
  137. package/plugins/kilo/plugin.json +0 -0
  138. package/plugins/kimi/icon-color.svg +0 -0
  139. package/plugins/kimi/icon-conflict.svg +0 -0
  140. package/plugins/kimi/icon.svg +0 -0
  141. package/plugins/kimi/plugin.js +122 -141
  142. package/plugins/kimi/plugin.json +5 -2
  143. package/plugins/kimi-k2/icon-color.svg +0 -0
  144. package/plugins/kimi-k2/icon.svg +0 -0
  145. package/plugins/kimi-k2/plugin.js +0 -0
  146. package/plugins/kimi-k2/plugin.json +0 -0
  147. package/plugins/kiro/icon-color.svg +0 -0
  148. package/plugins/kiro/icon-conflict.svg +0 -0
  149. package/plugins/kiro/icon.svg +0 -0
  150. package/plugins/kiro/plugin.js +49 -10
  151. package/plugins/kiro/plugin.json +3 -1
  152. package/plugins/litellm/icon.svg +0 -0
  153. package/plugins/litellm/plugin.js +0 -0
  154. package/plugins/litellm/plugin.json +0 -0
  155. package/plugins/llm-proxy/icon.svg +0 -0
  156. package/plugins/llm-proxy/plugin.js +0 -0
  157. package/plugins/llm-proxy/plugin.json +0 -0
  158. package/plugins/longcat/icon.svg +4 -0
  159. package/plugins/longcat/plugin.js +182 -0
  160. package/plugins/longcat/plugin.json +23 -0
  161. package/plugins/manus/icon.svg +0 -0
  162. package/plugins/manus/plugin.js +0 -0
  163. package/plugins/manus/plugin.json +0 -0
  164. package/plugins/mimo/icon.svg +0 -0
  165. package/plugins/mimo/plugin.js +0 -0
  166. package/plugins/mimo/plugin.json +0 -0
  167. package/plugins/minimax/icon-color.svg +0 -0
  168. package/plugins/minimax/icon-conflict.svg +0 -0
  169. package/plugins/minimax/icon.svg +0 -0
  170. package/plugins/minimax/plugin.js +25 -31
  171. package/plugins/minimax/plugin.json +0 -0
  172. package/plugins/mistral/icon-color.svg +0 -0
  173. package/plugins/mistral/icon.svg +0 -0
  174. package/plugins/mistral/plugin.js +0 -0
  175. package/plugins/mistral/plugin.json +0 -0
  176. package/plugins/moonshot/icon.svg +0 -0
  177. package/plugins/moonshot/plugin.js +8 -6
  178. package/plugins/moonshot/plugin.json +0 -0
  179. package/plugins/nanogpt/icon-color.svg +0 -0
  180. package/plugins/nanogpt/icon.svg +0 -0
  181. package/plugins/nanogpt/plugin.js +0 -0
  182. package/plugins/nanogpt/plugin.json +0 -0
  183. package/plugins/neuralwatt/icon.svg +0 -0
  184. package/plugins/neuralwatt/plugin.js +0 -0
  185. package/plugins/neuralwatt/plugin.json +0 -0
  186. package/plugins/notion/icon.svg +1 -0
  187. package/plugins/notion/plugin.js +282 -0
  188. package/plugins/notion/plugin.json +29 -0
  189. package/plugins/ollama/icon.svg +0 -0
  190. package/plugins/ollama/plugin.js +257 -56
  191. package/plugins/ollama/plugin.json +6 -3
  192. package/plugins/openai-api/icon.svg +0 -0
  193. package/plugins/openai-api/plugin.js +0 -0
  194. package/plugins/openai-api/plugin.json +0 -0
  195. package/plugins/opencode/icon.svg +0 -0
  196. package/plugins/opencode/plugin.js +0 -0
  197. package/plugins/opencode/plugin.json +0 -0
  198. package/plugins/opencode-go/icon-conflict.svg +0 -0
  199. package/plugins/opencode-go/icon.svg +0 -0
  200. package/plugins/opencode-go/plugin.js +78 -10
  201. package/plugins/opencode-go/plugin.json +5 -2
  202. package/plugins/openrouter/icon.svg +0 -0
  203. package/plugins/openrouter/plugin.js +371 -114
  204. package/plugins/openrouter/plugin.json +6 -0
  205. package/plugins/perplexity/icon-color.svg +0 -0
  206. package/plugins/perplexity/icon-conflict.svg +0 -0
  207. package/plugins/perplexity/icon.svg +0 -0
  208. package/plugins/perplexity/plugin.js +0 -0
  209. package/plugins/perplexity/plugin.json +0 -0
  210. package/plugins/poe/icon.svg +0 -0
  211. package/plugins/poe/plugin.js +24 -18
  212. package/plugins/poe/plugin.json +2 -0
  213. package/plugins/qoder/icon.svg +3 -0
  214. package/plugins/qoder/plugin.js +227 -0
  215. package/plugins/qoder/plugin.json +23 -0
  216. package/plugins/qwencloud/icon.svg +4 -0
  217. package/plugins/qwencloud/plugin.js +677 -0
  218. package/plugins/qwencloud/plugin.json +29 -0
  219. package/plugins/sakana/icon.svg +5 -0
  220. package/plugins/sakana/plugin.js +156 -0
  221. package/plugins/sakana/plugin.json +23 -0
  222. package/plugins/stepfun/icon-color.svg +0 -0
  223. package/plugins/stepfun/icon.svg +0 -0
  224. package/plugins/stepfun/plugin.js +0 -0
  225. package/plugins/stepfun/plugin.json +0 -0
  226. package/plugins/sub2api/icon.svg +4 -0
  227. package/plugins/sub2api/plugin.js +191 -0
  228. package/plugins/sub2api/plugin.json +16 -0
  229. package/plugins/synthetic/icon-conflict.svg +0 -0
  230. package/plugins/synthetic/icon.svg +0 -0
  231. package/plugins/synthetic/plugin.js +0 -0
  232. package/plugins/synthetic/plugin.json +0 -0
  233. package/plugins/t3chat/icon-color.svg +0 -0
  234. package/plugins/t3chat/icon.svg +0 -0
  235. package/plugins/t3chat/plugin.js +0 -0
  236. package/plugins/t3chat/plugin.json +0 -0
  237. package/plugins/venice/icon-color.svg +0 -0
  238. package/plugins/venice/icon.svg +0 -0
  239. package/plugins/venice/plugin.js +0 -0
  240. package/plugins/venice/plugin.json +0 -0
  241. package/plugins/vertex-ai/icon-color.svg +0 -0
  242. package/plugins/vertex-ai/icon.svg +0 -0
  243. package/plugins/vertex-ai/plugin.js +0 -0
  244. package/plugins/vertex-ai/plugin.json +0 -0
  245. package/plugins/warp/icon-color.svg +0 -0
  246. package/plugins/warp/icon.svg +0 -0
  247. package/plugins/warp/plugin.js +0 -0
  248. package/plugins/warp/plugin.json +0 -0
  249. package/plugins/wayfinder/icon.svg +5 -0
  250. package/plugins/wayfinder/plugin.js +47 -0
  251. package/plugins/wayfinder/plugin.json +16 -0
  252. package/plugins/windsurf/icon-conflict.svg +0 -0
  253. package/plugins/windsurf/icon.svg +0 -0
  254. package/plugins/windsurf/plugin.js +0 -0
  255. package/plugins/windsurf/plugin.json +0 -0
  256. package/plugins/xai/icon.svg +7 -0
  257. package/plugins/xai/plugin.js +162 -0
  258. package/plugins/xai/plugin.json +28 -0
  259. package/plugins/zai/icon-conflict.svg +0 -0
  260. package/plugins/zai/icon.svg +0 -0
  261. package/plugins/zai/plugin.js +111 -161
  262. package/plugins/zai/plugin.json +4 -1
  263. package/plugins/zed/icon.svg +0 -0
  264. package/plugins/zed/plugin.js +0 -0
  265. package/plugins/zed/plugin.json +0 -0
  266. package/plugins/zenmux/icon.svg +3 -0
  267. package/plugins/zenmux/plugin.js +102 -0
  268. package/plugins/zenmux/plugin.json +23 -0
  269. package/plugins/zoommate/icon.svg +1 -0
  270. package/plugins/zoommate/plugin.js +244 -0
  271. package/plugins/zoommate/plugin.json +29 -0
  272. package/usagestat +0 -0
  273. package/usagestatd +0 -0
@@ -0,0 +1,282 @@
1
+ (function () {
2
+ var BASE_URL = "https://app.notion.com";
3
+ var GET_SPACES_URL = "https://app.notion.com/api/v3/getSpaces";
4
+ var RATE_LIMIT_URL = "https://app.notion.com/api/v3/getCreditRateLimitStatus";
5
+ var SESSION_COOKIE_NAME = "token_v2";
6
+ var USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36";
7
+
8
+ function trim(value) {
9
+ return typeof value === "string" && value.trim() ? value.trim() : null;
10
+ }
11
+
12
+ function env(ctx, name) {
13
+ try {
14
+ return trim(ctx.host.env.get(name));
15
+ } catch (_) {
16
+ return null;
17
+ }
18
+ }
19
+
20
+ function setting(ctx, names) {
21
+ var settings = ctx.provider && ctx.provider.settings ? ctx.provider.settings : {};
22
+ for (var i = 0; i < names.length; i++) {
23
+ var value = trim(settings[names[i]]);
24
+ if (value) return value;
25
+ }
26
+ return null;
27
+ }
28
+
29
+ function normalizeCookie(raw) {
30
+ var value = trim(raw);
31
+ if (!value) return null;
32
+ if (value.slice(0, 7).toLowerCase() === "cookie:") value = value.slice(7).trim();
33
+ if (!value) return null;
34
+ if (value.indexOf("=") < 0) return SESSION_COOKIE_NAME + "=" + value;
35
+ var pairs = [];
36
+ var chunks = value.split(";");
37
+ for (var i = 0; i < chunks.length; i++) {
38
+ var idx = chunks[i].indexOf("=");
39
+ if (idx < 0) continue;
40
+ var name = chunks[i].slice(0, idx).trim();
41
+ var val = chunks[i].slice(idx + 1).trim();
42
+ if (name && val) pairs.push(name + "=" + val);
43
+ }
44
+ return pairs.length ? pairs.join("; ") : null;
45
+ }
46
+
47
+ function cookieHeader(ctx) {
48
+ return normalizeCookie(ctx.provider && ctx.provider.cookieHeader) ||
49
+ normalizeCookie(setting(ctx, ["cookieHeader", "cookie", "token"])) ||
50
+ normalizeCookie(env(ctx, "NOTION_COOKIE"));
51
+ }
52
+
53
+ function spaceOverride(ctx) {
54
+ return setting(ctx, ["spaceId", "workspaceId", "workspace"]) ||
55
+ trim(ctx.provider && ctx.provider.workspaceId) ||
56
+ env(ctx, "NOTION_SPACE_ID");
57
+ }
58
+
59
+ function requestJson(ctx, url, cookie, body) {
60
+ var result = ctx.util.requestJson({
61
+ method: "POST",
62
+ url: url,
63
+ headers: {
64
+ Cookie: cookie,
65
+ "Content-Type": "application/json",
66
+ Accept: "*/*",
67
+ "Accept-Language": "en-US,en;q=0.9",
68
+ "User-Agent": USER_AGENT,
69
+ Referer: BASE_URL + "/",
70
+ Origin: BASE_URL,
71
+ "Sec-Fetch-Dest": "empty",
72
+ "Sec-Fetch-Mode": "cors",
73
+ "Sec-Fetch-Site": "same-origin",
74
+ },
75
+ bodyText: JSON.stringify(body || {}),
76
+ timeoutMs: 15000,
77
+ });
78
+ if (result.resp.status === 401) throw "Notion login required.";
79
+ if (result.resp.status < 200 || result.resp.status >= 300) throw "Notion API error: HTTP " + result.resp.status + ".";
80
+ if (!result.json || typeof result.json !== "object") throw "Could not parse Notion usage.";
81
+ return result.json;
82
+ }
83
+
84
+ function unwrapRecord(raw) {
85
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
86
+ var value = raw.value && typeof raw.value === "object" ? raw.value : raw;
87
+ if (value.value && typeof value.value === "object" && !Array.isArray(value.value)) return value.value;
88
+ return value;
89
+ }
90
+
91
+ function resolveUserId(root) {
92
+ var ids = [];
93
+ var keys = Object.keys(root || {});
94
+ for (var i = 0; i < keys.length; i++) {
95
+ var key = keys[i];
96
+ var container = root[key];
97
+ var users = container && container.notion_user;
98
+ var record = users && unwrapRecord(users[key]);
99
+ if (record && record.id === key) ids.push(key);
100
+ }
101
+ if (ids.length === 1) return ids[0];
102
+ return keys.length === 1 ? keys[0] : null;
103
+ }
104
+
105
+ function normalizeSpaceId(raw) {
106
+ var text = trim(raw);
107
+ if (!text) return null;
108
+ var compact = text.replace(/-/g, "").toLowerCase();
109
+ if (/^[0-9a-f]{32}$/.test(compact)) {
110
+ return compact.slice(0, 8) + "-" + compact.slice(8, 12) + "-" + compact.slice(12, 16) + "-" +
111
+ compact.slice(16, 20) + "-" + compact.slice(20);
112
+ }
113
+ return text.toLowerCase();
114
+ }
115
+
116
+ function parseSpaces(json) {
117
+ var root = json && typeof json === "object" && !Array.isArray(json) ? json : null;
118
+ if (!root) throw "getSpaces response is not a JSON object.";
119
+ var userId = resolveUserId(root);
120
+ if (!userId || !root[userId]) throw "getSpaces response did not identify a single user.";
121
+ var container = root[userId];
122
+ var email = null;
123
+ var users = container.notion_user || {};
124
+ var record = unwrapRecord(users[userId]);
125
+ if (!record) {
126
+ var userKeys = Object.keys(users);
127
+ for (var i = 0; i < userKeys.length && !record; i++) record = unwrapRecord(users[userKeys[i]]);
128
+ }
129
+ if (record && typeof record.email === "string") email = record.email;
130
+
131
+ var spaces = [];
132
+ var spaceObj = container.space || {};
133
+ var spaceKeys = Object.keys(spaceObj).sort();
134
+ for (var j = 0; j < spaceKeys.length; j++) {
135
+ var key = spaceKeys[j];
136
+ var ws = unwrapRecord(spaceObj[key]);
137
+ if (!ws) continue;
138
+ spaces.push({
139
+ id: String(ws.id || key),
140
+ name: trim(ws.name),
141
+ tier: trim(ws.subscription_tier),
142
+ });
143
+ }
144
+ return { email: email, workspaces: spaces };
145
+ }
146
+
147
+ function mayHaveAllowance(workspace) {
148
+ var tier = String(workspace && workspace.tier || "").toLowerCase();
149
+ return tier === "business" || tier === "enterprise";
150
+ }
151
+
152
+ function pickWorkspace(account, preferredId) {
153
+ var preferred = normalizeSpaceId(preferredId);
154
+ if (preferred) {
155
+ for (var i = 0; i < account.workspaces.length; i++) {
156
+ if (normalizeSpaceId(account.workspaces[i].id) === preferred) return account.workspaces[i];
157
+ }
158
+ throw "Selected Notion workspace was not found in this account.";
159
+ }
160
+ for (var j = 0; j < account.workspaces.length; j++) {
161
+ if (mayHaveAllowance(account.workspaces[j])) return account.workspaces[j];
162
+ }
163
+ return account.workspaces[0] || null;
164
+ }
165
+
166
+ function title(value) {
167
+ var text = trim(value);
168
+ return text ? text.charAt(0).toUpperCase() + text.slice(1) : null;
169
+ }
170
+
171
+ function number(value) {
172
+ if (typeof value === "number" && Number.isFinite(value)) return value;
173
+ if (typeof value === "string" && value.trim()) {
174
+ var parsed = Number(value.replace(/,/g, ""));
175
+ if (Number.isFinite(parsed)) return parsed;
176
+ }
177
+ return null;
178
+ }
179
+
180
+ function percent(used, limit) {
181
+ used = number(used);
182
+ limit = number(limit);
183
+ if (used === null || limit === null || limit <= 0) return null;
184
+ return Math.max(0, used / limit * 100);
185
+ }
186
+
187
+ function minutesFromWindow(raw) {
188
+ var text = String(raw || "").trim().toLowerCase();
189
+ var match = /^(\d+)([mhdw])$/.exec(text);
190
+ if (!match) return null;
191
+ var n = Number(match[1]);
192
+ if (!n) return null;
193
+ if (match[2] === "m") return n;
194
+ if (match[2] === "h") return n * 60;
195
+ if (match[2] === "d") return n * 24 * 60;
196
+ return n * 7 * 24 * 60;
197
+ }
198
+
199
+ function rollingReset(ctx, seconds) {
200
+ var n = number(seconds);
201
+ if (n === null || n < 0) return null;
202
+ return ctx.util.toIso(Date.parse(ctx.nowIso) + n * 1000);
203
+ }
204
+
205
+ function dateFromMs(value) {
206
+ var n = number(value);
207
+ if (n === null || n <= 0) return null;
208
+ var date = new Date(n);
209
+ return Number.isFinite(date.getTime()) ? date.toISOString() : null;
210
+ }
211
+
212
+ function parseRateLimit(json) {
213
+ return {
214
+ status: trim(json && json.status),
215
+ window: json && json.window && typeof json.window === "object" ? json.window : null,
216
+ billing: json && json.billingPeriodWindow && typeof json.billingPeriodWindow === "object" ? json.billingPeriodWindow : null,
217
+ resetsInSeconds: number(json && json.resetsInSeconds),
218
+ };
219
+ }
220
+
221
+ function buildSnapshot(ctx, rateLimit, workspace, account) {
222
+ if (rateLimit.status && rateLimit.status.toLowerCase() === "not_applicable") {
223
+ throw "Notion AI usage allowance is not tracked for " + (workspace.name || "this workspace") + ".";
224
+ }
225
+ var lines = [];
226
+ if (rateLimit.window) {
227
+ var pct = percent(rateLimit.window.used, rateLimit.window.limit);
228
+ if (pct !== null) {
229
+ var minutes = minutesFromWindow(rateLimit.window.window);
230
+ var opts = {
231
+ label: "Rolling",
232
+ used: Math.max(0, Math.min(100, pct)),
233
+ limit: 100,
234
+ format: { kind: "percent" },
235
+ };
236
+ if (minutes && minutes !== 30 * 24 * 60) opts.periodDurationMs = minutes * 60 * 1000;
237
+ var rolling = rollingReset(ctx, rateLimit.resetsInSeconds);
238
+ if (rolling) opts.resetsAt = rolling;
239
+ lines.push(ctx.line.progress(opts));
240
+ }
241
+ }
242
+ if (rateLimit.billing) {
243
+ var bpct = percent(rateLimit.billing.used, rateLimit.billing.limit);
244
+ if (bpct !== null) {
245
+ var bopts = {
246
+ label: "Monthly",
247
+ used: Math.max(0, Math.min(100, bpct)),
248
+ limit: 100,
249
+ format: { kind: "percent" },
250
+ };
251
+ var reset = dateFromMs(rateLimit.billing.periodEndMs);
252
+ if (reset) {
253
+ bopts.resetsAt = reset;
254
+ bopts.periodDurationMs = ctx.util.calendarMonthDuration(reset);
255
+ }
256
+ var start = dateFromMs(rateLimit.billing.periodStartMs);
257
+ if (reset && start && Date.parse(reset) > Date.parse(start)) bopts.periodDurationMs = Date.parse(reset) - Date.parse(start);
258
+ lines.push(ctx.line.progress(bopts));
259
+ }
260
+ }
261
+ if (!lines.length) throw "getCreditRateLimitStatus returned no measurable usage windows.";
262
+ if (account.email) lines.push(ctx.line.text({ label: "Account", value: account.email }));
263
+ return {
264
+ displayName: "Notion AI",
265
+ source: "web",
266
+ plan: title(workspace.tier),
267
+ lines: lines,
268
+ };
269
+ }
270
+
271
+ function probe(ctx) {
272
+ var cookie = cookieHeader(ctx);
273
+ if (!cookie) throw "Notion session not configured. Set NOTION_COOKIE or provider cookieHeader.";
274
+ var account = parseSpaces(requestJson(ctx, GET_SPACES_URL, cookie, {}));
275
+ var workspace = pickWorkspace(account, spaceOverride(ctx));
276
+ if (!workspace) throw "No Notion workspace found for this account.";
277
+ var rateLimit = parseRateLimit(requestJson(ctx, RATE_LIMIT_URL, cookie, { spaceId: workspace.id }));
278
+ return buildSnapshot(ctx, rateLimit, workspace, account);
279
+ }
280
+
281
+ globalThis.__openusage_plugin = { id: "notion", probe: probe };
282
+ })();
@@ -0,0 +1,29 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "notion",
4
+ "name": "Notion AI",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "supportedModes": [
9
+ "web"
10
+ ],
11
+ "autoMode": "web",
12
+ "webUrl": "https://app.notion.com",
13
+ "usageDashboardUrl": "https://app.notion.com/",
14
+ "statusPageUrl": "https://status.notion.so",
15
+ "links": [
16
+ {
17
+ "label": "Dashboard",
18
+ "url": "https://app.notion.com/"
19
+ },
20
+ {
21
+ "label": "Status",
22
+ "url": "https://status.notion.so"
23
+ }
24
+ ],
25
+ "envVars": [
26
+ "NOTION_COOKIE",
27
+ "NOTION_SPACE_ID"
28
+ ]
29
+ }
File without changes
@@ -1,92 +1,293 @@
1
1
  (function () {
2
- var SETTINGS_URL = "https://ollama.com/settings"
3
- var SESSION_COOKIE = "__Secure-session"
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 getCookieHeader(ctx) {
6
- var raw = ctx.host.env.get("OLLAMA_COOKIE")
7
- if (!raw || !raw.trim()) {
8
- throw "Set OLLAMA_COOKIE to your " + SESSION_COOKIE + " cookie value from ollama.com/settings."
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
- return val
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 parsePercent(html, labels) {
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
- var idx = html.indexOf(labels[i])
25
- if (idx < 0) continue
26
- var window = html.slice(idx, idx + 800)
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(/&nbsp;/g, " ")
164
+ .replace(/&amp;/g, "&")
165
+ .replace(/&#x2F;/g, "/")
166
+ .replace(/\s+/g, " ")
167
+ .trim();
168
+ }
27
169
 
28
- var usedMatch = window.match(/(\d+(?:\.\d+)?)\s*%\s*used/)
29
- if (usedMatch) return parseFloat(usedMatch[1])
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
- var widthMatch = window.match(/width:\s*(\d+(?:\.\d+)?)%/)
32
- if (widthMatch) return parseFloat(widthMatch[1])
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 probe(ctx) {
38
- var cookieHeader = getCookieHeader(ctx)
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
- var resp = ctx.host.http.request({
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
- "Cookie": cookieHeader,
45
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
46
- "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
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 sessionPct = parsePercent(html, ["Session usage", "Hourly usage"])
64
- var weeklyPct = parsePercent(html, ["Weekly usage"])
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 (sessionPct === null && weeklyPct === null) {
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 (sessionPct !== null) {
72
- lines.push(ctx.line.progress({
73
- label: "Session",
74
- used: sessionPct,
75
- limit: 100,
76
- format: { kind: "percent" },
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 (weeklyPct !== null) {
80
- lines.push(ctx.line.progress({
81
- label: "Weekly",
82
- used: weeklyPct,
83
- limit: 100,
84
- format: { kind: "percent" },
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
- return { lines: lines }
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
  }