tokmon 0.29.3 → 0.29.5

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 (35) hide show
  1. package/README.md +2 -1
  2. package/dist/{bootstrap-ink-MPWICEIJ.js → bootstrap-ink-VORCPVK4.js} +38 -21
  3. package/dist/{chunk-AUUBRLBG.js → chunk-3SXVUTV2.js} +2 -2
  4. package/dist/{chunk-BLUQGJKM.js → chunk-6NJLM5IZ.js} +2 -2
  5. package/dist/{chunk-BYLWQ46U.js → chunk-6RPYSOBA.js} +6 -3
  6. package/dist/{chunk-WPZTPMBX.js → chunk-EZHV4R74.js} +20 -8
  7. package/dist/{chunk-G2A3DVPU.js → chunk-GQCFGAQ6.js} +40 -10
  8. package/dist/{chunk-5Q5HUOP4.js → chunk-LAJV5JMV.js} +160 -133
  9. package/dist/{chunk-3D6TO5TS.js → chunk-U2FQXNIU.js} +98 -66
  10. package/dist/{chunk-YWDMEU3L.js → chunk-UD2IPQK3.js} +1 -1
  11. package/dist/{cli-command-WL2K5WWR.js → cli-command-X75NGQON.js} +6 -6
  12. package/dist/cli.js +6 -6
  13. package/dist/{config-B7QJQTU2.js → config-4JC6X5QT.js} +3 -1
  14. package/dist/{daemon-A3WGDRFT.js → daemon-O7TOISBH.js} +6 -6
  15. package/dist/daemon-handle-NBGZUDKE.js +11 -0
  16. package/dist/server-VWBW53QU.js +12 -0
  17. package/dist/web/assets/{Area-CEpf7_6d.js → Area-B3FdWS80.js} +1 -1
  18. package/dist/web/assets/{analytics-CdjcYtmK.js → analytics-DSLBg5Lx.js} +2 -2
  19. package/dist/web/assets/{breakdown-DM6g0evr.js → breakdown-B1vg0Xn1.js} +1 -1
  20. package/dist/web/assets/{button-Cu4co7ZR.js → button-CDipMD0w.js} +1 -1
  21. package/dist/web/assets/{chart-YlnVH1If.js → chart-BRXIwVtE.js} +1 -1
  22. package/dist/web/assets/{explore-kLpZWR-_.js → explore-DyJiyG0m.js} +1 -1
  23. package/dist/web/assets/index-CEXpTvdh.js +83 -0
  24. package/dist/web/assets/{models-XKfoWTDv.js → models-_DeHctpR.js} +2 -2
  25. package/dist/web/assets/{overview-DC-WVKZ6.js → overview-CmPN-0w3.js} +2 -2
  26. package/dist/web/assets/{panel-B_U9oe1x.js → panel-DzILyYlc.js} +1 -1
  27. package/dist/web/assets/settings-sheet-AfoLwoCy.js +1 -0
  28. package/dist/web/assets/{share-sheet-BaEXe1m9.js → share-sheet-Cfl7PIrk.js} +1 -1
  29. package/dist/web/assets/{timeline-ar9mdhcb.js → timeline-D_JPTxAw.js} +1 -1
  30. package/dist/web/index.html +1 -1
  31. package/package.json +1 -1
  32. package/dist/daemon-handle-4E7RSSDB.js +0 -11
  33. package/dist/server-P6BRXBZ5.js +0 -12
  34. package/dist/web/assets/index-Dd37pBKy.js +0 -83
  35. package/dist/web/assets/settings-sheet-B7BuJ3j8.js +0 -1
package/README.md CHANGED
@@ -68,7 +68,8 @@ progress lines, and comfortable, compact, or tight spacing. Auto layout adapts
68
68
  to smaller displays; Custom mode exposes half-point edge and gap controls.
69
69
  Clicking the item opens a compact view of every enabled provider and account,
70
70
  including usage, reset times, token totals, spend, cache savings, and recent
71
- history.
71
+ history. The Desktop App settings can also launch Tokmon silently at login on
72
+ macOS and Windows, with native approval state shown in the app.
72
73
 
73
74
  ![Tokmon desktop quota popover](assets/desktop/tray-popover.png)
