tokentracker-cli 0.67.0 → 0.68.1
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.ja.md +2 -0
- package/README.ko.md +2 -0
- package/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/dashboard/dist/assets/{ActivityHeatmap-BLL6xjLG.js → ActivityHeatmap-stlH952K.js} +1 -1
- package/dashboard/dist/assets/{Card-BB6LiYM8.js → Card-CofgedFz.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-CX5gv4tk.js → ConfirmModal-n09o_RU7.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-DMvTS8wX.js → DashboardPage-iBPCT4vZ.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-vyJEBiMw.js → DevicePage-BGkZILd3.js} +1 -1
- package/dashboard/dist/assets/{DialogTitle-R2g9r9PR.js → DialogTitle-BbxEzA03.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-CQUbHzNN.js → FadeIn-E5ehYYk1.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-FwJX-pXB.js → HeaderGithubStar-CndrILKO.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-D_-1u2LS.js → IpCheckPage-5E_xMiUW.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-C-lWMwXP.js → LandingPage-CgTigoW1.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardAvatar-DskcTB9C.js → LeaderboardAvatar-Dvo78IQE.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-BjS-vLBW.js → LeaderboardPage-C4ksvNko.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-C6k4VVFS.js → LeaderboardProfileModal-UHYujOQG.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-o4fbDnzy.js → LeaderboardProfilePage-CNXemSOF.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-qv3ovWi8.js → LimitsPage-DHAbTe14.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-C1qyMDpn.js → LocalOnlyNotice-DzocMcPr.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-BKNFuYEQ.js → LoginPage-CVFvbmek.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-leUqNKuk.js → PopoverPopup-CH29Dl4m.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-D5yP5dZa.js → ResetPasswordPage-Oj3VwcUT.js} +1 -1
- package/dashboard/dist/assets/{Select-Bq8XjKo9.js → Select-COJepoOt.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-BUE4AAIY.js → SelectItemText-B1dgrCIE.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-DXTRXh8r.js → SettingsPage-CG0Eiwb0.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-Cz17a9ep.js → SkillsPage-DAht7mz-.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-CSYCzAMh.js → WidgetsPage-CCunqqWC.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-BM2BvUUN.js → WrappedPage-taTQKVSr.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-DfysM5Ft.js → agent-logos-CQnbfUYM.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-KJ5nNRaR.js → arrow-up-right-gF_F8D6S.js} +1 -1
- package/dashboard/dist/assets/{download-DPMzNjm9.js → download-DD5R30tG.js} +1 -1
- package/dashboard/dist/assets/{main-V7ji8RkP.js → main-BS4wXJQk.js} +2 -2
- package/dashboard/dist/assets/{use-limits-display-prefs-BhRZ2Vbm.js → use-limits-display-prefs-CWkXT8WC.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-CUwxJneK.js → use-native-settings-CkkMdU8D.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-Cg8uqKy8.js → use-ordered-list-DnvBYQP_.js} +1 -1
- package/dashboard/dist/assets/{use-quality-per-dollar-pref-DFYl9bWd.js → use-quality-per-dollar-pref-Cunr2tA3.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-TD09WmDE.js → use-usage-limits-DO7Asd2u.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-mDQb_32d.js → useCurrency-Bz7Vj74A.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-CP5GKZtC.js → useScrollLock-BnSHhdwy.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/commands/serve.js +32 -1
- package/src/commands/status.js +2 -2
- package/src/commands/sync.js +151 -70
- package/src/lib/browser-auth.js +120 -20
- package/src/lib/install-resolver.js +62 -0
- package/src/lib/multi-install-parser.js +103 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +104 -40
- package/src/lib/wsl-probe.js +21 -0
package/src/lib/rollout.js
CHANGED
|
@@ -7,6 +7,7 @@ const crypto = require("node:crypto");
|
|
|
7
7
|
const { ensureDir } = require("./fs");
|
|
8
8
|
const { readSqliteJsonRows } = require("./sqlite-reader");
|
|
9
9
|
const wsl = require("./wsl-probe");
|
|
10
|
+
const { resolveInstallPaths } = require("./install-resolver");
|
|
10
11
|
|
|
11
12
|
const DEFAULT_SOURCE = "codex";
|
|
12
13
|
const DEFAULT_MODEL = "unknown";
|
|
@@ -3787,23 +3788,11 @@ function resolveHermesPath(env = process.env, deps = {}) {
|
|
|
3787
3788
|
}
|
|
3788
3789
|
const home = require("node:os").homedir();
|
|
3789
3790
|
const defaultPath = path.join(home, ".hermes");
|
|
3790
|
-
// On Windows, scan BOTH the native install (%LOCALAPPDATA%\hermes) and
|
|
3791
|
-
// any WSL distros running Hermes Agent. Each install has its own independent
|
|
3792
|
-
// state.db with different sessions, so there is no double-counting risk.
|
|
3793
|
-
// TOKENTRACKER_WSL_MODE controls which source takes priority.
|
|
3794
3791
|
if (process.platform === "win32") {
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
const candidate = path.join(localAppData, "hermes");
|
|
3800
|
-
try {
|
|
3801
|
-
if (fssync.existsSync(candidate)) nativePath = candidate;
|
|
3802
|
-
} catch (_e) { }
|
|
3803
|
-
}
|
|
3804
|
-
}
|
|
3805
|
-
const wslPath = wsl.shouldProbeWsl(env) ? discoverWslHermesHome(deps) : null;
|
|
3806
|
-
const picked = wsl.pickWin32Path({ wslValue: wslPath, nativeValue: nativePath, env, platform: "win32" });
|
|
3792
|
+
const localAppData = typeof env.LOCALAPPDATA === "string" ? env.LOCALAPPDATA.trim() : "";
|
|
3793
|
+
const nativeValue = localAppData.length > 0 ? path.join(localAppData, "hermes") : null;
|
|
3794
|
+
const paths = resolveInstallPaths({ nativeValue, wslDir: ".hermes" }, env, deps);
|
|
3795
|
+
const picked = paths.native || paths.wsl;
|
|
3807
3796
|
if (picked) return picked;
|
|
3808
3797
|
const mode = wsl.getWslMode(env);
|
|
3809
3798
|
if (mode === "wsl-only" || mode === "native-only") return null;
|
|
@@ -3838,17 +3827,13 @@ function discoverWslHermesHome(deps = {}) {
|
|
|
3838
3827
|
return wsl.discoverWslHome(".hermes", deps);
|
|
3839
3828
|
}
|
|
3840
3829
|
|
|
3841
|
-
function pickWin32ProviderPath({ env = process.env, nativeValue, wslProviderDir, wslValue }) {
|
|
3842
|
-
const
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
nativeValue: nativeCandidate,
|
|
3849
|
-
env,
|
|
3850
|
-
platform: "win32",
|
|
3851
|
-
});
|
|
3830
|
+
function pickWin32ProviderPath({ env = process.env, nativeValue, wslProviderDir, wslValue, deps = {} }) {
|
|
3831
|
+
const paths = resolveInstallPaths({ nativeValue, wslDir: wslProviderDir, wslValue }, env, deps);
|
|
3832
|
+
return paths.native || paths.wsl;
|
|
3833
|
+
}
|
|
3834
|
+
|
|
3835
|
+
function resolveAllWin32ProviderPaths({ env = process.env, nativeValue, wslProviderDir, wslValue, deps = {} }) {
|
|
3836
|
+
return resolveInstallPaths({ nativeValue, wslDir: wslProviderDir, wslValue }, env, deps);
|
|
3852
3837
|
}
|
|
3853
3838
|
|
|
3854
3839
|
function resolveHermesDbPath(env = process.env) {
|
|
@@ -3936,6 +3921,86 @@ function readHermesSessions(dbPath, lastCompletedEpoch, unfinishedSessionIds = [
|
|
|
3936
3921
|
}
|
|
3937
3922
|
}
|
|
3938
3923
|
|
|
3924
|
+
// ── Dual-install cursor ownership probes ────────────────────────────────────
|
|
3925
|
+
// When a flat (pre-namespaced) cursor migrates to per-install namespaces
|
|
3926
|
+
// (multiInstallParse "both" mode), the migration must know which install the
|
|
3927
|
+
// flat cursor was tracking: only then may the OTHER install's namespace start
|
|
3928
|
+
// empty so its full history backfills. Guessing wrong wipes dedup state
|
|
3929
|
+
// (snapshots / sessionTotals / threadTotals) for an already-counted install,
|
|
3930
|
+
// and every re-read session lands in the hourly buckets a second time.
|
|
3931
|
+
// These probes answer "does this install's DB contain the flat cursor's own
|
|
3932
|
+
// ids?" — sampled from the cursor's per-session dedup maps. Any failure or
|
|
3933
|
+
// missing evidence returns false so the caller falls back to seeding every
|
|
3934
|
+
// namespace (bounded backfill loss, never a double count).
|
|
3935
|
+
|
|
3936
|
+
function sqliteDbContainsIds(dbPath, table, ids, sqliteOptions = {}) {
|
|
3937
|
+
if (!dbPath || !Array.isArray(ids) || ids.length === 0) return false;
|
|
3938
|
+
if (!fssync.existsSync(dbPath)) return false;
|
|
3939
|
+
const inList = ids.map(sqliteStringLiteral).join(",");
|
|
3940
|
+
const sql = `SELECT id FROM ${table} WHERE id IN (${inList}) LIMIT 1`;
|
|
3941
|
+
|
|
3942
|
+
let snapshot = null;
|
|
3943
|
+
let effectiveDbPath = dbPath;
|
|
3944
|
+
if (isUncPath(dbPath)) {
|
|
3945
|
+
try {
|
|
3946
|
+
snapshot = snapshotSqliteDb(dbPath);
|
|
3947
|
+
effectiveDbPath = snapshot.path;
|
|
3948
|
+
} catch (_e) { }
|
|
3949
|
+
}
|
|
3950
|
+
try {
|
|
3951
|
+
const rows = readSqliteJsonRows(effectiveDbPath, sql, {
|
|
3952
|
+
label: "InstallProbe",
|
|
3953
|
+
maxBuffer: 1024 * 1024,
|
|
3954
|
+
timeout: 10_000,
|
|
3955
|
+
...sqliteOptions,
|
|
3956
|
+
});
|
|
3957
|
+
return Array.isArray(rows) && rows.length > 0;
|
|
3958
|
+
} catch (_e) {
|
|
3959
|
+
return false;
|
|
3960
|
+
} finally {
|
|
3961
|
+
if (snapshot) snapshot.cleanup();
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
|
|
3965
|
+
// Sample the most recently inserted keys — most likely to still exist in the
|
|
3966
|
+
// DB (providers may delete old sessions, which would blind the probe).
|
|
3967
|
+
function sampleRecentKeys(obj, limit = 16) {
|
|
3968
|
+
if (!obj || typeof obj !== "object") return [];
|
|
3969
|
+
const keys = Object.keys(obj).filter((k) => typeof k === "string" && k.length > 0);
|
|
3970
|
+
return keys.slice(-limit);
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3973
|
+
function gooseInstallOwnsCursor(dbPath, flatState, sqliteOptions) {
|
|
3974
|
+
return sqliteDbContainsIds(dbPath, "sessions", sampleRecentKeys(flatState?.sessionTotals), sqliteOptions);
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
function zedInstallOwnsCursor(dbPath, flatState, sqliteOptions) {
|
|
3978
|
+
return sqliteDbContainsIds(dbPath, "threads", sampleRecentKeys(flatState?.threadTotals), sqliteOptions);
|
|
3979
|
+
}
|
|
3980
|
+
|
|
3981
|
+
function hermesInstallOwnsCursor(hermesPath, flatState, sqliteOptions) {
|
|
3982
|
+
const ids = new Set();
|
|
3983
|
+
const collect = (state) => {
|
|
3984
|
+
if (!state || typeof state !== "object") return;
|
|
3985
|
+
for (const key of sampleRecentKeys(state.snapshots)) ids.add(key);
|
|
3986
|
+
if (Array.isArray(state.unfinishedSessionIds)) {
|
|
3987
|
+
for (const id of state.unfinishedSessionIds) {
|
|
3988
|
+
if (typeof id === "string" && id.length > 0) ids.add(id);
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
};
|
|
3992
|
+
collect(flatState);
|
|
3993
|
+
if (flatState?.profiles && typeof flatState.profiles === "object") {
|
|
3994
|
+
for (const profileState of Object.values(flatState.profiles)) collect(profileState);
|
|
3995
|
+
}
|
|
3996
|
+
const sample = [...ids].slice(-16);
|
|
3997
|
+
if (sample.length === 0) return false;
|
|
3998
|
+
|
|
3999
|
+
const dbPaths = resolveAllHermesDBPaths({ hermesPath });
|
|
4000
|
+
const allDbs = [dbPaths.default, ...Object.values(dbPaths.profiles)].filter(Boolean);
|
|
4001
|
+
return allDbs.some((db) => sqliteDbContainsIds(db, "sessions", sample, sqliteOptions));
|
|
4002
|
+
}
|
|
4003
|
+
|
|
3939
4004
|
function hasLegacyHermesDefaultState(hermesState) {
|
|
3940
4005
|
return (
|
|
3941
4006
|
typeof hermesState.lastStartedAt === "number" ||
|
|
@@ -6526,13 +6591,11 @@ function resolveZedDbPath(env = process.env) {
|
|
|
6526
6591
|
if (process.platform === "win32") {
|
|
6527
6592
|
const local = env.LOCALAPPDATA || path.join(home, "AppData", "Local");
|
|
6528
6593
|
const native = path.join(local, "Zed", "threads", "threads.db");
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
}
|
|
6533
|
-
const
|
|
6534
|
-
const wslValue = wslDir ? path.join(wslDir, "threads.db") : null;
|
|
6535
|
-
const picked = wsl.pickWin32Path({ wslValue, nativeValue: nativeExists, env, platform: "win32" });
|
|
6594
|
+
const wslThreadsDir = wsl.shouldProbeWsl(env) ? wsl.discoverWslHome(".local/share/zed/threads", { env }) : null;
|
|
6595
|
+
const wslDbPath = wslThreadsDir && fssync.existsSync(path.join(wslThreadsDir, "threads.db"))
|
|
6596
|
+
? path.join(wslThreadsDir, "threads.db") : null;
|
|
6597
|
+
const paths = resolveInstallPaths({ nativeValue: native, wslValue: wslDbPath }, env);
|
|
6598
|
+
const picked = paths.native || paths.wsl;
|
|
6536
6599
|
if (picked) return picked;
|
|
6537
6600
|
const mode = wsl.getWslMode(env);
|
|
6538
6601
|
return mode === "wsl-only" || mode === "native-only" ? null : native;
|
|
@@ -6909,13 +6972,11 @@ function resolveGooseDbPath(env = process.env) {
|
|
|
6909
6972
|
} else if (process.platform === "win32") {
|
|
6910
6973
|
const appData = env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
6911
6974
|
const native = path.join(appData, "goose", "sessions", "sessions.db");
|
|
6912
|
-
let nativeExists = null;
|
|
6913
|
-
if (wsl.shouldProbeNative(env)) {
|
|
6914
|
-
try { if (fssync.existsSync(native)) nativeExists = native; } catch (_e) { }
|
|
6915
|
-
}
|
|
6916
6975
|
const wslDir = wsl.shouldProbeWsl(env) ? wsl.discoverWslHome(".local/share/goose/sessions", { env }) : null;
|
|
6917
|
-
const wslValue = wslDir
|
|
6918
|
-
|
|
6976
|
+
const wslValue = wslDir && fssync.existsSync(path.join(wslDir, "sessions.db"))
|
|
6977
|
+
? path.join(wslDir, "sessions.db") : null;
|
|
6978
|
+
const paths = resolveInstallPaths({ nativeValue: native, wslValue }, env);
|
|
6979
|
+
const picked = paths.native || paths.wsl;
|
|
6919
6980
|
if (picked) candidates.push(picked);
|
|
6920
6981
|
for (const c of candidates) {
|
|
6921
6982
|
if (fssync.existsSync(c)) return c;
|
|
@@ -9919,6 +9980,9 @@ module.exports = {
|
|
|
9919
9980
|
parseCursorApiIncremental,
|
|
9920
9981
|
parseKiroIncremental,
|
|
9921
9982
|
parseHermesIncremental,
|
|
9983
|
+
gooseInstallOwnsCursor,
|
|
9984
|
+
zedInstallOwnsCursor,
|
|
9985
|
+
hermesInstallOwnsCursor,
|
|
9922
9986
|
parseCopilotIncremental,
|
|
9923
9987
|
resolveKimiWireFiles,
|
|
9924
9988
|
resolveKimiDefaultModel,
|
package/src/lib/wsl-probe.js
CHANGED
|
@@ -16,6 +16,7 @@ const WSL_MODES = new Set([
|
|
|
16
16
|
"native-first",
|
|
17
17
|
"wsl-only",
|
|
18
18
|
"native-only",
|
|
19
|
+
"both",
|
|
19
20
|
]);
|
|
20
21
|
|
|
21
22
|
function defaultRunWsl(args, { utf16 = false } = {}) {
|
|
@@ -98,6 +99,7 @@ function pickWin32Path({
|
|
|
98
99
|
|
|
99
100
|
const mode = getWslMode(env);
|
|
100
101
|
|
|
102
|
+
if (mode === "both") return wslValue ?? nativeValue ?? null;
|
|
101
103
|
if (mode === "wsl-only") return wslValue ?? null;
|
|
102
104
|
if (mode === "native-only") return nativeValue ?? null;
|
|
103
105
|
if (mode === "native-first") return nativeValue ?? wslValue ?? null;
|
|
@@ -105,6 +107,24 @@ function pickWin32Path({
|
|
|
105
107
|
return wslValue ?? nativeValue ?? null;
|
|
106
108
|
}
|
|
107
109
|
|
|
110
|
+
function resolveAllWin32Paths({
|
|
111
|
+
nativeValue,
|
|
112
|
+
wslValue,
|
|
113
|
+
env = process.env,
|
|
114
|
+
platform = process.platform,
|
|
115
|
+
}) {
|
|
116
|
+
if (platform !== "win32") return { native: null, wsl: null };
|
|
117
|
+
|
|
118
|
+
const mode = getWslMode(env);
|
|
119
|
+
|
|
120
|
+
if (mode === "both") {
|
|
121
|
+
return { native: nativeValue ?? null, wsl: wslValue ?? null };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const single = pickWin32Path({ wslValue, nativeValue, env, platform });
|
|
125
|
+
return { native: single, wsl: null };
|
|
126
|
+
}
|
|
127
|
+
|
|
108
128
|
function lookupWslUser(distroName, runWsl, useCache) {
|
|
109
129
|
if (useCache && _cachedWslUsers.has(distroName)) {
|
|
110
130
|
return _cachedWslUsers.get(distroName);
|
|
@@ -177,5 +197,6 @@ module.exports = {
|
|
|
177
197
|
shouldProbeWsl,
|
|
178
198
|
shouldProbeNative,
|
|
179
199
|
pickWin32Path,
|
|
200
|
+
resolveAllWin32Paths,
|
|
180
201
|
normalizeWslMode,
|
|
181
202
|
};
|