tokmon 0.28.1 → 0.28.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +156 -234
  2. package/THIRD_PARTY_NOTICES.md +62 -0
  3. package/dist/{bootstrap-ink-L7QSJBMS.js → bootstrap-ink-WG4WG3R2.js} +1579 -1135
  4. package/dist/chunk-7CH6S7BR.js +447 -0
  5. package/dist/{chunk-CVKCVHS7.js → chunk-CFKUZCMB.js} +70 -310
  6. package/dist/chunk-HNWEJ6MS.js +1094 -0
  7. package/dist/chunk-JZSZEHVD.js +406 -0
  8. package/dist/{chunk-S33XIUAW.js → chunk-P57DVFNH.js} +277 -49
  9. package/dist/{chunk-4HU4EI5T.js → chunk-P5JN5DDX.js} +25 -46
  10. package/dist/chunk-TN6V7XSL.js +512 -0
  11. package/dist/chunk-USKOQIE3.js +278 -0
  12. package/dist/chunk-UYPDMVW5.js +21 -0
  13. package/dist/{cli-command-75LP4IDS.js → cli-command-MD3RJRP4.js} +249 -15
  14. package/dist/cli.js +6 -6
  15. package/dist/{config-HXFJTNLM.js → config-3DGZ47F7.js} +23 -3
  16. package/dist/daemon-EMOA36WB.js +377 -0
  17. package/dist/daemon-handle-JHE5MKMI.js +11 -0
  18. package/dist/server-EPSH52DR.js +12 -0
  19. package/dist/web/assets/{Area-CMXvOw33.js → Area-stRnp8Km.js} +1 -1
  20. package/dist/web/assets/analytics-moZZt9L_.js +2 -0
  21. package/dist/web/assets/breakdown-km9w7myU.js +4 -0
  22. package/dist/web/assets/button-DrX04Cc2.js +1 -0
  23. package/dist/web/assets/{chart-cZoLgpmG.js → chart-D0CtuL2k.js} +1 -1
  24. package/dist/web/assets/explore-B11uNJV1.js +22 -0
  25. package/dist/web/assets/index-RDhXsCNG.js +83 -0
  26. package/dist/web/assets/index-ZmOS8p9O.css +1 -0
  27. package/dist/web/assets/models-DYbZnY34.js +2 -0
  28. package/dist/web/assets/overview-CruICbfJ.js +2 -0
  29. package/dist/web/assets/panel-xckM3_GC.js +1 -0
  30. package/dist/web/assets/settings-sheet-G-BTSIF6.js +1 -0
  31. package/dist/web/assets/share-sheet-weIdd4DW.js +2 -0
  32. package/dist/web/assets/timeline-BHgniliZ.js +1 -0
  33. package/dist/web/index.html +30 -4
  34. package/package.json +13 -4
  35. package/site/THIRD_PARTY_NOTICES.md +83 -0
  36. package/site/public/fonts/OFL.txt +93 -0
  37. package/dist/chunk-3RTWFGGD.js +0 -275
  38. package/dist/chunk-FMP3P2WV.js +0 -299
  39. package/dist/chunk-HP5UZCXP.js +0 -436
  40. package/dist/chunk-SMPY52EV.js +0 -125
  41. package/dist/chunk-XDA5RJST.js +0 -193
  42. package/dist/daemon-CL4FJW26.js +0 -219
  43. package/dist/daemon-handle-QQLJE46Y.js +0 -10
  44. package/dist/server-EUO7CWYH.js +0 -11
  45. package/dist/web/assets/analytics-DqdZXOJL.js +0 -2
  46. package/dist/web/assets/breakdown-kOSSJ3mI.js +0 -4
  47. package/dist/web/assets/explore-vcsM8K9I.js +0 -22
  48. package/dist/web/assets/index-BErCXT7f.css +0 -1
  49. package/dist/web/assets/index-DH3dOnbg.js +0 -83
  50. package/dist/web/assets/models-B5ikm83_.js +0 -2
  51. package/dist/web/assets/overview-DD1kP8CP.js +0 -2
  52. package/dist/web/assets/panel-DTQBwExW.js +0 -1
  53. package/dist/web/assets/primitives-C36qJlA2.js +0 -1
  54. package/dist/web/assets/settings-sheet-_i2zTQjf.js +0 -1
  55. package/dist/web/assets/share-sheet-BLHmrFbm.js +0 -2
  56. package/dist/web/assets/timeline-B4uG-Emo.js +0 -1
  57. package/dist/web/assets/use-dialog-trap-bfaXEMz9.js +0 -1
