tokmon 0.23.5 → 0.25.0

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 (36) hide show
  1. package/README.md +25 -3
  2. package/dist/{bootstrap-ink-WA3ENW2M.js → bootstrap-ink-O55MPGXJ.js} +52 -314
  3. package/dist/chunk-45YYP2FJ.js +299 -0
  4. package/dist/{daemon-handle-Y2N2NA6D.js → chunk-AMD4PXDG.js} +2 -2
  5. package/dist/{chunk-5CWOJMAH.js → chunk-E2YXYU73.js} +2 -0
  6. package/dist/{chunk-IELQ5EY3.js → chunk-INBZLHJQ.js} +306 -549
  7. package/dist/{chunk-MJWFCDBB.js → chunk-IOIAXKW2.js} +38 -16
  8. package/dist/{chunk-3TJVFKXV.js → chunk-QM5E5RJZ.js} +1 -1
  9. package/dist/chunk-QUGH2XA6.js +275 -0
  10. package/dist/cli-command-YQ5T4R73.js +526 -0
  11. package/dist/cli.js +30 -5
  12. package/dist/{config-QC5QSP3G.js → config-2PXUENQL.js} +1 -1
  13. package/dist/{daemon-PVYTXJMS.js → daemon-QBOXJFRA.js} +7 -6
  14. package/dist/daemon-handle-2GVZT55B.js +10 -0
  15. package/dist/server-23ERUOJ7.js +11 -0
  16. package/dist/web/assets/{Area-DCES17aZ.js → Area-DbVPPS7M.js} +1 -1
  17. package/dist/web/assets/{analytics-DDW6zcnj.js → analytics-DS5p9Duq.js} +2 -2
  18. package/dist/web/assets/{breakdown-D5LPPtqh.js → breakdown-zYF0TXN5.js} +1 -1
  19. package/dist/web/assets/{chart-bHY7MQV0.js → chart-BZHliIXk.js} +1 -1
  20. package/dist/web/assets/{explore-DdWB-Pu7.js → explore-DYv8bb2r.js} +1 -1
  21. package/dist/web/assets/{index-C9jxb2o3.js → index-BO8m4dq1.js} +24 -24
  22. package/dist/web/assets/index-NnKaHxPO.css +1 -0
  23. package/dist/web/assets/{models-o1wJnG6w.js → models-P3OwakgO.js} +2 -2
  24. package/dist/web/assets/overview-5WswYjWK.js +2 -0
  25. package/dist/web/assets/{panel-DBnK1oy0.js → panel-CpwRWsNg.js} +1 -1
  26. package/dist/web/assets/{primitives-CBmteetD.js → primitives-Do0zWLZ4.js} +1 -1
  27. package/dist/web/assets/settings-sheet-uD6-A3tV.js +1 -0
  28. package/dist/web/assets/{share-sheet-DUee3FlK.js → share-sheet-CimegH2K.js} +2 -2
  29. package/dist/web/assets/{timeline-BxHwOJA_.js → timeline-Ch94Yv4z.js} +1 -1
  30. package/dist/web/assets/{use-dialog-trap-30EqxYF-.js → use-dialog-trap-LxEFLutK.js} +1 -1
  31. package/dist/web/index.html +2 -2
  32. package/package.json +1 -1
  33. package/dist/server-XUCQWX6N.js +0 -10
  34. package/dist/web/assets/index-m0KfWnN2.css +0 -1
  35. package/dist/web/assets/overview-wq2uAEk4.js +0 -2
  36. package/dist/web/assets/settings-sheet-CZ6GEPl3.js +0 -1
@@ -4,9 +4,21 @@ import {
4
4
  cacheDir,
5
5
  envDir,
6
6
  expandHome,
7
- isValidTimezone,
8
- slugify
9
- } from "./chunk-5CWOJMAH.js";
7
+ isValidTimezone
8
+ } from "./chunk-E2YXYU73.js";
9
+
10
+ // src/async.ts
11
+ var DEFAULT_TIMEOUT_MS = 3e4;
12
+ function withTimeout(promise, ms = DEFAULT_TIMEOUT_MS) {
13
+ let timer;
14
+ const timeout = new Promise((_, reject) => {
15
+ timer = setTimeout(() => reject(new Error("operation timeout")), ms);
16
+ timer.unref?.();
17
+ });
18
+ return Promise.race([promise, timeout]).finally(() => {
19
+ if (timer) clearTimeout(timer);
20
+ });
21
+ }
10
22
 
11
23
  // src/providers/usage-core.ts
12
24
  import { readFileSync, readdirSync } from "fs";
@@ -726,6 +738,240 @@ function coalesceTables(list) {
726
738
  return mergeTables(list);
727
739
  }
728
740
 
