@hashimkarim/usagestat-linux-x64-gnu 2.0.0-alpha.1

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 (225) hide show
  1. package/LICENSE +57744 -0
  2. package/native-manifest.json +1414 -0
  3. package/package.json +254 -0
  4. package/plugins/abacus-ai/icon.svg +18 -0
  5. package/plugins/abacus-ai/plugin.js +121 -0
  6. package/plugins/abacus-ai/plugin.json +17 -0
  7. package/plugins/alibaba/icon-color.svg +1 -0
  8. package/plugins/alibaba/icon.svg +1 -0
  9. package/plugins/alibaba/plugin.js +322 -0
  10. package/plugins/alibaba/plugin.json +20 -0
  11. package/plugins/alibaba-token-plan/icon-color.svg +1 -0
  12. package/plugins/alibaba-token-plan/icon.svg +1 -0
  13. package/plugins/alibaba-token-plan/plugin.js +544 -0
  14. package/plugins/alibaba-token-plan/plugin.json +19 -0
  15. package/plugins/amp/icon-color.svg +1 -0
  16. package/plugins/amp/icon-conflict.svg +6 -0
  17. package/plugins/amp/icon.svg +1 -0
  18. package/plugins/amp/plugin.js +175 -0
  19. package/plugins/amp/plugin.json +16 -0
  20. package/plugins/antigravity/icon-color.svg +1 -0
  21. package/plugins/antigravity/icon-conflict.svg +3 -0
  22. package/plugins/antigravity/icon.svg +1 -0
  23. package/plugins/antigravity/plugin.js +739 -0
  24. package/plugins/antigravity/plugin.json +31 -0
  25. package/plugins/antigravity-cli/icon.svg +3 -0
  26. package/plugins/antigravity-cli/plugin.js +570 -0
  27. package/plugins/antigravity-cli/plugin.json +16 -0
  28. package/plugins/antigravity-ide/icon.svg +3 -0
  29. package/plugins/antigravity-ide/plugin.js +257 -0
  30. package/plugins/antigravity-ide/plugin.json +29 -0
  31. package/plugins/augment/icon.svg +14 -0
  32. package/plugins/augment/plugin.js +59 -0
  33. package/plugins/augment/plugin.json +18 -0
  34. package/plugins/aws-bedrock/icon-color.svg +1 -0
  35. package/plugins/aws-bedrock/icon.svg +1 -0
  36. package/plugins/aws-bedrock/plugin.js +206 -0
  37. package/plugins/aws-bedrock/plugin.json +18 -0
  38. package/plugins/azure-openai/icon-color.svg +1 -0
  39. package/plugins/azure-openai/icon.svg +1 -0
  40. package/plugins/azure-openai/plugin.js +145 -0
  41. package/plugins/azure-openai/plugin.json +17 -0
  42. package/plugins/chutes/icon.svg +7 -0
  43. package/plugins/chutes/plugin.js +236 -0
  44. package/plugins/chutes/plugin.json +19 -0
  45. package/plugins/claude/icon-color.svg +1 -0
  46. package/plugins/claude/icon.svg +1 -0
  47. package/plugins/claude/plugin.js +1723 -0
  48. package/plugins/claude/plugin.json +30 -0
  49. package/plugins/codebuff/icon.svg +92 -0
  50. package/plugins/codebuff/plugin.js +113 -0
  51. package/plugins/codebuff/plugin.json +17 -0
  52. package/plugins/codex/icon-color.svg +5 -0
  53. package/plugins/codex/icon.svg +1 -0
  54. package/plugins/codex/plugin.js +1009 -0
  55. package/plugins/codex/plugin.json +82 -0
  56. package/plugins/command-code/icon.svg +3 -0
  57. package/plugins/command-code/plugin.js +184 -0
  58. package/plugins/command-code/plugin.json +20 -0
  59. package/plugins/copilot/icon-color.svg +1 -0
  60. package/plugins/copilot/icon.svg +1 -0
  61. package/plugins/copilot/plugin.js +684 -0
  62. package/plugins/copilot/plugin.json +21 -0
  63. package/plugins/crof/icon.svg +3 -0
  64. package/plugins/crof/plugin.js +53 -0
  65. package/plugins/crof/plugin.json +17 -0
  66. package/plugins/crofai/icon.svg +16 -0
  67. package/plugins/crofai/plugin.js +81 -0
  68. package/plugins/crofai/plugin.json +18 -0
  69. package/plugins/cursor/icon-conflict.svg +3 -0
  70. package/plugins/cursor/icon.svg +1 -0
  71. package/plugins/cursor/plugin.js +1379 -0
  72. package/plugins/cursor/plugin.json +23 -0
  73. package/plugins/cursor-nightly/icon.png +0 -0
  74. package/plugins/cursor-nightly/plugin.js +1379 -0
  75. package/plugins/cursor-nightly/plugin.json +23 -0
  76. package/plugins/deepgram/icon.svg +1 -0
  77. package/plugins/deepgram/plugin.js +20 -0
  78. package/plugins/deepgram/plugin.json +17 -0
  79. package/plugins/deepseek/icon-color.svg +1 -0
  80. package/plugins/deepseek/icon.svg +1 -0
  81. package/plugins/deepseek/plugin.js +71 -0
  82. package/plugins/deepseek/plugin.json +17 -0
  83. package/plugins/devin/icon.svg +5 -0
  84. package/plugins/devin/plugin.js +341 -0
  85. package/plugins/devin/plugin.json +16 -0
  86. package/plugins/doubao/icon-color.svg +1 -0
  87. package/plugins/doubao/icon.svg +1 -0
  88. package/plugins/doubao/plugin.js +92 -0
  89. package/plugins/doubao/plugin.json +17 -0
  90. package/plugins/droid/icon.svg +1 -0
  91. package/plugins/droid/plugin.json +20 -0
  92. package/plugins/elevenlabs/icon.svg +4 -0
  93. package/plugins/elevenlabs/plugin.js +48 -0
  94. package/plugins/elevenlabs/plugin.json +18 -0
  95. package/plugins/factory/icon-conflict.svg +1 -0
  96. package/plugins/factory/icon.svg +1 -0
  97. package/plugins/factory/plugin.js +487 -0
  98. package/plugins/factory/plugin.json +15 -0
  99. package/plugins/fireworks-ai/icon.svg +6 -0
  100. package/plugins/fireworks-ai/plugin.js +370 -0
  101. package/plugins/fireworks-ai/plugin.json +19 -0
  102. package/plugins/gemini/icon-color.svg +1 -0
  103. package/plugins/gemini/icon.svg +1 -0
  104. package/plugins/gemini/plugin.js +460 -0
  105. package/plugins/gemini/plugin.json +16 -0
  106. package/plugins/grok/icon.svg +1 -0
  107. package/plugins/grok/plugin.js +297 -0
  108. package/plugins/grok/plugin.json +19 -0
  109. package/plugins/groqcloud/icon.svg +1 -0
  110. package/plugins/groqcloud/plugin.js +117 -0
  111. package/plugins/groqcloud/plugin.json +19 -0
  112. package/plugins/jetbrains-ai-assistant/icon-color.svg +14 -0
  113. package/plugins/jetbrains-ai-assistant/icon-conflict.svg +3 -0
  114. package/plugins/jetbrains-ai-assistant/icon.svg +14 -0
  115. package/plugins/jetbrains-ai-assistant/plugin.js +313 -0
  116. package/plugins/jetbrains-ai-assistant/plugin.json +16 -0
  117. package/plugins/kilo/icon.svg +1 -0
  118. package/plugins/kilo/plugin.js +111 -0
  119. package/plugins/kilo/plugin.json +19 -0
  120. package/plugins/kimi/icon-color.svg +1 -0
  121. package/plugins/kimi/icon-conflict.svg +3 -0
  122. package/plugins/kimi/icon.svg +1 -0
  123. package/plugins/kimi/plugin.js +358 -0
  124. package/plugins/kimi/plugin.json +15 -0
  125. package/plugins/kimi-k2/icon-color.svg +1 -0
  126. package/plugins/kimi-k2/icon.svg +1 -0
  127. package/plugins/kimi-k2/plugin.js +58 -0
  128. package/plugins/kimi-k2/plugin.json +18 -0
  129. package/plugins/kiro/icon-color.svg +5 -0
  130. package/plugins/kiro/icon-conflict.svg +1 -0
  131. package/plugins/kiro/icon.svg +4 -0
  132. package/plugins/kiro/plugin.js +390 -0
  133. package/plugins/kiro/plugin.json +16 -0
  134. package/plugins/litellm/icon.svg +16 -0
  135. package/plugins/litellm/plugin.js +199 -0
  136. package/plugins/litellm/plugin.json +16 -0
  137. package/plugins/llm-proxy/icon.svg +1 -0
  138. package/plugins/llm-proxy/plugin.js +40 -0
  139. package/plugins/llm-proxy/plugin.json +17 -0
  140. package/plugins/manus/icon.svg +6 -0
  141. package/plugins/manus/plugin.js +50 -0
  142. package/plugins/manus/plugin.json +17 -0
  143. package/plugins/mimo/icon.svg +1 -0
  144. package/plugins/mimo/plugin.js +177 -0
  145. package/plugins/mimo/plugin.json +19 -0
  146. package/plugins/minimax/icon-color.svg +1 -0
  147. package/plugins/minimax/icon-conflict.svg +4 -0
  148. package/plugins/minimax/icon.svg +1 -0
  149. package/plugins/minimax/plugin.js +453 -0
  150. package/plugins/minimax/plugin.json +14 -0
  151. package/plugins/mistral/icon-color.svg +1 -0
  152. package/plugins/mistral/icon.svg +1 -0
  153. package/plugins/mistral/plugin.js +152 -0
  154. package/plugins/mistral/plugin.json +18 -0
  155. package/plugins/moonshot/icon.svg +1 -0
  156. package/plugins/moonshot/plugin.js +43 -0
  157. package/plugins/moonshot/plugin.json +16 -0
  158. package/plugins/nanogpt/icon-color.svg +17 -0
  159. package/plugins/nanogpt/icon.svg +10 -0
  160. package/plugins/nanogpt/plugin.js +80 -0
  161. package/plugins/nanogpt/plugin.json +17 -0
  162. package/plugins/neuralwatt/icon.svg +3 -0
  163. package/plugins/neuralwatt/plugin.js +152 -0
  164. package/plugins/neuralwatt/plugin.json +17 -0
  165. package/plugins/ollama/icon.svg +1 -0
  166. package/plugins/ollama/plugin.js +92 -0
  167. package/plugins/ollama/plugin.json +20 -0
  168. package/plugins/openai-api/icon.svg +1 -0
  169. package/plugins/openai-api/plugin.js +533 -0
  170. package/plugins/openai-api/plugin.json +26 -0
  171. package/plugins/opencode/icon.svg +1 -0
  172. package/plugins/opencode/plugin.js +342 -0
  173. package/plugins/opencode/plugin.json +19 -0
  174. package/plugins/opencode-go/icon-conflict.svg +1 -0
  175. package/plugins/opencode-go/icon.svg +1 -0
  176. package/plugins/opencode-go/plugin.js +617 -0
  177. package/plugins/opencode-go/plugin.json +50 -0
  178. package/plugins/openrouter/icon.svg +1 -0
  179. package/plugins/openrouter/plugin.js +156 -0
  180. package/plugins/openrouter/plugin.json +8 -0
  181. package/plugins/perplexity/icon-color.svg +1 -0
  182. package/plugins/perplexity/icon-conflict.svg +1 -0
  183. package/plugins/perplexity/icon.svg +1 -0
  184. package/plugins/perplexity/plugin.js +425 -0
  185. package/plugins/perplexity/plugin.json +19 -0
  186. package/plugins/poe/icon.svg +1 -0
  187. package/plugins/poe/plugin.js +190 -0
  188. package/plugins/poe/plugin.json +19 -0
  189. package/plugins/stepfun/icon-color.svg +1 -0
  190. package/plugins/stepfun/icon.svg +1 -0
  191. package/plugins/stepfun/plugin.js +155 -0
  192. package/plugins/stepfun/plugin.json +18 -0
  193. package/plugins/synthetic/icon-conflict.svg +12 -0
  194. package/plugins/synthetic/icon.svg +14 -0
  195. package/plugins/synthetic/plugin.js +331 -0
  196. package/plugins/synthetic/plugin.json +23 -0
  197. package/plugins/t3chat/icon-color.svg +28 -0
  198. package/plugins/t3chat/icon.svg +4 -0
  199. package/plugins/t3chat/plugin.js +243 -0
  200. package/plugins/t3chat/plugin.json +20 -0
  201. package/plugins/venice/icon-color.svg +1 -0
  202. package/plugins/venice/icon.svg +1 -0
  203. package/plugins/venice/plugin.js +69 -0
  204. package/plugins/venice/plugin.json +16 -0
  205. package/plugins/vertex-ai/icon-color.svg +1 -0
  206. package/plugins/vertex-ai/icon.svg +1 -0
  207. package/plugins/vertex-ai/plugin.js +172 -0
  208. package/plugins/vertex-ai/plugin.json +18 -0
  209. package/plugins/warp/icon-color.svg +10 -0
  210. package/plugins/warp/icon.svg +6 -0
  211. package/plugins/warp/plugin.js +108 -0
  212. package/plugins/warp/plugin.json +18 -0
  213. package/plugins/windsurf/icon-conflict.svg +3 -0
  214. package/plugins/windsurf/icon.svg +1 -0
  215. package/plugins/windsurf/plugin.js +208 -0
  216. package/plugins/windsurf/plugin.json +16 -0
  217. package/plugins/zai/icon-conflict.svg +5 -0
  218. package/plugins/zai/icon.svg +1 -0
  219. package/plugins/zai/plugin.js +188 -0
  220. package/plugins/zai/plugin.json +16 -0
  221. package/plugins/zed/icon.svg +4 -0
  222. package/plugins/zed/plugin.js +270 -0
  223. package/plugins/zed/plugin.json +19 -0
  224. package/usagestat +0 -0
  225. package/usagestatd +0 -0