@@ -5,7 +5,7 @@ import {
5
5
  envDir,
6
6
  expandHome,
7
7
  isValidTimezone
8
- } from "./chunk-HP5UZCXP.js";
8
+ } from "./chunk-HNWEJ6MS.js";
9
9
 
10
10
  // src/async.ts
11
11
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -20,13 +20,6 @@ function withTimeout(promise, ms = DEFAULT_TIMEOUT_MS) {
20
20
  });
21
21
  }
22
22
 
23
- // src/providers/usage-core.ts
24
- import { readFileSync, readdirSync } from "fs";
25
- import { readdir as readdir2 } from "fs/promises";
26
- import { dirname, join as join2, relative } from "path";
27
- import { createHash as createHash2 } from "crypto";
28
- import { fileURLToPath } from "url";
29
-
30
23
  // src/tz.ts
31
24
  function systemTimezone() {
32
25
  try {
@@ -120,14 +113,17 @@ function startOfWeek(ts, tz) {
120
113
  const offset = p.weekday === 0 ? 6 : p.weekday - 1;
121
114
  return instantFromTz(p.y, p.m, p.d - offset, 0, 0, 0, tz);
122
115
  }
123
- function monthsAgoStart(ts, months, tz) {
124
- const p = tzParts(ts, tz);
125
- return instantFromTz(p.y, p.m - months, 1, 0, 0, 0, tz);
126
- }
127
116
  function weekKey(ts, tz) {
128
117
  return dayKey(startOfWeek(ts, tz), tz);
129
118
  }
130
119
 
120
+ // src/providers/usage-core.ts
121
+ import { readFileSync, readdirSync } from "fs";
122
+ import { readdir as readdir2 } from "fs/promises";
123
+ import { dirname, join as join2, relative } from "path";
124
+ import { createHash as createHash2 } from "crypto";
125
+ import { fileURLToPath } from "url";
126
+
131
127
  // src/providers/_shared/metric.ts
132
128
  var finite = (value, fallback = 0) => typeof value === "number" && Number.isFinite(value) ? value : fallback;
133
129
  var finiteNumber = (value) => typeof value === "number" && Number.isFinite(value);
@@ -395,7 +391,7 @@ async function mapLimit(items, limit, fn) {
395
391
  }
396
392
 
397
393
  // src/providers/usage-core.ts
398
- var SPARK_DAYS = 14;
394
+ var SPARK_DAYS = 30;
399
395
  var DAY_MS = 864e5;
400
396
  var PRUNE_AGE_MS = 230 * DAY_MS;
401
397
  var stores = /* @__PURE__ */ new Map();
@@ -560,8 +556,8 @@ function dashboardSince(tz) {
560
556
  const now = Date.now();
561
557
  return Math.min(startOfMonth(now, tz), startOfWeek(now, tz), now - SPARK_DAYS * DAY_MS);
562
558
  }
563
- function tableSince(tz) {
564
- return monthsAgoStart(Date.now(), 6, tz);
559
+ function tableSince(_tz) {
560
+ return 0;
565
561
  }
566
562
  function cleanEntry(e) {
567
563
  return {
@@ -596,6 +592,7 @@ function summarize(entries, tz) {
596
592
  const byDay = /* @__PURE__ */ new Map();
597
593
  let oldestToday = now;
598
594
  let hadToday = false;
595
+ let lastActivityAt = null;
599
596
  const add = (s, e) => {
600
597
  s.cost += e.cost;
601
598
  s.tokens += e.input + e.output + e.cacheCreate + e.cacheRead;
@@ -605,6 +602,9 @@ function summarize(entries, tz) {
605
602
  };
606
603
  for (const raw of entries) {
607
604
  const e = cleanEntry(raw);
605
+ if (e.ts > 0 && (lastActivityAt === null || e.ts > lastActivityAt)) {
606
+ lastActivityAt = Math.floor(e.ts);
607
+ }
608
608
  if (e.ts >= monthStart) add(month, e);
609
609
  if (e.ts >= weekStart) add(week, e);
610
610
  if (e.ts >= todayStart) {
@@ -619,7 +619,7 @@ function summarize(entries, tz) {
619
619
  const rawBurnRate = hadToday ? today.cost / hrs : 0;
620
620
  const burnRate = Number.isFinite(rawBurnRate) ? rawBurnRate : 0;
621
621
  const series = lastDayKeys(now, tz, SPARK_DAYS).map((k) => byDay.get(k) ?? 0);
622
- return { today, week, month, burnRate, series };
622
+ return { today, week, month, burnRate, series, lastActivityAt };
623
623
  }
624
624
  function groupBy(entries, keyFn) {
625
625
  const groups = /* @__PURE__ */ new Map();
@@ -739,243 +739,6 @@ function coalesceTables(list) {
739
739
  return mergeTables(list);
740
740
  }
741
741
 
742
- // src/rpc/contract.ts
743
- import { Schema } from "effect";
744
- import * as Rpc from "effect/unstable/rpc/Rpc";
745
- import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
746
- var TOKMON_WS_PATH = "/ws";
747
- var TOKMON_PROTOCOL_VERSION = 3;
748
- var TOKMON_CAPABILITIES = ["config-cas", "config-revision", "allowed-hosts"];
749
- var TOKMON_WS_METHODS = {
750
- getConfig: "tokmon.getConfig",
751
- setConfig: "tokmon.setConfig",
752
- refresh: "tokmon.refresh",
753
- browseFs: "tokmon.browseFs",
754
- snapshot: "tokmon.snapshot",
755
- config: "tokmon.config"
756
- };
757
- var RefreshScopeSchema = Schema.Literals([
758
- "all",
759
- "summary",
760
- "table",
761
- "billing",
762
- "peak"
763
- ]);
764
- var ProviderIdSchema = Schema.Literals(PROVIDER_IDS);
765
- var NonNegativeIntegerSchema = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
766
- var PositiveFiniteSchema = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(1));
767
- var AccountSchema = Schema.Struct({
768
- id: Schema.String,
769
- providerId: ProviderIdSchema,
770
- name: Schema.String,
771
- homeDir: Schema.String,
772
- color: Schema.optionalKey(Schema.String)
773
- });
774
- var ConfigSchema = Schema.Struct({
775
- revision: NonNegativeIntegerSchema,
776
- interval: PositiveFiniteSchema,
777
- billingInterval: PositiveFiniteSchema,
778
- clearScreen: Schema.Boolean,
779
- privacyMode: Schema.Boolean,
780
- privacyToggleKey: Schema.String,
781
- timezone: Schema.NullOr(Schema.String),
782
- accounts: Schema.Array(AccountSchema),
783
- activeAccountId: Schema.NullOr(Schema.String),
784
- disabledProviders: Schema.Array(ProviderIdSchema),
785
- onboarded: Schema.Boolean,
786
- dashboardLayout: Schema.Literals(["grid", "single"]),
787
- defaultFocus: Schema.Literals(["all", "last"]),
788
- ascii: Schema.Literals(["auto", "on", "off"]),
789
- allowNetworkAccess: Schema.Boolean,
790
- allowedHosts: Schema.Array(Schema.String),
791
- resetDisplay: Schema.Literals(["relative", "absolute"]),
792
- knownProviders: Schema.Array(ProviderIdSchema)
793
- });
794
- var ProtocolInfoSchema = Schema.Struct({
795
- version: Schema.Literal(TOKMON_PROTOCOL_VERSION),
796
- // Capabilities are additive within a protocol version. Older clients must
797
- // preserve forward compatibility when a newer daemon advertises one they do
798
- // not understand yet.
799
- capabilities: Schema.Array(Schema.String)
800
- });
801
- var ConfigStateSchema = Schema.Struct({
802
- protocol: ProtocolInfoSchema,
803
- config: ConfigSchema
804
- });
805
- var ConfigUpdateRequestSchema = Schema.Struct({
806
- expectedRevision: NonNegativeIntegerSchema,
807
- config: ConfigSchema
808
- });
809
- var ConfigUpdateConflictSchema = Schema.Struct({
810
- kind: Schema.Literal("conflict"),
811
- state: ConfigStateSchema
812
- });
813
- var ConfigPersistenceFailureSchema = Schema.Struct({
814
- kind: Schema.Literal("persistence"),
815
- message: Schema.String
816
- });
817
- var FsEntrySchema = Schema.Struct({
818
- name: Schema.String,
819
- path: Schema.String,
820
- dir: Schema.Boolean
821
- });
822
- var FsListingSchema = Schema.Struct({
823
- path: Schema.String,
824
- parent: Schema.NullOr(Schema.String),
825
- entries: Schema.Array(FsEntrySchema)
826
- });
827
- var UsageSummarySchema = Schema.Struct({
828
- cost: Schema.Finite,
829
- tokens: Schema.Finite,
830
- input: Schema.Finite,
831
- cacheRead: Schema.Finite,
832
- cacheSavings: Schema.Finite
833
- });
834
- var ModelDetailSchema = Schema.Struct({
835
- name: Schema.String,
836
- input: Schema.Finite,
837
- output: Schema.Finite,
838
- cacheCreate: Schema.Finite,
839
- cacheRead: Schema.Finite,
840
- cacheSavings: Schema.Finite,
841
- cost: Schema.Finite,
842
- count: Schema.Finite
843
- });
844
- var TableRowSchema = Schema.Struct({
845
- label: Schema.String,
846
- models: Schema.Array(Schema.String),
847
- input: Schema.Finite,
848
- output: Schema.Finite,
849
- cacheCreate: Schema.Finite,
850
- cacheRead: Schema.Finite,
851
- cacheSavings: Schema.Finite,
852
- total: Schema.Finite,
853
- cost: Schema.Finite,
854
- count: Schema.Finite,
855
- breakdown: Schema.Array(ModelDetailSchema)
856
- });
857
- var DashboardDataSchema = Schema.Struct({
858
- today: UsageSummarySchema,
859
- week: UsageSummarySchema,
860
- month: UsageSummarySchema,
861
- burnRate: Schema.Finite,
862
- series: Schema.Array(Schema.Finite)
863
- });
864
- var TableDataSchema = Schema.Struct({
865
- daily: Schema.Array(TableRowSchema),
866
- weekly: Schema.Array(TableRowSchema),
867
- monthly: Schema.Array(TableRowSchema)
868
- });
869
- var MetricFormatSchema = Schema.Union([
870
- Schema.Struct({ kind: Schema.Literal("percent") }),
871
- Schema.Struct({ kind: Schema.Literal("dollars"), currency: Schema.optionalKey(Schema.String) }),
872
- Schema.Struct({ kind: Schema.Literal("count"), suffix: Schema.optionalKey(Schema.String) })
873
- ]);
874
- var MetricSchema = Schema.Struct({
875
- label: Schema.String,
876
- used: Schema.Finite,
877
- limit: Schema.NullOr(Schema.Finite),
878
- format: MetricFormatSchema,
879
- resetsAt: Schema.optionalKey(Schema.NullOr(Schema.String)),
880
- primary: Schema.optionalKey(Schema.Boolean)
881
- });
882
- var BillingResultSchema = Schema.Struct({
883
- plan: Schema.NullOr(Schema.String),
884
- metrics: Schema.Array(MetricSchema),
885
- error: Schema.NullOr(Schema.String),
886
- email: Schema.optionalKey(Schema.NullOr(Schema.String)),
887
- displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
888
- activity: Schema.optionalKey(Schema.NullOr(Schema.Struct({
889
- series: Schema.Array(Schema.Finite),
890
- summary: Schema.String
891
- }))),
892
- modelSpend: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({
893
- name: Schema.String,
894
- usd: Schema.Finite,
895
- requests: Schema.Finite
896
- })))),
897
- asOfMs: Schema.optionalKey(Schema.Finite)
898
- });
899
- var WebAccountSchema = Schema.Struct({
900
- id: Schema.String,
901
- providerId: ProviderIdSchema,
902
- name: Schema.String,
903
- color: Schema.String,
904
- homeDir: Schema.NullOr(Schema.String),
905
- hasUsage: Schema.Boolean,
906
- hasBilling: Schema.Boolean,
907
- email: Schema.optionalKey(Schema.NullOr(Schema.String)),
908
- displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
909
- plan: Schema.optionalKey(Schema.NullOr(Schema.String)),
910
- dashboard: Schema.NullOr(DashboardDataSchema),
911
- table: Schema.NullOr(TableDataSchema),
912
- billing: Schema.NullOr(BillingResultSchema),
913
- summaryState: Schema.Literals(["pending", "ready", "error"]),
914
- billingState: Schema.Literals(["pending", "ready", "error"]),
915
- tableState: Schema.Literals(["pending", "ready", "error"]),
916
- summaryUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
917
- billingUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
918
- tableUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema))
919
- });
920
- var WebSnapshotSchema = Schema.Struct({
921
- version: Schema.String,
922
- generatedAt: NonNegativeIntegerSchema,
923
- tz: Schema.String,
924
- intervalMs: PositiveFiniteSchema,
925
- billingIntervalMs: Schema.optionalKey(PositiveFiniteSchema),
926
- providers: Schema.Array(Schema.Struct({
927
- id: ProviderIdSchema,
928
- name: Schema.String,
929
- color: Schema.String
930
- })),
931
- accounts: Schema.Array(WebAccountSchema),
932
- seeded: Schema.Boolean,
933
- peak: Schema.NullOr(Schema.Struct({
934
- state: Schema.Literals(["peak", "off-peak", "weekend"]),
935
- label: Schema.String,
936
- // The upstream clock is only normalized to a finite number; do not make
937
- // the wire contract narrower than the producer's declared type.
938
- minutesUntilChange: Schema.NullOr(Schema.Finite),
939
- changesAt: Schema.optionalKey(Schema.NullOr(Schema.String))
940
- }))
941
- });
942
- var EmptyPayloadSchema = Schema.Struct({});
943
- var GetConfigRpc = Rpc.make(TOKMON_WS_METHODS.getConfig, {
944
- payload: EmptyPayloadSchema,
945
- success: ConfigStateSchema
946
- });
947
- var SetConfigRpc = Rpc.make(TOKMON_WS_METHODS.setConfig, {
948
- payload: ConfigUpdateRequestSchema,
949
- success: ConfigStateSchema,
950
- error: Schema.Union([ConfigUpdateConflictSchema, ConfigPersistenceFailureSchema])
951
- });
952
- var RefreshRpc = Rpc.make(TOKMON_WS_METHODS.refresh, {
953
- payload: Schema.Struct({ scope: RefreshScopeSchema }),
954
- success: Schema.Void
955
- });
956
- var BrowseFsRpc = Rpc.make(TOKMON_WS_METHODS.browseFs, {
957
- payload: Schema.Struct({ path: Schema.String }),
958
- success: FsListingSchema
959
- });
960
- var SnapshotRpc = Rpc.make(TOKMON_WS_METHODS.snapshot, {
961
- payload: EmptyPayloadSchema,
962
- success: WebSnapshotSchema,
963
- stream: true
964
- });
965
- var ConfigRpc = Rpc.make(TOKMON_WS_METHODS.config, {
966
- payload: EmptyPayloadSchema,
967
- success: ConfigStateSchema,
968
- stream: true
969
- });
970
- var TokmonRpcGroup = RpcGroup.make(
971
- GetConfigRpc,
972
- SetConfigRpc,
973
- RefreshRpc,
974
- BrowseFsRpc,
975
- SnapshotRpc,
976
- ConfigRpc
977
- );
978
-
979
742
  // src/shared/format.ts
980
743
  var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
981
744
  function formatCurrency(value, opts = {}) {
@@ -984,7 +747,10 @@ function formatCurrency(value, opts = {}) {
984
747
  const abs = Math.abs(value);
985
748
  if (abs >= 1e5) return `${sign}$${(value / 1e3).toFixed(0)}k`;
986
749
  if (abs >= 1e4) return `${sign}$${(value / 1e3).toFixed(1)}k`;
987
- if (abs >= 1) return `${sign}$${value.toFixed(2)}`;
750
+ if (abs >= 1) {
751
+ const polarity = value < 0 ? "-" : "";
752
+ return `${sign}${polarity}$${abs.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
753
+ }
988
754
  if (abs >= 0.01) return `${sign}$${value.toFixed(3)}`;
989
755
  if (abs === 0) return "$0.00";
990
756
  return `${sign}$${value.toFixed(4)}`;
@@ -1021,17 +787,22 @@ function formatShortDate(label, opts = {}) {
1021
787
  const day = Number(match[3]);
1022
788
  return `${MONTHS[Number(match[2]) - 1]} ${opts.padDay ? day.toString().padStart(2, " ") : day}`;
1023
789
  }
790
+ function resetParts(diffMs) {
791
+ if (!Number.isFinite(diffMs) || diffMs <= 0) return null;
792
+ const totalMinutes = Math.max(1, Math.ceil(diffMs / 6e4));
793
+ return {
794
+ days: Math.floor(totalMinutes / (24 * 60)),
795
+ hours: Math.floor(totalMinutes % (24 * 60) / 60),
796
+ minutes: totalMinutes % 60,
797
+ totalMinutes
798
+ };
799
+ }
1024
800
  function formatResetIn(iso, now = Date.now()) {
1025
- const diff = new Date(iso).getTime() - now;
1026
- if (!Number.isFinite(diff) || diff <= 0) return "now";
1027
- const minutes = Math.round(diff / 6e4);
1028
- if (minutes < 60) return `${minutes}m`;
1029
- const hours = Math.floor(minutes / 60);
1030
- const mins = minutes % 60;
1031
- if (hours < 24) return `${hours}h ${mins}m`;
1032
- const days = Math.floor(hours / 24);
1033
- const hrs = hours % 24;
1034
- return `${days}d ${hrs}h`;
801
+ const parts = resetParts(new Date(iso).getTime() - now);
802
+ if (!parts) return "now";
803
+ if (parts.days > 0) return `${parts.days}d ${parts.hours}h`;
804
+ if (parts.hours > 0) return `${parts.hours}h ${parts.minutes}m`;
805
+ return `${parts.minutes}m`;
1035
806
  }
1036
807
  function formatResetAt(iso, display, now = Date.now(), tz) {
1037
808
  const timestamp = Date.parse(iso);
@@ -1506,7 +1277,19 @@ function limitMetric(entry, primary) {
1506
1277
  if (used === void 0) return null;
1507
1278
  const label = limitLabel(o);
1508
1279
  if (!label) return null;
1509
- return percentMetric(label, used, resetFrom(o.resets_at), primary);
1280
+ const scope = recordValue(o.scope);
1281
+ const model = recordValue(scope?.model);
1282
+ const modelId = nonEmptyString(model?.id);
1283
+ const kind = nonEmptyString(o.kind)?.toLowerCase() ?? "";
1284
+ const scopedModel = kind === "weekly_scoped" || kind.startsWith("weekly_model");
1285
+ const role = kind === "session" || nonEmptyString(o.group)?.toLowerCase() === "session" ? "session" : kind === "weekly_all" ? "weekly" : modelId || scopedModel ? "model" : "other";
1286
+ return {
1287
+ ...percentMetric(label, used, resetFrom(o.resets_at), primary),
1288
+ key: modelId ? `model:${modelId}` : kind || label.toLowerCase(),
1289
+ role,
1290
+ modelId: modelId ?? (scopedModel ? label.toLowerCase().replace(/\s+/g, "-") : null),
1291
+ active: boolValue(o.is_active)
1292
+ };
1510
1293
  }
1511
1294
  function limitMetrics(limits) {
1512
1295
  if (!Array.isArray(limits)) return [];
@@ -1533,7 +1316,12 @@ function topLevelUsageMetrics(data) {
1533
1316
  const window = recordValue(value);
1534
1317
  if (!window || numberValue(window.utilization) === void 0) continue;
1535
1318
  const metric = usageMetric(usageLabelFromKey(key), window, key === "five_hour" ? true : void 0);
1536
- if (metric) metrics.push(metric);
1319
+ if (metric) metrics.push({
1320
+ ...metric,
1321
+ key,
1322
+ role: key === "five_hour" ? "session" : key === "seven_day" ? "weekly" : key.startsWith("seven_day_") ? "model" : "other",
1323
+ modelId: key.startsWith("seven_day_") ? key.slice("seven_day_".length) : null
1324
+ });
1537
1325
  }
1538
1326
  return metrics;
1539
1327
  }
@@ -1576,6 +1364,8 @@ async function claudeBilling(account) {
1576
1364
  if (usedCredits !== void 0 && (usedCredits > 0 || monthlyLimit !== void 0 && monthlyLimit > 0)) {
1577
1365
  const scale = decimalScale(data.extra_usage?.decimal_places);
1578
1366
  metrics.push({
1367
+ key: "extra_usage",
1368
+ role: "unbounded",
1579
1369
  label: "Extra",
1580
1370
  used: finite(usedCredits) / scale,
1581
1371
  limit: monthlyLimit !== void 0 && monthlyLimit > 0 ? monthlyLimit / scale : null,
@@ -1727,13 +1517,14 @@ function timestampSecond(value) {
1727
1517
  const ts = timestampMs(value);
1728
1518
  return ts === null ? null : new Date(ts).toISOString().slice(0, 19);
1729
1519
  }
1730
- async function hasThreadSpawn(path) {
1520
+ async function hasForkedHistory(path) {
1731
1521
  let handle = null;
1732
1522
  try {
1733
1523
  handle = await openFile(path, "r");
1734
1524
  const buffer = Buffer.alloc(16 * 1024);
1735
1525
  const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
1736
- return buffer.subarray(0, bytesRead).includes("thread_spawn");
1526
+ const prefix = buffer.subarray(0, bytesRead).toString("utf8");
1527
+ return prefix.includes("thread_spawn") && /"forked_from_id"\s*:\s*"[^"]+"/.test(prefix);
1737
1528
  } catch {
1738
1529
  return false;
1739
1530
  } finally {
@@ -1741,32 +1532,11 @@ async function hasThreadSpawn(path) {
1741
1532
  });
1742
1533
  }
1743
1534
  }
1744
- async function detectReplaySecond(path) {
1745
- if (!await hasThreadSpawn(path)) return null;
1746
- let first = null;
1747
- const input = createReadStream2(path);
1748
- input.on("error", () => {
1749
- });
1750
- const rl = createInterface2({ input, crlfDelay: Infinity });
1751
- try {
1752
- for await (const rawLine of rl) {
1753
- if (!rawLine.includes("token_count")) continue;
1754
- try {
1755
- const line = rawLine.charCodeAt(0) === 65279 ? rawLine.slice(1) : rawLine;
1756
- const obj = JSON.parse(line);
1757
- if ((obj?.payload?.type ?? obj?.type) !== "token_count") continue;
1758
- const info = obj?.payload?.info;
1759
- if (!normalizeUsage(info?.last_token_usage) && !normalizeUsage(info?.total_token_usage)) continue;
1760
- const second = timestampSecond(obj.timestamp ?? obj?.payload?.timestamp);
1761
- if (!second) continue;
1762
- if (!first) first = second;
1763
- else return first === second ? second : null;
1764
- } catch {
1765
- }
1766
- }
1767
- } catch {
1768
- }
1769
- return null;
1535
+ function isLiveTaskStart(obj) {
1536
+ if ((obj?.payload?.type ?? obj?.type) !== "task_started") return false;
1537
+ const eventSecond = timestampSecond(obj?.timestamp ?? obj?.payload?.timestamp);
1538
+ const startedSecond = timestampSecond(obj?.payload?.started_at);
1539
+ return eventSecond !== null && eventSecond === startedSecond;
1770
1540
  }
1771
1541
  function findUsage(obj) {
1772
1542
  return normalizeUsage(obj?.usage) ?? normalizeUsage(obj?.payload?.usage) ?? normalizeUsage(obj?.payload?.info?.usage) ?? normalizeUsage(obj?.result?.usage) ?? normalizeUsage(obj?.response?.usage) ?? normalizeUsage(obj?.token_usage) ?? normalizeUsage(obj);
@@ -1779,19 +1549,22 @@ async function parseFile2(path) {
1779
1549
  let model = "gpt-5";
1780
1550
  let prevTotal = null;
1781
1551
  let prevSig = null;
1782
- const replaySecond = await detectReplaySecond(path);
1783
- let skipReplay = replaySecond !== null;
1552
+ let skipReplay = await hasForkedHistory(path);
1784
1553
  const input = createReadStream2(path);
1785
1554
  input.on("error", () => {
1786
1555
  });
1787
1556
  const rl = createInterface2({ input, crlfDelay: Infinity });
1788
1557
  try {
1789
1558
  for await (const rawLine of rl) {
1790
- if (!rawLine.includes("token_count") && !rawLine.includes("turn_context") && !rawLine.includes('"usage"') && !rawLine.includes("input_tokens") && !rawLine.includes("prompt_tokens")) continue;
1559
+ if (!rawLine.includes("token_count") && !rawLine.includes("task_started") && !rawLine.includes("turn_context") && !rawLine.includes('"usage"') && !rawLine.includes("input_tokens") && !rawLine.includes("prompt_tokens")) continue;
1791
1560
  try {
1792
1561
  const line = rawLine.charCodeAt(0) === 65279 ? rawLine.slice(1) : rawLine;
1793
1562
  const obj = JSON.parse(line);
1794
1563
  const payloadType = obj?.payload?.type ?? obj?.type;
1564
+ if (skipReplay) {
1565
+ if (isLiveTaskStart(obj)) skipReplay = false;
1566
+ continue;
1567
+ }
1795
1568
  if (payloadType === "turn_context") {
1796
1569
  const m2 = extractModel(obj);
1797
1570
  if (typeof m2 === "string" && m2.trim()) model = m2;
@@ -1827,14 +1600,6 @@ async function parseFile2(path) {
1827
1600
  const total = normalizeUsage(info?.total_token_usage);
1828
1601
  const last = normalizeUsage(info?.last_token_usage);
1829
1602
  const tsValue = obj.timestamp ?? obj?.payload?.timestamp;
1830
- if (skipReplay && replaySecond) {
1831
- const second = timestampSecond(tsValue);
1832
- if (second === replaySecond) {
1833
- if (total) prevTotal = total;
1834
- continue;
1835
- }
1836
- if (second) skipReplay = false;
1837
- }
1838
1603
  const sig = eventSig(last, total);
1839
1604
  if (sig === prevSig) continue;
1840
1605
  prevSig = sig;
@@ -4622,11 +4387,6 @@ async function detectProviders() {
4622
4387
  }
4623
4388
 
4624
4389
  export {
4625
- TOKMON_WS_PATH,
4626
- TOKMON_PROTOCOL_VERSION,
4627
- TOKMON_CAPABILITIES,
4628
- TOKMON_WS_METHODS,
4629
- TokmonRpcGroup,
4630
4390
  withTimeout,
4631
4391
  systemTimezone,
4632
4392
  resolveTimezone,