741
+ // src/rpc/contract.ts
742
+ import { Schema } from "effect";
743
+ import * as Rpc from "effect/unstable/rpc/Rpc";
744
+ import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
745
+ var TOKMON_WS_PATH = "/ws";
746
+ var TOKMON_PROTOCOL_VERSION = 2;
747
+ var TOKMON_CAPABILITIES = ["config-cas", "config-revision"];
748
+ var TOKMON_WS_METHODS = {
749
+ getConfig: "tokmon.getConfig",
750
+ setConfig: "tokmon.setConfig",
751
+ refresh: "tokmon.refresh",
752
+ browseFs: "tokmon.browseFs",
753
+ snapshot: "tokmon.snapshot",
754
+ config: "tokmon.config"
755
+ };
756
+ var RefreshScopeSchema = Schema.Literals([
757
+ "all",
758
+ "summary",
759
+ "table",
760
+ "billing",
761
+ "peak"
762
+ ]);
763
+ var ProviderIdSchema = Schema.Literals(PROVIDER_IDS);
764
+ var NonNegativeIntegerSchema = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
765
+ var PositiveFiniteSchema = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(1));
766
+ var AccountSchema = Schema.Struct({
767
+ id: Schema.String,
768
+ providerId: ProviderIdSchema,
769
+ name: Schema.String,
770
+ homeDir: Schema.String,
771
+ color: Schema.optionalKey(Schema.String)
772
+ });
773
+ var ConfigSchema = Schema.Struct({
774
+ revision: NonNegativeIntegerSchema,
775
+ interval: PositiveFiniteSchema,
776
+ billingInterval: PositiveFiniteSchema,
777
+ clearScreen: Schema.Boolean,
778
+ privacyMode: Schema.Boolean,
779
+ privacyToggleKey: Schema.String,
780
+ timezone: Schema.NullOr(Schema.String),
781
+ accounts: Schema.Array(AccountSchema),
782
+ activeAccountId: Schema.NullOr(Schema.String),
783
+ disabledProviders: Schema.Array(ProviderIdSchema),
784
+ onboarded: Schema.Boolean,
785
+ dashboardLayout: Schema.Literals(["grid", "single"]),
786
+ defaultFocus: Schema.Literals(["all", "last"]),
787
+ ascii: Schema.Literals(["auto", "on", "off"]),
788
+ allowNetworkAccess: Schema.Boolean,
789
+ resetDisplay: Schema.Literals(["relative", "absolute"]),
790
+ knownProviders: Schema.Array(ProviderIdSchema)
791
+ });
792
+ var ProtocolInfoSchema = Schema.Struct({
793
+ version: Schema.Literal(TOKMON_PROTOCOL_VERSION),
794
+ // Capabilities are additive within a protocol version. Older clients must
795
+ // preserve forward compatibility when a newer daemon advertises one they do
796
+ // not understand yet.
797
+ capabilities: Schema.Array(Schema.String)
798
+ });
799
+ var ConfigStateSchema = Schema.Struct({
800
+ protocol: ProtocolInfoSchema,
801
+ config: ConfigSchema
802
+ });
803
+ var ConfigUpdateRequestSchema = Schema.Struct({
804
+ expectedRevision: NonNegativeIntegerSchema,
805
+ config: ConfigSchema
806
+ });
807
+ var ConfigUpdateConflictSchema = Schema.Struct({
808
+ kind: Schema.Literal("conflict"),
809
+ state: ConfigStateSchema
810
+ });
811
+ var ConfigPersistenceFailureSchema = Schema.Struct({
812
+ kind: Schema.Literal("persistence"),
813
+ message: Schema.String
814
+ });
815
+ var FsEntrySchema = Schema.Struct({
816
+ name: Schema.String,
817
+ path: Schema.String,
818
+ dir: Schema.Boolean
819
+ });
820
+ var FsListingSchema = Schema.Struct({
821
+ path: Schema.String,
822
+ parent: Schema.NullOr(Schema.String),
823
+ entries: Schema.Array(FsEntrySchema)
824
+ });
825
+ var UsageSummarySchema = Schema.Struct({
826
+ cost: Schema.Finite,
827
+ tokens: Schema.Finite,
828
+ cacheRead: Schema.Finite,
829
+ cacheSavings: Schema.Finite
830
+ });
831
+ var ModelDetailSchema = Schema.Struct({
832
+ name: Schema.String,
833
+ input: Schema.Finite,
834
+ output: Schema.Finite,
835
+ cacheCreate: Schema.Finite,
836
+ cacheRead: Schema.Finite,
837
+ cacheSavings: Schema.Finite,
838
+ cost: Schema.Finite,
839
+ count: Schema.Finite
840
+ });
841
+ var TableRowSchema = Schema.Struct({
842
+ label: Schema.String,
843
+ models: Schema.Array(Schema.String),
844
+ input: Schema.Finite,
845
+ output: Schema.Finite,
846
+ cacheCreate: Schema.Finite,
847
+ cacheRead: Schema.Finite,
848
+ cacheSavings: Schema.Finite,
849
+ total: Schema.Finite,
850
+ cost: Schema.Finite,
851
+ count: Schema.Finite,
852
+ breakdown: Schema.Array(ModelDetailSchema)
853
+ });
854
+ var DashboardDataSchema = Schema.Struct({
855
+ today: UsageSummarySchema,
856
+ week: UsageSummarySchema,
857
+ month: UsageSummarySchema,
858
+ burnRate: Schema.Finite,
859
+ series: Schema.Array(Schema.Finite)
860
+ });
861
+ var TableDataSchema = Schema.Struct({
862
+ daily: Schema.Array(TableRowSchema),
863
+ weekly: Schema.Array(TableRowSchema),
864
+ monthly: Schema.Array(TableRowSchema)
865
+ });
866
+ var MetricFormatSchema = Schema.Union([
867
+ Schema.Struct({ kind: Schema.Literal("percent") }),
868
+ Schema.Struct({ kind: Schema.Literal("dollars"), currency: Schema.optionalKey(Schema.String) }),
869
+ Schema.Struct({ kind: Schema.Literal("count"), suffix: Schema.optionalKey(Schema.String) })
870
+ ]);
871
+ var MetricSchema = Schema.Struct({
872
+ label: Schema.String,
873
+ used: Schema.Finite,
874
+ limit: Schema.NullOr(Schema.Finite),
875
+ format: MetricFormatSchema,
876
+ resetsAt: Schema.optionalKey(Schema.NullOr(Schema.String)),
877
+ primary: Schema.optionalKey(Schema.Boolean)
878
+ });
879
+ var BillingResultSchema = Schema.Struct({
880
+ plan: Schema.NullOr(Schema.String),
881
+ metrics: Schema.Array(MetricSchema),
882
+ error: Schema.NullOr(Schema.String),
883
+ email: Schema.optionalKey(Schema.NullOr(Schema.String)),
884
+ displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
885
+ activity: Schema.optionalKey(Schema.NullOr(Schema.Struct({
886
+ series: Schema.Array(Schema.Finite),
887
+ summary: Schema.String
888
+ }))),
889
+ modelSpend: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({
890
+ name: Schema.String,
891
+ usd: Schema.Finite,
892
+ requests: Schema.Finite
893
+ }))))
894
+ });
895
+ var WebAccountSchema = Schema.Struct({
896
+ id: Schema.String,
897
+ providerId: ProviderIdSchema,
898
+ name: Schema.String,
899
+ color: Schema.String,
900
+ homeDir: Schema.NullOr(Schema.String),
901
+ hasUsage: Schema.Boolean,
902
+ hasBilling: Schema.Boolean,
903
+ email: Schema.optionalKey(Schema.NullOr(Schema.String)),
904
+ displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
905
+ plan: Schema.optionalKey(Schema.NullOr(Schema.String)),
906
+ dashboard: Schema.NullOr(DashboardDataSchema),
907
+ table: Schema.NullOr(TableDataSchema),
908
+ billing: Schema.NullOr(BillingResultSchema),
909
+ summaryState: Schema.Literals(["pending", "ready", "error"]),
910
+ billingState: Schema.Literals(["pending", "ready", "error"]),
911
+ tableState: Schema.Literals(["pending", "ready", "error"]),
912
+ summaryUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
913
+ billingUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
914
+ tableUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema))
915
+ });
916
+ var WebSnapshotSchema = Schema.Struct({
917
+ version: Schema.String,
918
+ generatedAt: NonNegativeIntegerSchema,
919
+ tz: Schema.String,
920
+ intervalMs: PositiveFiniteSchema,
921
+ billingIntervalMs: Schema.optionalKey(PositiveFiniteSchema),
922
+ providers: Schema.Array(Schema.Struct({
923
+ id: ProviderIdSchema,
924
+ name: Schema.String,
925
+ color: Schema.String
926
+ })),
927
+ accounts: Schema.Array(WebAccountSchema),
928
+ seeded: Schema.Boolean,
929
+ peak: Schema.NullOr(Schema.Struct({
930
+ state: Schema.Literals(["peak", "off-peak", "weekend"]),
931
+ label: Schema.String,
932
+ // The upstream clock is only normalized to a finite number; do not make
933
+ // the wire contract narrower than the producer's declared type.
934
+ minutesUntilChange: Schema.NullOr(Schema.Finite),
935
+ changesAt: Schema.optionalKey(Schema.NullOr(Schema.String))
936
+ }))
937
+ });
938
+ var EmptyPayloadSchema = Schema.Struct({});
939
+ var GetConfigRpc = Rpc.make(TOKMON_WS_METHODS.getConfig, {
940
+ payload: EmptyPayloadSchema,
941
+ success: ConfigStateSchema
942
+ });
943
+ var SetConfigRpc = Rpc.make(TOKMON_WS_METHODS.setConfig, {
944
+ payload: ConfigUpdateRequestSchema,
945
+ success: ConfigStateSchema,
946
+ error: Schema.Union([ConfigUpdateConflictSchema, ConfigPersistenceFailureSchema])
947
+ });
948
+ var RefreshRpc = Rpc.make(TOKMON_WS_METHODS.refresh, {
949
+ payload: Schema.Struct({ scope: RefreshScopeSchema }),
950
+ success: Schema.Void
951
+ });
952
+ var BrowseFsRpc = Rpc.make(TOKMON_WS_METHODS.browseFs, {
953
+ payload: Schema.Struct({ path: Schema.String }),
954
+ success: FsListingSchema
955
+ });
956
+ var SnapshotRpc = Rpc.make(TOKMON_WS_METHODS.snapshot, {
957
+ payload: EmptyPayloadSchema,
958
+ success: WebSnapshotSchema,
959
+ stream: true
960
+ });
961
+ var ConfigRpc = Rpc.make(TOKMON_WS_METHODS.config, {
962
+ payload: EmptyPayloadSchema,
963
+ success: ConfigStateSchema,
964
+ stream: true
965
+ });
966
+ var TokmonRpcGroup = RpcGroup.make(
967
+ GetConfigRpc,
968
+ SetConfigRpc,
969
+ RefreshRpc,
970
+ BrowseFsRpc,
971
+ SnapshotRpc,
972
+ ConfigRpc
973
+ );
974
+
729
975
  // src/shared/format.ts
