@hanzo/browser-extension 1.9.31 → 1.9.34

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 (33) hide show
  1. package/dist/browser-extension/background-firefox.js +1427 -0
  2. package/dist/browser-extension/background.js +1443 -18
  3. package/dist/browser-extension/chrome/background.js +1443 -18
  4. package/dist/browser-extension/chrome/manifest.json +17 -2
  5. package/dist/browser-extension/chrome/newtab.css +277 -0
  6. package/dist/browser-extension/chrome/newtab.html +13 -0
  7. package/dist/browser-extension/chrome/newtab.js +64 -0
  8. package/dist/browser-extension/chrome/popup.html +28 -0
  9. package/dist/browser-extension/chrome/popup.js +67 -1
  10. package/dist/browser-extension/firefox/background.js +1427 -0
  11. package/dist/browser-extension/firefox/manifest.json +10 -2
  12. package/dist/browser-extension/firefox/newtab.css +277 -0
  13. package/dist/browser-extension/firefox/newtab.html +13 -0
  14. package/dist/browser-extension/firefox/newtab.js +64 -0
  15. package/dist/browser-extension/firefox/popup.html +28 -0
  16. package/dist/browser-extension/firefox/popup.js +67 -1
  17. package/dist/browser-extension/manifest.json +17 -2
  18. package/dist/browser-extension/newtab.js +64 -0
  19. package/dist/browser-extension/popup.js +67 -1
  20. package/dist/browser-extension/safari/Info.plist +2 -2
  21. package/dist/browser-extension/safari/background.js +1443 -18
  22. package/dist/browser-extension/safari/newtab.css +277 -0
  23. package/dist/browser-extension/safari/newtab.html +13 -0
  24. package/dist/browser-extension/safari/newtab.js +64 -0
  25. package/dist/browser-extension/safari/popup.html +28 -0
  26. package/dist/browser-extension/safari/popup.js +67 -1
  27. package/package.json +3 -1
  28. package/src/answer/AnswerEngine.tsx +553 -0
  29. package/src/manifest-firefox.json +10 -2
  30. package/src/manifest.json +17 -2
  31. package/src/newtab.css +277 -0
  32. package/src/newtab.html +13 -0
  33. package/src/popup.html +28 -0
@@ -3306,8 +3306,8 @@ var WebGPUAI = class {
3306
3306
  chunks.push(value);
3307
3307
  received += value.byteLength;
3308
3308
  if (total > 0 && received >= nextLog) {
3309
- const pct = Math.floor(received / total * 100);
3310
- console.log(`[Hanzo AI] ${label}: ${pct}% (${received}/${total} bytes)`);
3309
+ const pct2 = Math.floor(received / total * 100);
3310
+ console.log(`[Hanzo AI] ${label}: ${pct2}% (${received}/${total} bytes)`);
3311
3311
  nextLog = received + Math.floor(total * 0.05);
3312
3312
  }
3313
3313
  }
