tokentracker-cli 1.1.9 → 1.1.10
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/dashboard/dist/assets/{AchievementBadge-BeNHapqp.js → AchievementBadge-CH97d6cg.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-vNH31cFv.js → AchievementsPage-Dwvj09sF.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-ChGSmCUP.js → AchievementsSection-D6HHK1f-.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-CN0tcpEE.js → BadgeDetailModal-Bpr6rY9l.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-CKKYEJt-.js → BotAnimated-srF64s63.js} +1 -1
- package/dashboard/dist/assets/{Card-Dx3Pj7SE.js → Card-38v0-L6e.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-ChJEskg4.js → ClawdAnimated-C9a1u9lq.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-D4-sgyxP.js → CommandPalette-Bj0e0ye0.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-NUDCSEve.js → CommunityStatsModal-IoQLJCe2.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-q4EXjOda.js → ConfirmModal-B7uK0Wjr.js} +1 -1
- package/dashboard/dist/assets/{Controls-OjbHepPz.js → Controls-D5XuFAlI.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-F3swcQnu.js → DashboardPage-Ddb8k4DR.js} +1 -1
- package/dashboard/dist/assets/DevicePage-DE-NSyZg.js +1 -0
- package/dashboard/dist/assets/{DialogClose-DSp5s5nL.js → DialogClose-DB6ZRDR3.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-CaRzgdvP.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-M5ojVj4-.js → DialogTitle-CgwmTJf3.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-CWDRIc_C.js → FadeIn-CMjPtMSB.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-DR4Px_eh.js → HeaderGithubStar-D5cSnbcg.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-CsfynzUO.js → HoverTooltip-CFV8yLxb.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-C8Y1OFOn.js → IpCheckPage-Ctu-GWJQ.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-BnmP1NUm.js → LandingPage-PAIF034o.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-DprHPy4v.js → LeaderboardAvatar-D3CsY-nP.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-BI-ryUMj.js → LeaderboardPage-qE8DD-Ia.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-r-wgLtCo.js → LeaderboardProfileModal-BY3uaIdF.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-qBvdPj6z.js → LeaderboardProfilePage-CwX77U5Z.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage--ObDuhSD.js → LimitsPage-BlJgvFay.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-Cvg81MIm.js → LocalOnlyNotice-CLFKSu0V.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-T1vufQym.js → LoginCard-Bdp2gihl.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-CElUuzhg.js → LoginModal-o_t6uvYP.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-Decz8Qyn.js → LoginPage-Cc3ZHUU8.js} +1 -1
- package/dashboard/dist/assets/{PetPage-CitsUTsW.js → PetPage-DFaWnKsI.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-wx5xna8c.js → PopoverPopup-CGJBBnIf.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-DeCQ9ytY.js → ProviderIcon-1OG4Kbkl.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-BklHQ_i8.js → ResetPasswordPage-DbfS1gfH.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-DqbXKZfk.js → SegmentedControl-DPKl-28R.js} +1 -1
- package/dashboard/dist/assets/{Select-BPhSzg5T.js → Select-BTA6mSut.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-BEqonLIf.js → SelectItemText-CX5Br8hd.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-DnAv3J6d.js → ServiceStatusPage-CaTOLkT7.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-Fr5rFC4h.js → SessionsPage-Ixp6Fseo.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-DYB-pyw2.js → SettingsPage-BmeQse01.js} +2 -2
- package/dashboard/dist/assets/{SkillsPage-B9aU8WEc.js → SkillsPage-BUUfMFpU.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-ybV8JSOT.js → TokenGalaxy-DnfKnQhr.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-DTAiJvnu.js → ToolbarRootContext-D0wMtL6Z.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-BCpOG6Vh.js → TrendMonitor-D3WafvUS.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-Ypvi7zXN.js → WidgetsPage-Bfod5aSH.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-ZetHSH8Z.js → WrappedPage-fYftPA8s.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-C_FI6biS.js → __vite-browser-external-B98Yok9C.js} +2 -2
- package/dashboard/dist/assets/{agent-logos-BP-j-Uhx.js → agent-logos-C0O8Lnmv.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-BDfZ3gqx.js → arrow-up-right-Bs0RpChC.js} +1 -1
- package/dashboard/dist/assets/{check-DMSqoEnk.js → check-Dj5Wi1I7.js} +1 -1
- package/dashboard/dist/assets/{composite-BdUpN78Q.js → composite-BeHO1nUS.js} +1 -1
- package/dashboard/dist/assets/{copy-CSp3uU1K.js → copy-Dgp1NqhD.js} +1 -1
- package/dashboard/dist/assets/{download-5KK5dq2E.js → download-D1JugY5p.js} +1 -1
- package/dashboard/dist/assets/{external-link-CetdvgSB.js → external-link-ow0tAluE.js} +1 -1
- package/dashboard/dist/assets/{flame-CkGhWtYT.js → flame-Obus_5g4.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-CnlGC8B4.js → format-tokens-yToZ6yvw.js} +1 -1
- package/dashboard/dist/assets/{icons-BwUwzIE1.js → icons-Da1IxY6v.js} +1 -1
- package/dashboard/dist/assets/{index-DQV-HPM-.js → index-0hD-aFe2.js} +1 -1
- package/dashboard/dist/assets/{index-BLiO8Wso.js → index-BtmxTdj8.js} +1 -1
- package/dashboard/dist/assets/{index-D_-EkG8M.js → index-DduY8rpi.js} +1 -1
- package/dashboard/dist/assets/{info-KSAnlMrp.js → info-CQjQ1whr.js} +1 -1
- package/dashboard/dist/assets/{link-2-BGXe__2Q.js → link-2-BGYfWbPb.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-D7ZxwMME.js → loader-circle-WArW9cmn.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-D80lpJ50.js → maximize-2-Bhde1l0n.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-DStyqxn4.js → modifiers.esm-BRxUZvoy.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-D282iI2t.js → monitor-smartphone-2ZT8ACJS.js} +1 -1
- package/dashboard/dist/assets/{plus-Cn55gGq1.js → plus-C_cg6gJE.js} +1 -1
- package/dashboard/dist/assets/{provider-display-DvH8Y5lo.js → provider-display-CM8dzzcI.js} +1 -1
- package/dashboard/dist/assets/{react-B-73Uvu6.js → react-C3vDx5Sr.js} +1 -1
- package/dashboard/dist/assets/{search-DpOq2L2S.js → search-Vd9we7KU.js} +1 -1
- package/dashboard/dist/assets/{skills-api-CCIV2ZIq.js → skills-api-CHxubvN1.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-BjIamCRg.js → sortable.esm-rLR8Wj9r.js} +1 -1
- package/dashboard/dist/assets/{store-CmFrvUue.js → store-rdi5mnme.js} +1 -1
- package/dashboard/dist/assets/{terminal-CqeGAXgs.js → terminal-xEPZhi6v.js} +1 -1
- package/dashboard/dist/assets/{trash-CMLW5F5L.js → trash-Cad8nQc4.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-BKS7Gd-z.js → use-community-stats-SMq01IVW.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-I80QDt_F.js → use-latest-request-guard-CSQ3Ypu3.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-CPLY7mBi.js → use-limits-display-prefs-DaaT6TL9.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-wlw260-2.js → use-ordered-list-g58JllvT.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-Dgdv5SSP.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-BdVVDyew.js → use-session-efficiency-pref-DyATLZ-L.js} +1 -1
- package/dashboard/dist/assets/{use-transform-Daiv5LsX.js → use-transform-XYEzPgA-.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-CK1zK4GT.js → use-usage-limits-DL260plD.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-yxDqAL1Q.js → useCurrency-4xq0xUFx.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-BTYDgiyJ.js → useTokenFormat-B6fHArH6.js} +1 -1
- package/dashboard/dist/assets/{zap-CfNiBLFJ.js → zap-InLQedtD.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/commands/sync.js +14 -1
- package/src/lib/cloud-account.js +23 -12
- package/src/lib/cloud-device-token.js +74 -0
- package/src/lib/local-api.js +87 -35
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/upload-throttle.js +4 -1
- package/dashboard/dist/assets/DevicePage-CghTfQLN.js +0 -1
- package/dashboard/dist/assets/DialogDescription-Ln30UAVH.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-ClrSsBXW.js +0 -1
package/src/lib/local-api.js
CHANGED
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
const { accountSlugFor, fetchAccountUsage, mintAccessToken, invalidateCloudAccountPayloadCache, getCloudAccountSessionGeneration } = require("./cloud-account");
|
|
14
14
|
const { getOrCreateMachineId, computeStableMachineId } = require("./machine-id");
|
|
15
15
|
const { functionUrlFor, fetchFunctionResponse } = require("./function-url");
|
|
16
|
+
const { createCloudDeviceTokenStore } = require("./cloud-device-token");
|
|
16
17
|
|
|
17
18
|
const SYNC_TIMEOUT_MS = 120_000;
|
|
18
19
|
// A failed account-view request should not stall every dashboard refresh
|
|
@@ -896,7 +897,14 @@ function runSyncCommand(extraEnv = {}, opts = {}) {
|
|
|
896
897
|
if (opts.drain === true) args.push("--drain");
|
|
897
898
|
if (opts.waitForLock === true) args.push("--wait-for-lock");
|
|
898
899
|
const child = spawn(process.execPath, args, {
|
|
899
|
-
env: {
|
|
900
|
+
env: {
|
|
901
|
+
...process.env, ...extraEnv,
|
|
902
|
+
...(extraEnv.TOKENTRACKER_DEVICE_TOKEN ? {
|
|
903
|
+
TOKENTRACKER_INSFORGE_BASE_URL: extraEnv.TOKENTRACKER_INSFORGE_BASE_URL || resolveRuntimeConfig().baseUrl,
|
|
904
|
+
TOKENTRACKER_INSFORGE_ANON_KEY: resolveRuntimeConfig().anonKey,
|
|
905
|
+
} : {}),
|
|
906
|
+
TOKENTRACKER_LOCAL_SYNC_DEVICE_TOKEN: extraEnv.TOKENTRACKER_DEVICE_TOKEN || "",
|
|
907
|
+
},
|
|
900
908
|
stdio: ["ignore", "pipe", "pipe"],
|
|
901
909
|
});
|
|
902
910
|
let stdout = "";
|
|
@@ -1144,19 +1152,29 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
1144
1152
|
const localAuthToken = crypto.randomBytes(24).toString("hex");
|
|
1145
1153
|
const trackerDataDir = path.join(os.homedir(), ".tokentracker", "tracker");
|
|
1146
1154
|
const cookiePath = path.join(trackerDataDir, "relay-cookies.json");
|
|
1155
|
+
const localSyncDeviceTokenStore = createCloudDeviceTokenStore(path.join(trackerDataDir, "cloud-device-token.json"));
|
|
1147
1156
|
const localSyncDeviceTokenCache = new Map();
|
|
1148
1157
|
const localSyncDeviceTokenInflight = new Map();
|
|
1149
1158
|
let localSyncDeviceTokenUserId = "";
|
|
1150
1159
|
function clearLocalSyncDeviceTokenCache() {
|
|
1151
1160
|
localSyncDeviceTokenCache.clear();
|
|
1152
1161
|
localSyncDeviceTokenInflight.clear();
|
|
1162
|
+
localSyncDeviceTokenStore.clear();
|
|
1153
1163
|
localSyncDeviceTokenUserId = "";
|
|
1154
1164
|
}
|
|
1155
|
-
function
|
|
1165
|
+
function invalidateLocalSyncDeviceToken(token) {
|
|
1166
|
+
if (!token) return;
|
|
1167
|
+
for (const [key, value] of localSyncDeviceTokenCache) {
|
|
1168
|
+
if (value === token) localSyncDeviceTokenCache.delete(key);
|
|
1169
|
+
}
|
|
1170
|
+
localSyncDeviceTokenStore.invalidate(token);
|
|
1171
|
+
}
|
|
1172
|
+
function currentCloudUploadFailure(syncStartedAt, { backoff = false, deviceToken, attemptId } = {}) {
|
|
1156
1173
|
try {
|
|
1157
1174
|
const state = JSON.parse(fs.readFileSync(path.join(path.dirname(qp), "upload.throttle.json"), "utf8"));
|
|
1158
|
-
if (
|
|
1159
|
-
|
|
1175
|
+
if (attemptId && state.lastErrorAttemptId === attemptId &&
|
|
1176
|
+
Date.parse(state.lastErrorAt || "") >= syncStartedAt && Number(state.lastErrorStatus) > 0) {
|
|
1177
|
+
if (state.lastErrorCode === "CLOUD_DEVICE_TOKEN_REJECTED") invalidateLocalSyncDeviceToken(deviceToken);
|
|
1160
1178
|
return { code: state.lastErrorCode || "CLOUD_UPLOAD_FAILED", status: Number(state.lastErrorStatus) };
|
|
1161
1179
|
}
|
|
1162
1180
|
if (backoff && Number(state.backoffUntilMs || 0) > syncStartedAt) {
|
|
@@ -1170,7 +1188,7 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
1170
1188
|
// request for one account must not clear another account's failure), and
|
|
1171
1189
|
// suppress the duplicate fan-out while the first cloud probe is pending.
|
|
1172
1190
|
const accountViewFailures = new Map();
|
|
1173
|
-
const accountViewInFlight = new
|
|
1191
|
+
const accountViewInFlight = new Map();
|
|
1174
1192
|
let accountUploadState = readAccountUploadState();
|
|
1175
1193
|
|
|
1176
1194
|
function readAccountUploadState() {
|
|
@@ -1417,6 +1435,10 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
1417
1435
|
anonKey: runtime.anonKey,
|
|
1418
1436
|
refreshToken,
|
|
1419
1437
|
timeoutMs: runtime.httpTimeoutMs,
|
|
1438
|
+
throwOnFailure: true,
|
|
1439
|
+
}).catch((error) => {
|
|
1440
|
+
if (error.status === 401 && sessionAtStart === getCloudAccountSessionGeneration()) clearLocalSyncDeviceTokenCache();
|
|
1441
|
+
throw error;
|
|
1420
1442
|
});
|
|
1421
1443
|
if (!minted?.accessToken || sessionAtStart !== getCloudAccountSessionGeneration()) return null;
|
|
1422
1444
|
const rotatedRefreshToken =
|
|
@@ -1439,8 +1461,14 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
1439
1461
|
if (userId) localSyncDeviceTokenUserId = userId;
|
|
1440
1462
|
const identity = userId || `refresh:${rotatedRefreshToken || refreshToken}`;
|
|
1441
1463
|
const cacheKey = localSyncDeviceTokenCacheKey(identity, machineId, baseUrl);
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1464
|
+
const binding = { userId, baseUrl, machineId };
|
|
1465
|
+
const cachedToken = localSyncDeviceTokenCache.get(cacheKey) ||
|
|
1466
|
+
(userId ? localSyncDeviceTokenStore.get(binding) : null);
|
|
1467
|
+
if (cachedToken) {
|
|
1468
|
+
localSyncDeviceTokenCache.set(cacheKey, cachedToken);
|
|
1469
|
+
if (userId) localSyncDeviceTokenStore.set(binding, cachedToken);
|
|
1470
|
+
return cachedToken;
|
|
1471
|
+
}
|
|
1444
1472
|
const inflightToken = localSyncDeviceTokenInflight.get(cacheKey);
|
|
1445
1473
|
if (inflightToken?.sessionGeneration === sessionAtStart) return inflightToken.promise;
|
|
1446
1474
|
|
|
@@ -1482,13 +1510,19 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
1482
1510
|
});
|
|
1483
1511
|
if (sessionAtStart !== getCloudAccountSessionGeneration() || !getCloudSyncPref() || cloudPrefAtStart !== cloudSyncPrefGeneration) return null;
|
|
1484
1512
|
if (!res.ok) {
|
|
1485
|
-
if (res.status === 401
|
|
1513
|
+
if (res.status === 401 && sessionAtStart === getCloudAccountSessionGeneration()) {
|
|
1514
|
+
localSyncDeviceTokenCache.delete(cacheKey);
|
|
1515
|
+
localSyncDeviceTokenStore.clear(binding);
|
|
1516
|
+
}
|
|
1486
1517
|
return null;
|
|
1487
1518
|
}
|
|
1488
1519
|
const data = await res.json().catch(() => null);
|
|
1489
1520
|
const token = typeof data?.token === "string" ? data.token.trim() : "";
|
|
1490
1521
|
if (sessionAtStart !== getCloudAccountSessionGeneration() || !getCloudSyncPref() || cloudPrefAtStart !== cloudSyncPrefGeneration) return null;
|
|
1491
|
-
if (token)
|
|
1522
|
+
if (token) {
|
|
1523
|
+
localSyncDeviceTokenCache.set(cacheKey, token);
|
|
1524
|
+
if (userId) localSyncDeviceTokenStore.set(binding, token);
|
|
1525
|
+
}
|
|
1492
1526
|
return token || null;
|
|
1493
1527
|
})();
|
|
1494
1528
|
localSyncDeviceTokenInflight.set(cacheKey, { promise: issuePromise, sessionGeneration: sessionAtStart });
|
|
@@ -1535,32 +1569,46 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
1535
1569
|
invalidateAfterAccountUpload();
|
|
1536
1570
|
const runtime = resolveRuntimeConfig();
|
|
1537
1571
|
const failureKey = `${runtime.baseUrl}\0${refreshToken}`;
|
|
1538
|
-
const
|
|
1572
|
+
const sessionAtStart = getCloudAccountSessionGeneration();
|
|
1573
|
+
const cloudPrefAtStart = cloudSyncPrefGeneration;
|
|
1574
|
+
const params = new URLSearchParams(url.searchParams);
|
|
1575
|
+
params.sort();
|
|
1576
|
+
const requestKey = `${failureKey}\0${sessionAtStart}\0${cloudPrefAtStart}\0${accountUploadState}\0${usageSlug}\0${params}`;
|
|
1539
1577
|
if (url.searchParams.get("refresh") !== "1" && accountViewFailureUntil(failureKey) > Date.now()) {
|
|
1540
1578
|
return "transient-backoff";
|
|
1541
1579
|
}
|
|
1542
|
-
// Six native dashboard requests normally arrive together. If the cloud is
|
|
1543
|
-
// already being probed for this session, let the remaining requests use
|
|
1544
|
-
// their local fallback instead of opening more timeout-bound sockets.
|
|
1545
|
-
if (accountViewInFlight.has(requestKey)) {
|
|
1546
|
-
return "transient-inflight";
|
|
1547
|
-
}
|
|
1548
|
-
accountViewInFlight.add(requestKey);
|
|
1549
1580
|
const envTimeout = process.env.TOKENTRACKER_HTTP_TIMEOUT_MS
|
|
1550
1581
|
? parseInt(process.env.TOKENTRACKER_HTTP_TIMEOUT_MS, 10)
|
|
1551
1582
|
: 0;
|
|
1552
1583
|
const timeoutMs = envTimeout > 0 ? envTimeout : 4000;
|
|
1553
|
-
const sessionAtStart = getCloudAccountSessionGeneration();
|
|
1554
|
-
const cloudPrefAtStart = cloudSyncPrefGeneration;
|
|
1555
1584
|
try {
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1585
|
+
// An identical overlapping read shares the entire probe, including its
|
|
1586
|
+
// token mint. A healthy pending read must not look like a cloud outage
|
|
1587
|
+
// and trigger the native client's account recovery loop.
|
|
1588
|
+
let pending = accountViewInFlight.get(requestKey);
|
|
1589
|
+
if (!pending) {
|
|
1590
|
+
pending = fetchAccountUsage({
|
|
1591
|
+
usageSlug,
|
|
1592
|
+
searchParams: url.searchParams,
|
|
1593
|
+
baseUrl: runtime.baseUrl || DEFAULT_BASE_URL,
|
|
1594
|
+
anonKey: runtime.anonKey,
|
|
1595
|
+
refreshToken,
|
|
1596
|
+
timeoutMs,
|
|
1597
|
+
onSessionRefreshed: (session) => {
|
|
1598
|
+
if (sessionAtStart !== getCloudAccountSessionGeneration()) {
|
|
1599
|
+
throw Object.assign(new Error("Account session changed"), { code: "auth_session_changed" });
|
|
1600
|
+
}
|
|
1601
|
+
if (session.refreshToken) setRelayRefreshToken(session.refreshToken);
|
|
1602
|
+
if (session.csrfToken) setRelayCsrfToken(session.csrfToken);
|
|
1603
|
+
},
|
|
1604
|
+
});
|
|
1605
|
+
accountViewInFlight.set(requestKey, pending);
|
|
1606
|
+
const cleanup = () => {
|
|
1607
|
+
if (accountViewInFlight.get(requestKey) === pending) accountViewInFlight.delete(requestKey);
|
|
1608
|
+
};
|
|
1609
|
+
pending.then(cleanup, cleanup);
|
|
1610
|
+
}
|
|
1611
|
+
const out = await pending;
|
|
1564
1612
|
// `null` here means the slug has no cloud equivalent, or the session
|
|
1565
1613
|
// vanished mid-request — a routing/session fact, not an outage.
|
|
1566
1614
|
if (!out) return ACCOUNT_FALLBACK_SIGNED_OUT;
|
|
@@ -1571,8 +1619,6 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
1571
1619
|
rememberAccountViewFailure(failureKey);
|
|
1572
1620
|
return "transient-upstream";
|
|
1573
1621
|
}
|
|
1574
|
-
if (out.rotatedRefreshToken) setRelayRefreshToken(out.rotatedRefreshToken);
|
|
1575
|
-
if (out.rotatedCsrfToken) setRelayCsrfToken(out.rotatedCsrfToken);
|
|
1576
1622
|
if (!getCloudSyncPref()) return ACCOUNT_FALLBACK_CLOUD_SYNC_OFF;
|
|
1577
1623
|
if (cloudPrefAtStart !== cloudSyncPrefGeneration) {
|
|
1578
1624
|
json(res, { error: "Cloud sync preference changed", code: "CLOUD_SYNC_CHANGED" }, 409);
|
|
@@ -1587,8 +1633,15 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
1587
1633
|
res.end(JSON.stringify(out.data));
|
|
1588
1634
|
return "served";
|
|
1589
1635
|
} catch (e) {
|
|
1590
|
-
|
|
1591
|
-
|
|
1636
|
+
const currentSession = getCloudAccountSessionGeneration();
|
|
1637
|
+
if (e.code === "auth_session_changed" ||
|
|
1638
|
+
(sessionAtStart !== currentSession && e.invalidatedSessionGeneration !== currentSession)) {
|
|
1639
|
+
json(res, { error: "Account session changed", code: "auth_session_changed" }, 409);
|
|
1640
|
+
return "served";
|
|
1641
|
+
}
|
|
1642
|
+
if (!getCloudSyncPref()) return ACCOUNT_FALLBACK_CLOUD_SYNC_OFF;
|
|
1643
|
+
if (cloudPrefAtStart !== cloudSyncPrefGeneration) {
|
|
1644
|
+
json(res, { error: "Cloud sync preference changed", code: "CLOUD_SYNC_CHANGED" }, 409);
|
|
1592
1645
|
return "served";
|
|
1593
1646
|
}
|
|
1594
1647
|
// Signed in + cloud sync on, but the cloud read failed (offline, token
|
|
@@ -1601,8 +1654,6 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
1601
1654
|
}
|
|
1602
1655
|
rememberAccountViewFailure(failureKey);
|
|
1603
1656
|
return reason;
|
|
1604
|
-
} finally {
|
|
1605
|
-
accountViewInFlight.delete(requestKey);
|
|
1606
1657
|
}
|
|
1607
1658
|
}
|
|
1608
1659
|
|
|
@@ -2162,6 +2213,7 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
2162
2213
|
}
|
|
2163
2214
|
}
|
|
2164
2215
|
const syncStartedAt = Date.now();
|
|
2216
|
+
extraEnv.TOKENTRACKER_LOCAL_SYNC_ATTEMPT_ID = crypto.randomUUID();
|
|
2165
2217
|
const result = await runSyncCommand(extraEnv, {
|
|
2166
2218
|
drain,
|
|
2167
2219
|
auto,
|
|
@@ -2171,11 +2223,11 @@ function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
|
2171
2223
|
waitForLock:
|
|
2172
2224
|
!drain && !background && Boolean(extraEnv.TOKENTRACKER_DEVICE_TOKEN),
|
|
2173
2225
|
}).catch((error) => {
|
|
2174
|
-
const failure = currentCloudUploadFailure(syncStartedAt, { backoff: auto && drain });
|
|
2226
|
+
const failure = currentCloudUploadFailure(syncStartedAt, { backoff: auto && drain, deviceToken: extraEnv.TOKENTRACKER_DEVICE_TOKEN, attemptId: extraEnv.TOKENTRACKER_LOCAL_SYNC_ATTEMPT_ID });
|
|
2175
2227
|
if (failure) Object.assign(error, failure);
|
|
2176
2228
|
throw error;
|
|
2177
2229
|
});
|
|
2178
|
-
const failure = currentCloudUploadFailure(syncStartedAt);
|
|
2230
|
+
const failure = currentCloudUploadFailure(syncStartedAt, { deviceToken: extraEnv.TOKENTRACKER_DEVICE_TOKEN, attemptId: extraEnv.TOKENTRACKER_LOCAL_SYNC_ATTEMPT_ID });
|
|
2179
2231
|
if (failure) throw Object.assign(new Error("Cloud upload failed"), failure);
|
|
2180
2232
|
invalidateAfterAccountUpload();
|
|
2181
2233
|
if (drain) invalidateCloudAccountPayloadCache();
|