tokmon 0.23.4 → 0.24.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 (37) hide show
  1. package/README.md +23 -5
  2. package/dist/{bootstrap-ink-BIOLTP7F.js → bootstrap-ink-HL2BYNW3.js} +37 -292
  3. package/dist/{chunk-RM3ZE44Z.js → chunk-3TJVFKXV.js} +1 -1
  4. package/dist/chunk-42H7R376.js +298 -0
  5. package/dist/chunk-45ZP755H.js +275 -0
  6. package/dist/{chunk-DJPUYMZM.js → chunk-5CWOJMAH.js} +2 -0
  7. package/dist/{chunk-2AFQKH5O.js → chunk-O27I2XFN.js} +278 -539
  8. package/dist/{chunk-2K4L4QQK.js → chunk-REO4O5VE.js} +68 -79
  9. package/dist/{chunk-Q2DSDWO2.js → chunk-SMPY52EV.js} +1 -30
  10. package/dist/{daemon-handle-6F2IB6CU.js → chunk-W2WGBXZG.js} +8 -9
  11. package/dist/cli-command-TLGIGF63.js +526 -0
  12. package/dist/cli.js +30 -5
  13. package/dist/{config-5CNJN3P7.js → config-QC5QSP3G.js} +1 -1
  14. package/dist/{daemon-PP3QAT6E.js → daemon-F4EGY77J.js} +43 -17
  15. package/dist/daemon-handle-NLCLYWFU.js +10 -0
  16. package/dist/server-X63FGHIZ.js +11 -0
  17. package/dist/web/assets/{Area-BALPhJJp.js → Area-DCES17aZ.js} +1 -1
  18. package/dist/web/assets/{analytics-uHVeOhLh.js → analytics-DDW6zcnj.js} +2 -2
  19. package/dist/web/assets/{breakdown-CR5dNNSG.js → breakdown-D5LPPtqh.js} +1 -1
  20. package/dist/web/assets/{chart-DRkqw0fm.js → chart-bHY7MQV0.js} +1 -1
  21. package/dist/web/assets/{explore-C-bHaX-I.js → explore-DdWB-Pu7.js} +1 -1
  22. package/dist/web/assets/index-C9jxb2o3.js +83 -0
  23. package/dist/web/assets/index-m0KfWnN2.css +1 -0
  24. package/dist/web/assets/{models-Qc2daHCo.js → models-o1wJnG6w.js} +2 -2
  25. package/dist/web/assets/{overview-D4VG2awg.js → overview-wq2uAEk4.js} +2 -2
  26. package/dist/web/assets/{panel-C1CsBs1H.js → panel-DBnK1oy0.js} +1 -1
  27. package/dist/web/assets/{primitives-S4UDgnZq.js → primitives-CBmteetD.js} +1 -1
  28. package/dist/web/assets/settings-sheet-CZ6GEPl3.js +1 -0
  29. package/dist/web/assets/{share-sheet-BpZBju-k.js → share-sheet-DUee3FlK.js} +1 -1
  30. package/dist/web/assets/{timeline-Cp-fyeuK.js → timeline-BxHwOJA_.js} +1 -1
  31. package/dist/web/assets/{use-dialog-trap-DaKbCG1Z.js → use-dialog-trap-30EqxYF-.js} +1 -1
  32. package/dist/web/index.html +2 -2
  33. package/package.json +1 -1
  34. package/dist/server-UHQEOVQH.js +0 -10
  35. package/dist/web/assets/index-C1jQdagn.js +0 -83
  36. package/dist/web/assets/index-DSj-Bxov.css +0 -1
  37. package/dist/web/assets/settings-sheet-DHi4Dt2z.js +0 -1