74
75
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createDaemonRpcClient
4
- } from "./chunk-AUUBRLBG.js";
4
+ } from "./chunk-3SXVUTV2.js";
5
5
  import {
6
6
  accountIdentityText,
7
7
  accountsByProvider,
@@ -11,14 +11,15 @@ import {
11
11
  resolveQuotaViews,
12
12
  severity,
13
13
  usageFromHeadroom
14
- } from "./chunk-3D6TO5TS.js";
14
+ } from "./chunk-U2FQXNIU.js";
15
15
  import {
16
16
  PROVIDERS,
17
17
  PROVIDER_ORDER,
18
18
  coalesceTables,
19
19
  col,
20
20
  currency,
21
- detectProviders,
21
+ detectAccountProviders,
22
+ detectInstalledProviders,
22
23
  formatAgo,
23
24
  formatResetAt,
24
25
  resetAt,
@@ -28,8 +29,8 @@ import {
28
29
  time,
29
30
  tokens,
30
31
  withTimeout
31
- } from "./chunk-WPZTPMBX.js";
32
- import "./chunk-BYLWQ46U.js";
32
+ } from "./chunk-EZHV4R74.js";
33
+ import "./chunk-6RPYSOBA.js";
33
34
  import {
34
35
  COLOR_PALETTE,
35
36
  DEFAULTS,
@@ -55,7 +56,7 @@ import {
55
56
  setProviderTrackingEnabled,
56
57
  snapshotCacheFile,
57
58
  themePresetOption
58
- } from "./chunk-G2A3DVPU.js";
59
+ } from "./chunk-GQCFGAQ6.js";
59
60
  import {
60
61
  glyphs
61
62
  } from "./chunk-RF4GGQGM.js";
