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.
- package/README.md +2 -1
- package/dist/{bootstrap-ink-MPWICEIJ.js → bootstrap-ink-VORCPVK4.js} +38 -21
- package/dist/{chunk-AUUBRLBG.js → chunk-3SXVUTV2.js} +2 -2
- package/dist/{chunk-BLUQGJKM.js → chunk-6NJLM5IZ.js} +2 -2
- package/dist/{chunk-BYLWQ46U.js → chunk-6RPYSOBA.js} +6 -3
- package/dist/{chunk-WPZTPMBX.js → chunk-EZHV4R74.js} +20 -8
- package/dist/{chunk-G2A3DVPU.js → chunk-GQCFGAQ6.js} +40 -10
- package/dist/{chunk-5Q5HUOP4.js → chunk-LAJV5JMV.js} +160 -133
- package/dist/{chunk-3D6TO5TS.js → chunk-U2FQXNIU.js} +98 -66
- package/dist/{chunk-YWDMEU3L.js → chunk-UD2IPQK3.js} +1 -1
- package/dist/{cli-command-WL2K5WWR.js → cli-command-X75NGQON.js} +6 -6
- package/dist/cli.js +6 -6
- package/dist/{config-B7QJQTU2.js → config-4JC6X5QT.js} +3 -1
- package/dist/{daemon-A3WGDRFT.js → daemon-O7TOISBH.js} +6 -6
- package/dist/daemon-handle-NBGZUDKE.js +11 -0
- package/dist/server-VWBW53QU.js +12 -0
- package/dist/web/assets/{Area-CEpf7_6d.js → Area-B3FdWS80.js} +1 -1
- package/dist/web/assets/{analytics-CdjcYtmK.js → analytics-DSLBg5Lx.js} +2 -2
- package/dist/web/assets/{breakdown-DM6g0evr.js → breakdown-B1vg0Xn1.js} +1 -1
- package/dist/web/assets/{button-Cu4co7ZR.js → button-CDipMD0w.js} +1 -1
- package/dist/web/assets/{chart-YlnVH1If.js → chart-BRXIwVtE.js} +1 -1
- package/dist/web/assets/{explore-kLpZWR-_.js → explore-DyJiyG0m.js} +1 -1
- package/dist/web/assets/index-CEXpTvdh.js +83 -0
- package/dist/web/assets/{models-XKfoWTDv.js → models-_DeHctpR.js} +2 -2
- package/dist/web/assets/{overview-DC-WVKZ6.js → overview-CmPN-0w3.js} +2 -2
- package/dist/web/assets/{panel-B_U9oe1x.js → panel-DzILyYlc.js} +1 -1
- package/dist/web/assets/settings-sheet-AfoLwoCy.js +1 -0
- package/dist/web/assets/{share-sheet-BaEXe1m9.js → share-sheet-Cfl7PIrk.js} +1 -1
- package/dist/web/assets/{timeline-ar9mdhcb.js → timeline-D_JPTxAw.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/daemon-handle-4E7RSSDB.js +0 -11
- package/dist/server-P6BRXBZ5.js +0 -12
- package/dist/web/assets/index-Dd37pBKy.js +0 -83
- 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
|

|
|
74
75
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createDaemonRpcClient
|
|
4
|
-
} from "./chunk-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
32
|
-
import "./chunk-
|
|
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-
|
|
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 ? "
|
|
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
|
|
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
|
|
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
|
-
"
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
3781
|
-
[cfg,
|
|
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
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
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-
|
|
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-
|
|
7
|
+
} from "./chunk-6RPYSOBA.js";
|
|
8
8
|
import {
|
|
9
9
|
normalizeConfig
|
|
10
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
904
|
+
for (const configDir of claudeConfigDirs(homeDir)) {
|
|
905
905
|
try {
|
|
906
|
-
await access(
|
|
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")
|
|
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
|
|
4382
|
+
async function detectWhere(predicate) {
|
|
4377
4383
|
const found = await Promise.all(
|
|
4378
4384
|
PROVIDER_ORDER.map(async (id) => {
|
|
4379
4385
|
try {
|
|
4380
|
-
|
|
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
|
-
|
|
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" &&
|
|
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
|
-
|
|
1072
|
+
const config = canonicalizeConfigHomeRefs(repaired.config);
|
|
1073
|
+
if (repaired.repaired || JSON.stringify(config) !== JSON.stringify(repaired.config)) {
|
|
1063
1074
|
try {
|
|
1064
|
-
await saveConfig(
|
|
1075
|
+
await saveConfig(config);
|
|
1065
1076
|
} catch {
|
|
1066
1077
|
}
|
|
1067
1078
|
}
|
|
1068
|
-
return
|
|
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
|
};
|