@@ -4,9 +4,21 @@ import {
4
4
  cacheDir,
5
5
  envDir,
6
6
  expandHome,
7
- isValidTimezone,
8
- slugify
9
- } from "./chunk-DJPUYMZM.js";
7
+ isValidTimezone
8
+ } from "./chunk-5CWOJMAH.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,238 @@ 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
+ knownProviders: Schema.Array(ProviderIdSchema)
790
+ });
791
+ var ProtocolInfoSchema = Schema.Struct({
792
+ version: Schema.Literal(TOKMON_PROTOCOL_VERSION),
793
+ // Capabilities are additive within a protocol version. Older clients must
794
+ // preserve forward compatibility when a newer daemon advertises one they do
795
+ // not understand yet.
796
+ capabilities: Schema.Array(Schema.String)
797
+ });
798
+ var ConfigStateSchema = Schema.Struct({
799
+ protocol: ProtocolInfoSchema,
800
+ config: ConfigSchema
801
+ });
802
+ var ConfigUpdateRequestSchema = Schema.Struct({
803
+ expectedRevision: NonNegativeIntegerSchema,
804
+ config: ConfigSchema
805
+ });
806
+ var ConfigUpdateConflictSchema = Schema.Struct({
807
+ kind: Schema.Literal("conflict"),
808
+ state: ConfigStateSchema
809
+ });
810
+ var ConfigPersistenceFailureSchema = Schema.Struct({
811
+ kind: Schema.Literal("persistence"),
812
+ message: Schema.String
813
+ });
814
+ var FsEntrySchema = Schema.Struct({
815
+ name: Schema.String,
816
+ path: Schema.String,
817
+ dir: Schema.Boolean
818
+ });
819
+ var FsListingSchema = Schema.Struct({
820
+ path: Schema.String,
821
+ parent: Schema.NullOr(Schema.String),
822
+ entries: Schema.Array(FsEntrySchema)
823
+ });
824
+ var UsageSummarySchema = Schema.Struct({
825
+ cost: Schema.Finite,
826
+ tokens: Schema.Finite,
827
+ cacheRead: Schema.Finite,
828
+ cacheSavings: Schema.Finite
829
+ });
830
+ var ModelDetailSchema = Schema.Struct({
831
+ name: Schema.String,
832
+ input: Schema.Finite,
833
+ output: Schema.Finite,
834
+ cacheCreate: Schema.Finite,
835
+ cacheRead: Schema.Finite,
836
+ cacheSavings: Schema.Finite,
837
+ cost: Schema.Finite,
838
+ count: Schema.Finite
839
+ });
840
+ var TableRowSchema = Schema.Struct({
841
+ label: Schema.String,
842
+ models: Schema.Array(Schema.String),
843
+ input: Schema.Finite,
844
+ output: Schema.Finite,
845
+ cacheCreate: Schema.Finite,
846
+ cacheRead: Schema.Finite,
847
+ cacheSavings: Schema.Finite,
848
+ total: Schema.Finite,
849
+ cost: Schema.Finite,
850
+ count: Schema.Finite,
851
+ breakdown: Schema.Array(ModelDetailSchema)
852
+ });
853
+ var DashboardDataSchema = Schema.Struct({
854
+ today: UsageSummarySchema,
855
+ week: UsageSummarySchema,
856
+ month: UsageSummarySchema,
857
+ burnRate: Schema.Finite,
858
+ series: Schema.Array(Schema.Finite)
859
+ });
860
+ var TableDataSchema = Schema.Struct({
861
+ daily: Schema.Array(TableRowSchema),
862
+ weekly: Schema.Array(TableRowSchema),
863
+ monthly: Schema.Array(TableRowSchema)
864
+ });
865
+ var MetricFormatSchema = Schema.Union([
866
+ Schema.Struct({ kind: Schema.Literal("percent") }),
867
+ Schema.Struct({ kind: Schema.Literal("dollars"), currency: Schema.optionalKey(Schema.String) }),
868
+ Schema.Struct({ kind: Schema.Literal("count"), suffix: Schema.optionalKey(Schema.String) })
869
+ ]);
870
+ var MetricSchema = Schema.Struct({
871
+ label: Schema.String,
872
+ used: Schema.Finite,
873
+ limit: Schema.NullOr(Schema.Finite),
874
+ format: MetricFormatSchema,
875
+ resetsAt: Schema.optionalKey(Schema.NullOr(Schema.String)),
876
+ primary: Schema.optionalKey(Schema.Boolean)
877
+ });
878
+ var BillingResultSchema = Schema.Struct({
879
+ plan: Schema.NullOr(Schema.String),
880
+ metrics: Schema.Array(MetricSchema),
881
+ error: Schema.NullOr(Schema.String),
882
+ email: Schema.optionalKey(Schema.NullOr(Schema.String)),
883
+ displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
884
+ activity: Schema.optionalKey(Schema.NullOr(Schema.Struct({
885
+ series: Schema.Array(Schema.Finite),
886
+ summary: Schema.String
887
+ }))),
888
+ modelSpend: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({
889
+ name: Schema.String,
890
+ usd: Schema.Finite,
891
+ requests: Schema.Finite
892
+ }))))
893
+ });
894
+ var WebAccountSchema = Schema.Struct({
895
+ id: Schema.String,
896
+ providerId: ProviderIdSchema,
897
+ name: Schema.String,
898
+ color: Schema.String,
899
+ homeDir: Schema.NullOr(Schema.String),
900
+ hasUsage: Schema.Boolean,
901
+ hasBilling: Schema.Boolean,
902
+ email: Schema.optionalKey(Schema.NullOr(Schema.String)),
903
+ displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
904
+ plan: Schema.optionalKey(Schema.NullOr(Schema.String)),
905
+ dashboard: Schema.NullOr(DashboardDataSchema),
906
+ table: Schema.NullOr(TableDataSchema),
907
+ billing: Schema.NullOr(BillingResultSchema),
908
+ summaryState: Schema.Literals(["pending", "ready", "error"]),
909
+ billingState: Schema.Literals(["pending", "ready", "error"]),
910
+ tableState: Schema.Literals(["pending", "ready", "error"]),
911
+ summaryUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
912
+ billingUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
913
+ tableUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema))
914
+ });
915
+ var WebSnapshotSchema = Schema.Struct({
916
+ version: Schema.String,
917
+ generatedAt: NonNegativeIntegerSchema,
918
+ tz: Schema.String,
919
+ intervalMs: PositiveFiniteSchema,
920
+ billingIntervalMs: Schema.optionalKey(PositiveFiniteSchema),
921
+ providers: Schema.Array(Schema.Struct({
922
+ id: ProviderIdSchema,
923
+ name: Schema.String,
924
+ color: Schema.String
925
+ })),
926
+ accounts: Schema.Array(WebAccountSchema),
927
+ seeded: Schema.Boolean,
928
+ peak: Schema.NullOr(Schema.Struct({
929
+ state: Schema.Literals(["peak", "off-peak", "weekend"]),
930
+ label: Schema.String,
931
+ // The upstream clock is only normalized to a finite number; do not make
932
+ // the wire contract narrower than the producer's declared type.
933
+ minutesUntilChange: Schema.NullOr(Schema.Finite)
934
+ }))
935
+ });
936
+ var EmptyPayloadSchema = Schema.Struct({});
937
+ var GetConfigRpc = Rpc.make(TOKMON_WS_METHODS.getConfig, {
938
+ payload: EmptyPayloadSchema,
939
+ success: ConfigStateSchema
940
+ });
941
+ var SetConfigRpc = Rpc.make(TOKMON_WS_METHODS.setConfig, {
942
+ payload: ConfigUpdateRequestSchema,
943
+ success: ConfigStateSchema,
944
+ error: Schema.Union([ConfigUpdateConflictSchema, ConfigPersistenceFailureSchema])
945
+ });
946
+ var RefreshRpc = Rpc.make(TOKMON_WS_METHODS.refresh, {
947
+ payload: Schema.Struct({ scope: RefreshScopeSchema }),
948
+ success: Schema.Void
949
+ });
950
+ var BrowseFsRpc = Rpc.make(TOKMON_WS_METHODS.browseFs, {
951
+ payload: Schema.Struct({ path: Schema.String }),
952
+ success: FsListingSchema
953
+ });
954
+ var SnapshotRpc = Rpc.make(TOKMON_WS_METHODS.snapshot, {
955
+ payload: EmptyPayloadSchema,
956
+ success: WebSnapshotSchema,
957
+ stream: true
958
+ });
959
+ var ConfigRpc = Rpc.make(TOKMON_WS_METHODS.config, {
960
+ payload: EmptyPayloadSchema,
961
+ success: ConfigStateSchema,
962
+ stream: true
963
+ });
964
+ var TokmonRpcGroup = RpcGroup.make(
965
+ GetConfigRpc,
966
+ SetConfigRpc,
967
+ RefreshRpc,
968
+ BrowseFsRpc,
969
+ SnapshotRpc,
970
+ ConfigRpc
971
+ );
972
+
729
973
  // src/shared/format.ts