@@ -1605,7 +1606,7 @@ var SettingsView = memo4(function SettingsView2({
1605
1606
  const identityLabel = config.privacyMode ? redactEmail(rawIdentityLabel) : rawIdentityLabel;
1606
1607
  const plan = identity?.plan ?? null;
1607
1608
  const ignored = acc.source === "ignored";
1608
- const sourceLabel = acc.source === "auto" ? "auto tracking" : ignored ? "ignored" : "configured";
1609
+ const sourceLabel = acc.source === "auto" ? "auto tracking" : ignored ? "removed" : acc.enabled ? "configured" : "disabled";
1609
1610
  return /* @__PURE__ */ jsxs6(Box6, { children: [
1610
1611
  /* @__PURE__ */ jsxs6(Text6, { color: selected ? theme.accent : void 0, children: [
1611
1612
  selected ? glyphs().caretR : " ",
@@ -1615,7 +1616,7 @@ var SettingsView = memo4(function SettingsView2({
1615
1616
  ignored ? glyphs().warn : isActive ? glyphs().dot : glyphs().radioOff,
1616
1617
  " "
1617
1618
  ] }),
1618
- /* @__PURE__ */ jsx7(Box6, { width: 28, children: /* @__PURE__ */ jsx7(Text6, { bold: !ignored, dimColor: ignored, children: truncateName(identityLabel, 27) }) }),
1619
+ /* @__PURE__ */ jsx7(Box6, { width: 28, children: /* @__PURE__ */ jsx7(Text6, { bold: !ignored && acc.enabled, dimColor: ignored || !acc.enabled, children: truncateName(identityLabel, 27) }) }),
1619
1620
  /* @__PURE__ */ jsx7(Box6, { width: 9, children: /* @__PURE__ */ jsx7(Text6, { color: provider.color, children: provider.name }) }),
1620
1621
  /* @__PURE__ */ jsx7(Box6, { width: 18, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: plan ? truncateName(plan, 17) : "" }) }),
1621
1622
  /* @__PURE__ */ jsx7(Box6, { width: 14, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: sourceLabel }) }),
@@ -1699,7 +1700,7 @@ var SettingsView = memo4(function SettingsView2({
1699
1700
  glyphs().middot,
1700
1701
  " space activate ",
1701
1702
  glyphs().middot,
1702
- " x ignore ",
1703
+ " x remove ",
1703
1704
  glyphs().middot,
1704
1705
  " s/Esc close"
1705
1706
  ] }) : trackedAccounts[cursor]?.source === "ignored" ? /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
@@ -1707,7 +1708,7 @@ var SettingsView = memo4(function SettingsView2({
1707
1708
  glyphs().arrowD,
1708
1709
  " select ",
1709
1710
  glyphs().middot,
1710
- " Enter/x turn on ",
1711
+ " Enter/x restore ",
1711
1712
  glyphs().middot,
1712
1713
  " s/Esc close"
1713
1714
  ] }) : /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
@@ -1723,7 +1724,7 @@ var SettingsView = memo4(function SettingsView2({
1723
1724
  glyphs().middot,
1724
1725
  " Enter edit ",
1725
1726
  glyphs().middot,
1726
- " space activate ",
1727
+ " e enable/disable ",
1727
1728
  glyphs().middot,
1728
1729
  " d delete ",
1729
1730
  glyphs().middot,
@@ -2775,6 +2776,14 @@ function handleSettings(input, key, ctx) {
2775
2776
  deleteAccount(row.explicitId);
2776
2777
  return;
2777
2778
  }
2779
+ if (row.source === "configured" && row.explicitId && input === "e") {
2780
+ updateConfig((current) => ({
2781
+ ...current,
2782
+ activeAccountId: !row.enabled || current.activeAccountId !== row.id ? current.activeAccountId : null,
2783
+ accounts: current.accounts.map((account) => account.id === row.explicitId ? { ...account, enabled: !row.enabled } : account)
2784
+ }));
2785
+ return;
2786
+ }
2778
2787
  if (row.source === "auto" && input === "x") {
2779
2788
  updateConfig((current) => ({
2780
2789
  ...current,
@@ -2786,7 +2795,7 @@ function handleSettings(input, key, ctx) {
2786
2795
  }));
2787
2796
  return;
2788
2797
  }
2789
- if (input === " ") {
2798
+ if (input === " " && row.enabled) {
2790
2799
  updateConfig((current) => ({ ...current, activeAccountId: row.id }));
2791
2800
  return;
2792
2801
  }
@@ -3729,6 +3738,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3729
3738
  daemon
3730
3739
  });
3731
3740
  const [detected, setDetected] = useState10([]);
3741
+ const [detectedAccounts, setDetectedAccounts] = useState10([]);
3732
3742
  const [tab, setTab] = useState10(0);
3733
3743
  const [view, setView] = useState10(0);
3734
3744
  const [cursor, setCursor] = useState10(0);
@@ -3768,17 +3778,22 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3768
3778
  () => [...new Set((snapshot?.accounts ?? []).map((account) => account.providerId))],
3769
3779
  [snapshot]
3770
3780
  );
3771
- const detectedProviders = connected ? daemonDetected : detected;
3781
+ const detectedProviders = useMemo3(
3782
+ () => [.../* @__PURE__ */ new Set([...snapshot?.installedProviders ?? detected, ...daemonDetected])],
3783
+ [daemonDetected, detected, snapshot?.installedProviders]
3784
+ );
3785
+ const detectedAccountProviders = connected ? daemonDetected : detectedAccounts;
3772
3786
  const accounts = useMemo3(() => connected ? (snapshot?.accounts ?? []).map((account) => ({
3773
3787
  id: account.id,
3774
3788
  providerId: account.providerId,
3775
3789
  name: account.name,
3776
3790
  color: account.color,
3777
- homeDir: account.homeDir ?? void 0
3778
- })) : buildAccounts(cfg, detectedProviders), [cfg, connected, detectedProviders, snapshot]);
3791
+ homeDir: account.homeDir ?? void 0,
3792
+ source: account.source
3793
+ })) : buildAccounts(cfg, detectedAccountProviders), [cfg, connected, detectedAccountProviders, snapshot]);
3779
3794
  const trackedAccountRows = useMemo3(
3780
- () => getTrackedAccountRows(cfg, detectedProviders, accounts),
3781
- [cfg, detectedProviders, accounts]
3795
+ () => getTrackedAccountRows(cfg, detectedAccountProviders, accounts),
3796
+ [cfg, detectedAccountProviders, accounts]
3782
3797
  );
3783
3798
  const settingsRowCount = settingsTab === "general" ? GENERAL_ROWS : settingsTab === "theme" ? THEME_ROWS : settingsTab === "desktop" ? DESKTOP_FIXED_ROWS : settingsTab === "providers" ? PROVIDER_ORDER.length + 1 : trackedAccountRows.length + 1;
3784
3799
  const accountsRef = useRef8([]);
@@ -3955,9 +3970,11 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3955
3970
  enabled: onboardEnabled.includes(pid)
3956
3971
  }));
