@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,331 @@
1
+ (function () {
2
+ var API_URL = "https://api.synthetic.new/v2/quotas";
3
+ var ONE_HOUR_MS = 60 * 60 * 1000;
4
+
5
+ var CROSSUSAGE_CONFIG_PATH = "~/.crossusage/config.json";
6
+ var DEFAULT_PI_AGENT_DIR = "~/.pi/agent";
7
+ var FACTORY_SETTINGS_PATH = "~/.factory/settings.json";
8
+ var OPENCODE_AUTH_PATH = "~/.local/share/opencode/auth.json";
9
+
10
+ // Provider names a user might register Synthetic under in various harnesses
11
+ var PROVIDER_NAMES = ["synthetic", "synthetic.new", "syn"];
12
+
13
+ function resolvePiAgentDir(ctx) {
14
+ var envDir = ctx.host.env.get("PI_CODING_AGENT_DIR");
15
+ if (typeof envDir === "string" && envDir.trim()) {
16
+ return envDir.trim();
17
+ }
18
+ return DEFAULT_PI_AGENT_DIR;
19
+ }
20
+
21
+ function extractKey(value) {
22
+ if (typeof value === "string" && value.trim()) return value.trim();
23
+ return null;
24
+ }
25
+
26
+ // Search a parsed JSON object for a Synthetic API key under known provider names
27
+ function findKeyInProviderMap(obj) {
28
+ if (!obj || typeof obj !== "object") return null;
29
+ for (var i = 0; i < PROVIDER_NAMES.length; i++) {
30
+ var entry = obj[PROVIDER_NAMES[i]];
31
+ if (!entry) continue;
32
+ // Pi auth.json style: { "synthetic": { "type": "api_key", "key": "syn_..." } }
33
+ var k = extractKey(entry.key);
34
+ if (k) return k;
35
+ // Pi models.json style: { "providers": { "synthetic": { "apiKey": "syn_..." } } }
36
+ k = extractKey(entry.apiKey);
37
+ if (k) return k;
38
+ }
39
+ return null;
40
+ }
41
+
42
+ function tryReadJson(ctx, path) {
43
+ try {
44
+ if (!ctx.host.fs.exists(path)) return null;
45
+ return ctx.util.tryParseJson(ctx.host.fs.readText(path));
46
+ } catch (e) {
47
+ ctx.host.log.warn("Failed to read " + path + ": " + e);
48
+ return null;
49
+ }
50
+ }
51
+
52
+ function toFiniteNumber(v) {
53
+ if (typeof v === "number" && Number.isFinite(v)) return v;
54
+ if (typeof v === "string" && v.trim() !== "") {
55
+ var n = Number(v);
56
+ if (Number.isFinite(n)) return n;
57
+ }
58
+ return null;
59
+ }
60
+
61
+ /** API may nest the payload under `data`. */
62
+ function unwrapQuotasJson(raw) {
63
+ if (!raw || typeof raw !== "object") return raw;
64
+ if (raw.data && typeof raw.data === "object") {
65
+ return Object.assign({}, raw.data, raw);
66
+ }
67
+ return raw;
68
+ }
69
+
70
+ function pickRollingFiveHour(json) {
71
+ return json.rollingFiveHourLimit || json.rolling_five_hour_limit || null;
72
+ }
73
+
74
+ function pickWeeklyToken(json) {
75
+ return json.weeklyTokenLimit || json.weekly_token_limit || null;
76
+ }
77
+
78
+ function pickFreeToolCalls(json) {
79
+ return json.freeToolCalls || json.free_tool_calls || null;
80
+ }
81
+
82
+ function hasUsableRollingFiveHourLimit(value) {
83
+ if (!value || typeof value !== "object") return false;
84
+ var max = toFiniteNumber(value.max);
85
+ var remaining = toFiniteNumber(value.remaining);
86
+ return max !== null && remaining !== null;
87
+ }
88
+
89
+ function hasUsableWeeklyTokenLimit(value) {
90
+ if (!value || typeof value !== "object") return false;
91
+ return toFiniteNumber(value.percentRemaining) !== null;
92
+ }
93
+
94
+ function normalizeApiErrorMessage(json, status) {
95
+ var fallback = "Request failed (HTTP " + status + ")";
96
+ if (!json || typeof json !== "object") return fallback;
97
+
98
+ var direct = typeof json.error === "string" ? json.error.trim() : "";
99
+ if (direct) return direct;
100
+
101
+ if (json.error && typeof json.error === "object") {
102
+ var nested = typeof json.error.message === "string" ? json.error.message.trim() : "";
103
+ if (nested) return nested;
104
+
105
+ var serialized = JSON.stringify(json.error);
106
+ if (serialized && serialized !== "{}") return serialized;
107
+ }
108
+
109
+ var message = typeof json.message === "string" ? json.message.trim() : "";
110
+ if (message) return message;
111
+
112
+ return fallback;
113
+ }
114
+
115
+ function loadApiKey(ctx) {
116
+ // 0. CrossUsage user config (created on first app launch)
117
+ var cu = tryReadJson(ctx, CROSSUSAGE_CONFIG_PATH);
118
+ var sk;
119
+ if (cu && cu.synthetic && typeof cu.synthetic === "object") {
120
+ sk = extractKey(cu.synthetic.apiKey);
121
+ if (sk) return sk;
122
+ }
123
+ if (cu && typeof cu.syntheticApiKey === "string") {
124
+ sk = extractKey(cu.syntheticApiKey);
125
+ if (sk) return sk;
126
+ }
127
+
128
+ var piDir = resolvePiAgentDir(ctx);
129
+
130
+ // 1. Pi auth.json — primary source
131
+ var piAuth = tryReadJson(ctx, piDir + "/auth.json");
132
+ var key = findKeyInProviderMap(piAuth);
133
+ if (key) return key;
134
+
135
+ // 2. Pi models.json — custom provider config with apiKey field
136
+ var piModels = tryReadJson(ctx, piDir + "/models.json");
137
+ if (piModels && piModels.providers) {
138
+ key = findKeyInProviderMap(piModels.providers);
139
+ if (key) return key;
140
+ }
141
+
142
+ // 3. Factory/Droid settings.json — custom models with synthetic.new baseUrl
143
+ var factorySettings = tryReadJson(ctx, FACTORY_SETTINGS_PATH);
144
+ if (factorySettings && Array.isArray(factorySettings.customModels)) {
145
+ for (var i = 0; i < factorySettings.customModels.length; i++) {
146
+ var model = factorySettings.customModels[i];
147
+ if (
148
+ model &&
149
+ typeof model.baseUrl === "string" &&
150
+ model.baseUrl.indexOf("synthetic.new") !== -1
151
+ ) {
152
+ key = extractKey(model.apiKey);
153
+ if (key) return key;
154
+ }
155
+ }
156
+ }
157
+
158
+ // 4. OpenCode auth.json
159
+ var ocAuth = tryReadJson(ctx, OPENCODE_AUTH_PATH);
160
+ key = findKeyInProviderMap(ocAuth);
161
+ if (key) return key;
162
+
163
+ // 5. SYNTHETIC_API_KEY env var
164
+ var envKey = ctx.host.env.get("SYNTHETIC_API_KEY");
165
+ if (typeof envKey === "string" && envKey.trim()) {
166
+ return envKey.trim();
167
+ }
168
+
169
+ return null;
170
+ }
171
+
172
+ function probe(ctx) {
173
+ var apiKey = loadApiKey(ctx);
174
+ if (!apiKey) {
175
+ throw "Synthetic API key not found. Put your syn_ key in ~/.crossusage/config.json under synthetic.apiKey, set SYNTHETIC_API_KEY, or configure Pi / Factory / OpenCode.";
176
+ }
177
+
178
+ var resp, json;
179
+ try {
180
+ var result = ctx.util.requestJson({
181
+ method: "GET",
182
+ url: API_URL,
183
+ headers: {
184
+ Authorization: "Bearer " + apiKey,
185
+ Accept: "application/json",
186
+ },
187
+ timeoutMs: 15000,
188
+ });
189
+ resp = result.resp;
190
+ json = result.json;
191
+ } catch (e) {
192
+ throw "Request failed. Check your connection.";
193
+ }
194
+
195
+ if (ctx.util.isAuthStatus(resp.status)) {
196
+ throw "API key invalid or expired. Check your Synthetic API key.";
197
+ }
198
+
199
+ if (resp.status < 200 || resp.status >= 300) {
200
+ var msg = normalizeApiErrorMessage(json, resp.status);
201
+ throw msg;
202
+ }
203
+
204
+ if (!json) {
205
+ throw "Could not parse usage data.";
206
+ }
207
+
208
+ json = unwrapQuotasJson(json);
209
+
210
+ var rfl = pickRollingFiveHour(json);
211
+ var wtl = pickWeeklyToken(json);
212
+
213
+ var lines = [];
214
+
215
+ // 5h Rate Limit — hero metric (immediate blocker)
216
+ if (hasUsableRollingFiveHourLimit(rfl)) {
217
+ var rflMax = toFiniteNumber(rfl.max);
218
+ var rflRem = toFiniteNumber(rfl.remaining);
219
+ var rflUsed = Math.max(0, rflMax - rflRem);
220
+ lines.push(ctx.line.progress({
221
+ label: "5h Rate Limit",
222
+ used: rflUsed,
223
+ limit: rflMax,
224
+ format: { kind: "count", suffix: "requests" },
225
+ }));
226
+ }
227
+
228
+ // Mana Bar — longer-term weekly budget
229
+ if (hasUsableWeeklyTokenLimit(wtl)) {
230
+ var pct = toFiniteNumber(wtl.percentRemaining);
231
+ var manaUsed = Math.max(0, Math.round(100 - pct));
232
+ lines.push(ctx.line.progress({
233
+ label: "Mana Bar",
234
+ used: manaUsed,
235
+ limit: 100,
236
+ format: { kind: "percent" },
237
+ }));
238
+ }
239
+
240
+ // Rate Limited badge — only when actively limited
241
+ if (rfl && rfl.limited === true) {
242
+ lines.push(
243
+ ctx.line.badge({
244
+ label: "Rate Limited",
245
+ text: "Rate limited",
246
+ color: "#ef4444",
247
+ })
248
+ );
249
+ }
250
+
251
+ // Subscription — legacy request count, only shown if NOT on v3 rate limits
252
+ var onV3 = hasUsableRollingFiveHourLimit(rfl) || hasUsableWeeklyTokenLimit(wtl);
253
+ if (!onV3 && json.subscription) {
254
+ var sub = json.subscription;
255
+ var subLimit = toFiniteNumber(sub.limit);
256
+ if (subLimit !== null) {
257
+ var subReq = toFiniteNumber(sub.requests);
258
+ if (subReq === null) subReq = 0;
259
+ var subOpts = {
260
+ label: "Subscription",
261
+ used: subReq,
262
+ limit: subLimit,
263
+ format: { kind: "count", suffix: "requests" },
264
+ };
265
+ var subReset = ctx.util.toIso(sub.renewsAt);
266
+ if (subReset) subOpts.resetsAt = subReset;
267
+ lines.push(ctx.line.progress(subOpts));
268
+ }
269
+ }
270
+
271
+ // Free Tool Calls — legacy only, zeroed out on v3
272
+ var ftc = pickFreeToolCalls(json);
273
+ if (!onV3 && ftc) {
274
+ var ftcLimit = toFiniteNumber(ftc.limit);
275
+ if (ftcLimit !== null && ftcLimit > 0) {
276
+ var ftcReq = toFiniteNumber(ftc.requests);
277
+ if (ftcReq === null) ftcReq = 0;
278
+ var ftcOpts = {
279
+ label: "Free Tool Calls",
280
+ used: Math.round(ftcReq),
281
+ limit: ftcLimit,
282
+ format: { kind: "count", suffix: "requests" },
283
+ };
284
+ var ftcReset = ctx.util.toIso(ftc.renewsAt);
285
+ if (ftcReset) ftcOpts.resetsAt = ftcReset;
286
+ lines.push(ctx.line.progress(ftcOpts));
287
+ }
288
+ }
289
+
290
+ // Search — hourly search quota (detail)
291
+ if (json.search && json.search.hourly) {
292
+ var srch = json.search.hourly;
293
+ var srchLimit = toFiniteNumber(srch.limit);
294
+ if (srchLimit !== null) {
295
+ var srchReq = toFiniteNumber(srch.requests);
296
+ if (srchReq === null) srchReq = 0;
297
+ var srchOpts = {
298
+ label: "Search",
299
+ used: srchReq,
300
+ limit: srchLimit,
301
+ format: { kind: "count", suffix: "requests" },
302
+ periodDurationMs: ONE_HOUR_MS,
303
+ };
304
+ var srchReset = ctx.util.toIso(srch.renewsAt);
305
+ if (srchReset) srchOpts.resetsAt = srchReset;
306
+ lines.push(ctx.line.progress(srchOpts));
307
+ }
308
+ }
309
+
310
+ if (lines.length === 0) {
311
+ var keys = Object.keys(json).filter(function (k) {
312
+ return k !== "data";
313
+ });
314
+ ctx.host.log.warn(
315
+ "Synthetic /v2/quotas: no quota rows matched; top-level keys: " + keys.join(", ")
316
+ );
317
+ lines.push(
318
+ ctx.line.badge({
319
+ label: "Quotas",
320
+ text:
321
+ "API OK — no fields matched (check Synthetic API changes or open a GitHub issue)",
322
+ color: "#64748b",
323
+ })
324
+ );
325
+ }
326
+
327
+ return { lines: lines };
328
+ }
329
+
330
+ globalThis.__openusage_plugin = { id: "synthetic", probe: probe };
331
+ })();
@@ -0,0 +1,23 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "synthetic",
4
+ "name": "Synthetic",
5
+ "version": "0.0.1",
6
+ "entry": "plugin.js",
7
+ "icon": "icon.svg",
8
+ "brandColor": "#000000",
9
+ "lines": [
10
+ { "type": "progress", "label": "5h Rate Limit", "scope": "overview", "primaryOrder": 1 },
11
+ { "type": "progress", "label": "Mana Bar", "scope": "overview" },
12
+ { "type": "badge", "label": "Rate Limited", "scope": "overview" },
13
+ { "type": "badge", "label": "Quotas", "scope": "overview" },
14
+ { "type": "progress", "label": "Subscription", "scope": "overview", "primaryOrder": 2 },
15
+ { "type": "progress", "label": "Free Tool Calls", "scope": "detail" },
16
+ { "type": "progress", "label": "Search", "scope": "overview" }
17
+ ],
18
+ "supportedModes": ["api", "local"],
19
+ "autoMode": "local",
20
+ "links": [
21
+ { "label": "Dashboard", "url": "https://synthetic.new/billing" }
22
+ ]
23
+ }
@@ -0,0 +1,28 @@
1
+ <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <title>T3 Chat</title>
3
+ <defs>
4
+ <linearGradient id="bg" x1="256" y1="0" x2="256" y2="512" gradientUnits="userSpaceOnUse">
5
+ <stop offset="0" stop-color="#FF25B7"/>
6
+ <stop offset="1" stop-color="#8F005F"/>
7
+ </linearGradient>
8
+
9
+ <radialGradient id="bubble" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
10
+ gradientTransform="translate(240 210) rotate(90) scale(190 245)">
11
+ <stop offset="0" stop-color="#FFD6FF"/>
12
+ <stop offset="0.55" stop-color="#F8A4F7"/>
13
+ <stop offset="1" stop-color="#EF79E8"/>
14
+ </radialGradient>
15
+
16
+ <filter id="shadow" x="60" y="80" width="420" height="380" filterUnits="userSpaceOnUse">
17
+ <feDropShadow dx="18" dy="28" stdDeviation="20" flood-color="#5A003B" flood-opacity="0.45"/>
18
+ </filter>
19
+ </defs>
20
+
21
+ <rect width="512" height="512" rx="92" fill="url(#bg)"/>
22
+
23
+ <path
24
+ filter="url(#shadow)"
25
+ fill="url(#bubble)"
26
+ d="M434.5 238.5C434.5 314.8 354.6 376.7 256 376.7C240.1 376.7 224.7 375.1 210 372.1L120.1 408.8C116.5 410.3 112.7 407.4 113.2 403.5L123.5 330.8C94.6 306.4 77.5 274.2 77.5 238.5C77.5 162.2 157.4 100.3 256 100.3C354.6 100.3 434.5 162.2 434.5 238.5Z"
27
+ />
28
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg fill="currentColor" height="1em" style="flex:none;line-height:1" viewBox="0 0 512 512" width="1em" xmlns="http://www.w3.org/2000/svg">
2
+ <title>T3 Chat</title>
3
+ <path d="M434.5 238.5C434.5 314.8 354.6 376.7 256 376.7C240.1 376.7 224.7 375.1 210 372.1L120.1 408.8C116.5 410.3 112.7 407.4 113.2 403.5L123.5 330.8C94.6 306.4 77.5 274.2 77.5 238.5C77.5 162.2 157.4 100.3 256 100.3C354.6 100.3 434.5 162.2 434.5 238.5Z"/>
4
+ </svg>
@@ -0,0 +1,243 @@
1
+ (function () {
2
+ const INPUT = '{"0":{"json":{"sessionId":null},"meta":{"values":{"sessionId":["undefined"]}}}}'
3
+ const CUSTOMER_DATA_URL =
4
+ "https://t3.chat/api/trpc/getCustomerData?batch=1&input=" + encodeURIComponent(INPUT)
5
+ const FORWARDED_HEADERS = {
6
+ "accept": "Accept",
7
+ "accept-language": "Accept-Language",
8
+ "cache-control": "Cache-Control",
9
+ "pragma": "Pragma",
10
+ "priority": "Priority",
11
+ "referer": "Referer",
12
+ "sec-fetch-dest": "Sec-Fetch-Dest",
13
+ "sec-fetch-mode": "Sec-Fetch-Mode",
14
+ "sec-fetch-site": "Sec-Fetch-Site",
15
+ "trpc-accept": "trpc-accept",
16
+ "user-agent": "User-Agent",
17
+ "x-client-context": "x-client-context",
18
+ "x-deployment-id": "X-Deployment-Id",
19
+ "x-trpc-batch": "x-trpc-batch",
20
+ "x-trpc-source": "x-trpc-source",
21
+ }
22
+
23
+ function rawCredential(ctx) {
24
+ return (
25
+ (ctx.provider && ctx.provider.cookieHeader) ||
26
+ ctx.host.env.get("T3CHAT_COOKIE") ||
27
+ ctx.host.env.get("T3_CHAT_COOKIE") ||
28
+ ""
29
+ ).trim()
30
+ }
31
+
32
+ function unquoteShell(value) {
33
+ value = String(value || "").trim()
34
+ if (!value) return ""
35
+ if ((value[0] === "'" && value[value.length - 1] === "'") || (value[0] === '"' && value[value.length - 1] === '"')) {
36
+ value = value.slice(1, -1)
37
+ }
38
+ return value.replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\")
39
+ }
40
+
41
+ function headerFieldsFromCurl(raw) {
42
+ const fields = []
43
+ const text = String(raw || "")
44
+ const re = /(?:^|\s)(?:-H|--header)(?:\s+|=)(?:"([^"]*)"|'([^']*)'|(\S+))/g
45
+ let match
46
+ while ((match = re.exec(text)) !== null) {
47
+ fields.push(unquoteShell(match[1] || match[2] || match[3] || ""))
48
+ }
49
+ return fields
50
+ }
51
+
52
+ function cookieFieldsFromCurl(raw) {
53
+ const fields = []
54
+ const text = String(raw || "")
55
+ const re = /(?:^|\s)(?:-b|--cookie)(?:\s+|=)(?:"([^"]*)"|'([^']*)'|(\S+))/g
56
+ let match
57
+ while ((match = re.exec(text)) !== null) {
58
+ fields.push(unquoteShell(match[1] || match[2] || match[3] || ""))
59
+ }
60
+ return fields
61
+ }
62
+
63
+ function requestContext(raw) {
64
+ raw = String(raw || "").trim()
65
+ if (!raw) return null
66
+ const fields = headerFieldsFromCurl(raw)
67
+ const cookieFields = cookieFieldsFromCurl(raw)
68
+ if (!fields.length && !cookieFields.length) return { cookieHeader: raw, headers: {} }
69
+
70
+ let cookie = ""
71
+ const headers = {}
72
+ if (cookieFields.length) {
73
+ cookie = cookieFields[cookieFields.length - 1]
74
+ }
75
+ for (let i = 0; i < fields.length; i += 1) {
76
+ const field = fields[i]
77
+ const idx = field.indexOf(":")
78
+ if (idx === -1) continue
79
+ const name = field.slice(0, idx).trim()
80
+ const value = field.slice(idx + 1).trim()
81
+ if (!name || !value) continue
82
+ if (name.toLowerCase() === "cookie") {
83
+ cookie = value
84
+ continue
85
+ }
86
+ const canonical = FORWARDED_HEADERS[name.toLowerCase()]
87
+ if (canonical) headers[canonical] = value
88
+ }
89
+ return cookie ? { cookieHeader: cookie, headers: headers } : null
90
+ }
91
+
92
+ function findCustomerData(value) {
93
+ if (!value || typeof value !== "object") return null
94
+ if (
95
+ value.usageFourHourPercentage !== undefined ||
96
+ value.usageMonthPercentage !== undefined ||
97
+ (value.subscription && value.usageBand !== undefined)
98
+ ) {
99
+ return value
100
+ }
101
+ if (Array.isArray(value)) {
102
+ for (let i = 0; i < value.length; i += 1) {
103
+ const found = findCustomerData(value[i])
104
+ if (found) return found
105
+ }
106
+ return null
107
+ }
108
+ for (const key in value) {
109
+ const found = findCustomerData(value[key])
110
+ if (found) return found
111
+ }
112
+ return null
113
+ }
114
+
115
+ function parseCustomerData(text) {
116
+ const lines = String(text || "").split(/\r?\n/)
117
+ for (let i = 0; i < lines.length; i += 1) {
118
+ const line = lines[i].trim()
119
+ if (!line) continue
120
+ const parsed = ctxSafeJson(line)
121
+ const customer = findCustomerData(parsed)
122
+ if (customer) return customer
123
+ }
124
+ const parsed = ctxSafeJson(text)
125
+ const customer = findCustomerData(parsed)
126
+ if (customer) return customer
127
+ throw "Could not parse T3 Chat usage: missing customer data object."
128
+ }
129
+
130
+ function ctxSafeJson(text) {
131
+ try {
132
+ return JSON.parse(text)
133
+ } catch (_) {
134
+ return null
135
+ }
136
+ }
137
+
138
+ function percent(value) {
139
+ const n = Number(value)
140
+ if (!Number.isFinite(n)) return 0
141
+ return Math.max(0, Math.min(100, n))
142
+ }
143
+
144
+ function isoFromEpoch(value) {
145
+ const n = Number(value)
146
+ if (!Number.isFinite(n) || n <= 0) return null
147
+ const ms = n > 10000000000 ? n : n * 1000
148
+ return new Date(ms).toISOString()
149
+ }
150
+
151
+ function planLabel(data) {
152
+ const raw =
153
+ (data.subscription && data.subscription.productName) ||
154
+ data.subTier ||
155
+ ""
156
+ return String(raw).trim().replace(/[-_]+/g, " ").replace(/\b\w/g, function (c) {
157
+ return c.toUpperCase()
158
+ })
159
+ }
160
+
161
+ function isVercelChallenge(resp) {
162
+ if (!resp) return false
163
+ const headers = resp.headers || {}
164
+ const mitigated =
165
+ headers["x-vercel-mitigated"] ||
166
+ headers["X-Vercel-Mitigated"] ||
167
+ headers["X-Vercel-mitigated"]
168
+ return (
169
+ Number(resp.status) === 429 &&
170
+ String(mitigated || "").toLowerCase() === "challenge"
171
+ ) || String(resp.bodyText || "").indexOf("Vercel") !== -1
172
+ }
173
+
174
+ function probe(ctx) {
175
+ const requestCtx = requestContext(rawCredential(ctx))
176
+ if (!requestCtx || !requestCtx.cookieHeader) {
177
+ throw "T3 Chat session not configured. Paste a full browser cURL capture into the Cookie header field, or set T3CHAT_COOKIE/T3_CHAT_COOKIE."
178
+ }
179
+
180
+ const headers = {
181
+ Accept: "*/*",
182
+ "Accept-Language": "en-US,en;q=0.9",
183
+ "Cache-Control": "no-cache",
184
+ Cookie: requestCtx.cookieHeader,
185
+ Pragma: "no-cache",
186
+ Priority: "u=4",
187
+ Referer: "https://t3.chat/settings/customization",
188
+ "Sec-Fetch-Dest": "empty",
189
+ "Sec-Fetch-Mode": "cors",
190
+ "Sec-Fetch-Site": "same-origin",
191
+ "User-Agent":
192
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
193
+ "trpc-accept": "application/jsonl",
194
+ "x-trpc-batch": "true",
195
+ "x-trpc-source": "web-client",
196
+ }
197
+ if (requestCtx && requestCtx.headers) {
198
+ for (const name in requestCtx.headers) headers[name] = requestCtx.headers[name]
199
+ }
200
+
201
+ const resp = ctx.util.request({
202
+ method: "GET",
203
+ url: CUSTOMER_DATA_URL,
204
+ headers: headers,
205
+ timeoutMs: 15000,
206
+ })
207
+
208
+ if ((resp.status < 200 || resp.status >= 300) && isVercelChallenge(resp)) {
209
+ throw "T3 Chat returned a Vercel security challenge. Paste the full browser cURL request into the Cookie header field. Run `usagestat auth curl --provider t3chat` for steps."
210
+ }
211
+ if (ctx.util.isAuthStatus(resp.status)) throw "T3 Chat session cookie is invalid or expired."
212
+ if (resp.status < 200 || resp.status >= 300) throw "T3 Chat API error (HTTP " + resp.status + ")."
213
+
214
+ const data = parseCustomerData(resp.bodyText)
215
+ const lines = []
216
+ lines.push(ctx.line.progress({
217
+ label: "Base",
218
+ used: percent(data.usageFourHourPercentage || data.usagePeriodPercentage),
219
+ limit: 100,
220
+ resetsAt: isoFromEpoch(data.usageFourHourNextResetAt || data.usageWindowNextResetAt),
221
+ periodDurationMs: 4 * 60 * 60 * 1000,
222
+ format: { kind: "percent" },
223
+ }))
224
+ lines.push(ctx.line.progress({
225
+ label: "Overage",
226
+ used: percent(data.usageMonthPercentage || data.usagePeriodPercentage),
227
+ limit: 100,
228
+ resetsAt: isoFromEpoch(data.subscription && data.subscription.currentPeriodEnd),
229
+ format: { kind: "percent" },
230
+ }))
231
+ if (data.lifetimeBalance !== undefined && data.lifetimeBalance !== null) {
232
+ lines.push(ctx.line.text({ label: "Balance", value: String(data.lifetimeBalance) }))
233
+ }
234
+
235
+ return {
236
+ source: "web",
237
+ plan: planLabel(data),
238
+ lines: lines,
239
+ }
240
+ }
241
+
242
+ globalThis.__openusage_plugin = { id: "t3chat", probe: probe };
243
+ })();
@@ -0,0 +1,20 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "t3chat",
4
+ "name": "T3 Chat",
5
+ "version": "0.0.1",
6
+ "entry": "plugin.js",
7
+ "brandColor": "#F56647",
8
+ "links": [
9
+ { "label": "Customization", "url": "https://t3.chat/settings/customization" },
10
+ { "label": "Subscription", "url": "https://t3.chat/settings/subscription" }
11
+ ],
12
+ "usageDashboardUrl": "https://t3.chat/settings/customization",
13
+ "supportedModes": ["web"],
14
+ "autoMode": "web",
15
+ "webUrl": "https://t3.chat",
16
+ "lines": [
17
+ { "type": "progress", "label": "Base", "scope": "overview", "primaryOrder": 1 },
18
+ { "type": "progress", "label": "Overage", "scope": "detail" }
19
+ ]
20
+ }
@@ -0,0 +1 @@
1
+ <svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Venice</title><path clip-rule="evenodd" d="M8.295 16.074a4.239 4.239 0 00-1.37-.685 4.255 4.255 0 00-1.636-.138 4.27 4.27 0 00-1.566.499c-.472.26-.875.602-1.19.973-.315.37-.59.824-.773 1.336a4.432 4.432 0 00-.257 1.644c.02.564.155 1.125.375 1.621s.528.93.87 1.275c.34.346.766.657 1.256.881.49.224 1.041.36 1.598.381a4.258 4.258 0 001.62-.26 4.25 4.25 0 001.318-.786c.365-.319.703-.728.959-1.206.256-.479.43-1.027.492-1.589a4.436 4.436 0 00-.137-1.659 4.346 4.346 0 00-.675-1.39l.95-.964.691.702h.393l.475-.481v-.399l-.692-.702L12 14.11l1.004 1.018-.691.702v.399l.474.48h.393l.691-.701.95.964a4.346 4.346 0 00-.675 1.39 4.435 4.435 0 00-.137 1.66c.062.56.236 1.11.492 1.588s.594.887.96 1.206c.364.32.812.599 1.316.785a4.258 4.258 0 001.621.261 4.262 4.262 0 001.598-.38c.49-.225.916-.536 1.257-.882.341-.346.648-.779.869-1.275.22-.496.355-1.057.375-1.62a4.433 4.433 0 00-.257-1.645 4.338 4.338 0 00-.774-1.336 4.272 4.272 0 00-1.189-.973 4.27 4.27 0 00-1.566-.5 4.255 4.255 0 00-1.635.14 4.239 4.239 0 00-1.37.684l-.933-.947.69-.702v-.398l-.49-.498h-.393l-.692.701-1.004-1.018L18.15 7.87l2.132 2.163v-2.25H22.5L20.368 5.62 22.5 3.457V3.06l-.491-.498h-.393L12 12.316 2.384 2.56H1.99l-.491.498v.398L3.632 5.62 1.5 7.782h2.218v2.25L5.85 7.87l5.266 5.342-1.004 1.018-.692-.701h-.393l-.49.498v.399l.69.7-.932.948zm8.546 4.95a1.59 1.59 0 00.107 1.49c.256.43.799.73 1.294.716.494.014 1.037-.287 1.293-.716a1.59 1.59 0 00.108-1.49l.054-.06a1.53 1.53 0 001.471-.107c.424-.26.72-.81.707-1.313.014-.502-.283-1.053-.707-1.313a1.53 1.53 0 00-1.471-.107l-.054-.059a1.59 1.59 0 00-.108-1.49c-.256-.43-.799-.73-1.293-.717-.495-.013-1.038.287-1.294.716a1.59 1.59 0 00-.107 1.491l-.054.059a1.53 1.53 0 00-1.472.107c-.424.26-.72.81-.707 1.313-.013.502.283 1.053.707 1.313a1.53 1.53 0 001.472.107l.054.06zm-9.79 1.49a1.59 1.59 0 00.108-1.49l.054-.06a1.53 1.53 0 001.471-.107c.424-.26.72-.81.707-1.313.014-.502-.283-1.053-.707-1.313a1.53 1.53 0 00-1.47-.107l-.055-.059a1.59 1.59 0 00-.107-1.49c-.257-.43-.8-.73-1.294-.717-.495-.013-1.037.287-1.294.716a1.59 1.59 0 00-.107 1.491l-.054.059a1.53 1.53 0 00-1.471.107c-.424.26-.72.81-.707 1.313-.014.502.283 1.053.707 1.313a1.53 1.53 0 001.471.107l.054.06a1.59 1.59 0 00.107 1.49c.257.43.8.73 1.294.716.495.014 1.037-.287 1.294-.716z" fill="#E05A2D" fill-rule="evenodd"></path><path clip-rule="evenodd" d="M12.274 2.56L14.8 0l1.25 1.268v4.274l-3.462 3.512h-.625L8.5 5.542V1.268L9.75 0l2.524 2.56zM9.75.898l2.212 2.243v4.12L9.75 5.018V.897zm2.837 2.243L14.799.897v4.12l-2.212 2.244V3.14z" fill="#E05A2D" fill-rule="evenodd"></path></svg>
@@ -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>Venice</title><path clip-rule="evenodd" d="M8.295 16.074a4.239 4.239 0 00-1.37-.685 4.255 4.255 0 00-1.636-.138 4.27 4.27 0 00-1.566.499c-.472.26-.875.602-1.19.973-.315.37-.59.824-.773 1.336a4.432 4.432 0 00-.257 1.644c.02.564.155 1.125.375 1.621s.528.93.87 1.275c.34.346.766.657 1.256.881.49.224 1.041.36 1.598.381a4.258 4.258 0 001.62-.26 4.25 4.25 0 001.318-.786c.365-.319.703-.728.959-1.206.256-.479.43-1.027.492-1.589a4.436 4.436 0 00-.137-1.659 4.346 4.346 0 00-.675-1.39l.95-.964.691.702h.393l.475-.481v-.399l-.692-.702L12 14.11l1.004 1.018-.691.702v.399l.474.48h.393l.691-.701.95.964a4.346 4.346 0 00-.675 1.39 4.435 4.435 0 00-.137 1.66c.062.56.236 1.11.492 1.588s.594.887.96 1.206c.364.32.812.599 1.316.785a4.258 4.258 0 001.621.261 4.262 4.262 0 001.598-.38c.49-.225.916-.536 1.257-.882.341-.346.648-.779.869-1.275.22-.496.355-1.057.375-1.62a4.433 4.433 0 00-.257-1.645 4.338 4.338 0 00-.774-1.336 4.272 4.272 0 00-1.189-.973 4.27 4.27 0 00-1.566-.5 4.255 4.255 0 00-1.635.14 4.239 4.239 0 00-1.37.684l-.933-.947.69-.702v-.398l-.49-.498h-.393l-.692.701-1.004-1.018L18.15 7.87l2.132 2.163v-2.25H22.5L20.368 5.62 22.5 3.457V3.06l-.491-.498h-.393L12 12.316 2.384 2.56H1.99l-.491.498v.398L3.632 5.62 1.5 7.782h2.218v2.25L5.85 7.87l5.266 5.342-1.004 1.018-.692-.701h-.393l-.49.498v.399l.69.7-.932.948zm8.546 4.95a1.59 1.59 0 00.107 1.49c.256.43.799.73 1.294.716.494.014 1.037-.287 1.293-.716a1.59 1.59 0 00.108-1.49l.054-.06a1.53 1.53 0 001.471-.107c.424-.26.72-.81.707-1.313.014-.502-.283-1.053-.707-1.313a1.53 1.53 0 00-1.471-.107l-.054-.059a1.59 1.59 0 00-.108-1.49c-.256-.43-.799-.73-1.293-.717-.495-.013-1.038.287-1.294.716a1.59 1.59 0 00-.107 1.491l-.054.059a1.53 1.53 0 00-1.472.107c-.424.26-.72.81-.707 1.313-.013.502.283 1.053.707 1.313a1.53 1.53 0 001.472.107l.054.06zm-9.79 1.49a1.59 1.59 0 00.108-1.49l.054-.06a1.53 1.53 0 001.471-.107c.424-.26.72-.81.707-1.313.014-.502-.283-1.053-.707-1.313a1.53 1.53 0 00-1.47-.107l-.055-.059a1.59 1.59 0 00-.107-1.49c-.257-.43-.8-.73-1.294-.717-.495-.013-1.037.287-1.294.716a1.59 1.59 0 00-.107 1.491l-.054.059a1.53 1.53 0 00-1.471.107c-.424.26-.72.81-.707 1.313-.014.502.283 1.053.707 1.313a1.53 1.53 0 001.471.107l.054.06a1.59 1.59 0 00.107 1.49c.257.43.8.73 1.294.716.495.014 1.037-.287 1.294-.716z"></path><path clip-rule="evenodd" d="M12.274 2.56L14.8 0l1.25 1.268v4.274l-3.462 3.512h-.625L8.5 5.542V1.268L9.75 0l2.524 2.56zM9.75.898l2.212 2.243v4.12L9.75 5.018V.897zm2.837 2.243L14.799.897v4.12l-2.212 2.244V3.14z"></path></svg>