730
974
  var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
731
975
  function formatCurrency(value, opts = {}) {
@@ -2758,7 +3002,7 @@ function ghConfigDir(homeDir) {
2758
3002
  function ghHostsPath(homeDir) {
2759
3003
  return join13(ghConfigDir(homeDir), "hosts.yml");
2760
3004
  }
2761
- async function detectCopilot(homeDir) {
3005
+ function copilotStateDirs(homeDir) {
2762
3006
  const home = homeDir ?? homedir9();
2763
3007
  const candidates = [
2764
3008
  join13(home, ".config", "github-copilot"),
@@ -2769,7 +3013,10 @@ async function detectCopilot(homeDir) {
2769
3013
  if (process.platform === "win32") {
2770
3014
  candidates.push(join13(envDir("LOCALAPPDATA") ?? join13(home, "AppData", "Local"), "github-copilot"));
2771
3015
  }
2772
- for (const path of candidates) {
3016
+ return candidates;
3017
+ }
3018
+ async function detectCopilot(homeDir) {
3019
+ for (const path of copilotStateDirs(homeDir)) {
2773
3020
  try {
2774
3021
  await access7(path);
2775
3022
  return true;
@@ -4294,550 +4541,42 @@ async function detectProviders() {
4294
4541
  return found.filter((id) => id !== null);
4295
4542
  }
4296
4543
 
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
- knownProviders: Schema.Array(ProviderIdSchema)
4635
- });
4636
- var ProtocolInfoSchema = Schema.Struct({
4637
- version: Schema.Literal(TOKMON_PROTOCOL_VERSION),
4638
- // Capabilities are additive within a protocol version. Older clients must
4639
- // preserve forward compatibility when a newer daemon advertises one they do
4640
- // not understand yet.
4641
- capabilities: Schema.Array(Schema.String)
4642
- });
4643
- var ConfigStateSchema = Schema.Struct({
4644
- protocol: ProtocolInfoSchema,
4645
- config: ConfigSchema
4646
- });
4647
- var ConfigUpdateRequestSchema = Schema.Struct({
4648
- expectedRevision: NonNegativeIntegerSchema,
4649
- config: ConfigSchema
4650
- });
4651
- var ConfigUpdateConflictSchema = Schema.Struct({
4652
- kind: Schema.Literal("conflict"),
4653
- state: ConfigStateSchema
4654
- });
4655
- var ConfigPersistenceFailureSchema = Schema.Struct({
4656
- kind: Schema.Literal("persistence"),
4657
- message: Schema.String
4658
- });
4659
- var FsEntrySchema = Schema.Struct({
4660
- name: Schema.String,
4661
- path: Schema.String,
4662
- dir: Schema.Boolean
4663
- });
4664
- var FsListingSchema = Schema.Struct({
4665
- path: Schema.String,
4666
- parent: Schema.NullOr(Schema.String),
4667
- entries: Schema.Array(FsEntrySchema)
4668
- });
4669
- var UsageSummarySchema = Schema.Struct({
4670
- cost: Schema.Finite,
4671
- tokens: Schema.Finite,
4672
- cacheRead: Schema.Finite,
4673
- cacheSavings: Schema.Finite
4674
- });
4675
- var ModelDetailSchema = Schema.Struct({
4676
- name: Schema.String,
4677
- input: Schema.Finite,
4678
- output: Schema.Finite,
4679
- cacheCreate: Schema.Finite,
4680
- cacheRead: Schema.Finite,
4681
- cacheSavings: Schema.Finite,
4682
- cost: Schema.Finite,
4683
- count: Schema.Finite
4684
- });
4685
- var TableRowSchema = Schema.Struct({
4686
- label: Schema.String,
4687
- models: Schema.Array(Schema.String),
4688
- input: Schema.Finite,
4689
- output: Schema.Finite,
4690
- cacheCreate: Schema.Finite,
4691
- cacheRead: Schema.Finite,
4692
- cacheSavings: Schema.Finite,
4693
- total: Schema.Finite,
4694
- cost: Schema.Finite,
4695
- count: Schema.Finite,
4696
- breakdown: Schema.Array(ModelDetailSchema)
4697
- });
4698
- var DashboardDataSchema = Schema.Struct({
4699
- today: UsageSummarySchema,
4700
- week: UsageSummarySchema,
4701
- month: UsageSummarySchema,
4702
- burnRate: Schema.Finite,
4703
- series: Schema.Array(Schema.Finite)
4704
- });
4705
- var TableDataSchema = Schema.Struct({
4706
- daily: Schema.Array(TableRowSchema),
4707
- weekly: Schema.Array(TableRowSchema),
4708
- monthly: Schema.Array(TableRowSchema)
4709
- });
4710
- var MetricFormatSchema = Schema.Union([
4711
- Schema.Struct({ kind: Schema.Literal("percent") }),
4712
- Schema.Struct({ kind: Schema.Literal("dollars"), currency: Schema.optionalKey(Schema.String) }),
4713
- Schema.Struct({ kind: Schema.Literal("count"), suffix: Schema.optionalKey(Schema.String) })
4714
- ]);
4715
- var MetricSchema = Schema.Struct({
4716
- label: Schema.String,
4717
- used: Schema.Finite,
4718
- limit: Schema.NullOr(Schema.Finite),
4719
- format: MetricFormatSchema,
4720
- resetsAt: Schema.optionalKey(Schema.NullOr(Schema.String)),
4721
- primary: Schema.optionalKey(Schema.Boolean)
4722
- });
4723
- var BillingResultSchema = Schema.Struct({
4724
- plan: Schema.NullOr(Schema.String),
4725
- metrics: Schema.Array(MetricSchema),
4726
- error: Schema.NullOr(Schema.String),
4727
- email: Schema.optionalKey(Schema.NullOr(Schema.String)),
4728
- displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
4729
- activity: Schema.optionalKey(Schema.NullOr(Schema.Struct({
4730
- series: Schema.Array(Schema.Finite),
4731
- summary: Schema.String
4732
- }))),
4733
- modelSpend: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({
4734
- name: Schema.String,
4735
- usd: Schema.Finite,
4736
- requests: Schema.Finite
4737
- }))))
4738
- });
4739
- var WebAccountSchema = Schema.Struct({
4740
- id: Schema.String,
4741
- providerId: ProviderIdSchema,
4742
- name: Schema.String,
4743
- color: Schema.String,
4744
- homeDir: Schema.NullOr(Schema.String),
4745
- hasUsage: Schema.Boolean,
4746
- hasBilling: Schema.Boolean,
4747
- email: Schema.optionalKey(Schema.NullOr(Schema.String)),
4748
- displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
4749
- plan: Schema.optionalKey(Schema.NullOr(Schema.String)),
4750
- dashboard: Schema.NullOr(DashboardDataSchema),
4751
- table: Schema.NullOr(TableDataSchema),
4752
- billing: Schema.NullOr(BillingResultSchema),
4753
- summaryState: Schema.Literals(["pending", "ready", "error"]),
4754
- billingState: Schema.Literals(["pending", "ready", "error"]),
4755
- tableState: Schema.Literals(["pending", "ready", "error"]),
4756
- summaryUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
4757
- billingUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
4758
- tableUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema))
4759
- });
4760
- var WebSnapshotSchema = Schema.Struct({
4761
- version: Schema.String,
4762
- generatedAt: NonNegativeIntegerSchema,
4763
- tz: Schema.String,
4764
- intervalMs: PositiveFiniteSchema,
4765
- billingIntervalMs: Schema.optionalKey(PositiveFiniteSchema),
4766
- providers: Schema.Array(Schema.Struct({
4767
- id: ProviderIdSchema,
4768
- name: Schema.String,
4769
- color: Schema.String
4770
- })),
4771
- accounts: Schema.Array(WebAccountSchema),
4772
- seeded: Schema.Boolean,
4773
- peak: Schema.NullOr(Schema.Struct({
4774
- state: Schema.Literals(["peak", "off-peak", "weekend"]),
4775
- label: Schema.String,
4776
- // The upstream clock is only normalized to a finite number; do not make
4777
- // the wire contract narrower than the producer's declared type.
4778
- minutesUntilChange: Schema.NullOr(Schema.Finite)
4779
- }))
4780
- });
4781
- var EmptyPayloadSchema = Schema.Struct({});
4782
- var GetConfigRpc = Rpc.make(TOKMON_WS_METHODS.getConfig, {
4783
- payload: EmptyPayloadSchema,
4784
- success: ConfigStateSchema
4785
- });
4786
- var SetConfigRpc = Rpc.make(TOKMON_WS_METHODS.setConfig, {
4787
- payload: ConfigUpdateRequestSchema,
4788
- success: ConfigStateSchema,
4789
- error: Schema.Union([ConfigUpdateConflictSchema, ConfigPersistenceFailureSchema])
4790
- });
4791
- var RefreshRpc = Rpc.make(TOKMON_WS_METHODS.refresh, {
4792
- payload: Schema.Struct({ scope: RefreshScopeSchema }),
4793
- success: Schema.Void
4794
- });
4795
- var BrowseFsRpc = Rpc.make(TOKMON_WS_METHODS.browseFs, {
4796
- payload: Schema.Struct({ path: Schema.String }),
4797
- success: FsListingSchema
4798
- });
4799
- var SnapshotRpc = Rpc.make(TOKMON_WS_METHODS.snapshot, {
4800
- payload: EmptyPayloadSchema,
4801
- success: WebSnapshotSchema,
4802
- stream: true
4803
- });
4804
- var ConfigRpc = Rpc.make(TOKMON_WS_METHODS.config, {
4805
- payload: EmptyPayloadSchema,
4806
- success: ConfigStateSchema,
4807
- stream: true
4808
- });
4809
- var TokmonRpcGroup = RpcGroup.make(
4810
- GetConfigRpc,
4811
- SetConfigRpc,
4812
- RefreshRpc,
4813
- BrowseFsRpc,
4814
- SnapshotRpc,
4815
- ConfigRpc
4816
- );
4817
-
4818
4544
  export {
4545
+ TOKMON_WS_PATH,
4546
+ TOKMON_PROTOCOL_VERSION,
4547
+ TOKMON_CAPABILITIES,
4548
+ TOKMON_WS_METHODS,
4549
+ TokmonRpcGroup,
4550
+ withTimeout,
4819
4551
  systemTimezone,
4820
4552
  resolveTimezone,
4553
+ dayKey,
4554
+ startOfMonth,
4555
+ startOfWeek,
4821
4556
  flushDisk,
4822
4557
  coalesceTables,
4558
+ claudeConfigDirs,
4823
4559
  currency,
4824
4560
  tokens,
4825
4561
  time,
4826
4562
  shortDate,
4827
4563
  col,
4564
+ readJson,
4565
+ readClaudeIdentity,
4566
+ codexHomes,
4567
+ identityFromIdToken,
4568
+ cursorStateDb,
4569
+ piSessionsDir,
4570
+ opencodeDbPaths,
4571
+ ghHostsPath,
4572
+ copilotStateDirs,
4573
+ antigravityStateDb,
4574
+ geminiTmpDir,
4575
+ geminiCredsPath,
4576
+ geminiDir,
4577
+ grokHomes,
4578
+ grokAuthPaths,
4828
4579
  PROVIDER_ORDER,
4829
4580
  PROVIDERS,
4830
- detectProviders,
4831
- buildAccounts,
4832
- accountsByProvider,
4833
- namedHex,
4834
- colorHex,
4835
- modelColor,
4836
- withTimeout,
4837
- fetchPeak,
4838
- TOKMON_WS_PATH,
4839
- TOKMON_PROTOCOL_VERSION,
4840
- TOKMON_CAPABILITIES,
4841
- TOKMON_WS_METHODS,
4842
- TokmonRpcGroup
4581
+ detectProviders
4843
4582
  };