3957
3972
  useEffect9(() => {
3958
- if (connected) return;
3959
- void detectProviders().then(setDetected);
3960
- }, [connected]);
3973
+ void Promise.all([detectInstalledProviders(), detectAccountProviders()]).then(([installed, accounts2]) => {
3974
+ setDetected(installed);
3975
+ setDetectedAccounts(accounts2);
3976
+ });
3977
+ }, []);
3961
3978
  const tableKey = useMemo3(
3962
3979
  () => `${effTableProvider}|${tableAccounts.map(acctKey).join(",")}|${tz}`,
3963
3980
  [effTableProvider, tableAccounts, tz]
@@ -4088,7 +4105,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
4088
4105
  if (webStartingRef.current) return;
4089
4106
  webStartingRef.current = true;
4090
4107
  try {
4091
- const { startWebServer } = await import("./server-P6BRXBZ5.js");
4108
+ const { startWebServer } = await import("./server-VWBW53QU.js");
4092
4109
  const ctrl = await startWebServer({ config: cfg, log: false });
4093
4110
  webRef.current = ctrl;
4094
4111
  openUrl(ctrl.url);
@@ -4,10 +4,10 @@ import {
4
4
  TOKMON_WS_METHODS,
5
5
  TOKMON_WS_PATH,
6
6
  TokmonRpcGroup
7
- } from "./chunk-BYLWQ46U.js";
7
+ } from "./chunk-6RPYSOBA.js";
8
8
  import {
9
9
  normalizeConfig
10
- } from "./chunk-G2A3DVPU.js";
10
+ } from "./chunk-GQCFGAQ6.js";
11
11
 
12
12
  // src/client/daemon-rpc-client.ts
13
13
  import { Cause, Context, Duration, Effect, Exit, Fiber, Layer, ManagedRuntime, Schedule, Schema, Stream } from "effect";
@@ -8,14 +8,14 @@ import {
8
8
  readLock,
9
9
  retireIncompatibleCliOwner,
10
10
  verifyLock
11
- } from "./chunk-YWDMEU3L.js";
11
+ } from "./chunk-UD2IPQK3.js";
12
12
  import {
13
13
  daemonChannelFromWire,
14
14
  resolveDaemonChannel
15
15
  } from "./chunk-UYPDMVW5.js";
16
16
  import {
17
17
  TOKMON_PROTOCOL_VERSION
18
- } from "./chunk-BYLWQ46U.js";
18
+ } from "./chunk-6RPYSOBA.js";
19
19
 
20
20
  // src/client/daemon-handle.ts
21
21
  import { spawn } from "child_process";
@@ -4,7 +4,7 @@ import {
4
4
  DESKTOP_GRAPH_RANGES,
5
5
  PROVIDER_IDS,
6
6
  THEME_PRESET_IDS
7
- } from "./chunk-G2A3DVPU.js";
7
+ } from "./chunk-GQCFGAQ6.js";
8
8
 
9
9
  // src/rpc/contract.ts
10
10
  import { Schema } from "effect";
@@ -12,7 +12,7 @@ import * as Rpc from "effect/unstable/rpc/Rpc";
12
12
  import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
13
13
  var TOKMON_WS_PATH = "/ws";
14
14
  var TOKMON_PROTOCOL_VERSION = 4;