@@ -3585,14 +3585,14 @@ function parseTabId(raw) {
3585
3585
  function unwrapEvaluateResult(raw) {
3586
3586
  if (raw === null || raw === void 0) return raw;
3587
3587
  if (typeof raw !== "object") return raw;
3588
- const obj = raw;
3589
- const inner = obj.result;
3588
+ const obj3 = raw;
3589
+ const inner = obj3.result;
3590
3590
  if (inner && typeof inner === "object" && "value" in inner) {
3591
3591
  return inner.value;
3592
3592
  }
3593
- if ("result" in obj) return obj.result;
3594
- if ("value" in obj) return obj.value;
3595
- if (Object.keys(obj).length === 0 && !Array.isArray(obj)) return void 0;
3593
+ if ("result" in obj3) return obj3.result;
3594
+ if ("value" in obj3) return obj3.value;
3595
+ if (Object.keys(obj3).length === 0 && !Array.isArray(obj3)) return void 0;
3596
3596
  return raw;
3597
3597
  }
3598
3598
 
@@ -4377,6 +4377,1419 @@ async function getAuthStatus2() {
4377
4377
  return getAuthStatus(adapter.storage);
4378
4378
  }
4379
4379
 
4380
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/host.js
4381
+ var expandHome = (host, path) => path.startsWith("~/") ? `${host.homeDir()}/${path.slice(2)}` : path;
4382
+ var readJsonFile = async (host, path) => {
4383
+ const text = await host.readTextFile(expandHome(host, path));
4384
+ if (text === void 0)
4385
+ return void 0;
4386
+ try {
4387
+ return JSON.parse(text);
4388
+ } catch {
4389
+ return void 0;
4390
+ }
4391
+ };
4392
+
4393
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/provider.js
4394
+ var matchesMode = (kind, mode) => {
4395
+ if (mode === "auto")
4396
+ return true;
4397
+ if (mode === "web")
4398
+ return kind === "web";
4399
+ if (mode === "cli")
4400
+ return kind === "cli" || kind === "localProbe";
4401
+ if (mode === "oauth")
4402
+ return kind === "oauth";
4403
+ return kind === "apiToken";
4404
+ };
4405
+ var forMode = (all, mode) => all.filter((s) => matchesMode(s.kind, mode));
4406
+ var runPipeline = async (descriptor, ctx) => {
4407
+ var _a;
4408
+ const attempts = [];
4409
+ let lastError;
4410
+ for (const strategy of descriptor.strategies(ctx.sourceMode)) {
4411
+ if (!await strategy.isAvailable(ctx)) {
4412
+ attempts.push({ strategyId: strategy.id, ok: false, skipped: true });
4413
+ continue;
4414
+ }
4415
+ try {
4416
+ const result = await strategy.fetch(ctx);
4417
+ attempts.push({ strategyId: strategy.id, ok: true });
4418
+ return { result, attempts };
4419
+ } catch (error) {
4420
+ attempts.push({
4421
+ strategyId: strategy.id,
4422
+ ok: false,
4423
+ error: error instanceof Error ? error.message : String(error)
4424
+ });
4425
+ lastError = error;
4426
+ const fallback = ((_a = strategy.shouldFallback) == null ? void 0 : _a.call(strategy, error, ctx)) ?? true;
4427
+ if (!fallback)
4428
+ break;
4429
+ }
4430
+ }
4431
+ return { error: lastError ?? new Error(`${descriptor.id}: no strategy available`), attempts };
4432
+ };
4433
+
4434
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/providers/codex.js
4435
+ var codexHome = (ctx) => ctx.host.env("CODEX_HOME") ?? expandHome(ctx.host, "~/.codex");
4436
+ var toWindow = (w) => {
4437
+ if (!w || typeof w.used_percent !== "number")
4438
+ return void 0;
4439
+ return {
4440
+ usedPercent: w.used_percent,
4441
+ windowMinutes: w.limit_window_seconds ? Math.round(w.limit_window_seconds / 60) : void 0,
4442
+ resetsAt: w.reset_at ? new Date(w.reset_at * 1e3).toISOString() : void 0
4443
+ };
4444
+ };
4445
+ var oauthStrategy = {
4446
+ id: "codex.oauth",
4447
+ kind: "oauth",
4448
+ async isAvailable(ctx) {
4449
+ var _a;
4450
+ const auth = await readJsonFile(ctx.host, `${codexHome(ctx)}/auth.json`);
4451
+ return Boolean((_a = auth == null ? void 0 : auth.tokens) == null ? void 0 : _a.access_token);
4452
+ },
4453
+ async fetch(ctx) {
4454
+ var _a, _b, _c, _d, _e;
4455
+ const auth = await readJsonFile(ctx.host, `${codexHome(ctx)}/auth.json`);
4456
+ const token = (_a = auth == null ? void 0 : auth.tokens) == null ? void 0 : _a.access_token;
4457
+ if (!token)
4458
+ throw new Error("codex: no access token in auth.json");
4459
+ const headers = {
4460
+ Authorization: `Bearer ${token}`,
4461
+ Accept: "application/json",
4462
+ "User-Agent": "HanzoUsage"
4463
+ };
4464
+ if ((_b = auth == null ? void 0 : auth.tokens) == null ? void 0 : _b.account_id)
4465
+ headers["ChatGPT-Account-Id"] = auth.tokens.account_id;
4466
+ const res = await ctx.host.http({
4467
+ url: "https://chatgpt.com/backend-api/wham/usage",
4468
+ headers,
4469
+ timeoutMs: 3e4
4470
+ });
4471
+ if (res.status !== 200)
4472
+ throw new Error(`codex usage HTTP ${res.status}`);
4473
+ const body = JSON.parse(res.text);
4474
+ const now = ctx.host.now().toISOString();
4475
+ const usage = {
4476
+ providerId: "codex",
4477
+ primary: toWindow((_c = body.rate_limit) == null ? void 0 : _c.primary_window),
4478
+ secondary: toWindow((_d = body.rate_limit) == null ? void 0 : _d.secondary_window),
4479
+ extraRateWindows: (body.additional_rate_limits ?? []).map((extra) => {
4480
+ var _a2;
4481
+ const window = toWindow((_a2 = extra.rate_limit) == null ? void 0 : _a2.primary_window);
4482
+ if (!window || !extra.limit_name)
4483
+ return void 0;
4484
+ return { id: extra.limit_name, title: extra.limit_name, window, usageKnown: true };
4485
+ }).filter((w) => Boolean(w)),
4486
+ identity: { providerId: "codex", plan: body.plan_type, loginMethod: "oauth" },
4487
+ dataConfidence: "percentOnly",
4488
+ updatedAt: now
4489
+ };
4490
+ let credits;
4491
+ if ((_e = body.credits) == null ? void 0 : _e.has_credits) {
4492
+ credits = {
4493
+ remaining: body.credits.balance ?? 0,
4494
+ unlimited: body.credits.unlimited,
4495
+ updatedAt: now
4496
+ };
4497
+ }
4498
+ return {
4499
+ usage,
4500
+ credits,
4501
+ sourceLabel: "OpenAI OAuth",
4502
+ strategyId: this.id,
4503
+ strategyKind: this.kind
4504
+ };
4505
+ }
4506
+ };
4507
+ var codexProvider = {
4508
+ id: "codex",
4509
+ metadata: {
4510
+ displayName: "Codex",
4511
+ sessionLabel: "5h limit",
4512
+ weeklyLabel: "Weekly limit",
4513
+ supportsCredits: true,
4514
+ defaultEnabled: true,
4515
+ dashboardUrl: "https://chatgpt.com/codex/settings/usage",
4516
+ statusPageUrl: "https://status.openai.com",
4517
+ color: "#10a37f"
4518
+ },
4519
+ strategies: (mode) => forMode([oauthStrategy], mode)
4520
+ };
4521
+
4522
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/providers/claude.js
4523
+ var WINDOW_MINUTES = {
4524
+ five_hour: 300,
4525
+ seven_day: 10080,
4526
+ seven_day_sonnet: 10080,
4527
+ seven_day_opus: 10080
4528
+ };
4529
+ var toWindow2 = (key, w) => {
4530
+ const used = (w == null ? void 0 : w.utilization) ?? (w == null ? void 0 : w.used_percent);
4531
+ if (typeof used !== "number")
4532
+ return void 0;
4533
+ return { usedPercent: used, windowMinutes: WINDOW_MINUTES[key], resetsAt: w == null ? void 0 : w.resets_at };
4534
+ };
4535
+ var mapUsage = (body, now, loginMethod, plan) => {
4536
+ const extras = [];
4537
+ for (const [key, title] of [
4538
+ ["seven_day_sonnet", "Sonnet weekly"],
4539
+ ["seven_day_opus", "Opus weekly"]
4540
+ ]) {
4541
+ const window = toWindow2(key, body[key]);
4542
+ if (window)
4543
+ extras.push({ id: key, title, window, usageKnown: true });
4544
+ }
4545
+ return {
4546
+ providerId: "claude",
4547
+ primary: toWindow2("five_hour", body.five_hour),
4548
+ secondary: toWindow2("seven_day", body.seven_day),
4549
+ extraRateWindows: extras.length ? extras : void 0,
4550
+ identity: {
4551
+ providerId: "claude",
4552
+ plan: plan ?? body.subscriptionType ?? body.rate_limit_tier,
4553
+ loginMethod
4554
+ },
4555
+ providerCost: body.extra_usage && typeof body.extra_usage.used_cents === "number" ? {
4556
+ used: body.extra_usage.used_cents / 100,
4557
+ limit: typeof body.extra_usage.limit_cents === "number" ? body.extra_usage.limit_cents / 100 : void 0,
4558
+ currencyCode: "USD",
4559
+ period: "monthly",
4560
+ updatedAt: now
4561
+ } : void 0,
4562
+ dataConfidence: "percentOnly",
4563
+ updatedAt: now
4564
+ };
4565
+ };
4566
+ var credentialPaths = ["~/.claude/.credentials.json", "~/.config/claude/.credentials.json"];
4567
+ var readCredentials = async (ctx) => {
4568
+ var _a;
4569
+ for (const path of credentialPaths) {
4570
+ const file = await readJsonFile(ctx.host, path);
4571
+ if ((_a = file == null ? void 0 : file.claudeAiOauth) == null ? void 0 : _a.accessToken)
4572
+ return file.claudeAiOauth;
4573
+ }
4574
+ return void 0;
4575
+ };
4576
+ var oauthStrategy2 = {
4577
+ id: "claude.oauth",
4578
+ kind: "oauth",
4579
+ async isAvailable(ctx) {
4580
+ return Boolean(await readCredentials(ctx));
4581
+ },
4582
+ async fetch(ctx) {
4583
+ const creds = await readCredentials(ctx);
4584
+ if (!(creds == null ? void 0 : creds.accessToken))
4585
+ throw new Error("claude: no OAuth credentials");
4586
+ const res = await ctx.host.http({
4587
+ url: "https://api.anthropic.com/api/oauth/usage",
4588
+ headers: {
4589
+ Authorization: `Bearer ${creds.accessToken}`,
4590
+ "anthropic-beta": "oauth-2025-04-20",
4591
+ Accept: "application/json"
4592
+ },
4593
+ timeoutMs: 3e4
4594
+ });
4595
+ if (res.status !== 200)
4596
+ throw new Error(`claude usage HTTP ${res.status}`);
4597
+ const body = JSON.parse(res.text);
4598
+ return {
4599
+ usage: mapUsage(body, ctx.host.now().toISOString(), "oauth", creds.subscriptionType),
4600
+ sourceLabel: "Claude OAuth",
4601
+ strategyId: this.id,
4602
+ strategyKind: this.kind
4603
+ };
4604
+ }
4605
+ };
4606
+ var webStrategy = {
4607
+ id: "claude.web",
4608
+ kind: "web",
4609
+ async isAvailable(ctx) {
4610
+ var _a;
4611
+ return typeof ((_a = ctx.settings) == null ? void 0 : _a.cookieHeader) === "string";
4612
+ },
4613
+ async fetch(ctx) {
4614
+ var _a, _b;
4615
+ const cookie = String((_a = ctx.settings) == null ? void 0 : _a.cookieHeader);
4616
+ const get = async (path) => {
4617
+ const res = await ctx.host.http({
4618
+ url: `https://claude.ai/api${path}`,
4619
+ headers: { Cookie: cookie, Accept: "application/json" },
4620
+ timeoutMs: 3e4
4621
+ });
4622
+ if (res.status !== 200)
4623
+ throw new Error(`claude.ai${path} HTTP ${res.status}`);
4624
+ return JSON.parse(res.text);
4625
+ };
4626
+ const orgs = await get("/organizations");
4627
+ const orgId = (_b = orgs[0]) == null ? void 0 : _b.uuid;
4628
+ if (!orgId)
4629
+ throw new Error("claude: no organization for session");
4630
+ const body = await get(`/organizations/${orgId}/usage`);
4631
+ return {
4632
+ usage: mapUsage(body, ctx.host.now().toISOString(), "web"),
4633
+ sourceLabel: "claude.ai session",
4634
+ strategyId: this.id,
4635
+ strategyKind: this.kind
4636
+ };
4637
+ }
4638
+ };
4639
+ var claudeProvider = {
4640
+ id: "claude",
4641
+ metadata: {
4642
+ displayName: "Claude",
4643
+ sessionLabel: "Session (5h)",
4644
+ weeklyLabel: "Weekly limit",
4645
+ defaultEnabled: true,
4646
+ dashboardUrl: "https://claude.ai/settings/usage",
4647
+ statusPageUrl: "https://status.anthropic.com",
4648
+ color: "#d97757"
4649
+ },
4650
+ strategies: (mode) => forMode([oauthStrategy2, webStrategy], mode)
4651
+ };
4652
+
4653
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/providers/hanzo.js
4654
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
4655
+ var cloudStrategy = {
4656
+ id: "hanzo.cloud",
4657
+ kind: "apiToken",
4658
+ async isAvailable(ctx) {
4659
+ var _a, _b;
4660
+ return Boolean(((_a = ctx.settings) == null ? void 0 : _a.getToken) || ((_b = ctx.settings) == null ? void 0 : _b.apiKey));
4661
+ },
4662
+ async fetch(ctx) {
4663
+ var _a, _b, _c, _d;
4664
+ const base = ((_a = ctx.settings) == null ? void 0 : _a.baseUrl) ?? "https://api.hanzo.ai";
4665
+ const token = await ((_c = (_b = ctx.settings) == null ? void 0 : _b.getToken) == null ? void 0 : _c.call(_b)) ?? ((_d = ctx.settings) == null ? void 0 : _d.apiKey);
4666
+ if (!token)
4667
+ throw new Error("hanzo: no token for cloud usage");
4668
+ const res = await ctx.host.http({
4669
+ url: `${base}/v1/billing/usage`,
4670
+ headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
4671
+ timeoutMs: 3e4
4672
+ });
4673
+ if (res.status !== 200)
4674
+ throw new Error(`hanzo billing usage HTTP ${res.status}`);
4675
+ const body = JSON.parse(res.text);
4676
+ const entries = Array.isArray(body.usage) ? body.usage : Array.isArray(body.data) ? body.data : [];
4677
+ let tokens = num(body.total_tokens ?? body.totalTokens);
4678
+ let spendCents = num(body.spend_cents ?? body.spendCents);
4679
+ let requests = num(body.total_requests ?? body.requests);
4680
+ for (const e of entries) {
4681
+ tokens += num(e.total_tokens ?? e.totalTokens ?? e.tokens);
4682
+ spendCents += num(e.spend_cents ?? e.spendCents ?? e.cost_cents);
4683
+ requests += num(e.requests ?? e.request_count ?? 1) - (e.requests === void 0 && e.request_count === void 0 ? 1 : 0);
4684
+ }
4685
+ const now = ctx.host.now().toISOString();
4686
+ const usage = {
4687
+ providerId: "hanzo",
4688
+ identity: { providerId: "hanzo", loginMethod: "iam" },
4689
+ providerCost: {
4690
+ used: spendCents / 100,
4691
+ currencyCode: "USD",
4692
+ period: "monthly",
4693
+ updatedAt: now
4694
+ },
4695
+ totals: { tokens, requests },
4696
+ dataConfidence: "exact",
4697
+ updatedAt: now
4698
+ };
4699
+ return {
4700
+ usage,
4701
+ sourceLabel: "Hanzo Cloud",
4702
+ strategyId: this.id,
4703
+ strategyKind: this.kind
4704
+ };
4705
+ }
4706
+ };
4707
+ var devHome = async (ctx) => {
4708
+ const candidates = [
4709
+ ctx.host.env("HANZO_HOME"),
4710
+ ctx.host.env("CODEX_HOME"),
4711
+ expandHome(ctx.host, "~/.hanzo"),
4712
+ expandHome(ctx.host, "~/.codex")
4713
+ ].filter((c) => Boolean(c));
4714
+ for (const dir of candidates) {
4715
+ if ((await ctx.host.listDir(`${dir}/sessions`)).length > 0)
4716
+ return dir;
4717
+ }
4718
+ return void 0;
4719
+ };
4720
+ var newest = (names) => names.filter((n) => /^\d+$/.test(n)).sort().at(-1);
4721
+ var toRateWindow = (w, now) => {
4722
+ if (!w || typeof w.used_percent !== "number")
4723
+ return void 0;
4724
+ return {
4725
+ usedPercent: w.used_percent,
4726
+ windowMinutes: w.window_minutes,
4727
+ resetsAt: typeof w.resets_in_seconds === "number" ? new Date(now.getTime() + w.resets_in_seconds * 1e3).toISOString() : void 0
4728
+ };
4729
+ };
4730
+ var devStrategy = {
4731
+ id: "hanzo.dev",
4732
+ kind: "localProbe",
4733
+ async isAvailable(ctx) {
4734
+ return Boolean(await devHome(ctx));
4735
+ },
4736
+ async fetch(ctx) {
4737
+ var _a, _b, _c, _d;
4738
+ const home = await devHome(ctx);
4739
+ if (!home)
4740
+ throw new Error("hanzo: no dev CLI home with sessions");
4741
+ const sessions = `${home}/sessions`;
4742
+ const year = newest(await ctx.host.listDir(sessions));
4743
+ const month = year && newest(await ctx.host.listDir(`${sessions}/${year}`));
4744
+ const day = month && newest(await ctx.host.listDir(`${sessions}/${year}/${month}`));
4745
+ if (!day)
4746
+ throw new Error("hanzo: no dev sessions found");
4747
+ const dayDir = `${sessions}/${year}/${month}/${day}`;
4748
+ const rollouts = (await ctx.host.listDir(dayDir)).filter((f) => f.endsWith(".jsonl")).sort();
4749
+ const latest = rollouts.at(-1);
4750
+ if (!latest)
4751
+ throw new Error("hanzo: no rollout files today");
4752
+ const text = await ctx.host.readTextFile(`${dayDir}/${latest}`) ?? "";
4753
+ let payload;
4754
+ for (const line of text.split("\n")) {
4755
+ if (!line.includes('"token_count"'))
4756
+ continue;
4757
+ try {
4758
+ const item = JSON.parse(line);
4759
+ if (((_a = item.payload) == null ? void 0 : _a.type) === "token_count")
4760
+ payload = item.payload;
4761
+ } catch {
4762
+ }
4763
+ }
4764
+ const now = ctx.host.now();
4765
+ const totals = (_b = payload == null ? void 0 : payload.info) == null ? void 0 : _b.total_token_usage;
4766
+ const usage = {
4767
+ providerId: "hanzo",
4768
+ primary: toRateWindow((_c = payload == null ? void 0 : payload.rate_limits) == null ? void 0 : _c.primary, now),
4769
+ secondary: toRateWindow((_d = payload == null ? void 0 : payload.rate_limits) == null ? void 0 : _d.secondary, now),
4770
+ identity: { providerId: "hanzo", loginMethod: "dev-cli" },
4771
+ totals: totals ? {
4772
+ tokens: totals.total_tokens ?? 0,
4773
+ inputTokens: totals.input_tokens ?? 0,
4774
+ outputTokens: totals.output_tokens ?? 0,
4775
+ cachedInputTokens: totals.cached_input_tokens ?? 0
4776
+ } : void 0,
4777
+ dataConfidence: totals ? "exact" : "unknown",
4778
+ updatedAt: now.toISOString()
4779
+ };
4780
+ return {
4781
+ usage,
4782
+ sourceLabel: "Hanzo Dev CLI",
4783
+ strategyId: this.id,
4784
+ strategyKind: this.kind
4785
+ };
4786
+ }
4787
+ };
4788
+ var hanzoProvider = {
4789
+ id: "hanzo",
4790
+ metadata: {
4791
+ displayName: "Hanzo",
4792
+ sessionLabel: "5h limit",
4793
+ weeklyLabel: "Weekly limit",
4794
+ defaultEnabled: true,
4795
+ dashboardUrl: "https://console.hanzo.ai/billing/usage",
4796
+ color: "#ff2d55"
4797
+ },
4798
+ strategies: (mode) => forMode([cloudStrategy, devStrategy], mode)
4799
+ };
4800
+
4801
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/providers/api-token.js
4802
+ var resolveBaseUrl = (ctx, envKeys = []) => {
4803
+ var _a;
4804
+ const fromSettings = (_a = ctx.settings) == null ? void 0 : _a.baseUrl;
4805
+ if (typeof fromSettings === "string" && fromSettings.length > 0)
4806
+ return fromSettings;
4807
+ for (const name of envKeys) {
4808
+ const value = ctx.host.env(name);
4809
+ if (value)
4810
+ return value;
4811
+ }
4812
+ return void 0;
4813
+ };
4814
+ var resolveApiKey = (ctx, envKeys = []) => {
4815
+ var _a;
4816
+ const fromSettings = (_a = ctx.settings) == null ? void 0 : _a.apiKey;
4817
+ if (typeof fromSettings === "string" && fromSettings.length > 0)
4818
+ return fromSettings;
4819
+ for (const name of envKeys) {
4820
+ const value = ctx.host.env(name);
4821
+ if (value)
4822
+ return value;
4823
+ }
4824
+ return void 0;
4825
+ };
4826
+ var makeApiTokenStrategy = (config) => {
4827
+ const label = config.sourceLabel ?? config.metadata.displayName;
4828
+ return {
4829
+ id: `${config.id}.apiToken`,
4830
+ kind: "apiToken",
4831
+ async isAvailable(ctx) {
4832
+ if (!resolveApiKey(ctx, config.envKeys))
4833
+ return false;
4834
+ if (config.requireBaseUrl && !resolveBaseUrl(ctx, config.baseUrlEnv))
4835
+ return false;
4836
+ return true;
4837
+ },
4838
+ async fetch(ctx) {
4839
+ const apiKey = resolveApiKey(ctx, config.envKeys);
4840
+ if (!apiKey)
4841
+ throw new Error(`${config.id}: no API key`);
4842
+ const baseUrl = resolveBaseUrl(ctx, config.baseUrlEnv);
4843
+ if (config.requireBaseUrl && !baseUrl)
4844
+ throw new Error(`${config.id}: no base URL`);
4845
+ const settings = { ...ctx.settings, apiKey, ...baseUrl ? { baseUrl } : {} };
4846
+ const req = config.request(settings, ctx);
4847
+ const res = await ctx.host.http({ method: "GET", timeoutMs: 3e4, ...req });
4848
+ if (res.status !== 200)
4849
+ throw new Error(`${config.id} HTTP ${res.status}`);
4850
+ let body;
4851
+ try {
4852
+ body = JSON.parse(res.text);
4853
+ } catch {
4854
+ throw new Error(`${config.id}: invalid JSON body`);
4855
+ }
4856
+ const now = ctx.host.now();
4857
+ const iso = now.toISOString();
4858
+ const { usage, credits } = config.map(body, now, settings);
4859
+ return {
4860
+ usage: { ...usage, providerId: config.id, updatedAt: iso },
4861
+ credits: credits ? { ...credits, updatedAt: iso } : void 0,
4862
+ sourceLabel: label,
4863
+ strategyId: `${config.id}.apiToken`,
4864
+ strategyKind: "apiToken"
4865
+ };
4866
+ },
4867
+ // API-key providers have exactly one source; nothing to fall back to.
4868
+ shouldFallback() {
4869
+ return false;
4870
+ }
4871
+ };
4872
+ };
4873
+ var makeApiTokenProvider = (config) => {
4874
+ const strategy = makeApiTokenStrategy(config);
4875
+ return {
4876
+ id: config.id,
4877
+ metadata: config.metadata,
4878
+ strategies: (mode) => forMode([strategy], mode)
4879
+ };
4880
+ };
4881
+ var numberOrUndefined = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
4882
+ var bearer = (key) => ({
4883
+ Authorization: `Bearer ${key}`,
4884
+ Accept: "application/json"
4885
+ });
4886
+
4887
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/providers/groq.js
4888
+ var ENV_KEYS = ["GROQ_API_KEY"];
4889
+ var ENV_URL = "GROQ_API_URL";
4890
+ var DEFAULT_BASE = "https://api.groq.com/v1";
4891
+ var QUERIES = {
4892
+ requests: "sum(model_project_id_status_code:requests:rate5m)",
4893
+ tokensIn: "sum(model_project_id:tokens_in:rate5m)",
4894
+ tokensOut: "sum(model_project_id:tokens_out:rate5m)"
4895
+ };
4896
+ var sumRate = (body) => {
4897
+ var _a, _b;
4898
+ if (body.status !== "success")
4899
+ return 0;
4900
+ let total = 0;
4901
+ for (const series of ((_a = body.data) == null ? void 0 : _a.result) ?? []) {
4902
+ const raw = (_b = series.value) == null ? void 0 : _b[1];
4903
+ const n = typeof raw === "number" ? raw : Number(raw);
4904
+ if (Number.isFinite(n))
4905
+ total += n;
4906
+ }
4907
+ return total;
4908
+ };
4909
+ var groqStrategy = {
4910
+ id: "groq.apiToken",
4911
+ kind: "apiToken",
4912
+ async isAvailable(ctx) {
4913
+ return Boolean(resolveApiKey(ctx, ENV_KEYS));
4914
+ },
4915
+ async fetch(ctx) {
4916
+ var _a;
4917
+ const key = resolveApiKey(ctx, ENV_KEYS);
4918
+ if (!key)
4919
+ throw new Error("groq: no API key");
4920
+ const base = ((_a = ctx.settings) == null ? void 0 : _a.baseUrl) ?? ctx.host.env(ENV_URL) ?? DEFAULT_BASE;
4921
+ const query = async (promql) => {
4922
+ const res = await ctx.host.http({
4923
+ url: `${base}/metrics/prometheus/api/v1/query?query=${encodeURIComponent(promql)}`,
4924
+ headers: bearer(key),
4925
+ timeoutMs: 3e4
4926
+ });
4927
+ if (res.status !== 200)
4928
+ throw new Error(`groq metrics HTTP ${res.status}`);
4929
+ return JSON.parse(res.text);
4930
+ };
4931
+ const [requests, tokensIn, tokensOut] = await Promise.all([
4932
+ query(QUERIES.requests),
4933
+ query(QUERIES.tokensIn),
4934
+ query(QUERIES.tokensOut)
4935
+ ]);
4936
+ const requestsPerMin = Math.round(sumRate(requests) * 60);
4937
+ const tokensPerMin = Math.round((sumRate(tokensIn) + sumRate(tokensOut)) * 60);
4938
+ const now = ctx.host.now().toISOString();
4939
+ const usage = {
4940
+ providerId: "groq",
4941
+ identity: { providerId: "groq", loginMethod: "api" },
4942
+ totals: { tokens: tokensPerMin, requests: requestsPerMin },
4943
+ dataConfidence: "estimated",
4944
+ updatedAt: now
4945
+ };
4946
+ return { usage, sourceLabel: "Groq metrics", strategyId: this.id, strategyKind: this.kind };
4947
+ },
4948
+ shouldFallback() {
4949
+ return false;
4950
+ }
4951
+ };
4952
+ var groqProvider = {
4953
+ id: "groq",
4954
+ metadata: {
4955
+ displayName: "Groq",
4956
+ sessionLabel: "Throughput (req/min)",
4957
+ weeklyLabel: "Tokens/min",
4958
+ dashboardUrl: "https://console.groq.com/metrics",
4959
+ color: "#f55036"
4960
+ },
4961
+ strategies: (mode) => forMode([groqStrategy], mode)
4962
+ };
4963
+
4964
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/providers/deepgram.js
4965
+ var ENV_KEYS2 = ["DEEPGRAM_API_KEY"];
4966
+ var ENV_URL2 = "DEEPGRAM_API_URL";
4967
+ var ENV_PROJECT = "DEEPGRAM_PROJECT_ID";
4968
+ var DEFAULT_BASE2 = "https://api.deepgram.com/v1";
4969
+ var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
4970
+ var deepgramStrategy = {
4971
+ id: "deepgram.apiToken",
4972
+ kind: "apiToken",
4973
+ async isAvailable(ctx) {
4974
+ return Boolean(resolveApiKey(ctx, ENV_KEYS2));
4975
+ },
4976
+ async fetch(ctx) {
4977
+ var _a, _b;
4978
+ const key = resolveApiKey(ctx, ENV_KEYS2);
4979
+ if (!key)
4980
+ throw new Error("deepgram: no API key");
4981
+ const base = ((_a = ctx.settings) == null ? void 0 : _a.baseUrl) ?? ctx.host.env(ENV_URL2) ?? DEFAULT_BASE2;
4982
+ const headers = { Authorization: `Token ${key}`, Accept: "application/json" };
4983
+ const get = async (path) => {
4984
+ const res = await ctx.host.http({ url: `${base}${path}`, headers, timeoutMs: 3e4 });
4985
+ if (res.status !== 200)
4986
+ throw new Error(`deepgram ${path} HTTP ${res.status}`);
4987
+ return JSON.parse(res.text);
4988
+ };
4989
+ const configured = (typeof ((_b = ctx.settings) == null ? void 0 : _b.projectId) === "string" ? ctx.settings.projectId : void 0) ?? ctx.host.env(ENV_PROJECT);
4990
+ let projectIds;
4991
+ if (configured) {
4992
+ projectIds = [configured];
4993
+ } else {
4994
+ const body = await get("/projects");
4995
+ const projects = Array.isArray(body.projects) ? body.projects : [];
4996
+ projectIds = projects.map((p) => p.project_id).filter((id) => Boolean(id));
4997
+ }
4998
+ let hours = 0;
4999
+ let tokens = 0;
5000
+ let requests = 0;
5001
+ for (const id of projectIds) {
5002
+ const body = await get(`/projects/${id}/usage/breakdown`);
5003
+ for (const r of Array.isArray(body.results) ? body.results : []) {
5004
+ hours += num2(r.total_hours);
5005
+ tokens += num2(r.tokens_in) + num2(r.tokens_out);
5006
+ requests += num2(r.requests);
5007
+ }
5008
+ }
5009
+ const now = ctx.host.now().toISOString();
5010
+ const usage = {
5011
+ providerId: "deepgram",
5012
+ identity: {
5013
+ providerId: "deepgram",
5014
+ loginMethod: "api",
5015
+ accountOrganization: projectIds.length === 1 ? projectIds[0] : `${projectIds.length} projects`
5016
+ },
5017
+ totals: { tokens, requests },
5018
+ dataConfidence: tokens > 0 || requests > 0 || hours > 0 ? "exact" : "unknown",
5019
+ updatedAt: now
5020
+ };
5021
+ return { usage, sourceLabel: "Deepgram usage", strategyId: this.id, strategyKind: this.kind };
5022
+ },
5023
+ shouldFallback() {
5024
+ return false;
5025
+ }
5026
+ };
5027
+ var deepgramProvider = {
5028
+ id: "deepgram",
5029
+ metadata: {
5030
+ displayName: "Deepgram",
5031
+ sessionLabel: "Usage",
5032
+ weeklyLabel: "Requests",
5033
+ dashboardUrl: "https://console.deepgram.com/usage",
5034
+ color: "#13ef93"
5035
+ },
5036
+ strategies: (mode) => forMode([deepgramStrategy], mode)
5037
+ };
5038
+
5039
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/providers/byo.js
5040
+ var ENV_KEYS3 = ["BYO_API_KEY"];
5041
+ var ENV_URL3 = ["BYO_BASE_URL"];
5042
+ var num3 = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
5043
+ var obj = (v) => v && typeof v === "object" ? v : {};
5044
+ var stripV1 = (base) => base.replace(/\/v1\/?$/, "").replace(/\/$/, "");
5045
+ var keyInfo = async (ctx, base, key, now) => {
5046
+ const res = await ctx.host.http({
5047
+ url: `${stripV1(base)}/key/info`,
5048
+ headers: bearer(key),
5049
+ timeoutMs: 15e3
5050
+ });
5051
+ if (res.status !== 200)
5052
+ return void 0;
5053
+ let info;
5054
+ try {
5055
+ info = obj(obj(JSON.parse(res.text)).info);
5056
+ } catch {
5057
+ return void 0;
5058
+ }
5059
+ if (Object.keys(info).length === 0)
5060
+ return void 0;
5061
+ const spend = num3(info.spend);
5062
+ return {
5063
+ providerId: "byo",
5064
+ identity: {
5065
+ providerId: "byo",
5066
+ loginMethod: "api",
5067
+ accountEmail: typeof info.user_id === "string" ? info.user_id : void 0,
5068
+ accountOrganization: typeof info.team_id === "string" ? info.team_id : void 0,
5069
+ plan: typeof info.key_name === "string" ? info.key_name : void 0
5070
+ },
5071
+ providerCost: { used: spend, currencyCode: "USD", period: "key", updatedAt: now.toISOString() },
5072
+ subscriptionExpiresAt: typeof info.expires === "string" ? info.expires : void 0,
5073
+ dataConfidence: "exact",
5074
+ updatedAt: now.toISOString()
5075
+ };
5076
+ };
5077
+ var modelsLiveness = async (ctx, base, key, now) => {
5078
+ const root = stripV1(base);
5079
+ const res = await ctx.host.http({
5080
+ url: `${root}/v1/models`,
5081
+ headers: bearer(key),
5082
+ timeoutMs: 15e3
5083
+ });
5084
+ if (res.status !== 200)
5085
+ throw new Error(`byo: /v1/models HTTP ${res.status}`);
5086
+ let count;
5087
+ try {
5088
+ const data = obj(JSON.parse(res.text)).data;
5089
+ if (Array.isArray(data))
5090
+ count = data.length;
5091
+ } catch {
5092
+ }
5093
+ return {
5094
+ providerId: "byo",
5095
+ identity: {
5096
+ providerId: "byo",
5097
+ loginMethod: "api",
5098
+ accountOrganization: count !== void 0 ? `${count} models` : void 0
5099
+ },
5100
+ dataConfidence: "unknown",
5101
+ updatedAt: now.toISOString()
5102
+ };
5103
+ };
5104
+ var byoStrategy = {
5105
+ id: "byo.apiToken",
5106
+ kind: "apiToken",
5107
+ async isAvailable(ctx) {
5108
+ return Boolean(resolveApiKey(ctx, ENV_KEYS3) && resolveBaseUrl(ctx, ENV_URL3));
5109
+ },
5110
+ async fetch(ctx) {
5111
+ const key = resolveApiKey(ctx, ENV_KEYS3);
5112
+ const base = resolveBaseUrl(ctx, ENV_URL3);
5113
+ if (!key || !base)
5114
+ throw new Error("byo: baseUrl and apiKey are required");
5115
+ const now = ctx.host.now();
5116
+ const info = await keyInfo(ctx, base, key, now);
5117
+ if (info) {
5118
+ return { usage: info, sourceLabel: "BYO /key/info", strategyId: this.id, strategyKind: this.kind };
5119
+ }
5120
+ const usage = await modelsLiveness(ctx, base, key, now);
5121
+ return { usage, sourceLabel: "BYO liveness", strategyId: this.id, strategyKind: this.kind };
5122
+ },
5123
+ shouldFallback() {
5124
+ return false;
5125
+ }
5126
+ };
5127
+ var byoProvider = {
5128
+ id: "byo",
5129
+ metadata: {
5130
+ displayName: "BYO endpoint",
5131
+ sessionLabel: "Key spend",
5132
+ weeklyLabel: "Liveness",
5133
+ dashboardUrl: "https://docs.hanzo.ai/docs/gateway",
5134
+ color: "#64748b"
5135
+ },
5136
+ strategies: (mode) => forMode([byoStrategy], mode)
5137
+ };
5138
+
5139
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/providers/api-token-providers.js
5140
+ var num4 = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
5141
+ var loose = (v) => {
5142
+ if (typeof v === "number" && Number.isFinite(v))
5143
+ return v;
5144
+ if (typeof v === "string") {
5145
+ const n = Number(v);
5146
+ return Number.isFinite(n) ? n : void 0;
5147
+ }
5148
+ return void 0;
5149
+ };
5150
+ var pct = (used, limit) => limit > 0 ? Math.min(100, Math.max(0, used / limit * 100)) : 0;
5151
+ var obj2 = (v) => v && typeof v === "object" ? v : {};
5152
+ var openrouterProvider = makeApiTokenProvider({
5153
+ id: "openrouter",
5154
+ metadata: {
5155
+ displayName: "OpenRouter",
5156
+ sessionLabel: "Credits used",
5157
+ weeklyLabel: "Credit limit",
5158
+ supportsCredits: true,
5159
+ dashboardUrl: "https://openrouter.ai/settings/credits",
5160
+ color: "#6467f2"
5161
+ },
5162
+ envKeys: ["OPENROUTER_API_KEY"],
5163
+ request: (s, ctx) => ({
5164
+ url: `${s.baseUrl ?? ctx.host.env("OPENROUTER_API_URL") ?? "https://openrouter.ai/api/v1"}/credits`,
5165
+ headers: bearer(s.apiKey)
5166
+ }),
5167
+ map: (json, now) => {
5168
+ const d = obj2(obj2(json).data);
5169
+ const total = num4(d.total_credits);
5170
+ const used = num4(d.total_usage);
5171
+ return {
5172
+ usage: {
5173
+ identity: { providerId: "openrouter", loginMethod: "api" },
5174
+ providerCost: { used, limit: total, currencyCode: "USD", period: "total", updatedAt: now.toISOString() },
5175
+ dataConfidence: "exact"
5176
+ },
5177
+ credits: { remaining: Math.max(0, total - used) }
5178
+ };
5179
+ }
5180
+ });
5181
+ var deepseekProvider = makeApiTokenProvider({
5182
+ id: "deepseek",
5183
+ metadata: {
5184
+ displayName: "DeepSeek",
5185
+ sessionLabel: "Balance",
5186
+ weeklyLabel: "Balance",
5187
+ supportsCredits: true,
5188
+ dashboardUrl: "https://platform.deepseek.com/usage",
5189
+ color: "#527df0"
5190
+ },
5191
+ envKeys: ["DEEPSEEK_API_KEY", "DEEPSEEK_KEY"],
5192
+ request: (s) => ({ url: "https://api.deepseek.com/user/balance", headers: bearer(s.apiKey) }),
5193
+ map: (json) => {
5194
+ const infos = Array.isArray(obj2(json).balance_infos) ? obj2(json).balance_infos : [];
5195
+ const bal = (r) => loose(r.total_balance) ?? 0;
5196
+ const row = infos.find((r) => r.currency === "USD" && bal(r) > 0) ?? infos.find((r) => bal(r) > 0) ?? infos.find((r) => r.currency === "USD") ?? infos[0];
5197
+ return {
5198
+ usage: {
5199
+ identity: { providerId: "deepseek", loginMethod: "api", plan: typeof (row == null ? void 0 : row.currency) === "string" ? row.currency : void 0 },
5200
+ dataConfidence: "exact"
5201
+ },
5202
+ credits: { remaining: row ? bal(row) : 0 }
5203
+ };
5204
+ }
5205
+ });
5206
+ var elevenlabsProvider = makeApiTokenProvider({
5207
+ id: "elevenlabs",
5208
+ metadata: {
5209
+ displayName: "ElevenLabs",
5210
+ sessionLabel: "Characters",
5211
+ weeklyLabel: "Character limit",
5212
+ dashboardUrl: "https://elevenlabs.io/app/subscription",
5213
+ color: "#ebebe6"
5214
+ },
5215
+ envKeys: ["ELEVENLABS_API_KEY", "XI_API_KEY"],
5216
+ request: (s, ctx) => ({
5217
+ url: `${s.baseUrl ?? ctx.host.env("ELEVENLABS_API_URL") ?? "https://api.elevenlabs.io"}/v1/user/subscription`,
5218
+ headers: { "xi-api-key": s.apiKey, Accept: "application/json" }
5219
+ }),
5220
+ map: (json, now) => {
5221
+ const b = obj2(json);
5222
+ const count = num4(b.character_count);
5223
+ const limit = num4(b.character_limit);
5224
+ const resetUnix = numberOrUndefined(b.next_character_count_reset_unix);
5225
+ const resetsAt = resetUnix !== void 0 ? new Date(resetUnix * 1e3).toISOString() : void 0;
5226
+ const overage = obj2(b.current_overage);
5227
+ const overageAmount = loose(overage.amount);
5228
+ return {
5229
+ usage: {
5230
+ primary: { usedPercent: pct(count, limit), resetsAt },
5231
+ identity: { providerId: "elevenlabs", loginMethod: "api", plan: typeof b.tier === "string" ? b.tier : void 0 },
5232
+ subscriptionRenewsAt: resetsAt,
5233
+ providerCost: overageAmount !== void 0 && overageAmount > 0 ? {
5234
+ used: overageAmount,
5235
+ currencyCode: typeof overage.currency === "string" ? overage.currency : "USD",
5236
+ period: "overage",
5237
+ updatedAt: now.toISOString()
5238
+ } : void 0,
5239
+ dataConfidence: "exact"
5240
+ }
5241
+ };
5242
+ }
5243
+ });
5244
+ var poeProvider = makeApiTokenProvider({
5245
+ id: "poe",
5246
+ metadata: {
5247
+ displayName: "Poe",
5248
+ sessionLabel: "Points",
5249
+ weeklyLabel: "Points",
5250
+ supportsCredits: true,
5251
+ dashboardUrl: "https://poe.com/api/keys",
5252
+ color: "#5d5cde"
5253
+ },
5254
+ envKeys: ["POE_API_KEY"],
5255
+ request: (s) => ({ url: "https://api.poe.com/usage/current_balance", headers: bearer(s.apiKey) }),
5256
+ map: (json) => ({
5257
+ usage: { identity: { providerId: "poe", loginMethod: "api" }, dataConfidence: "exact" },
5258
+ credits: { remaining: num4(obj2(json).current_point_balance) }
5259
+ })
5260
+ });
5261
+ var veniceProvider = makeApiTokenProvider({
5262
+ id: "venice",
5263
+ metadata: {
5264
+ displayName: "Venice",
5265
+ sessionLabel: "Balance",
5266
+ weeklyLabel: "DIEM allocation",
5267
+ supportsCredits: true,
5268
+ dashboardUrl: "https://venice.ai/settings/api",
5269
+ color: "#3399ff"
5270
+ },
5271
+ envKeys: ["VENICE_API_KEY", "VENICE_KEY"],
5272
+ request: (s) => ({ url: "https://api.venice.ai/api/v1/billing/balance", headers: bearer(s.apiKey) }),
5273
+ map: (json) => {
5274
+ const b = obj2(json);
5275
+ const balances = obj2(b.balances);
5276
+ const canConsume = b.canConsume !== false;
5277
+ const currency = (typeof b.consumptionCurrency === "string" ? b.consumptionCurrency : "USD").toUpperCase();
5278
+ const usd = loose(balances.usd);
5279
+ const diem = loose(balances.diem);
5280
+ const alloc = loose(b.diemEpochAllocation);
5281
+ const isDiem = currency === "DIEM";
5282
+ const remaining = (isDiem ? diem : usd) ?? 0;
5283
+ let usedPercent = 0;
5284
+ if (!canConsume)
5285
+ usedPercent = 100;
5286
+ else if (isDiem && alloc && alloc > 0)
5287
+ usedPercent = pct(Math.max(0, alloc - remaining), alloc);
5288
+ return {
5289
+ usage: {
5290
+ primary: { usedPercent },
5291
+ identity: { providerId: "venice", loginMethod: "api", plan: currency },
5292
+ dataConfidence: "exact"
5293
+ },
5294
+ credits: { remaining }
5295
+ };
5296
+ }
5297
+ });
5298
+ var chutesWindow = (container, windowMinutes) => {
5299
+ const w = obj2(container);
5300
+ const pick = (keys) => {
5301
+ for (const k of keys) {
5302
+ const v = loose(w[k]);
5303
+ if (v !== void 0)
5304
+ return v;
5305
+ }
5306
+ return void 0;
5307
+ };
5308
+ const explicit = pick(["percent_used", "usage_percent", "used_percent", "utilization", "utilization_percent"]);
5309
+ const remainingPct = pick(["percent_remaining", "remaining_percent"]);
5310
+ const used = pick(["used", "usage", "consumed", "current", "requests", "tokens", "monthly_usage"]);
5311
+ const limit = pick(["limit", "cap", "max", "maximum", "quota", "quota_limit", "monthly_limit", "total"]);
5312
+ const remaining = pick(["remaining", "available", "balance", "left"]);
5313
+ let usedPercent = explicit;
5314
+ if (usedPercent === void 0 && remainingPct !== void 0)
5315
+ usedPercent = 100 - remainingPct;
5316
+ if (usedPercent !== void 0 && usedPercent <= 1 && usedPercent >= 0)
5317
+ usedPercent *= 100;
5318
+ if (usedPercent === void 0 && limit !== void 0) {
5319
+ const u = used ?? (remaining !== void 0 ? limit - remaining : void 0);
5320
+ if (u !== void 0)
5321
+ usedPercent = pct(u, limit);
5322
+ }
5323
+ if (usedPercent === void 0)
5324
+ return void 0;
5325
+ const resetRaw = pick(["reset_at", "resets_at", "next_reset_at", "period_end", "current_period_end", "expires_at", "window_end"]);
5326
+ return {
5327
+ usedPercent: Math.min(100, Math.max(0, usedPercent)),
5328
+ windowMinutes,
5329
+ resetsAt: resetRaw !== void 0 ? new Date(resetRaw * 1e3).toISOString() : void 0
5330
+ };
5331
+ };
5332
+ var chutesProvider = makeApiTokenProvider({
5333
+ id: "chutes",
5334
+ metadata: {
5335
+ displayName: "Chutes",
5336
+ sessionLabel: "4h quota",
5337
+ weeklyLabel: "Monthly quota",
5338
+ dashboardUrl: "https://chutes.ai",
5339
+ color: "#eab308"
5340
+ },
5341
+ envKeys: ["CHUTES_API_KEY"],
5342
+ request: (s, ctx) => ({
5343
+ url: `${s.baseUrl ?? ctx.host.env("CHUTES_API_URL") ?? "https://api.chutes.ai"}/users/me/subscription_usage`,
5344
+ headers: bearer(s.apiKey)
5345
+ }),
5346
+ map: (json) => {
5347
+ const b = obj2(json);
5348
+ const rolling = chutesWindow(b.rolling, 240) ?? chutesWindow(b.rolling_window, 240) ?? chutesWindow(b.four_hour, 240);
5349
+ const monthly = chutesWindow(b.monthly, 43200) ?? chutesWindow(b.subscription, 43200) ?? chutesWindow(b.subscription_usage, 43200);
5350
+ const plan = b.plan_name ?? b.plan ?? b.tier;
5351
+ return {
5352
+ usage: {
5353
+ primary: rolling,
5354
+ secondary: monthly,
5355
+ identity: { providerId: "chutes", loginMethod: "api", plan: typeof plan === "string" ? plan : void 0 },
5356
+ dataConfidence: rolling || monthly ? "percentOnly" : "unknown"
5357
+ }
5358
+ };
5359
+ }
5360
+ });
5361
+ var moonshotBase = (region) => region === "china" ? "https://api.moonshot.cn" : "https://api.moonshot.ai";
5362
+ var moonshotProvider = makeApiTokenProvider({
5363
+ id: "moonshot",
5364
+ metadata: {
5365
+ displayName: "Moonshot",
5366
+ sessionLabel: "Balance",
5367
+ weeklyLabel: "Balance",
5368
+ supportsCredits: true,
5369
+ dashboardUrl: "https://platform.moonshot.ai/console/info",
5370
+ color: "#16a34a"
5371
+ },
5372
+ envKeys: ["MOONSHOT_API_KEY", "MOONSHOT_KEY"],
5373
+ request: (s, ctx) => ({
5374
+ url: `${s.baseUrl ?? moonshotBase(s.region ?? ctx.host.env("MOONSHOT_REGION"))}/v1/users/me/balance`,
5375
+ headers: bearer(s.apiKey)
5376
+ }),
5377
+ map: (json) => {
5378
+ const d = obj2(obj2(json).data);
5379
+ return {
5380
+ usage: { identity: { providerId: "moonshot", loginMethod: "api" }, dataConfidence: "exact" },
5381
+ credits: { remaining: num4(d.available_balance) }
5382
+ };
5383
+ }
5384
+ });
5385
+ var kimiProvider = makeApiTokenProvider({
5386
+ id: "kimi",
5387
+ metadata: {
5388
+ displayName: "Kimi",
5389
+ sessionLabel: "Weekly",
5390
+ weeklyLabel: "Rate (5h)",
5391
+ dashboardUrl: "https://www.kimi.com/code/console",
5392
+ color: "#111111"
5393
+ },
5394
+ envKeys: ["KIMI_CODE_API_KEY"],
5395
+ request: (s, ctx) => ({
5396
+ url: `${s.baseUrl ?? ctx.host.env("KIMI_CODE_BASE_URL") ?? "https://api.kimi.com"}/coding/v1/usages`,
5397
+ headers: bearer(s.apiKey)
5398
+ }),
5399
+ map: (json) => {
5400
+ const b = obj2(json);
5401
+ const detail = obj2(b.usage);
5402
+ const limit = loose(detail.limit) ?? 0;
5403
+ const used = loose(detail.used) ?? (limit && loose(detail.remaining) !== void 0 ? limit - loose(detail.remaining) : 0);
5404
+ const resetTime = detail.resetTime ?? detail.resetAt ?? detail.reset_time;
5405
+ const rate = obj2(obj2(Array.isArray(b.limits) ? b.limits[0] : void 0).detail);
5406
+ const rateLimit = loose(rate.limit);
5407
+ const rateUsed = loose(rate.used);
5408
+ const secondary = rateLimit !== void 0 ? { usedPercent: pct(rateUsed ?? 0, rateLimit), windowMinutes: 300 } : void 0;
5409
+ return {
5410
+ usage: {
5411
+ primary: { usedPercent: pct(used, limit), resetsAt: typeof resetTime === "string" ? resetTime : void 0 },
5412
+ secondary,
5413
+ identity: { providerId: "kimi", loginMethod: "api" },
5414
+ dataConfidence: "percentOnly"
5415
+ }
5416
+ };
5417
+ }
5418
+ });
5419
+ var kimik2Provider = makeApiTokenProvider({
5420
+ id: "kimik2",
5421
+ metadata: {
5422
+ displayName: "Kimi K2",
5423
+ sessionLabel: "Credits",
5424
+ weeklyLabel: "Credits",
5425
+ supportsCredits: true,
5426
+ dashboardUrl: "https://kimi-k2.ai",
5427
+ color: "#111111"
5428
+ },
5429
+ envKeys: ["KIMI_K2_API_KEY", "KIMI_API_KEY", "KIMI_KEY"],
5430
+ request: (s) => ({ url: "https://kimi-k2.ai/api/user/credits", headers: bearer(s.apiKey) }),
5431
+ map: (json) => {
5432
+ const b = obj2(json);
5433
+ const nested = { ...obj2(b.data), ...obj2(b.result), ...obj2(b.usage), ...obj2(b.credits) };
5434
+ const pick = (keys) => {
5435
+ for (const k of keys) {
5436
+ const v = loose(b[k]) ?? loose(nested[k]);
5437
+ if (v !== void 0)
5438
+ return v;
5439
+ }
5440
+ return void 0;
5441
+ };
5442
+ const remaining = pick([
5443
+ "credits_remaining",
5444
+ "creditsRemaining",
5445
+ "remaining_credits",
5446
+ "remainingCredits",
5447
+ "available_credits",
5448
+ "availableCredits",
5449
+ "credits_left",
5450
+ "creditsLeft",
5451
+ "remaining"
5452
+ ]) ?? 0;
5453
+ return {
5454
+ usage: { identity: { providerId: "kimik2", loginMethod: "api" }, dataConfidence: "exact" },
5455
+ credits: { remaining }
5456
+ };
5457
+ }
5458
+ });
5459
+ var zaiBase = (region) => region === "bigmodel-cn" ? "https://open.bigmodel.cn" : "https://api.z.ai";
5460
+ var ZAI_UNIT_MINUTES = { 1: 1440, 3: 60, 5: 1, 6: 10080 };
5461
+ var zaiWindow = (raw) => {
5462
+ const limit = loose(raw.usage) ?? 0;
5463
+ const current = loose(raw.currentValue);
5464
+ const remaining = loose(raw.remaining);
5465
+ const used = current ?? (remaining !== void 0 ? Math.max(0, limit - remaining) : void 0);
5466
+ const explicit = loose(raw.percentage);
5467
+ const usedPercent = used !== void 0 && limit > 0 ? pct(used, limit) : explicit ?? 0;
5468
+ const unit = loose(raw.unit);
5469
+ const number = loose(raw.number);
5470
+ const resetMs = loose(raw.nextResetTime);
5471
+ return {
5472
+ usedPercent,
5473
+ windowMinutes: unit !== void 0 && number !== void 0 ? (ZAI_UNIT_MINUTES[unit] ?? 0) * number : void 0,
5474
+ resetsAt: resetMs !== void 0 ? new Date(resetMs).toISOString() : void 0
5475
+ };
5476
+ };
5477
+ var zaiProvider = makeApiTokenProvider({
5478
+ id: "zai",
5479
+ metadata: {
5480
+ displayName: "z.ai",
5481
+ sessionLabel: "Token quota",
5482
+ weeklyLabel: "Time quota",
5483
+ dashboardUrl: "https://z.ai/manage-apikey/coding-plan/personal/my-plan",
5484
+ color: "#3b82f6"
5485
+ },
5486
+ envKeys: ["Z_AI_API_KEY"],
5487
+ baseUrlEnv: ["Z_AI_API_HOST"],
5488
+ request: (s, ctx) => ({
5489
+ url: `${resolveZaiBase(s, ctx.host.env("Z_AI_API_HOST"))}/api/monitor/usage/quota/limit`,
5490
+ headers: { ...bearer(s.apiKey), accept: "application/json" }
5491
+ }),
5492
+ map: (json) => {
5493
+ const data = obj2(obj2(json).data);
5494
+ const limits = Array.isArray(data.limits) ? data.limits : [];
5495
+ const tokens = limits.filter((l) => l.type === "TOKENS_LIMIT").map(zaiWindow);
5496
+ const time = limits.find((l) => l.type === "TIME_LIMIT");
5497
+ const plan = data.planName ?? data.plan ?? data.planType ?? data.packageName;
5498
+ return {
5499
+ usage: {
5500
+ primary: tokens[0],
5501
+ secondary: time ? zaiWindow(time) : void 0,
5502
+ tertiary: tokens[1],
5503
+ identity: { providerId: "zai", loginMethod: "api", plan: typeof plan === "string" ? plan : void 0 },
5504
+ dataConfidence: tokens.length || time ? "percentOnly" : "unknown"
5505
+ }
5506
+ };
5507
+ }
5508
+ });
5509
+ var resolveZaiBase = (s, hostEnv) => s.baseUrl ?? hostEnv ?? zaiBase(s.region);
5510
+ var openaiProvider = makeApiTokenProvider({
5511
+ id: "openai",
5512
+ metadata: {
5513
+ displayName: "OpenAI",
5514
+ sessionLabel: "Credits used",
5515
+ weeklyLabel: "Credit grant",
5516
+ supportsCredits: true,
5517
+ dashboardUrl: "https://platform.openai.com/usage",
5518
+ statusPageUrl: "https://status.openai.com",
5519
+ color: "#0f826e"
5520
+ },
5521
+ envKeys: ["OPENAI_ADMIN_KEY", "OPENAI_API_KEY"],
5522
+ request: (s) => ({
5523
+ url: "https://api.openai.com/v1/dashboard/billing/credit_grants",
5524
+ headers: bearer(s.apiKey)
5525
+ }),
5526
+ map: (json, now) => {
5527
+ const b = obj2(json);
5528
+ const granted = num4(b.total_granted);
5529
+ const used = num4(b.total_used);
5530
+ const available = num4(b.total_available);
5531
+ return {
5532
+ usage: {
5533
+ primary: { usedPercent: pct(used, granted) },
5534
+ identity: { providerId: "openai", loginMethod: "api" },
5535
+ providerCost: { used, limit: granted, currencyCode: "USD", period: "grant", updatedAt: now.toISOString() },
5536
+ dataConfidence: "exact"
5537
+ },
5538
+ credits: { remaining: available }
5539
+ };
5540
+ }
5541
+ });
5542
+ var stripV12 = (base) => base.replace(/\/v1\/?$/, "");
5543
+ var litellmProvider = makeApiTokenProvider({
5544
+ id: "litellm",
5545
+ metadata: {
5546
+ displayName: "LiteLLM",
5547
+ sessionLabel: "Key spend",
5548
+ weeklyLabel: "Budget",
5549
+ dashboardUrl: "https://docs.litellm.ai/docs/proxy/cost_tracking",
5550
+ color: "#22c55e"
5551
+ },
5552
+ envKeys: ["LITELLM_API_KEY"],
5553
+ baseUrlEnv: ["LITELLM_BASE_URL"],
5554
+ requireBaseUrl: true,
5555
+ request: (s) => ({ url: `${stripV12(s.baseUrl)}/key/info`, headers: bearer(s.apiKey) }),
5556
+ map: (json, now) => {
5557
+ const info = obj2(obj2(json).info);
5558
+ const spend = num4(info.spend);
5559
+ return {
5560
+ usage: {
5561
+ identity: {
5562
+ providerId: "litellm",
5563
+ loginMethod: "api",
5564
+ accountOrganization: typeof info.team_id === "string" ? info.team_id : void 0,
5565
+ accountEmail: typeof info.user_id === "string" ? info.user_id : void 0,
5566
+ plan: typeof info.key_name === "string" ? info.key_name : void 0
5567
+ },
5568
+ providerCost: { used: spend, currencyCode: "USD", period: "key", updatedAt: now.toISOString() },
5569
+ subscriptionExpiresAt: typeof info.expires === "string" ? info.expires : void 0,
5570
+ dataConfidence: "exact"
5571
+ }
5572
+ };
5573
+ }
5574
+ });
5575
+ var llmProxyV1 = (base) => /\/v1\/?$/.test(base) ? base.replace(/\/$/, "") : `${base.replace(/\/$/, "")}/v1`;
5576
+ var llmproxyProvider = makeApiTokenProvider({
5577
+ id: "llmproxy",
5578
+ metadata: {
5579
+ displayName: "LLM Proxy",
5580
+ sessionLabel: "Quota used",
5581
+ weeklyLabel: "Approx. spend",
5582
+ dashboardUrl: "https://github.com/hzruo/LLM-API-Key-Proxy",
5583
+ color: "#8b5cf6"
5584
+ },
5585
+ envKeys: ["LLM_PROXY_API_KEY"],
5586
+ baseUrlEnv: ["LLM_PROXY_BASE_URL"],
5587
+ requireBaseUrl: true,
5588
+ request: (s) => ({ url: `${llmProxyV1(s.baseUrl)}/quota-stats`, headers: bearer(s.apiKey) }),
5589
+ map: (json, now) => {
5590
+ const b = obj2(json);
5591
+ const providers = obj2(b.providers);
5592
+ const summary = obj2(b.summary);
5593
+ let minRemaining = 100;
5594
+ let earliestReset;
5595
+ let sumTokens = 0;
5596
+ let sumRequests = 0;
5597
+ let sumCost = 0;
5598
+ for (const p of Object.values(providers)) {
5599
+ const stats = obj2(p);
5600
+ sumRequests += num4(stats.total_requests);
5601
+ sumCost += num4(stats.approx_cost);
5602
+ const t = obj2(stats.tokens);
5603
+ sumTokens += num4(t.input_cached) + num4(t.input_uncached) + num4(t.output);
5604
+ const groups = Array.isArray(stats.quota_groups) ? stats.quota_groups : Object.values(obj2(stats.quota_groups));
5605
+ for (const g of groups) {
5606
+ const grp = obj2(g);
5607
+ const rem = loose(grp.remaining_percent);
5608
+ if (rem !== void 0 && rem < minRemaining)
5609
+ minRemaining = rem;
5610
+ const reset = typeof grp.reset_time === "string" ? grp.reset_time : void 0;
5611
+ if (reset && (!earliestReset || reset < earliestReset))
5612
+ earliestReset = reset;
5613
+ }
5614
+ }
5615
+ const totalRequests = numberOrUndefined(summary.total_requests) ?? sumRequests;
5616
+ const totalTokens = numberOrUndefined(summary.total_tokens) ?? sumTokens;
5617
+ const approxCost = numberOrUndefined(summary.approx_cost) ?? sumCost;
5618
+ return {
5619
+ usage: {
5620
+ primary: { usedPercent: Math.min(100, Math.max(0, 100 - minRemaining)), resetsAt: earliestReset },
5621
+ identity: { providerId: "llmproxy", loginMethod: "api" },
5622
+ totals: { tokens: totalTokens, requests: totalRequests },
5623
+ providerCost: approxCost > 0 ? { used: approxCost, currencyCode: "USD", period: "approx", updatedAt: now.toISOString() } : void 0,
5624
+ dataConfidence: "exact"
5625
+ }
5626
+ };
5627
+ }
5628
+ });
5629
+ var minimaxApiBase = (region) => region === "cn" || region === "china" ? "https://api.minimaxi.com" : "https://api.minimax.io";
5630
+ var minimaxWindow = (m, prefix, windowMinutes) => {
5631
+ const remainingPct = loose(m[`${prefix}_remaining_percent`]);
5632
+ const total = loose(m[`${prefix}_total_count`]);
5633
+ const usedCount = loose(m[`${prefix}_usage_count`]);
5634
+ let usedPercent;
5635
+ if (remainingPct !== void 0)
5636
+ usedPercent = 100 - remainingPct;
5637
+ else if (total !== void 0 && usedCount !== void 0)
5638
+ usedPercent = pct(usedCount, total);
5639
+ if (usedPercent === void 0)
5640
+ return void 0;
5641
+ return { usedPercent: Math.min(100, Math.max(0, usedPercent)), windowMinutes };
5642
+ };
5643
+ var minimaxProvider = makeApiTokenProvider({
5644
+ id: "minimax",
5645
+ metadata: {
5646
+ displayName: "MiniMax",
5647
+ sessionLabel: "Interval",
5648
+ weeklyLabel: "Weekly",
5649
+ dashboardUrl: "https://platform.minimax.io/user-center/payment/coding-plan",
5650
+ color: "#f43f5e"
5651
+ },
5652
+ envKeys: ["MINIMAX_CODING_API_KEY", "MINIMAX_API_KEY"],
5653
+ request: (s, ctx) => ({
5654
+ url: `${s.baseUrl ?? minimaxApiBase(s.region ?? ctx.host.env("MINIMAX_REGION"))}/v1/api/openplatform/coding_plan/remains`,
5655
+ headers: { ...bearer(s.apiKey), accept: "application/json", "MM-API-Source": "HanzoUsage" }
5656
+ }),
5657
+ map: (json) => {
5658
+ const data = obj2(obj2(json).data);
5659
+ const models = Array.isArray(data.model_remains) ? data.model_remains : [];
5660
+ const m = obj2(models[0]);
5661
+ const plan = data.plan_name ?? data.current_plan_title ?? data.current_subscribe_title;
5662
+ const points = loose(data.points_balance) ?? loose(data.balance);
5663
+ return {
5664
+ usage: {
5665
+ primary: minimaxWindow(m, "current_interval", 0),
5666
+ secondary: minimaxWindow(m, "current_weekly", 10080),
5667
+ identity: { providerId: "minimax", loginMethod: "api", plan: typeof plan === "string" ? plan : void 0 },
5668
+ dataConfidence: models.length ? "percentOnly" : "unknown"
5669
+ },
5670
+ credits: points !== void 0 ? { remaining: points } : void 0
5671
+ };
5672
+ }
5673
+ });
5674
+
5675
+ // ../../node_modules/.pnpm/@hanzo+usage@0.1.6_react@18.3.1/node_modules/@hanzo/usage/dist/index.js
5676
+ var providerRegistry = {
5677
+ hanzo: hanzoProvider,
5678
+ codex: codexProvider,
5679
+ claude: claudeProvider,
5680
+ openai: openaiProvider,
5681
+ openrouter: openrouterProvider,
5682
+ deepseek: deepseekProvider,
5683
+ elevenlabs: elevenlabsProvider,
5684
+ deepgram: deepgramProvider,
5685
+ groq: groqProvider,
5686
+ poe: poeProvider,
5687
+ venice: veniceProvider,
5688
+ chutes: chutesProvider,
5689
+ moonshot: moonshotProvider,
5690
+ kimi: kimiProvider,
5691
+ kimik2: kimik2Provider,
5692
+ zai: zaiProvider,
5693
+ litellm: litellmProvider,
5694
+ llmproxy: llmproxyProvider,
5695
+ minimax: minimaxProvider,
5696
+ byo: byoProvider
5697
+ };
5698
+ var allProviders = Object.values(providerRegistry);
5699
+ var trackedProviderIds = Object.keys(providerRegistry);
5700
+
5701
+ // src/shared/usage.ts
5702
+ var http = async (req) => {
5703
+ const controller = new AbortController();
5704
+ const timeout = req.timeoutMs ? setTimeout(() => controller.abort(), req.timeoutMs) : void 0;
5705
+ try {
5706
+ const res = await fetch(req.url, {
5707
+ method: req.method ?? "GET",
5708
+ headers: req.headers,
5709
+ body: req.body,
5710
+ credentials: "include",
5711
+ signal: controller.signal
5712
+ });
5713
+ const headers = {};
5714
+ res.headers.forEach((v, k) => {
5715
+ headers[k] = v;
5716
+ });
5717
+ return { status: res.status, headers, text: await res.text() };
5718
+ } finally {
5719
+ if (timeout) clearTimeout(timeout);
5720
+ }
5721
+ };
5722
+ var browserHost = {
5723
+ readTextFile: async () => void 0,
5724
+ listDir: async () => [],
5725
+ writeTextFile: async () => {
5726
+ },
5727
+ http,
5728
+ env: () => void 0,
5729
+ homeDir: () => "",
5730
+ now: () => /* @__PURE__ */ new Date()
5731
+ };
5732
+ var isAuthError = (text) => /\b(401|403)\b/.test(text);
5733
+ var outcomeError = (providerHost, outcome) => {
5734
+ const attempt = [...outcome.attempts].reverse().find((a) => a.error);
5735
+ const detail = (attempt == null ? void 0 : attempt.error) ?? "no data";
5736
+ return isAuthError(detail) ? `Open ${providerHost} to sign in` : detail;
5737
+ };
5738
+ var fetchClaude = async () => {
5739
+ const { displayName, color, dashboardUrl } = claudeProvider.metadata;
5740
+ const base = { providerId: "claude", displayName, color, dashboardUrl };
5741
+ try {
5742
+ const outcome = await runPipeline(claudeProvider, {
5743
+ host: browserHost,
5744
+ sourceMode: "web",
5745
+ settings: { cookieHeader: "" }
5746
+ });
5747
+ if (outcome.result) return { ...base, snapshot: outcome.result.usage };
5748
+ return { ...base, error: outcomeError("claude.ai", outcome) };
5749
+ } catch (e) {
5750
+ return { ...base, error: e instanceof Error ? e.message : String(e) };
5751
+ }
5752
+ };
5753
+ var toWindow3 = (w) => {
5754
+ if (!w || typeof w.used_percent !== "number") return void 0;
5755
+ return {
5756
+ usedPercent: w.used_percent,
5757
+ windowMinutes: w.limit_window_seconds ? Math.round(w.limit_window_seconds / 60) : void 0,
5758
+ resetsAt: w.reset_at ? new Date(w.reset_at * 1e3).toISOString() : void 0
5759
+ };
5760
+ };
5761
+ var fetchCodex = async () => {
5762
+ var _a, _b;
5763
+ const { displayName, color, dashboardUrl } = codexProvider.metadata;
5764
+ const base = { providerId: "codex", displayName, color, dashboardUrl };
5765
+ try {
5766
+ const res = await http({
5767
+ url: "https://chatgpt.com/backend-api/wham/usage",
5768
+ headers: { Accept: "application/json" },
5769
+ timeoutMs: 3e4
5770
+ });
5771
+ if (res.status !== 200) {
5772
+ return {
5773
+ ...base,
5774
+ error: isAuthError(String(res.status)) ? "Open chatgpt.com to sign in" : `HTTP ${res.status}`
5775
+ };
5776
+ }
5777
+ const body = JSON.parse(res.text);
5778
+ const snapshot = {
5779
+ providerId: "codex",
5780
+ primary: toWindow3((_a = body.rate_limit) == null ? void 0 : _a.primary_window),
5781
+ secondary: toWindow3((_b = body.rate_limit) == null ? void 0 : _b.secondary_window),
5782
+ identity: { providerId: "codex", plan: body.plan_type, loginMethod: "web" },
5783
+ dataConfidence: "percentOnly",
5784
+ updatedAt: browserHost.now().toISOString()
5785
+ };
5786
+ return { ...base, snapshot };
5787
+ } catch (e) {
5788
+ return { ...base, error: e instanceof Error ? e.message : String(e) };
5789
+ }
5790
+ };
5791
+ var fetchAllUsage = async () => Promise.all([fetchClaude(), fetchCodex()]);
5792
+
4380
5793
  // src/chat-client.ts
4381
5794
  var API_BASE = API_BASE_URL;
4382
5795
  async function listModels(token) {
@@ -4676,19 +6089,19 @@ var PageMonitor = class {
4676
6089
  Object.assign(this.config, config);
4677
6090
  }
4678
6091
  // --- Helpers ---
4679
- formatRemoteObject(obj) {
4680
- if (!obj) return "undefined";
4681
- if (obj.value !== void 0) return String(obj.value);
4682
- if (obj.description) return obj.description;
4683
- if (obj.type === "undefined") return "undefined";
4684
- if (obj.type === "object" && obj.subtype === "null") return "null";
4685
- if (obj.unserializableValue) return obj.unserializableValue;
4686
- if (obj.preview) {
4687
- const props = obj.preview.properties || [];
6092
+ formatRemoteObject(obj3) {
6093
+ if (!obj3) return "undefined";
6094
+ if (obj3.value !== void 0) return String(obj3.value);
6095
+ if (obj3.description) return obj3.description;
6096
+ if (obj3.type === "undefined") return "undefined";
6097
+ if (obj3.type === "object" && obj3.subtype === "null") return "null";
6098
+ if (obj3.unserializableValue) return obj3.unserializableValue;
6099
+ if (obj3.preview) {
6100
+ const props = obj3.preview.properties || [];
4688
6101
  const pairs = props.map((p) => `${p.name}: ${p.value ?? p.type}`).join(", ");
4689
- return obj.preview.type === "object" ? `{${pairs}}` : `[${pairs}]`;
6102
+ return obj3.preview.type === "object" ? `{${pairs}}` : `[${pairs}]`;
4690
6103
  }
4691
- return obj.type || "unknown";
6104
+ return obj3.type || "unknown";
4692
6105
  }
4693
6106
  truncate(str) {
4694
6107
  if (str.length <= this.config.stringSizeLimit) return str;
@@ -7204,6 +8617,18 @@ async function handleMessage(request, sender, sendResponse) {
7204
8617
  })();
7205
8618
  break;
7206
8619
  }
8620
+ // --- AI provider usage (Claude + Codex, via @hanzo/usage) ---
8621
+ case "usage.fetch": {
8622
+ (async () => {
8623
+ try {
8624
+ const providers = await fetchAllUsage();
8625
+ sendResponse({ success: true, providers });
8626
+ } catch (e) {
8627
+ sendResponse({ success: false, error: e.message });
8628
+ }
8629
+ })();
8630
+ break;
8631
+ }
7207
8632
  }
7208
8633
  }
7209
8634
  chrome.tabs.onRemoved.addListener((tabId) => {