tokentracker-cli 0.68.0 → 0.69.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.
- 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-Bqy-meR1.js +42 -0
- package/dashboard/dist/assets/{Card--_I4rbLK.js → Card-DbzltTXg.js} +1 -1
- package/dashboard/dist/assets/CommunityStatsModal-CumafhRo.js +1 -0
- package/dashboard/dist/assets/ConfirmModal-CfmWYFAl.js +1 -0
- package/dashboard/dist/assets/DashboardPage-beIJMVvO.js +19 -0
- package/dashboard/dist/assets/DevicePage-Cb_cMnJj.js +1 -0
- package/dashboard/dist/assets/DialogClose-245t1wj3.js +1 -0
- package/dashboard/dist/assets/DialogDescription-CMG2f0Xr.js +1 -0
- package/dashboard/dist/assets/DialogTitle-C_S_W-qN.js +1 -0
- package/dashboard/dist/assets/{FadeIn-CJo3CY8P.js → FadeIn-9qSEABKW.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-DlIkA0ge.js → HeaderGithubStar-C42NExcO.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-D11sDL_F.js → IpCheckPage-C_LVvRK3.js} +3 -3
- package/dashboard/dist/assets/LandingPage-1ng5HTtU.js +4252 -0
- package/dashboard/dist/assets/LeaderboardAvatar-Dud2isij.js +1 -0
- package/dashboard/dist/assets/LeaderboardPage-BVLHknpa.js +2 -0
- package/dashboard/dist/assets/{LeaderboardProfileModal-BFCT2cD1.js → LeaderboardProfileModal-creqSCBC.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-kYZ1Pk0J.js → LeaderboardProfilePage-CtCiW1ZR.js} +3 -3
- package/dashboard/dist/assets/{LimitsPage-wsAp4jGy.js → LimitsPage-DXhNfD6c.js} +3 -3
- package/dashboard/dist/assets/{LocalOnlyNotice-DH4MorD1.js → LocalOnlyNotice-CupEXgLA.js} +1 -1
- package/dashboard/dist/assets/LoginPage-Bvb6khA_.js +1 -0
- package/dashboard/dist/assets/PopoverPopup-1RwMOuZR.js +1 -0
- package/dashboard/dist/assets/ResetPasswordPage-C1t2TVSo.js +1 -0
- package/dashboard/dist/assets/Select-35dakfdf.js +1 -0
- package/dashboard/dist/assets/SelectItemText-Bn80bCzm.js +1 -0
- package/dashboard/dist/assets/SettingsPage-BtNI2EB5.js +1 -0
- package/dashboard/dist/assets/SkillsPage-DrtaOCt8.js +1 -0
- package/dashboard/dist/assets/WidgetsPage-DBP3CcAv.js +1 -0
- package/dashboard/dist/assets/{WrappedPage-CA9NmFM8.js → WrappedPage-Dxzewd0b.js} +1 -1
- package/dashboard/dist/assets/agent-logos-DXWdLSyr.js +1 -0
- package/dashboard/dist/assets/{arrow-up-right-DfeFj5sx.js → arrow-up-right-CAv_ofGl.js} +1 -1
- package/dashboard/dist/assets/check-BzPtb9x7.js +1 -0
- package/dashboard/dist/assets/copy-CkAl55Gc.js +1 -0
- package/dashboard/dist/assets/{download-CAPdZzcq.js → download-CHuHPQRG.js} +1 -1
- package/dashboard/dist/assets/format-tokens-BhF7ktma.js +1 -0
- package/dashboard/dist/assets/info-4RqgE-Sk.js +1 -0
- package/dashboard/dist/assets/main-CjU-7igC.css +1 -0
- package/dashboard/dist/assets/{main-QK2wZyt4.js → main-Cy1-zTjI.js} +69 -40
- package/dashboard/dist/assets/use-community-stats-BHARwgjJ.js +1 -0
- package/dashboard/dist/assets/{use-limits-display-prefs-CC1SjRQA.js → use-limits-display-prefs-D6TuBuXc.js} +1 -1
- package/dashboard/dist/assets/use-native-settings-8aQ6BEe6.js +1 -0
- package/dashboard/dist/assets/{use-ordered-list-DykwKTTq.js → use-ordered-list-B0XApham.js} +1 -1
- package/dashboard/dist/assets/{use-quality-per-dollar-pref-VMVZhzOm.js → use-quality-per-dollar-pref-DNa5uaGP.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-CIwmIjBw.js → use-usage-limits-C1V3apqc.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-Bm3doFX6.js → useCurrency-BEkNDMgc.js} +1 -1
- package/dashboard/dist/assets/useScrollLock-BRpWM_0H.js +1 -0
- package/dashboard/dist/brand-logos/deepseek.svg +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +1 -1
- package/src/commands/serve.js +32 -1
- package/src/lib/browser-auth.js +120 -20
- package/src/lib/cloud-account.js +8 -6
- package/src/lib/local-api.js +98 -56
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/dashboard/dist/assets/ActivityHeatmap-Cd928Ptz.js +0 -42
- package/dashboard/dist/assets/ConfirmModal-BJryA878.js +0 -1
- package/dashboard/dist/assets/DashboardPage-CikzR9-n.js +0 -19
- package/dashboard/dist/assets/DevicePage-Cucg8IJm.js +0 -1
- package/dashboard/dist/assets/DialogTitle-CBT-8O9t.js +0 -1
- package/dashboard/dist/assets/LandingPage-BR1-nUHp.js +0 -4415
- package/dashboard/dist/assets/LeaderboardAvatar-DbGYLk_s.js +0 -1
- package/dashboard/dist/assets/LeaderboardPage-BFPX67-5.js +0 -2
- package/dashboard/dist/assets/LoginPage-BNXijxzk.js +0 -1
- package/dashboard/dist/assets/PopoverPopup-Dqz11HEi.js +0 -1
- package/dashboard/dist/assets/ResetPasswordPage-CWvCPu5G.js +0 -1
- package/dashboard/dist/assets/Select-B1n0PW4j.js +0 -1
- package/dashboard/dist/assets/SelectItemText-BYGw7q1R.js +0 -1
- package/dashboard/dist/assets/SettingsPage-CCwxdKVg.js +0 -1
- package/dashboard/dist/assets/SkillsPage-ds_IoVAo.js +0 -1
- package/dashboard/dist/assets/WidgetsPage-DCI26iOU.js +0 -1
- package/dashboard/dist/assets/agent-logos-BASBNeBF.js +0 -1
- package/dashboard/dist/assets/main-B6BudUsC.css +0 -1
- package/dashboard/dist/assets/use-native-settings-BeV60g8D.js +0 -1
- package/dashboard/dist/assets/useScrollLock-DecJNW0u.js +0 -1
package/src/lib/local-api.js
CHANGED
|
@@ -768,6 +768,8 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
768
768
|
const localAuthToken = crypto.randomBytes(24).toString("hex");
|
|
769
769
|
const trackerDataDir = path.join(os.homedir(), ".tokentracker", "tracker");
|
|
770
770
|
const cookiePath = path.join(trackerDataDir, "relay-cookies.json");
|
|
771
|
+
const localSyncDeviceTokenCache = new Map();
|
|
772
|
+
const localSyncDeviceTokenInflight = new Map();
|
|
771
773
|
|
|
772
774
|
// Load persisted cookies on startup
|
|
773
775
|
try {
|
|
@@ -941,65 +943,93 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
941
943
|
}
|
|
942
944
|
}
|
|
943
945
|
|
|
944
|
-
|
|
946
|
+
function localSyncDeviceTokenCacheKey(refreshToken, machineId, baseUrl) {
|
|
947
|
+
return `${baseUrl}\0${refreshToken}\0${machineId}`;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
async function issueDeviceTokenForLocalSync(queuePathForMachineId, options = {}) {
|
|
945
951
|
if (!getCloudSyncPref()) return null;
|
|
946
952
|
const refreshToken = getRefreshTokenForCloud();
|
|
947
953
|
if (!refreshToken) return null;
|
|
948
|
-
|
|
949
|
-
const runtime = resolveRuntimeConfig();
|
|
950
|
-
const baseUrl = runtime.baseUrl || DEFAULT_BASE_URL;
|
|
951
|
-
const minted = await mintAccessToken({
|
|
952
|
-
baseUrl,
|
|
953
|
-
anonKey: runtime.anonKey,
|
|
954
|
-
refreshToken,
|
|
955
|
-
timeoutMs: runtime.httpTimeoutMs,
|
|
956
|
-
});
|
|
957
|
-
if (!minted?.accessToken) return null;
|
|
958
|
-
if (minted.refreshToken) setRelayRefreshToken(minted.refreshToken);
|
|
959
|
-
if (minted.csrfToken) setRelayCsrfToken(minted.csrfToken);
|
|
960
|
-
|
|
961
954
|
const machineId = getOrCreateMachineId(queuePathForMachineId);
|
|
962
955
|
if (!machineId) return null;
|
|
963
956
|
|
|
964
|
-
const
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
957
|
+
const runtime = resolveRuntimeConfig();
|
|
958
|
+
const baseUrl =
|
|
959
|
+
normalizeRemoteHttpBaseUrl(options.baseUrl) ||
|
|
960
|
+
normalizeRemoteHttpBaseUrl(runtime.baseUrl) ||
|
|
961
|
+
normalizeRemoteHttpBaseUrl(DEFAULT_BASE_URL);
|
|
962
|
+
const cacheKey = localSyncDeviceTokenCacheKey(refreshToken, machineId, baseUrl);
|
|
963
|
+
const cachedToken = localSyncDeviceTokenCache.get(cacheKey);
|
|
964
|
+
if (cachedToken) return cachedToken;
|
|
965
|
+
const inflightToken = localSyncDeviceTokenInflight.get(cacheKey);
|
|
966
|
+
if (inflightToken) return inflightToken;
|
|
967
|
+
|
|
968
|
+
const issuePromise = (async () => {
|
|
969
|
+
const minted = await mintAccessToken({
|
|
970
|
+
baseUrl,
|
|
971
|
+
anonKey: runtime.anonKey,
|
|
972
|
+
refreshToken,
|
|
973
|
+
timeoutMs: runtime.httpTimeoutMs,
|
|
974
|
+
});
|
|
975
|
+
if (!minted?.accessToken) return null;
|
|
976
|
+
const rotatedRefreshToken =
|
|
977
|
+
typeof minted.refreshToken === "string" && minted.refreshToken.trim()
|
|
978
|
+
? minted.refreshToken.trim()
|
|
979
|
+
: "";
|
|
980
|
+
if (rotatedRefreshToken) setRelayRefreshToken(rotatedRefreshToken);
|
|
981
|
+
if (minted.csrfToken) setRelayCsrfToken(minted.csrfToken);
|
|
982
|
+
|
|
983
|
+
const root = String(baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
984
|
+
const headers = {
|
|
985
|
+
"Content-Type": "application/json",
|
|
986
|
+
Accept: "application/json",
|
|
987
|
+
Authorization: `Bearer ${minted.accessToken}`,
|
|
988
|
+
};
|
|
989
|
+
if (runtime.anonKey) headers.apikey = runtime.anonKey;
|
|
971
990
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
991
|
+
let timeoutId;
|
|
992
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
993
|
+
if (controller && runtime.httpTimeoutMs > 0) {
|
|
994
|
+
timeoutId = setTimeout(() => controller.abort(), runtime.httpTimeoutMs);
|
|
995
|
+
}
|
|
977
996
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
997
|
+
const dashboardPlatform =
|
|
998
|
+
process.platform === "darwin" ? "MacIntel" :
|
|
999
|
+
process.platform === "win32" ? "Win32" :
|
|
1000
|
+
process.platform === "linux" ? "Linux x86_64" :
|
|
1001
|
+
"web";
|
|
1002
|
+
|
|
1003
|
+
const res = await fetch(`${root}/functions/tokentracker-device-token-issue`, {
|
|
1004
|
+
method: "POST",
|
|
1005
|
+
headers,
|
|
1006
|
+
signal: controller ? controller.signal : undefined,
|
|
1007
|
+
body: JSON.stringify({
|
|
1008
|
+
// 必须和 dashboard/src/lib/cloud-sync.ts 使用同一个设备身份。
|
|
1009
|
+
// 旧云端设备按 (platform, device_name) 认领;如果这里发明
|
|
1010
|
+
// local-sync 身份,会多出一个 active device,账户视图会把历史求和两次。
|
|
1011
|
+
device_name: `Token Tracker (dashboard) #${machineId.slice(0, 8)}`,
|
|
1012
|
+
platform: dashboardPlatform,
|
|
1013
|
+
machine_id: machineId,
|
|
1014
|
+
}),
|
|
1015
|
+
}).finally(() => {
|
|
1016
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1017
|
+
});
|
|
1018
|
+
if (!res.ok) return null;
|
|
1019
|
+
const data = await res.json().catch(() => null);
|
|
1020
|
+
const token = typeof data?.token === "string" ? data.token.trim() : "";
|
|
1021
|
+
if (token) {
|
|
1022
|
+
const activeRefreshToken = rotatedRefreshToken || refreshToken;
|
|
1023
|
+
localSyncDeviceTokenCache.set(localSyncDeviceTokenCacheKey(activeRefreshToken, machineId, baseUrl), token);
|
|
1024
|
+
}
|
|
1025
|
+
return token || null;
|
|
1026
|
+
})();
|
|
1027
|
+
localSyncDeviceTokenInflight.set(cacheKey, issuePromise);
|
|
1028
|
+
try {
|
|
1029
|
+
return await issuePromise;
|
|
1030
|
+
} finally {
|
|
1031
|
+
localSyncDeviceTokenInflight.delete(cacheKey);
|
|
1032
|
+
}
|
|
1003
1033
|
}
|
|
1004
1034
|
|
|
1005
1035
|
// Returns "served" when the cross-device aggregate was written to `res`, or
|
|
@@ -1462,6 +1492,7 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
1462
1492
|
if (typeof body.deviceToken === "string" && body.deviceToken.trim()) {
|
|
1463
1493
|
extraEnv.TOKENTRACKER_DEVICE_TOKEN = body.deviceToken.trim();
|
|
1464
1494
|
}
|
|
1495
|
+
let localSyncBaseUrl = null;
|
|
1465
1496
|
if (body.insforgeBaseUrl != null) {
|
|
1466
1497
|
const allowedBaseUrl = resolveAllowedInsforgeBaseUrl(body.insforgeBaseUrl);
|
|
1467
1498
|
if (!allowedBaseUrl) {
|
|
@@ -1469,14 +1500,25 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
1469
1500
|
return true;
|
|
1470
1501
|
}
|
|
1471
1502
|
extraEnv.TOKENTRACKER_INSFORGE_BASE_URL = allowedBaseUrl;
|
|
1503
|
+
localSyncBaseUrl = allowedBaseUrl;
|
|
1472
1504
|
}
|
|
1473
|
-
if (
|
|
1474
|
-
|
|
1505
|
+
if (!extraEnv.TOKENTRACKER_DEVICE_TOKEN && getCloudSyncPref() && getRefreshTokenForCloud()) {
|
|
1506
|
+
let issuedToken = null;
|
|
1507
|
+
try {
|
|
1508
|
+
issuedToken = await issueDeviceTokenForLocalSync(qp, { baseUrl: localSyncBaseUrl });
|
|
1509
|
+
} catch (e) {
|
|
1510
|
+
if (resolveRuntimeConfig().debug) {
|
|
1511
|
+
console.warn("[LocalAPI] local sync device token issue failed:", e?.message || e);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1475
1514
|
if (!issuedToken) {
|
|
1476
|
-
|
|
1477
|
-
|
|
1515
|
+
if (drain) {
|
|
1516
|
+
json(res, { ok: false, error: "Unable to issue cloud device token for local sync" }, 502);
|
|
1517
|
+
return true;
|
|
1518
|
+
}
|
|
1519
|
+
} else {
|
|
1520
|
+
extraEnv.TOKENTRACKER_DEVICE_TOKEN = issuedToken;
|
|
1478
1521
|
}
|
|
1479
|
-
extraEnv.TOKENTRACKER_DEVICE_TOKEN = issuedToken;
|
|
1480
1522
|
}
|
|
1481
1523
|
const result = await runSyncCommand(extraEnv, { drain });
|
|
1482
1524
|
try {
|