15
- var TOKMON_CAPABILITIES = ["config-cas", "config-revision", "allowed-hosts", "tray-config", "usage-activity", "tray-pins", "provider-pins", "desktop-disclosure", "desktop-graph-range", "provider-headroom", "canonical-identity", "appearance-v1", "theme-engine", "account-detection-v1", "menu-bar-today-tokens", "menu-bar-builder-v1"];
15
+ var TOKMON_CAPABILITIES = ["config-cas", "config-revision", "allowed-hosts", "tray-config", "usage-activity", "tray-pins", "provider-pins", "desktop-disclosure", "desktop-graph-range", "provider-headroom", "canonical-identity", "appearance-v1", "theme-engine", "account-detection-v1", "account-provenance-v1", "installed-harnesses-v1", "discovery-refresh-v1", "menu-bar-today-tokens", "menu-bar-builder-v1"];
16
16
  var TOKMON_WS_METHODS = {
17
17
  getConfig: "tokmon.getConfig",
18
18
  setConfig: "tokmon.setConfig",
@@ -36,7 +36,8 @@ var AccountSchema = Schema.Struct({
36
36
  providerId: ProviderIdSchema,
37
37
  name: Schema.String,
38
38
  homeDir: Schema.String,
39
- color: Schema.optionalKey(Schema.String)
39
+ color: Schema.optionalKey(Schema.String),
40
+ enabled: Schema.optionalKey(Schema.Boolean)
40
41
  });
41
42
  var TrayConfigSchema = Schema.Struct({
42
43
  enabled: Schema.Boolean,
@@ -339,6 +340,7 @@ var WebAccountSchema = Schema.Struct({
339
340
  name: Schema.String,
340
341
  color: Schema.String,
341
342
  homeDir: Schema.NullOr(Schema.String),
343
+ source: Schema.optionalKey(Schema.Literals(["auto", "configured"])),
342
344
  hasUsage: Schema.Boolean,
343
345
  hasBilling: Schema.Boolean,
344
346
  email: Schema.optionalKey(Schema.NullOr(Schema.String)),
@@ -364,6 +366,7 @@ var WebSnapshotSchema = Schema.Struct({
364
366
  tz: Schema.String,
365
367
  intervalMs: PositiveFiniteSchema,
366
368
  billingIntervalMs: Schema.optionalKey(PositiveFiniteSchema),
369
+ installedProviders: Schema.optionalKey(Schema.Array(ProviderIdSchema)),
367
370
  providers: Schema.Array(Schema.Struct({
368
371
  id: ProviderIdSchema,
369
372
  name: Schema.String,
@@ -5,7 +5,7 @@ import {
5
5
  envDir,
6
6
  expandHome,
7
7
  isValidTimezone
8
- } from "./chunk-G2A3DVPU.js";
8
+ } from "./chunk-GQCFGAQ6.js";
9
9
 
10
10
  // src/async.ts
11
11
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -901,13 +901,19 @@ function getClaudeDirs(homeDir) {
901
901
  return claudeConfigDirs(homeDir).map((d) => join3(d, "projects"));
902
902
  }
903
903
  async function detectClaude(homeDir) {
904
- for (const dir of getClaudeDirs(homeDir)) {
904
+ for (const configDir of claudeConfigDirs(homeDir)) {
905
905
  try {
906
- await access(dir);
906
+ await access(join3(configDir, ".credentials.json"));
907
907
  return true;
908
908
  } catch {
909
909
  }
910
910
  }
911
+ for (const dir of getClaudeDirs(homeDir)) {
912
+ try {
913
+ if ((await walkFiles(dir)).some((file) => file.endsWith(".jsonl"))) return true;
914
+ } catch {
915
+ }
916
+ }
911
917
  return false;
912
918
  }
913
919
  function claudePriceFor(model, timestamp = Date.now()) {
@@ -4069,7 +4075,7 @@ function priceFor(model) {
4069
4075
  }
4070
4076
  async function detectGrok(homeDir) {
4071
4077
  for (const home of grokHomes(homeDir)) {
4072
- for (const p of [join19(home, "logs", "unified.jsonl"), join19(home, "sessions"), join19(home, "auth.json"), join19(home, "bin", "grok")]) {
4078
+ for (const p of [join19(home, "logs", "unified.jsonl"), join19(home, "sessions"), join19(home, "auth.json")]) {
4073
4079
  try {
4074
4080
  await access10(p);
4075
4081
  return true;
@@ -4373,12 +4379,11 @@ var PROVIDERS = {
4373
4379
  gemini: geminiProvider,
4374
4380
  grok: grokProvider
4375
4381
  };
4376
- async function detectProviders() {
4382
+ async function detectWhere(predicate) {
4377
4383
  const found = await Promise.all(
4378
4384
  PROVIDER_ORDER.map(async (id) => {
4379
4385
  try {
4380
- if (installSignals(id)) return id;
4381
- return await PROVIDERS[id].detect() ? id : null;
4386
+ return await predicate(id) ? id : null;
4382
4387
  } catch {
4383
4388
  return null;
4384
4389
  }
@@ -4386,6 +4391,12 @@ async function detectProviders() {
4386
4391
  );
4387
4392
  return found.filter((id) => id !== null);
4388
4393
  }
4394
+ function detectInstalledProviders() {
4395
+ return detectWhere(async (id) => installSignals(id) || await PROVIDERS[id].detect());
4396
+ }
4397
+ function detectAccountProviders() {
4398
+ return detectWhere((id) => PROVIDERS[id].detect());
4399
+ }
4389
4400
 
4390
4401
  export {
4391
4402
  withTimeout,
@@ -4422,5 +4433,6 @@ export {
4422
4433
  grokAuthPaths,
4423
4434
  PROVIDER_ORDER,
4424
4435
  PROVIDERS,
4425
- detectProviders
4436
+ detectInstalledProviders,
4437
+ detectAccountProviders
4426
4438
  };
@@ -3,7 +3,7 @@
3
3
  // src/config.ts
4
4
  import { chmod, readFile, writeFile, mkdir, rename, unlink } from "fs/promises";
5
5
  import { chmodSync, mkdirSync, renameSync, unlinkSync, writeFileSync } from "fs";
6
- import { join, isAbsolute } from "path";
6
+ import { join, isAbsolute, resolve } from "path";
7
7
  import { homedir } from "os";
8
8
 
9
9
  // src/providers/types.ts
@@ -613,6 +613,7 @@ function getTrackedAccountRows(config, trackedProviders = PROVIDER_ORDER.filter(
613
613
  const excludedKeys = new Set(
614
614
  config.accountDetection.excludedAccounts.map((ref) => keyFor(ref.providerId, ref.homeDir))
615
615
  );
616
+ const liveById = new Map((autoAccounts ?? []).map((account) => [account.id, account]));
616
617
  const rememberRow = (row) => {
617
618
  rowIds.add(row.id);
618
619
  rowKeys.add(keyFor(row.providerId, row.homeDir));
@@ -622,6 +623,10 @@ function getTrackedAccountRows(config, trackedProviders = PROVIDER_ORDER.filter(
622
623
  const meta = PROVIDER_META[account.providerId];
623
624
  configuredIds.add(account.id);
624
625
  configuredKeys.add(keyFor(account.providerId, account.homeDir));
626
+ const live = liveById.get(account.id);
627
+ if (live) {
628
+ configuredKeys.add(keyFor(account.providerId, live.homeDir));
629
+ }
625
630
  rememberRow({
626
631
  id: account.id,
627
632
  providerId: account.providerId,
@@ -629,6 +634,7 @@ function getTrackedAccountRows(config, trackedProviders = PROVIDER_ORDER.filter(
629
634
  homeDir: account.homeDir || "~",
630
635
  color: account.color || meta.color,
631
636
  source: "configured",
637
+ enabled: account.enabled !== false,
632
638
  explicitId: account.id,
633
639
  explicitIndex
634
640
  });
@@ -647,11 +653,12 @@ function getTrackedAccountRows(config, trackedProviders = PROVIDER_ORDER.filter(
647
653
  name: account.name,
648
654
  homeDir: account.homeDir || "~",
649
655
  color: account.color || meta.color,
650
- source: "auto"
656
+ source: "auto",
657
+ enabled: true
651
658
  });
652
659
  }
653
660
  }
654
- for (const providerId of PROVIDER_ORDER) {
661
+ if (autoAccounts === void 0) for (const providerId of PROVIDER_ORDER) {
655
662
  if (!detectionEnabled || detectorDisabled.has(providerId)) continue;
656
663
  if (config.disabledProviders.includes(providerId)) continue;
657
664
  if (!tracked.has(providerId)) continue;
@@ -665,7 +672,8 @@ function getTrackedAccountRows(config, trackedProviders = PROVIDER_ORDER.filter(
665
672
  name: meta.name,
666
673
  homeDir: "~",
667
674
  color: meta.color,
668
- source: "auto"
675
+ source: "auto",
676
+ enabled: true
669
677
  });
670
678
  }
671
679
  for (const ref of config.accountDetection.excludedAccounts) {
@@ -678,6 +686,7 @@ function getTrackedAccountRows(config, trackedProviders = PROVIDER_ORDER.filter(
678
686
  homeDir: ref.homeDir,
679
687
  color: meta.color,
680
688
  source: "ignored",
689
+ enabled: false,
681
690
  excludedRef: ref
682
691
  });
683
692
  }
@@ -840,7 +849,8 @@ function repairConfig(input) {
840
849
  providerId,
841
850
  name,
842
851
  homeDir: typeof raw.homeDir === "string" && raw.homeDir.trim() ? raw.homeDir : "~",
843
- ...typeof raw.color === "string" && raw.color.trim() ? { color: raw.color } : {}
852
+ ...typeof raw.color === "string" && raw.color.trim() ? { color: raw.color } : {},
853
+ ...raw.enabled === false ? { enabled: false } : {}
844
854
  });
845
855
  });
846
856
  const disabledProviders = (Array.isArray(parsed.disabledProviders) ? parsed.disabledProviders : []).filter(validProvider);
@@ -929,7 +939,7 @@ function repairConfig(input) {
929
939
  graphRangeDays: DESKTOP_GRAPH_RANGES.includes(rawDesktop.graphRangeDays) ? rawDesktop.graphRangeDays : DEFAULT_DESKTOP_CONFIG.graphRangeDays
930
940
  };
931
941
  if (parsed.desktop !== void 0 && !sameJson(parsed.desktop, desktop)) reasons.push("desktop contained invalid settings");
932
- const activeAccountId = typeof parsed.activeAccountId === "string" && (accountIds.has(parsed.activeAccountId) || PROVIDER_IDS.includes(parsed.activeAccountId)) ? parsed.activeAccountId : null;
942
+ const activeAccountId = typeof parsed.activeAccountId === "string" && parsed.activeAccountId.trim() ? parsed.activeAccountId.trim() : null;
933
943
  if (parsed.activeAccountId !== void 0 && parsed.activeAccountId !== null && activeAccountId === null) {
934
944
  reasons.push("activeAccountId did not match a known account/provider");
935
945
  }
@@ -1059,13 +1069,14 @@ async function loadConfig() {
1059
1069
  return { ...DEFAULTS };
1060
1070
  }
1061
1071
  const repaired = repairConfig(parsed);
1062
- if (repaired.repaired) {
1072
+ const config = canonicalizeConfigHomeRefs(repaired.config);
1073
+ if (repaired.repaired || JSON.stringify(config) !== JSON.stringify(repaired.config)) {
1063
1074
  try {
1064
- await saveConfig(repaired.config);
1075
+ await saveConfig(config);
1065
1076
  } catch {
1066
1077
  }
1067
1078
  }
1068
- return repaired.config;
1079
+ return config;
1069
1080
  }
1070
1081
  var saveQueue = Promise.resolve();
1071
1082
  var tempSequence = 0;
@@ -1074,7 +1085,7 @@ function configTempFile(dir) {
1074
1085
  return join(dir, `config.json.${process.pid}.${Date.now()}.${tempSequence}.tmp`);
1075
1086
  }
1076
1087
  function configJson(config) {
1077
- return JSON.stringify(normalizeConfig(config), null, 2) + "\n";
1088
+ return JSON.stringify(canonicalizeConfigHomeRefs(normalizeConfig(config)), null, 2) + "\n";
1078
1089
  }
1079
1090
  function saveConfig(config) {
1080
1091
  const write = async () => {
@@ -1121,6 +1132,24 @@ function expandHome(p) {
1121
1132
  if (p.startsWith("~/") || p.startsWith("~\\")) return join(homedir(), p.slice(2));
1122
1133
  return p;
1123
1134
  }
1135
+ function canonicalizeConfigHomeRefs(config) {
1136
+ const defaultHome = resolve(homedir());
1137
+ const canonical = (value) => resolve(expandHome(value)) === defaultHome ? "~" : value;
1138
+ return {
1139
+ ...config,
1140
+ accounts: config.accounts.map((account) => ({
1141
+ ...account,
1142
+ homeDir: canonical(account.homeDir || "~")
1143
+ })),
1144
+ accountDetection: {
1145
+ ...config.accountDetection,
1146
+ excludedAccounts: config.accountDetection.excludedAccounts.map((ref) => ({
1147
+ ...ref,
1148
+ homeDir: canonical(ref.homeDir)
1149
+ }))
1150
+ }
1151
+ };
1152
+ }
1124
1153
  function findAccount(config, id) {
1125
1154
  if (!id) return null;
1126
1155
  return config.accounts.find((a) => a.id === id) ?? null;
@@ -1171,5 +1200,6 @@ export {
1171
1200
  saveConfig,
1172
1201
  saveConfigSync,
1173
1202
  expandHome,
1203
+ canonicalizeConfigHomeRefs,
1174
1204
  findAccount
1175
1205
  };