730
976
  var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
731
977
  function formatCurrency(value, opts = {}) {
@@ -773,6 +1019,18 @@ function formatResetIn(iso, now = Date.now()) {
773
1019
  const hrs = hours % 24;
774
1020
  return `${days}d ${hrs}h`;
775
1021
  }
1022
+ function formatResetAt(iso, display, now = Date.now(), tz) {
1023
+ const timestamp = Date.parse(iso);
1024
+ if (!Number.isFinite(timestamp)) return iso;
1025
+ if (display === "relative") return formatResetIn(iso, now);
1026
+ return new Intl.DateTimeFormat(void 0, {
1027
+ month: "short",
1028
+ day: "numeric",
1029
+ hour: "numeric",
1030
+ minute: "2-digit",
1031
+ timeZone: tz
1032
+ }).format(new Date(timestamp));
1033
+ }
776
1034
 
777
1035
  // src/format.ts
778
1036
  var currency = formatCurrency;
@@ -786,7 +1044,7 @@ function col(s, w, align = "right") {
786
1044
  const spaces = " ".repeat(w - s.length);
787
1045
  return align === "right" ? spaces + s : s + spaces;
788
1046
  }
789
- var resetIn = formatResetIn;
1047
+ var resetAt = formatResetAt;
790
1048
 
791
1049
  // src/providers/claude/usage.ts
792
1050
  import { stat as fsStat, access } from "fs/promises";
@@ -1188,11 +1446,13 @@ function boolValue(value) {
1188
1446
  return false;
1189
1447
  }
1190
1448
  function resetFrom(value) {
1191
- if (typeof value === "string" && value.trim()) return resetIn(value);
1449
+ if (typeof value === "string" && value.trim()) {
1450
+ const timestamp = Date.parse(value);
1451
+ return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : null;
1452
+ }
1192
1453
  const n = numberValue(value);
1193
1454
  if (n === void 0) return null;
1194
- const iso = msToIso(Math.abs(n) < 1e10 ? n * 1e3 : n);
1195
- return iso ? resetIn(iso) : null;
1455
+ return msToIso(Math.abs(n) < 1e10 ? n * 1e3 : n);
1196
1456
  }
1197
1457
  function usageMetric(label, window, primary) {
1198
1458
  const used = numberValue(window?.utilization);
@@ -1745,8 +2005,7 @@ function resetDateMs(window) {
1745
2005
  }
1746
2006
  function resetFrom2(window) {
1747
2007
  const ms = resetDateMs(window);
1748
- const iso = ms === null ? null : msToIso(ms);
1749
- return iso ? resetIn(iso) : null;
2008
+ return ms === null ? null : msToIso(ms);
1750
2009
  }
1751
2010
  function windowSeconds(window) {
1752
2011
  const seconds = numberValue(window?.limit_window_seconds ?? window?.period_seconds ?? window?.window_seconds);
@@ -2325,7 +2584,7 @@ async function cursorBillingCore(account) {
2325
2584
  const rawEnd = usage.billingCycleEnd;
2326
2585
  const endMs = timestampMs(rawEnd);
2327
2586
  const iso = endMs === null ? null : msToIso(endMs);
2328
- const resets = iso ? resetIn(iso) : null;
2587
+ const resets = iso;
2329
2588
  const limit = numberValue(pu.limit);
2330
2589
  const planUsedCents = numberValue(pu.totalSpend) ?? (limit !== void 0 && numberValue(pu.remaining) !== void 0 ? limit - numberValue(pu.remaining) : void 0);
2331
2590
  const computedPercent = limit !== void 0 && limit > 0 && planUsedCents !== void 0 ? planUsedCents / limit * 100 : void 0;
@@ -2758,7 +3017,7 @@ function ghConfigDir(homeDir) {
2758
3017
  function ghHostsPath(homeDir) {
2759
3018
  return join13(ghConfigDir(homeDir), "hosts.yml");
2760
3019
  }
2761
- async function detectCopilot(homeDir) {
3020
+ function copilotStateDirs(homeDir) {
2762
3021
  const home = homeDir ?? homedir9();
2763
3022
  const candidates = [
2764
3023
  join13(home, ".config", "github-copilot"),
@@ -2769,7 +3028,10 @@ async function detectCopilot(homeDir) {
2769
3028
  if (process.platform === "win32") {
2770
3029
  candidates.push(join13(envDir("LOCALAPPDATA") ?? join13(home, "AppData", "Local"), "github-copilot"));
2771
3030
  }
2772
- for (const path of candidates) {
3031
+ return candidates;
3032
+ }
3033
+ async function detectCopilot(homeDir) {
3034
+ for (const path of copilotStateDirs(homeDir)) {
2773
3035
  try {
2774
3036
  await access7(path);
2775
3037
  return true;
@@ -2871,7 +3133,9 @@ function redactToken(token) {
2871
3133
  return token.length <= 4 ? "****" : `****${token.slice(-4)}`;
2872
3134
  }
2873
3135
  function resetDate(value) {
2874
- return typeof value === "string" && value.trim() ? resetIn(value) : null;
3136
+ if (typeof value !== "string" || !value.trim()) return null;
3137
+ const timestamp = Date.parse(value);
3138
+ return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : null;
2875
3139
  }
2876
3140
  function boolValue2(value) {
2877
3141
  if (typeof value === "boolean") return value;
@@ -3378,7 +3642,7 @@ function cloudCodeBucketsToMetrics(buckets, options = {}) {
3378
3642
  used: Math.round((1 - clamped) * 100),
3379
3643
  limit: 100,
3380
3644
  format: { kind: "percent" },
3381
- resetsAt: bucket.resetTime && Number.isFinite(Date.parse(bucket.resetTime)) ? resetIn(bucket.resetTime) : null,
3645
+ resetsAt: bucket.resetTime && Number.isFinite(Date.parse(bucket.resetTime)) ? new Date(bucket.resetTime).toISOString() : null,
3382
3646
  primary: i === 0
3383
3647
  };
3384
3648
  });
@@ -4294,551 +4558,44 @@ async function detectProviders() {
4294
4558
  return found.filter((id) => id !== null);
4295
4559
  }
4296
4560
 
4297
- // src/accounts.ts
4298
- import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync } from "fs";
4299
- import { homedir as homedir16 } from "os";
4300
- import { basename, join as join22, resolve } from "path";
4301
-
4302
- // src/providers/codex/identity.ts
4303
- import { readFileSync as readFileSync4 } from "fs";
4304
- import { join as join21 } from "path";
4305
- function codexAuthPaths(homeDir) {
4306
- return [join21(homeDir, ".codex", "auth.json"), join21(homeDir, "auth.json")];
4307
- }
4308
- function readCodexIdentity(homeDir) {
4309
- for (const path of codexAuthPaths(homeDir)) {
4310
- try {
4311
- const parsed = JSON.parse(readFileSync4(path, "utf-8"));
4312
- const { email, displayName, payload } = identityFromIdToken(parsed?.tokens?.id_token);
4313
- if (!payload) continue;
4314
- return { email, displayName };
4315
- } catch {
4316
- }
4317
- }
4318
- return {};
4319
- }
4320
-
4321
- // src/accounts.ts
4322
- function accountKey(providerId, homeDir) {
4323
- return `${providerId}:${homeDir ? resolve(expandHome(homeDir)) : homedir16()}`;
4324
- }
4325
- function uniqueId(base, used) {
4326
- let id = slugify(base) || "account";
4327
- if (!used.has(id)) {
4328
- used.add(id);
4329
- return id;
4330
- }
4331
- for (let i = 2; i < 1e3; i++) {
4332
- const next = `${id}_${i}`;
4333
- if (!used.has(next)) {
4334
- used.add(next);
4335
- return next;
4336
- }
4337
- }
4338
- id = `${id}_${Date.now()}`;
4339
- used.add(id);
4340
- return id;
4341
- }
4342
- function hasClaudeState(homeDir) {
4343
- return existsSync2(join22(homeDir, ".claude.json")) || existsSync2(join22(homeDir, ".claude", ".credentials.json")) || existsSync2(join22(homeDir, ".claude", "projects")) || existsSync2(join22(homeDir, ".config", "claude", ".credentials.json")) || existsSync2(join22(homeDir, ".config", "claude", "projects"));
4344
- }
4345
- function candidateAlternateHomes(prefix) {
4346
- const home = homedir16();
4347
- let entries;
4348
- try {
4349
- entries = readdirSync2(home);
4350
- } catch {
4351
- return [];
4352
- }
4353
- const out = [];
4354
- const pattern = new RegExp(`^\\.${prefix}[_-]`);
4355
- for (const name of entries) {
4356
- if (!pattern.test(name)) continue;
4357
- const path = join22(home, name);
4358
- try {
4359
- if (!statSync(path).isDirectory()) continue;
4360
- out.push(path);
4361
- } catch {
4362
- }
4363
- }
4364
- return out.sort();
4365
- }
4366
- function labelForClaudeHome(homeDir) {
4367
- const identity = readClaudeIdentity(homeDir);
4368
- if (identity.email) return `Claude ${identity.email}`;
4369
- if (identity.displayName) return `Claude ${identity.displayName}`;
4370
- const raw = basename(homeDir).replace(/^\.claude[_-]?/, "").replace(/[_-]+/g, " ").trim();
4371
- return raw ? `Claude ${raw}` : "Claude";
4372
- }
4373
- function hasCodexAuth(homeDir) {
4374
- for (const path of codexAuthPaths(homeDir)) {
4375
- try {
4376
- const parsed = JSON.parse(readFileSync5(path, "utf-8"));
4377
- const accessToken = parsed?.tokens?.access_token;
4378
- if (typeof accessToken === "string" && accessToken.trim()) return true;
4379
- } catch {
4380
- }
4381
- }
4382
- return false;
4383
- }
4384
- function labelForCodexHome(homeDir) {
4385
- const identity = readCodexIdentity(homeDir);
4386
- if (identity.email) return `Codex ${identity.email}`;
4387
- if (identity.displayName) return `Codex ${identity.displayName}`;
4388
- const raw = basename(homeDir).replace(/^\.codex[_-]?/, "").replace(/[_-]+/g, " ").trim();
4389
- return raw ? `Codex ${raw}` : "Codex";
4390
- }
4391
- function discoverClaudeAccounts(usedIds) {
4392
- const provider = PROVIDERS.claude;
4393
- const out = [];
4394
- for (const homeDir of candidateAlternateHomes("claude")) {
4395
- if (!hasClaudeState(homeDir)) continue;
4396
- const suffix = basename(homeDir).replace(/^\.claude[_-]?/, "") || basename(homeDir);
4397
- out.push({
4398
- id: uniqueId(`claude_${suffix}`, usedIds),
4399
- providerId: "claude",
4400
- name: labelForClaudeHome(homeDir),
4401
- color: provider.color,
4402
- homeDir
4403
- });
4404
- }
4405
- return out;
4406
- }
4407
- function discoverCodexAccounts(usedIds) {
4408
- const provider = PROVIDERS.codex;
4409
- const out = [];
4410
- for (const homeDir of candidateAlternateHomes("codex")) {
4411
- if (!hasCodexAuth(homeDir)) continue;
4412
- const suffix = basename(homeDir).replace(/^\.codex[_-]?/, "") || basename(homeDir);
4413
- out.push({
4414
- id: uniqueId(`codex_${suffix}`, usedIds),
4415
- providerId: "codex",
4416
- name: labelForCodexHome(homeDir),
4417
- color: provider.color,
4418
- homeDir
4419
- });
4420
- }
4421
- return out;
4422
- }
4423
- function discoverProviderAccounts(providerId, usedIds) {
4424
- if (providerId === "claude") return discoverClaudeAccounts(usedIds);
4425
- if (providerId === "codex") return discoverCodexAccounts(usedIds);
4426
- return [];
4427
- }
4428
- function buildAccounts(config, detected) {
4429
- const out = [];
4430
- const usedIds = new Set(config.accounts.map((a) => a.id));
4431
- const seenKeys = /* @__PURE__ */ new Set();
4432
- const add = (account) => {
4433
- const key = accountKey(account.providerId, account.homeDir);
4434
- if (seenKeys.has(key)) return;
4435
- seenKeys.add(key);
4436
- out.push(account);
4437
- };
4438
- for (const pid of PROVIDER_ORDER) {
4439
- if (config.disabledProviders.includes(pid)) continue;
4440
- const provider = PROVIDERS[pid];
4441
- const configured = config.accounts.filter((a) => a.providerId === pid);
4442
- for (const a of configured) {
4443
- add({
4444
- id: a.id,
4445
- providerId: pid,
4446
- name: a.name,
4447
- color: a.color || provider.color,
4448
- homeDir: a.homeDir && a.homeDir !== "~" ? expandHome(a.homeDir) : void 0
4449
- });
4450
- }
4451
- const discovered = discoverProviderAccounts(pid, usedIds);
4452
- if (detected.includes(pid)) {
4453
- add({ id: pid, providerId: pid, name: provider.name, color: provider.color, homeDir: void 0 });
4454
- }
4455
- for (const account of discovered) {
4456
- add(account);
4457
- }
4458
- }
4459
- return out;
4460
- }
4461
- function accountsByProvider(accounts) {
4462
- const groups = [];
4463
- for (const pid of PROVIDER_ORDER) {
4464
- const list = accounts.filter((a) => a.providerId === pid);
4465
- if (list.length > 0) groups.push({ provider: pid, accounts: list });
4466
- }
4467
- return groups;
4468
- }
4469
-
4470
- // src/shared/colors.ts
4471
- var FALLBACK_HEX = "#8d9090";
4472
- var NAMED_HEX = {
4473
- green: "#6caa71",
4474
- greenBright: "#79be7e",
4475
- cyan: "#7ccbcd",
4476
- cyanBright: "#84dde0",
4477
- blue: "#6d96b4",
4478
- blueBright: "#67b5ed",
4479
- magenta: "#bd7bcd",
4480
- magentaBright: "#d389e5",
4481
- yellow: "#c4ac62",
4482
- yellowBright: "#d9c074",
4483
- red: "#b45648",
4484
- redBright: "#cf6a5a",
4485
- white: "#dee5eb",
4486
- whiteBright: "#f3f5f5",
4487
- gray: FALLBACK_HEX,
4488
- grey: FALLBACK_HEX
4489
- };
4490
- var PROVIDER_HEX = {
4491
- claude: NAMED_HEX.green,
4492
- codex: NAMED_HEX.cyan,
4493
- cursor: NAMED_HEX.magenta,
4494
- copilot: NAMED_HEX.white,
4495
- pi: NAMED_HEX.blue,
4496
- opencode: NAMED_HEX.yellow,
4497
- antigravity: NAMED_HEX.red,
4498
- gemini: NAMED_HEX.greenBright,
4499
- grok: NAMED_HEX.yellowBright
4500
- };
4501
- function namedHex(name) {
4502
- if (!name) return FALLBACK_HEX;
4503
- if (name.startsWith("#")) return name;
4504
- return NAMED_HEX[name] ?? FALLBACK_HEX;
4505
- }
4506
- function colorHex(accountColor, providerColorName) {
4507
- if (accountColor) {
4508
- if (accountColor.startsWith("#")) return accountColor;
4509
- const named = NAMED_HEX[accountColor];
4510
- if (named) return named;
4511
- }
4512
- return namedHex(providerColorName);
4513
- }
4514
- var MODEL_PALETTE = [
4515
- "#00d7ff",
4516
- "#00d787",
4517
- "#e6b450",
4518
- "#d75f87",
4519
- "#5f87ff",
4520
- "#af87ff",
4521
- "#5fd7a7",
4522
- "#ff8787",
4523
- "#d7af5f",
4524
- "#87d7ff",
4525
- "#d787d7",
4526
- "#9ee493",
4527
- "#ffb454",
4528
- "#7aa2f7",
4529
- "#bb9af7"
4530
- ];
4531
- var modelColorCache = /* @__PURE__ */ new Map();
4532
- function modelColor(name) {
4533
- const cached = modelColorCache.get(name);
4534
- if (cached) return cached;
4535
- let hash2 = 0;
4536
- for (let i = 0; i < name.length; i++) hash2 = hash2 * 31 + name.charCodeAt(i) >>> 0;
4537
- const color = MODEL_PALETTE[hash2 % MODEL_PALETTE.length];
4538
- modelColorCache.set(name, color);
4539
- return color;
4540
- }
4541
- var TOKEN_BUCKET = {
4542
- input: NAMED_HEX.blue,
4543
- output: NAMED_HEX.green,
4544
- cacheCreate: NAMED_HEX.yellow,
4545
- cacheRead: NAMED_HEX.cyan
4546
- };
4547
-
4548
- // src/async.ts
4549
- var DEFAULT_TIMEOUT_MS = 3e4;
4550
- function withTimeout(promise, ms = DEFAULT_TIMEOUT_MS) {
4551
- let timer;
4552
- const timeout = new Promise((_, reject) => {
4553
- timer = setTimeout(() => reject(new Error("operation timeout")), ms);
4554
- timer.unref?.();
4555
- });
4556
- return Promise.race([promise, timeout]).finally(() => {
4557
- if (timer) clearTimeout(timer);
4558
- });
4559
- }
4560
-
4561
- // src/peak.ts
4562
- async function fetchPeak() {
4563
- try {
4564
- const res = await fetch("https://promoclock.co/api/status", {
4565
- headers: { "Accept": "application/json", "User-Agent": "tokmon" },
4566
- signal: AbortSignal.timeout(3e3)
4567
- });
4568
- if (!res.ok) return null;
4569
- const data = await readJson(res);
4570
- if (!data) return null;
4571
- let state;
4572
- if (data.isPeak === true || data.status === "peak") state = "peak";
4573
- else if (data.isWeekend === true || data.status === "weekend") state = "weekend";
4574
- else if (data.isOffPeak === true || data.status === "off_peak" || data.status === "off-peak") state = "off-peak";
4575
- else return null;
4576
- const minutesUntilChange = typeof data.minutesUntilChange === "number" && Number.isFinite(data.minutesUntilChange) ? data.minutesUntilChange : null;
4577
- return {
4578
- state,
4579
- label: state === "peak" ? "Peak" : state === "weekend" ? "Weekend" : "Off-Peak",
4580
- minutesUntilChange
4581
- };
4582
- } catch {
4583
- return null;
4584
- }
4585
- }
4586
-
4587
- // src/rpc/contract.ts
4588
- import { Schema } from "effect";
4589
- import * as Rpc from "effect/unstable/rpc/Rpc";
4590
- import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
4591
- var TOKMON_WS_PATH = "/ws";
4592
- var TOKMON_PROTOCOL_VERSION = 2;
4593
- var TOKMON_CAPABILITIES = ["config-cas", "config-revision"];
4594
- var TOKMON_WS_METHODS = {
4595
- getConfig: "tokmon.getConfig",
4596
- setConfig: "tokmon.setConfig",
4597
- refresh: "tokmon.refresh",
4598
- browseFs: "tokmon.browseFs",
4599
- snapshot: "tokmon.snapshot",
4600
- config: "tokmon.config"
4601
- };
4602
- var RefreshScopeSchema = Schema.Literals([
4603
- "all",
4604
- "summary",
4605
- "table",
4606
- "billing",
4607
- "peak"
4608
- ]);
4609
- var ProviderIdSchema = Schema.Literals(PROVIDER_IDS);
4610
- var NonNegativeIntegerSchema = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
4611
- var PositiveFiniteSchema = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(1));
4612
- var AccountSchema = Schema.Struct({
4613
- id: Schema.String,
4614
- providerId: ProviderIdSchema,
4615
- name: Schema.String,
4616
- homeDir: Schema.String,
4617
- color: Schema.optionalKey(Schema.String)
4618
- });
4619
- var ConfigSchema = Schema.Struct({
4620
- revision: NonNegativeIntegerSchema,
4621
- interval: PositiveFiniteSchema,
4622
- billingInterval: PositiveFiniteSchema,
4623
- clearScreen: Schema.Boolean,
4624
- privacyMode: Schema.Boolean,
4625
- privacyToggleKey: Schema.String,
4626
- timezone: Schema.NullOr(Schema.String),
4627
- accounts: Schema.Array(AccountSchema),
4628
- activeAccountId: Schema.NullOr(Schema.String),
4629
- disabledProviders: Schema.Array(ProviderIdSchema),
4630
- onboarded: Schema.Boolean,
4631
- dashboardLayout: Schema.Literals(["grid", "single"]),
4632
- defaultFocus: Schema.Literals(["all", "last"]),
4633
- ascii: Schema.Literals(["auto", "on", "off"]),
4634
- allowNetworkAccess: Schema.Boolean,
4635
- knownProviders: Schema.Array(ProviderIdSchema)
4636
- });
4637
- var ProtocolInfoSchema = Schema.Struct({
4638
- version: Schema.Literal(TOKMON_PROTOCOL_VERSION),
4639
- // Capabilities are additive within a protocol version. Older clients must
4640
- // preserve forward compatibility when a newer daemon advertises one they do
4641
- // not understand yet.
4642
- capabilities: Schema.Array(Schema.String)
4643
- });
4644
- var ConfigStateSchema = Schema.Struct({
4645
- protocol: ProtocolInfoSchema,
4646
- config: ConfigSchema
4647
- });
4648
- var ConfigUpdateRequestSchema = Schema.Struct({
4649
- expectedRevision: NonNegativeIntegerSchema,
4650
- config: ConfigSchema
4651
- });
4652
- var ConfigUpdateConflictSchema = Schema.Struct({
4653
- kind: Schema.Literal("conflict"),
4654
- state: ConfigStateSchema
4655
- });
4656
- var ConfigPersistenceFailureSchema = Schema.Struct({
4657
- kind: Schema.Literal("persistence"),
4658
- message: Schema.String
4659
- });
4660
- var FsEntrySchema = Schema.Struct({
4661
- name: Schema.String,
4662
- path: Schema.String,
4663
- dir: Schema.Boolean
4664
- });
4665
- var FsListingSchema = Schema.Struct({
4666
- path: Schema.String,
4667
- parent: Schema.NullOr(Schema.String),
4668
- entries: Schema.Array(FsEntrySchema)
4669
- });
4670
- var UsageSummarySchema = Schema.Struct({
4671
- cost: Schema.Finite,
4672
- tokens: Schema.Finite,
4673
- cacheRead: Schema.Finite,
4674
- cacheSavings: Schema.Finite
4675
- });
4676
- var ModelDetailSchema = Schema.Struct({
4677
- name: Schema.String,
4678
- input: Schema.Finite,
4679
- output: Schema.Finite,
4680
- cacheCreate: Schema.Finite,
4681
- cacheRead: Schema.Finite,
4682
- cacheSavings: Schema.Finite,
4683
- cost: Schema.Finite,
4684
- count: Schema.Finite
4685
- });
4686
- var TableRowSchema = Schema.Struct({
4687
- label: Schema.String,
4688
- models: Schema.Array(Schema.String),
4689
- input: Schema.Finite,
4690
- output: Schema.Finite,
4691
- cacheCreate: Schema.Finite,
4692
- cacheRead: Schema.Finite,
4693
- cacheSavings: Schema.Finite,
4694
- total: Schema.Finite,
4695
- cost: Schema.Finite,
4696
- count: Schema.Finite,
4697
- breakdown: Schema.Array(ModelDetailSchema)
4698
- });
4699
- var DashboardDataSchema = Schema.Struct({
4700
- today: UsageSummarySchema,
4701
- week: UsageSummarySchema,
4702
- month: UsageSummarySchema,
4703
- burnRate: Schema.Finite,
4704
- series: Schema.Array(Schema.Finite)
4705
- });
4706
- var TableDataSchema = Schema.Struct({
4707
- daily: Schema.Array(TableRowSchema),
4708
- weekly: Schema.Array(TableRowSchema),
4709
- monthly: Schema.Array(TableRowSchema)
4710
- });
4711
- var MetricFormatSchema = Schema.Union([
4712
- Schema.Struct({ kind: Schema.Literal("percent") }),
4713
- Schema.Struct({ kind: Schema.Literal("dollars"), currency: Schema.optionalKey(Schema.String) }),
4714
- Schema.Struct({ kind: Schema.Literal("count"), suffix: Schema.optionalKey(Schema.String) })
4715
- ]);
4716
- var MetricSchema = Schema.Struct({
4717
- label: Schema.String,
4718
- used: Schema.Finite,
4719
- limit: Schema.NullOr(Schema.Finite),
4720
- format: MetricFormatSchema,
4721
- resetsAt: Schema.optionalKey(Schema.NullOr(Schema.String)),
4722
- primary: Schema.optionalKey(Schema.Boolean)
4723
- });
4724
- var BillingResultSchema = Schema.Struct({
4725
- plan: Schema.NullOr(Schema.String),
4726
- metrics: Schema.Array(MetricSchema),
4727
- error: Schema.NullOr(Schema.String),
4728
- email: Schema.optionalKey(Schema.NullOr(Schema.String)),
4729
- displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
4730
- activity: Schema.optionalKey(Schema.NullOr(Schema.Struct({
4731
- series: Schema.Array(Schema.Finite),
4732
- summary: Schema.String
4733
- }))),
4734
- modelSpend: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({
4735
- name: Schema.String,
4736
- usd: Schema.Finite,
4737
- requests: Schema.Finite
4738
- }))))
4739
- });
4740
- var WebAccountSchema = Schema.Struct({
4741
- id: Schema.String,
4742
- providerId: ProviderIdSchema,
4743
- name: Schema.String,
4744
- color: Schema.String,
4745
- homeDir: Schema.NullOr(Schema.String),
4746
- hasUsage: Schema.Boolean,
4747
- hasBilling: Schema.Boolean,
4748
- email: Schema.optionalKey(Schema.NullOr(Schema.String)),
4749
- displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
4750
- plan: Schema.optionalKey(Schema.NullOr(Schema.String)),
4751
- dashboard: Schema.NullOr(DashboardDataSchema),
4752
- table: Schema.NullOr(TableDataSchema),
4753
- billing: Schema.NullOr(BillingResultSchema),
4754
- summaryState: Schema.Literals(["pending", "ready", "error"]),
4755
- billingState: Schema.Literals(["pending", "ready", "error"]),
4756
- tableState: Schema.Literals(["pending", "ready", "error"]),
4757
- summaryUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
4758
- billingUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
4759
- tableUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema))
4760
- });
4761
- var WebSnapshotSchema = Schema.Struct({
4762
- version: Schema.String,
4763
- generatedAt: NonNegativeIntegerSchema,
4764
- tz: Schema.String,
4765
- intervalMs: PositiveFiniteSchema,
4766
- billingIntervalMs: Schema.optionalKey(PositiveFiniteSchema),
4767
- providers: Schema.Array(Schema.Struct({
4768
- id: ProviderIdSchema,
4769
- name: Schema.String,
4770
- color: Schema.String
4771
- })),
4772
- accounts: Schema.Array(WebAccountSchema),
4773
- seeded: Schema.Boolean,
4774
- peak: Schema.NullOr(Schema.Struct({
4775
- state: Schema.Literals(["peak", "off-peak", "weekend"]),
4776
- label: Schema.String,
4777
- // The upstream clock is only normalized to a finite number; do not make
4778
- // the wire contract narrower than the producer's declared type.
4779
- minutesUntilChange: Schema.NullOr(Schema.Finite)
4780
- }))
4781
- });
4782
- var EmptyPayloadSchema = Schema.Struct({});
4783
- var GetConfigRpc = Rpc.make(TOKMON_WS_METHODS.getConfig, {
4784
- payload: EmptyPayloadSchema,
4785
- success: ConfigStateSchema
4786
- });
4787
- var SetConfigRpc = Rpc.make(TOKMON_WS_METHODS.setConfig, {
4788
- payload: ConfigUpdateRequestSchema,
4789
- success: ConfigStateSchema,
4790
- error: Schema.Union([ConfigUpdateConflictSchema, ConfigPersistenceFailureSchema])
4791
- });
4792
- var RefreshRpc = Rpc.make(TOKMON_WS_METHODS.refresh, {
4793
- payload: Schema.Struct({ scope: RefreshScopeSchema }),
4794
- success: Schema.Void
4795
- });
4796
- var BrowseFsRpc = Rpc.make(TOKMON_WS_METHODS.browseFs, {
4797
- payload: Schema.Struct({ path: Schema.String }),
4798
- success: FsListingSchema
4799
- });
4800
- var SnapshotRpc = Rpc.make(TOKMON_WS_METHODS.snapshot, {
4801
- payload: EmptyPayloadSchema,
4802
- success: WebSnapshotSchema,
4803
- stream: true
4804
- });
4805
- var ConfigRpc = Rpc.make(TOKMON_WS_METHODS.config, {
4806
- payload: EmptyPayloadSchema,
4807
- success: ConfigStateSchema,
4808
- stream: true
4809
- });
4810
- var TokmonRpcGroup = RpcGroup.make(
4811
- GetConfigRpc,
4812
- SetConfigRpc,
4813
- RefreshRpc,
4814
- BrowseFsRpc,
4815
- SnapshotRpc,
4816
- ConfigRpc
4817
- );
4818
-
4819
4561
  export {
4562
+ TOKMON_WS_PATH,
4563
+ TOKMON_PROTOCOL_VERSION,
4564
+ TOKMON_CAPABILITIES,
4565
+ TOKMON_WS_METHODS,
4566
+ TokmonRpcGroup,
4567
+ withTimeout,
4820
4568
  systemTimezone,
4821
4569
  resolveTimezone,
4570
+ dayKey,
4571
+ startOfMonth,
4572
+ startOfWeek,
4822
4573
  flushDisk,
4823
4574
  coalesceTables,
4575
+ claudeConfigDirs,
4576
+ readJson,
4577
+ readClaudeIdentity,
4578
+ codexHomes,
4579
+ identityFromIdToken,
4580
+ formatResetAt,
4824
4581
  currency,
4825
4582
  tokens,
4826
4583
  time,
4827
4584
  shortDate,
4828
4585
  col,
4586
+ resetAt,
4587
+ cursorStateDb,
4588
+ piSessionsDir,
4589
+ opencodeDbPaths,
4590
+ ghHostsPath,
4591
+ copilotStateDirs,
4592
+ antigravityStateDb,
4593
+ geminiTmpDir,
4594
+ geminiCredsPath,
4595
+ geminiDir,
4596
+ grokHomes,
4597
+ grokAuthPaths,
4829
4598
  PROVIDER_ORDER,
4830
4599
  PROVIDERS,
4831
- detectProviders,
4832
- buildAccounts,
4833
- accountsByProvider,
4834
- namedHex,
4835
- colorHex,
4836
- modelColor,
4837
- withTimeout,
4838
- fetchPeak,
4839
- TOKMON_WS_PATH,
4840
- TOKMON_PROTOCOL_VERSION,
4841
- TOKMON_CAPABILITIES,
4842
- TOKMON_WS_METHODS,
4843
- TokmonRpcGroup
4600
+ detectProviders
4844
4601
  };