@@ -0,0 +1,17 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "doubao",
4
+ "name": "Doubao",
5
+ "version": "0.0.1",
6
+ "entry": "plugin.js",
7
+ "brandColor": "#1E6FFF",
8
+ "links": [
9
+ { "label": "Dashboard", "url": "https://console.volcengine.com/ark/region:ark+cn-beijing/usage" }
10
+ ],
11
+ "envVars": ["ARK_API_KEY", "DOUBAO_API_KEY", "VOLCENGINE_API_KEY"],
12
+ "supportedModes": ["api"],
13
+ "autoMode": "api",
14
+ "lines": [
15
+ { "type": "progress", "label": "Requests", "scope": "overview" }
16
+ ]
17
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 67 65" id="mobile-nav-logo" class="h-auto w-10 md:hidden"><path fill="currentColor" d="M47.75 11.15a.867.867 0 0 1-.671-.806.84.84 0 0 1 .067-.362c1.688-4.007 2.433-7.213 1.23-8.555-3.183-3.56-15.952 3.52-20.024 5.919a.9.9 0 0 1-1.273-.41c-1.711-3.998-3.51-6.78-5.334-6.9-4.833-.323-8.73 13.49-9.87 17.992a.85.85 0 0 1-.459.563.9.9 0 0 1-.737.027c-4.109-1.647-7.398-2.373-8.773-1.2-3.651 3.104 3.609 15.557 6.068 19.528a.85.85 0 0 1-.11 1.031.9.9 0 0 1-.31.21C3.455 39.856.604 41.61.478 43.389c-.329 4.713 13.834 8.513 18.452 9.625q.186.046.337.163a.87.87 0 0 1 .332.642.84.84 0 0 1-.067.362c-1.688 4.007-2.433 7.214-1.23 8.555 3.183 3.561 15.954-3.519 20.025-5.917a.9.9 0 0 1 1.058.107.9.9 0 0 1 .215.302c1.711 3.997 3.509 6.779 5.334 6.9 4.833.322 8.73-13.49 9.868-17.993a.85.85 0 0 1 .168-.33.88.88 0 0 1 .659-.324.9.9 0 0 1 .371.066c4.109 1.647 7.397 2.372 8.773 1.2 3.651-3.105-3.61-15.559-6.07-19.53a.85.85 0 0 1 .111-1.03.9.9 0 0 1 .31-.21c4.1-1.67 6.952-3.424 7.075-5.203.331-4.713-13.833-8.513-18.45-9.623m-5.546-4.518c.93 1.624-3.858 12.446-7.42 20.015a.7.7 0 0 1-.28.303.71.71 0 0 1-.796-.059.7.7 0 0 1-.23-.341c-1.439-4.921-3.082-10.704-4.841-15.612a.84.84 0 0 1 .01-.594.87.87 0 0 1 .401-.446c4.392-2.34 11.908-5.446 13.156-3.266m-21.048 1.34c1.833.507 6.294 11.46 9.264 19.268a.67.67 0 0 1-.2.754.71.71 0 0 1-.794.08c-4.589-2.485-9.94-5.444-14.743-7.702a.87.87 0 0 1-.422-.427.84.84 0 0 1-.04-.591c1.414-4.679 4.471-12.063 6.935-11.383M7.243 23.433c1.664-.906 12.762 3.763 20.522 7.235.13.058.239.154.311.274a.67.67 0 0 1-.06.776.7.7 0 0 1-.35.225c-5.045 1.403-10.976 3.006-16.01 4.721a.9.9 0 0 1-.607-.01.88.88 0 0 1-.456-.391c-2.395-4.284-5.586-11.613-3.35-12.83M8.617 43.96c.519-1.788 11.752-6.14 19.758-9.035a.72.72 0 0 1 .773.195.67.67 0 0 1 .081.774c-2.548 4.475-5.582 9.694-7.898 14.377a.87.87 0 0 1-.437.413.9.9 0 0 1-.607.039c-4.797-1.37-12.37-4.36-11.67-6.763m15.855 13.568c-.93-1.623 3.859-12.446 7.42-20.014a.7.7 0 0 1 .28-.303.715.715 0 0 1 .796.059.7.7 0 0 1 .23.34c1.439 4.92 3.083 10.705 4.841 15.613a.84.84 0 0 1-.01.593.87.87 0 0 1-.402.445c-4.391 2.335-11.908 5.447-13.15 3.267zm21.049-1.34c-1.836-.506-6.297-11.461-9.266-19.269a.67.67 0 0 1 .2-.755.71.71 0 0 1 .795-.078c4.587 2.484 9.94 5.445 14.742 7.703.189.088.339.24.423.426a.84.84 0 0 1 .039.592c-1.413 4.686-4.47 12.063-6.933 11.381m13.912-15.462c-1.665.907-12.762-3.763-20.523-7.236a.7.7 0 0 1-.311-.273.67.67 0 0 1 .06-.777.7.7 0 0 1 .35-.225c5.046-1.402 10.975-3.005 16.009-4.72a.9.9 0 0 1 .609.01.88.88 0 0 1 .457.392c2.393 4.282 5.584 11.613 3.349 12.829M58.06 20.2c-.521 1.79-11.753 6.14-19.759 9.036a.72.72 0 0 1-.774-.195.67.67 0 0 1-.08-.776c2.547-4.474 5.581-9.694 7.897-14.377a.87.87 0 0 1 .437-.412.9.9 0 0 1 .607-.038c4.797 1.377 12.37 4.359 11.672 6.762"></path></svg>
@@ -0,0 +1,20 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "droid",
4
+ "name": "Droid",
5
+ "version": "0.0.1",
6
+ "entry": "../factory/plugin.js",
7
+ "icon": "../factory/icon.svg",
8
+ "brandColor": "#020202",
9
+ "links": [
10
+ { "label": "Factory", "url": "https://app.factory.ai" },
11
+ { "label": "Status", "url": "https://status.factory.ai" }
12
+ ],
13
+ "supportedModes": ["oauth", "local"],
14
+ "autoMode": "oauth",
15
+ "webUrl": "https://app.factory.ai",
16
+ "lines": [
17
+ { "type": "progress", "label": "Standard", "scope": "overview", "primaryOrder": 1 },
18
+ { "type": "progress", "label": "Premium", "scope": "detail" }
19
+ ]
20
+ }
@@ -0,0 +1,4 @@
1
+ <svg viewBox="0 0 32 32" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M7 6h4v20H7V6Zm7 0h4v20h-4V6Zm7 0h4v20h-4V6Z"/>
3
+ <path d="M3.5 15h25v2h-25v-2Z" opacity=".35"/>
4
+ </svg>
@@ -0,0 +1,48 @@
1
+ (function () {
2
+ function apiKey(ctx) {
3
+ return ctx.host.env.get("ELEVENLABS_API_KEY") || ctx.host.env.get("XI_API_KEY");
4
+ }
5
+
6
+ function apiBase(ctx) {
7
+ return ctx.host.env.get("ELEVENLABS_API_URL") || "https://api.elevenlabs.io";
8
+ }
9
+
10
+ function probe(ctx) {
11
+ var key = apiKey(ctx);
12
+ if (!key) throw "ElevenLabs API key not found. Set ELEVENLABS_API_KEY or XI_API_KEY.";
13
+
14
+ var result = ctx.util.requestJson({
15
+ method: "GET",
16
+ url: apiBase(ctx).replace(/\/$/, "") + "/v1/user/subscription",
17
+ headers: { "xi-api-key": key, Accept: "application/json" },
18
+ timeoutMs: 15000,
19
+ });
20
+ if (ctx.util.isAuthStatus(result.resp.status)) throw "ElevenLabs API key invalid or expired.";
21
+ if (result.resp.status < 200 || result.resp.status >= 300) throw "ElevenLabs API error (HTTP " + result.resp.status + ").";
22
+ if (!result.json) throw "Could not parse ElevenLabs subscription response.";
23
+
24
+ var sub = result.json;
25
+ var used = Number(sub.character_count || 0);
26
+ var limit = Number(sub.character_limit || 0);
27
+ var lines = [];
28
+ var opts = {
29
+ label: "Characters",
30
+ used: used,
31
+ limit: limit > 0 ? limit : Math.max(used, 1),
32
+ format: { kind: "count", suffix: "chars" },
33
+ };
34
+ if (sub.next_character_count_reset_unix) opts.resetsAt = ctx.util.toIso(Number(sub.next_character_count_reset_unix) * 1000);
35
+ lines.push(ctx.line.progress(opts));
36
+
37
+ var voiceUsed = Number(sub.voice_count || 0);
38
+ var voiceLimit = Number(sub.voice_limit || sub.professional_voice_limit || 0);
39
+ if (voiceLimit > 0 || voiceUsed > 0) {
40
+ lines.push(ctx.line.text({ label: "Voice slots", value: voiceUsed + " / " + voiceLimit }));
41
+ }
42
+ if (sub.tier) lines.push(ctx.line.badge({ label: "Plan", text: String(sub.tier), color: "#22c55e" }));
43
+
44
+ return { lines: lines };
45
+ }
46
+
47
+ globalThis.__openusage_plugin = { id: "elevenlabs", probe: probe };
48
+ })();
@@ -0,0 +1,18 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "elevenlabs",
4
+ "name": "ElevenLabs",
5
+ "version": "0.0.1",
6
+ "entry": "plugin.js",
7
+ "brandColor": "#111111",
8
+ "links": [
9
+ { "label": "Usage", "url": "https://elevenlabs.io/app/subscription" },
10
+ { "label": "Status", "url": "https://status.elevenlabs.io" }
11
+ ],
12
+ "supportedModes": ["api"],
13
+ "autoMode": "api",
14
+ "lines": [
15
+ { "type": "progress", "label": "Characters", "scope": "overview", "primaryOrder": 1 },
16
+ { "type": "text", "label": "Voice slots", "scope": "detail" }
17
+ ]
18
+ }
@@ -0,0 +1 @@
1
+ <svg width="100" height="100" viewBox="0 0 67 65" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M47.75 11.15a.867.867 0 0 1-.671-.806.84.84 0 0 1 .067-.362c1.688-4.007 2.433-7.213 1.23-8.555-3.183-3.56-15.952 3.52-20.024 5.919a.9.9 0 0 1-1.273-.41c-1.711-3.998-3.51-6.78-5.334-6.9-4.833-.323-8.73 13.49-9.87 17.992a.85.85 0 0 1-.459.563.9.9 0 0 1-.737.027c-4.109-1.647-7.398-2.373-8.773-1.2-3.651 3.104 3.609 15.557 6.068 19.528a.85.85 0 0 1-.11 1.031.9.9 0 0 1-.31.21C3.455 39.856.604 41.61.478 43.389c-.329 4.713 13.834 8.513 18.452 9.625q.186.046.337.163a.87.87 0 0 1 .332.642.84.84 0 0 1-.067.362c-1.688 4.007-2.433 7.214-1.23 8.555 3.183 3.561 15.954-3.519 20.025-5.917a.9.9 0 0 1 1.058.107.9.9 0 0 1 .215.302c1.711 3.997 3.509 6.779 5.334 6.9 4.833.322 8.73-13.49 9.868-17.993a.85.85 0 0 1 .168-.33.88.88 0 0 1 .659-.324.9.9 0 0 1 .371.066c4.109 1.647 7.397 2.372 8.773 1.2 3.651-3.105-3.61-15.559-6.07-19.53a.85.85 0 0 1 .111-1.03.9.9 0 0 1 .31-.21c4.1-1.67 6.952-3.424 7.075-5.203.331-4.713-13.833-8.513-18.45-9.623m-5.546-4.518c.93 1.624-3.858 12.446-7.42 20.015a.7.7 0 0 1-.28.303.71.71 0 0 1-.796-.059.7.7 0 0 1-.23-.341c-1.439-4.921-3.082-10.704-4.841-15.612a.84.84 0 0 1 .01-.594.87.87 0 0 1 .401-.446c4.392-2.34 11.908-5.446 13.156-3.266m-21.048 1.34c1.833.507 6.294 11.46 9.264 19.268a.67.67 0 0 1-.2.754.71.71 0 0 1-.794.08c-4.589-2.485-9.94-5.444-14.743-7.702a.87.87 0 0 1-.422-.427.84.84 0 0 1-.04-.591c1.414-4.679 4.471-12.063 6.935-11.383M7.243 23.433c1.664-.906 12.762 3.763 20.522 7.235.13.058.239.154.311.274a.67.67 0 0 1-.06.776.7.7 0 0 1-.35.225c-5.045 1.403-10.976 3.006-16.01 4.721a.9.9 0 0 1-.607-.01.88.88 0 0 1-.456-.391c-2.395-4.284-5.586-11.613-3.35-12.83M8.617 43.96c.519-1.788 11.752-6.14 19.758-9.035a.72.72 0 0 1 .773.195.67.67 0 0 1 .081.774c-2.548 4.475-5.582 9.694-7.898 14.377a.87.87 0 0 1-.437.413.9.9 0 0 1-.607.039c-4.797-1.37-12.37-4.36-11.67-6.763m15.855 13.568c-.93-1.623 3.859-12.446 7.42-20.014a.7.7 0 0 1 .28-.303.715.715 0 0 1 .796.059.7.7 0 0 1 .23.34c1.439 4.92 3.083 10.705 4.841 15.613a.84.84 0 0 1-.01.593.87.87 0 0 1-.402.445c-4.391 2.335-11.908 5.447-13.15 3.267zm21.049-1.34c-1.836-.506-6.297-11.461-9.266-19.269a.67.67 0 0 1 .2-.755.71.71 0 0 1 .795-.078c4.587 2.484 9.94 5.445 14.742 7.703.189.088.339.24.423.426a.84.84 0 0 1 .039.592c-1.413 4.686-4.47 12.063-6.933 11.381m13.912-15.462c-1.665.907-12.762-3.763-20.523-7.236a.7.7 0 0 1-.311-.273.67.67 0 0 1 .06-.777.7.7 0 0 1 .35-.225c5.046-1.402 10.975-3.005 16.009-4.72a.9.9 0 0 1 .609.01.88.88 0 0 1 .457.392c2.393 4.282 5.584 11.613 3.349 12.829M58.06 20.2c-.521 1.79-11.753 6.14-19.759 9.036a.72.72 0 0 1-.774-.195.67.67 0 0 1-.08-.776c2.547-4.474 5.581-9.694 7.897-14.377a.87.87 0 0 1 .437-.412.9.9 0 0 1 .607-.038c4.797 1.377 12.37 4.359 11.672 6.762"></path></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 67 65" id="mobile-nav-logo" class="h-auto w-10 md:hidden"><path fill="currentColor" d="M47.75 11.15a.867.867 0 0 1-.671-.806.84.84 0 0 1 .067-.362c1.688-4.007 2.433-7.213 1.23-8.555-3.183-3.56-15.952 3.52-20.024 5.919a.9.9 0 0 1-1.273-.41c-1.711-3.998-3.51-6.78-5.334-6.9-4.833-.323-8.73 13.49-9.87 17.992a.85.85 0 0 1-.459.563.9.9 0 0 1-.737.027c-4.109-1.647-7.398-2.373-8.773-1.2-3.651 3.104 3.609 15.557 6.068 19.528a.85.85 0 0 1-.11 1.031.9.9 0 0 1-.31.21C3.455 39.856.604 41.61.478 43.389c-.329 4.713 13.834 8.513 18.452 9.625q.186.046.337.163a.87.87 0 0 1 .332.642.84.84 0 0 1-.067.362c-1.688 4.007-2.433 7.214-1.23 8.555 3.183 3.561 15.954-3.519 20.025-5.917a.9.9 0 0 1 1.058.107.9.9 0 0 1 .215.302c1.711 3.997 3.509 6.779 5.334 6.9 4.833.322 8.73-13.49 9.868-17.993a.85.85 0 0 1 .168-.33.88.88 0 0 1 .659-.324.9.9 0 0 1 .371.066c4.109 1.647 7.397 2.372 8.773 1.2 3.651-3.105-3.61-15.559-6.07-19.53a.85.85 0 0 1 .111-1.03.9.9 0 0 1 .31-.21c4.1-1.67 6.952-3.424 7.075-5.203.331-4.713-13.833-8.513-18.45-9.623m-5.546-4.518c.93 1.624-3.858 12.446-7.42 20.015a.7.7 0 0 1-.28.303.71.71 0 0 1-.796-.059.7.7 0 0 1-.23-.341c-1.439-4.921-3.082-10.704-4.841-15.612a.84.84 0 0 1 .01-.594.87.87 0 0 1 .401-.446c4.392-2.34 11.908-5.446 13.156-3.266m-21.048 1.34c1.833.507 6.294 11.46 9.264 19.268a.67.67 0 0 1-.2.754.71.71 0 0 1-.794.08c-4.589-2.485-9.94-5.444-14.743-7.702a.87.87 0 0 1-.422-.427.84.84 0 0 1-.04-.591c1.414-4.679 4.471-12.063 6.935-11.383M7.243 23.433c1.664-.906 12.762 3.763 20.522 7.235.13.058.239.154.311.274a.67.67 0 0 1-.06.776.7.7 0 0 1-.35.225c-5.045 1.403-10.976 3.006-16.01 4.721a.9.9 0 0 1-.607-.01.88.88 0 0 1-.456-.391c-2.395-4.284-5.586-11.613-3.35-12.83M8.617 43.96c.519-1.788 11.752-6.14 19.758-9.035a.72.72 0 0 1 .773.195.67.67 0 0 1 .081.774c-2.548 4.475-5.582 9.694-7.898 14.377a.87.87 0 0 1-.437.413.9.9 0 0 1-.607.039c-4.797-1.37-12.37-4.36-11.67-6.763m15.855 13.568c-.93-1.623 3.859-12.446 7.42-20.014a.7.7 0 0 1 .28-.303.715.715 0 0 1 .796.059.7.7 0 0 1 .23.34c1.439 4.92 3.083 10.705 4.841 15.613a.84.84 0 0 1-.01.593.87.87 0 0 1-.402.445c-4.391 2.335-11.908 5.447-13.15 3.267zm21.049-1.34c-1.836-.506-6.297-11.461-9.266-19.269a.67.67 0 0 1 .2-.755.71.71 0 0 1 .795-.078c4.587 2.484 9.94 5.445 14.742 7.703.189.088.339.24.423.426a.84.84 0 0 1 .039.592c-1.413 4.686-4.47 12.063-6.933 11.381m13.912-15.462c-1.665.907-12.762-3.763-20.523-7.236a.7.7 0 0 1-.311-.273.67.67 0 0 1 .06-.777.7.7 0 0 1 .35-.225c5.046-1.402 10.975-3.005 16.009-4.72a.9.9 0 0 1 .609.01.88.88 0 0 1 .457.392c2.393 4.282 5.584 11.613 3.349 12.829M58.06 20.2c-.521 1.79-11.753 6.14-19.759 9.036a.72.72 0 0 1-.774-.195.67.67 0 0 1-.08-.776c2.547-4.474 5.581-9.694 7.897-14.377a.87.87 0 0 1 .437-.412.9.9 0 0 1 .607-.038c4.797 1.377 12.37 4.359 11.672 6.762"></path></svg>
@@ -0,0 +1,487 @@
1
+ (function () {
2
+ const AUTH_V2_PATH = "~/.factory/auth.v2.file"
3
+ const AUTH_V2_KEY_PATH = "~/.factory/auth.v2.key"
4
+ const AUTH_PATHS = ["~/.factory/auth.encrypted", "~/.factory/auth.json"]
5
+ const KEYCHAIN_SERVICES = ["Factory Token", "Factory token", "Factory Auth", "Droid Auth"]
6
+ const WORKOS_CLIENT_ID = "client_01HNM792M5G5G1A2THWPXKFMXB"
7
+ const WORKOS_AUTH_URL = "https://api.workos.com/user_management/authenticate"
8
+ const USAGE_URL = "https://api.factory.ai/api/organization/subscription/usage"
9
+ const TOKEN_REFRESH_THRESHOLD_MS = 24 * 60 * 60 * 1000 // 24 hours before expiry
10
+
11
+ function decodeHexUtf8(hex) {
12
+ try {
13
+ const bytes = []
14
+ for (let i = 0; i < hex.length; i += 2) {
15
+ bytes.push(parseInt(hex.slice(i, i + 2), 16))
16
+ }
17
+
18
+ if (typeof TextDecoder !== "undefined") {
19
+ try {
20
+ return new TextDecoder("utf-8", { fatal: false }).decode(new Uint8Array(bytes))
21
+ } catch {}
22
+ }
23
+
24
+ let escaped = ""
25
+ for (const b of bytes) {
26
+ const h = b.toString(16)
27
+ escaped += "%" + (h.length === 1 ? "0" + h : h)
28
+ }
29
+ return decodeURIComponent(escaped)
30
+ } catch {
31
+ return null
32
+ }
33
+ }
34
+
35
+ function tryParseAuthJson(ctx, text) {
36
+ if (!text) return null
37
+ const parsed = ctx.util.tryParseJson(text)
38
+ if (parsed !== null) return parsed
39
+
40
+ // Some keychain payloads can be returned as hex-encoded UTF-8 bytes.
41
+ let hex = String(text).trim()
42
+ if (hex.startsWith("0x") || hex.startsWith("0X")) hex = hex.slice(2)
43
+ if (!hex || hex.length % 2 !== 0) return null
44
+ if (!/^[0-9a-fA-F]+$/.test(hex)) return null
45
+
46
+ const decoded = decodeHexUtf8(hex)
47
+ if (!decoded) return null
48
+ return ctx.util.tryParseJson(decoded)
49
+ }
50
+
51
+ function looksLikeJwt(value) {
52
+ return /^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/.test(value)
53
+ }
54
+
55
+ function normalizeAuthPayload(raw, opts) {
56
+ const allowPartial = Boolean(opts && opts.allowPartial)
57
+ if (!raw || typeof raw !== "object") return null
58
+
59
+ const accessToken =
60
+ raw.access_token ||
61
+ raw.accessToken ||
62
+ (raw.tokens && (raw.tokens.access_token || raw.tokens.accessToken))
63
+
64
+ const refreshToken =
65
+ raw.refresh_token ||
66
+ raw.refreshToken ||
67
+ (raw.tokens && (raw.tokens.refresh_token || raw.tokens.refreshToken))
68
+
69
+ const hasAccess = typeof accessToken === "string" && accessToken
70
+ const hasRefresh = typeof refreshToken === "string" && refreshToken
71
+ if (!hasAccess && !(allowPartial && hasRefresh)) return null
72
+
73
+ return {
74
+ access_token: hasAccess ? accessToken : null,
75
+ refresh_token: hasRefresh ? refreshToken : null,
76
+ }
77
+ }
78
+
79
+ function parseAuthPayload(ctx, rawText, opts) {
80
+ const parsed = tryParseAuthJson(ctx, rawText)
81
+ const normalized = normalizeAuthPayload(parsed, opts)
82
+ if (normalized) return normalized
83
+
84
+ if (typeof parsed === "string" && looksLikeJwt(parsed)) {
85
+ return { access_token: parsed, refresh_token: null }
86
+ }
87
+
88
+ const direct = String(rawText || "").trim()
89
+ if (looksLikeJwt(direct)) {
90
+ return { access_token: direct, refresh_token: null }
91
+ }
92
+
93
+ return null
94
+ }
95
+
96
+ function loadAuthFromV2File(ctx) {
97
+ if (!ctx.host.crypto || typeof ctx.host.crypto.decryptAes256Gcm !== "function") {
98
+ return null
99
+ }
100
+ if (!ctx.host.fs.exists(AUTH_V2_PATH) || !ctx.host.fs.exists(AUTH_V2_KEY_PATH)) {
101
+ return null
102
+ }
103
+
104
+ try {
105
+ const envelope = ctx.host.fs.readText(AUTH_V2_PATH)
106
+ const key = ctx.host.fs.readText(AUTH_V2_KEY_PATH)
107
+ const decrypted = ctx.host.crypto.decryptAes256Gcm(envelope, key)
108
+ const auth = parseAuthPayload(ctx, decrypted, { allowPartial: true })
109
+ if (!auth) {
110
+ ctx.host.log.warn("auth file exists but has no valid auth payload: " + AUTH_V2_PATH)
111
+ return null
112
+ }
113
+ ctx.host.log.info("auth loaded from file: " + AUTH_V2_PATH)
114
+ return {
115
+ auth,
116
+ source: "file-v2",
117
+ authPath: AUTH_V2_PATH,
118
+ authKey: key,
119
+ keychainService: null,
120
+ }
121
+ } catch (e) {
122
+ ctx.host.log.warn("auth file read failed: " + String(e))
123
+ return null
124
+ }
125
+ }
126
+
127
+ function loadAuthFromFiles(ctx) {
128
+ const v2Auth = loadAuthFromV2File(ctx)
129
+ if (v2Auth) return v2Auth
130
+
131
+ for (const authPath of AUTH_PATHS) {
132
+ if (!ctx.host.fs.exists(authPath)) continue
133
+
134
+ try {
135
+ const text = ctx.host.fs.readText(authPath)
136
+ const auth = parseAuthPayload(ctx, text, { allowPartial: true })
137
+ if (!auth) {
138
+ ctx.host.log.warn("auth file exists but has no valid auth payload: " + authPath)
139
+ continue
140
+ }
141
+ ctx.host.log.info("auth loaded from file: " + authPath)
142
+ return { auth, source: "file", authPath, keychainService: null }
143
+ } catch (e) {
144
+ ctx.host.log.warn("auth file read failed: " + String(e))
145
+ }
146
+ }
147
+
148
+ return null
149
+ }
150
+
151
+ function loadAuthFromKeychain(ctx) {
152
+ if (!ctx.host.keychain || typeof ctx.host.keychain.readGenericPassword !== "function") {
153
+ return null
154
+ }
155
+
156
+ for (const service of KEYCHAIN_SERVICES) {
157
+ try {
158
+ const value = ctx.host.keychain.readGenericPassword(service)
159
+ if (!value) continue
160
+
161
+ const auth = parseAuthPayload(ctx, value)
162
+ if (!auth) {
163
+ ctx.host.log.warn("keychain has data but no valid auth payload: " + service)
164
+ continue
165
+ }
166
+
167
+ ctx.host.log.info("auth loaded from keychain: " + service)
168
+ return { auth, source: "keychain", authPath: null, keychainService: service }
169
+ } catch (e) {
170
+ ctx.host.log.info("keychain read failed (may not exist): " + String(e))
171
+ }
172
+ }
173
+
174
+ return null
175
+ }
176
+
177
+ function loadAuth(ctx) {
178
+ const fileAuth = loadAuthFromFiles(ctx)
179
+ if (fileAuth) return fileAuth
180
+
181
+ const keychainAuth = loadAuthFromKeychain(ctx)
182
+ if (keychainAuth) return keychainAuth
183
+
184
+ if (!ctx.host.fs.exists(AUTH_V2_PATH)) {
185
+ ctx.host.log.warn("auth file not found: " + AUTH_V2_PATH)
186
+ }
187
+ if (!ctx.host.fs.exists(AUTH_V2_KEY_PATH)) {
188
+ ctx.host.log.warn("auth file not found: " + AUTH_V2_KEY_PATH)
189
+ }
190
+ for (const authPath of AUTH_PATHS) {
191
+ if (!ctx.host.fs.exists(authPath)) {
192
+ ctx.host.log.warn("auth file not found: " + authPath)
193
+ }
194
+ }
195
+
196
+ return null
197
+ }
198
+
199
+ function saveAuth(ctx, authState) {
200
+ const auth = authState && authState.auth ? authState.auth : null
201
+ if (!auth) return false
202
+
203
+ try {
204
+ if (authState.source === "file-v2" && authState.authPath && authState.authKey) {
205
+ if (!ctx.host.crypto || typeof ctx.host.crypto.encryptAes256Gcm !== "function") {
206
+ ctx.host.log.warn("auth persistence skipped: unsupported source " + authState.source)
207
+ return false
208
+ }
209
+ const envelope = ctx.host.crypto.encryptAes256Gcm(JSON.stringify(auth, null, 2), authState.authKey)
210
+ ctx.host.fs.writeText(authState.authPath, envelope)
211
+ ctx.host.log.info("auth file updated: " + authState.authPath)
212
+ return true
213
+ }
214
+
215
+ if (authState.source === "file" && authState.authPath) {
216
+ ctx.host.fs.writeText(authState.authPath, JSON.stringify(auth, null, 2))
217
+ ctx.host.log.info("auth file updated: " + authState.authPath)
218
+ return true
219
+ }
220
+
221
+ if (
222
+ authState.source === "keychain" &&
223
+ authState.keychainService &&
224
+ ctx.host.keychain &&
225
+ typeof ctx.host.keychain.writeGenericPassword === "function"
226
+ ) {
227
+ ctx.host.keychain.writeGenericPassword(authState.keychainService, JSON.stringify(auth))
228
+ ctx.host.log.info("auth keychain item updated: " + authState.keychainService)
229
+ return true
230
+ }
231
+
232
+ ctx.host.log.warn("auth persistence skipped: unsupported source")
233
+ return false
234
+ } catch (e) {
235
+ ctx.host.log.warn("failed to save auth: " + String(e))
236
+ return false
237
+ }
238
+ }
239
+
240
+ function getAccessTokenExpiryMs(ctx, accessToken) {
241
+ const payload = ctx.jwt.decodePayload(accessToken)
242
+ return payload && typeof payload.exp === "number" ? payload.exp * 1000 : null
243
+ }
244
+
245
+ function needsRefresh(ctx, accessToken, nowMs) {
246
+ return ctx.util.needsRefreshByExpiry({
247
+ nowMs,
248
+ expiresAtMs: getAccessTokenExpiryMs(ctx, accessToken),
249
+ bufferMs: TOKEN_REFRESH_THRESHOLD_MS,
250
+ })
251
+ }
252
+
253
+ function canUseExistingAccessToken(ctx, accessToken, nowMs) {
254
+ const expiresAtMs = getAccessTokenExpiryMs(ctx, accessToken)
255
+ return typeof expiresAtMs === "number" && nowMs < expiresAtMs
256
+ }
257
+
258
+ function refreshToken(ctx, authState) {
259
+ const auth = authState.auth
260
+ if (!auth.refresh_token) {
261
+ ctx.host.log.warn("refresh skipped: no refresh token")
262
+ return null
263
+ }
264
+
265
+ ctx.host.log.info("attempting token refresh via WorkOS")
266
+ try {
267
+ const resp = ctx.util.request({
268
+ method: "POST",
269
+ url: WORKOS_AUTH_URL,
270
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
271
+ bodyText:
272
+ "grant_type=refresh_token" +
273
+ "&refresh_token=" + encodeURIComponent(auth.refresh_token) +
274
+ "&client_id=" + encodeURIComponent(WORKOS_CLIENT_ID),
275
+ timeoutMs: 15000,
276
+ })
277
+
278
+ if (resp.status === 400 || resp.status === 401) {
279
+ ctx.host.log.error("refresh failed: status=" + resp.status)
280
+ throw "Session expired. Run `droid` to log in again."
281
+ }
282
+ if (resp.status < 200 || resp.status >= 300) {
283
+ ctx.host.log.warn("refresh returned unexpected status: " + resp.status)
284
+ return null
285
+ }
286
+
287
+ const body = ctx.util.tryParseJson(resp.bodyText)
288
+ if (!body) {
289
+ ctx.host.log.warn("refresh response not valid JSON")
290
+ return null
291
+ }
292
+ const newAccessToken = body.access_token
293
+ if (!newAccessToken) {
294
+ ctx.host.log.warn("refresh response missing access_token")
295
+ return null
296
+ }
297
+
298
+ // Update auth object with new tokens
299
+ auth.access_token = newAccessToken
300
+ if (body.refresh_token) {
301
+ auth.refresh_token = body.refresh_token
302
+ }
303
+
304
+ // Save updated auth
305
+ saveAuth(ctx, authState)
306
+ ctx.host.log.info("refresh succeeded")
307
+
308
+ return newAccessToken
309
+ } catch (e) {
310
+ if (typeof e === "string") throw e
311
+ ctx.host.log.error("refresh exception: " + String(e))
312
+ return null
313
+ }
314
+ }
315
+
316
+ function fetchUsage(ctx, accessToken) {
317
+ var resp = ctx.util.request({
318
+ method: "POST",
319
+ url: USAGE_URL,
320
+ headers: {
321
+ Authorization: "Bearer " + accessToken,
322
+ "Content-Type": "application/json",
323
+ Accept: "application/json",
324
+ "User-Agent": "OpenUsage",
325
+ },
326
+ bodyText: JSON.stringify({ useCache: true }),
327
+ timeoutMs: 10000,
328
+ })
329
+ if (resp.status === 405) {
330
+ ctx.host.log.info("POST returned 405, retrying with GET")
331
+ resp = ctx.util.request({
332
+ method: "GET",
333
+ url: USAGE_URL,
334
+ headers: {
335
+ Authorization: "Bearer " + accessToken,
336
+ Accept: "application/json",
337
+ "User-Agent": "OpenUsage",
338
+ },
339
+ timeoutMs: 10000,
340
+ })
341
+ }
342
+ return resp
343
+ }
344
+
345
+ function probe(ctx) {
346
+ const authState = loadAuth(ctx)
347
+ if (!authState) {
348
+ ctx.host.log.error("probe failed: not logged in")
349
+ throw "Not logged in. Run `droid` to authenticate."
350
+ }
351
+
352
+ const auth = authState.auth
353
+ if (!auth.access_token) {
354
+ ctx.host.log.error("probe failed: no access_token in auth data")
355
+ throw "Invalid auth file. Run `droid` to authenticate."
356
+ }
357
+
358
+ let accessToken = auth.access_token
359
+
360
+ // Check if token needs refresh
361
+ const nowMs = Date.now()
362
+ if (needsRefresh(ctx, accessToken, nowMs)) {
363
+ ctx.host.log.info("token near expiry, refreshing")
364
+ try {
365
+ const refreshed = refreshToken(ctx, authState)
366
+ if (refreshed) {
367
+ accessToken = refreshed
368
+ } else {
369
+ ctx.host.log.warn("proactive refresh failed, trying with existing token")
370
+ }
371
+ } catch (e) {
372
+ if (!canUseExistingAccessToken(ctx, accessToken, nowMs)) throw e
373
+ ctx.host.log.warn("proactive refresh failed but access token is still valid, trying existing token")
374
+ }
375
+ }
376
+
377
+ let resp
378
+ let didRefresh = false
379
+ try {
380
+ resp = ctx.util.retryOnceOnAuth({
381
+ request: (token) => {
382
+ try {
383
+ return fetchUsage(ctx, token || accessToken)
384
+ } catch (e) {
385
+ ctx.host.log.error("usage request exception: " + String(e))
386
+ if (didRefresh) {
387
+ throw "Usage request failed after refresh. Try again."
388
+ }
389
+ throw "Usage request failed. Check your connection."
390
+ }
391
+ },
392
+ refresh: () => {
393
+ ctx.host.log.info("usage returned 401, attempting refresh")
394
+ didRefresh = true
395
+ return refreshToken(ctx, authState)
396
+ },
397
+ })
398
+ } catch (e) {
399
+ if (typeof e === "string") throw e
400
+ ctx.host.log.error("usage request failed: " + String(e))
401
+ throw "Usage request failed. Check your connection."
402
+ }
403
+
404
+ if (ctx.util.isAuthStatus(resp.status)) {
405
+ ctx.host.log.error("usage returned auth error after all retries: status=" + resp.status)
406
+ throw "Token expired. Run `droid` to log in again."
407
+ }
408
+
409
+ if (resp.status < 200 || resp.status >= 300) {
410
+ ctx.host.log.error("usage returned error: status=" + resp.status)
411
+ throw "Usage request failed (HTTP " + String(resp.status) + "). Try again later."
412
+ }
413
+
414
+ ctx.host.log.info("usage fetch succeeded")
415
+
416
+ const data = ctx.util.tryParseJson(resp.bodyText)
417
+ if (data === null) {
418
+ throw "Usage response invalid. Try again later."
419
+ }
420
+
421
+ const usage = data.usage
422
+ if (!usage) {
423
+ throw "Usage response missing data. Try again later."
424
+ }
425
+
426
+ const lines = []
427
+
428
+ // Calculate reset time and period from usage dates
429
+ const endDate = usage.endDate
430
+ const startDate = usage.startDate
431
+ const resetsAt = typeof endDate === "number" ? ctx.util.toIso(endDate) : null
432
+ const periodDurationMs = (typeof endDate === "number" && typeof startDate === "number")
433
+ ? (endDate - startDate)
434
+ : null
435
+
436
+ // Standard tokens (primary line)
437
+ const standard = usage.standard
438
+ if (standard && typeof standard.totalAllowance === "number") {
439
+ const used = standard.orgTotalTokensUsed || 0
440
+ const limit = standard.totalAllowance
441
+ lines.push(ctx.line.progress({
442
+ label: "Standard",
443
+ used: used,
444
+ limit: limit,
445
+ format: { kind: "count", suffix: "tokens" },
446
+ resetsAt: resetsAt,
447
+ periodDurationMs: periodDurationMs,
448
+ }))
449
+ }
450
+
451
+ // Premium tokens (detail line, only if plan includes premium)
452
+ const premium = usage.premium
453
+ if (premium && typeof premium.totalAllowance === "number" && premium.totalAllowance > 0) {
454
+ const used = premium.orgTotalTokensUsed || 0
455
+ const limit = premium.totalAllowance
456
+ lines.push(ctx.line.progress({
457
+ label: "Premium",
458
+ used: used,
459
+ limit: limit,
460
+ format: { kind: "count", suffix: "tokens" },
461
+ resetsAt: resetsAt,
462
+ periodDurationMs: periodDurationMs,
463
+ }))
464
+ }
465
+
466
+ // Infer plan from allowance
467
+ let plan = null
468
+ if (standard && typeof standard.totalAllowance === "number") {
469
+ const allowance = standard.totalAllowance
470
+ if (allowance >= 200000000) {
471
+ plan = "Max"
472
+ } else if (allowance >= 20000000) {
473
+ plan = "Pro"
474
+ } else if (allowance > 0) {
475
+ plan = "Basic"
476
+ }
477
+ }
478
+
479
+ if (lines.length === 0) {
480
+ lines.push(ctx.line.badge({ label: "Status", text: "No usage data", color: "#a3a3a3" }))
481
+ }
482
+
483
+ return { plan: plan, lines: lines }
484
+ }
485
+
486
+ globalThis.__openusage_plugin = { id: "factory", probe }
487
+ })()
@@ -0,0 +1,15 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "factory",
4
+ "name": "Factory",
5
+ "version": "0.0.1",
6
+ "entry": "plugin.js",
7
+ "icon": "icon.svg",
8
+ "brandColor": "#020202",
9
+ "supportedModes": ["oauth", "local"],
10
+ "autoMode": "oauth",
11
+ "lines": [
12
+ { "type": "progress", "label": "Standard", "scope": "overview", "primaryOrder": 1 },
13
+ { "type": "progress", "label": "Premium", "scope": "detail" }
14
+ ]
15
+ }
@@ -0,0 +1,6 @@
1
+ <svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" fill="none" aria-hidden="true">
2
+ <path
3
+ d="M314.333 110.167L255.98 251.729l-58.416-141.562h-37.459l64 154.75c5.23 12.854 17.771 21.312 31.646 21.312s26.417-8.437 31.646-21.27l64.396-154.792h-37.459zm24.917 215.666L446 216.583l-14.562-34.77-116.584 119.562c-9.708 9.958-12.541 24.833-7.146 37.646 5.292 12.73 17.792 21.083 31.584 21.083l.042.063L506 359.75l-14.562-34.77-152.146.853h-.042zM66 216.5l14.563-34.77 116.583 119.562a34.592 34.592 0 017.146 37.646C199 351.667 186.5 360.02 172.708 360.02l-166.666-.375-.042.042 14.563-34.771 152.145.875L66 216.5z"
4
+ fill="currentColor"
5
+ />
6
+ </svg>