tokentracker-cli 0.87.15 → 0.87.16
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.de.md +2 -2
- package/README.ja.md +2 -2
- package/README.ko.md +2 -2
- package/README.md +3 -3
- package/README.zh-CN.md +2 -2
- package/dashboard/dist/assets/{AchievementBadge-Ca-tQO2m.js → AchievementBadge-DaKHLTL2.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-l8piG098.js → AchievementsPage-DJdtjodG.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-DILM1j6f.js → AchievementsSection-DVtxGDIP.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-CP4kIXJ6.js → BadgeDetailModal-CF2dlQkT.js} +1 -1
- package/dashboard/dist/assets/{Card-vJSP5AIR.js → Card-DuQWS5aB.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-DRXRgleI.js → ClawdAnimated-l8AN6_pc.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-CFYDM40w.js → CommandPalette-DWKyGhWG.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-DeBswg2S.js → CommunityStatsModal-CoJ9i3QH.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-DnHQ6ZjR.js → ConfirmModal-DM6A6p4g.js} +1 -1
- package/dashboard/dist/assets/{Controls-CEgdStDg.js → Controls-DBUMibne.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-CRA9C12j.js → DashboardPage-BgVXjT0v.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-DJHYCHvk.js → DevicePage-Beby6BhC.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-Dfv009JY.js → DialogClose-nwx-N3WL.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-BFVB627n.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-B50QLs-X.js → DialogTitle-Cw53obf0.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-C0gR94fP.js → FadeIn-CTTwoVh-.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-C640OuU1.js → HeaderGithubStar-BvaEHM9B.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-DX83f-xN.js → HoverTooltip-CbJqz6cF.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-BSFyNVPV.js → IpCheckPage-I0REWeew.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-SsOTjgMK.js → LandingPage-CuMWPQBC.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-B2195RbN.js → LeaderboardAvatar-MHfIP4_U.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-B2HVZ1LA.js → LeaderboardPage-DfDM-dH9.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-CV07_l8k.js → LeaderboardProfileModal-hmpU-JV7.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-Dx3je17Y.js → LeaderboardProfilePage-DtUb4dDZ.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-CtA0c2G5.js +4 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-CIcgZ_DK.js → LocalOnlyNotice-WI3Oke4U.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-RoSvQEcf.js → LoginCard-DMprjRI7.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-CyyqwoxT.js → LoginModal-DffmK0ro.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-Df1TNN5I.js → LoginPage-B2ZgB6K7.js} +1 -1
- package/dashboard/dist/assets/{PetPage-DNzJwiHw.js → PetPage-D_U7yXgF.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-DQeWO0iF.js → PopoverPopup-Ph-9l9jU.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-lzUcnmCH.js → ProviderIcon-D2s_KSYC.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-zMccm6ur.js → ResetPasswordPage-Cpg2C8BI.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-CJFGvTrI.js → SegmentedControl-C89jlPzS.js} +1 -1
- package/dashboard/dist/assets/{Select-C3Omtbu7.js → Select-BLPgobGl.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-yhAb3Grm.js → SelectItemText-CLTEQqQZ.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-DKO2U7tP.js → ServiceStatusPage-Bd1ec7bz.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-Db7QQKkq.js → SessionsPage-HPbQDLZ5.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-Wt0c9Tom.js → SettingsPage-CwfcRYYu.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-CiIK-x9u.js → SkillsPage-DflhF8pa.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-lenaRGrK.js → TokenGalaxy-CylVSV2J.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-DM5LAbiN.js → ToolbarRootContext-BzBAJ8C5.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-DsjkB17f.js → TrendMonitor-B0IzqLaN.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-DRHgj8mv.js → WidgetsPage-Bvt5JCoS.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-CnqvO_pe.js → WrappedPage-BXjDpjD_.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-Dt0UC4VW.js → __vite-browser-external-CDv96ZcG.js} +14 -11
- package/dashboard/dist/assets/{agent-logos-B5opZwZN.js → agent-logos-bPIt1QVl.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DthPMeHa.js → arrow-up-right-C_6NrnRB.js} +1 -1
- package/dashboard/dist/assets/{check-BjhrWH3T.js → check-ovHAYNwt.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-BllZqKsq.js → chevron-right-BeHvjzPf.js} +1 -1
- package/dashboard/dist/assets/{copy-DPdzUcUh.js → copy-DRQHwFKm.js} +1 -1
- package/dashboard/dist/assets/{download-BSl1NoxY.js → download-qPfSWDyf.js} +1 -1
- package/dashboard/dist/assets/{external-link-DpSJMzDV.js → external-link-t7Qbyp7r.js} +1 -1
- package/dashboard/dist/assets/{flame-BuP9lXyr.js → flame-DlRdMOxO.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DxpuHAMo.js → format-tokens-DP1DrOxZ.js} +1 -1
- package/dashboard/dist/assets/{icons-N_skbXnV.js → icons-DBgKdjpV.js} +1 -1
- package/dashboard/dist/assets/{index-CkdHt2G8.js → index-BFaAjCwg.js} +1 -1
- package/dashboard/dist/assets/{index-VgEi64Pa.js → index-C1wNjCL_.js} +1 -1
- package/dashboard/dist/assets/{index-BJbM1KfA.js → index-Dtzl1NhK.js} +1 -1
- package/dashboard/dist/assets/{info-B7vTy0RN.js → info-D9fn_ZQQ.js} +1 -1
- package/dashboard/dist/assets/limits-providers-JyTKN-3e.js +1 -0
- package/dashboard/dist/assets/{link-2-BL9btf_U.js → link-2-CmnuQiZs.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-B3ZUl9Xl.js → loader-circle-Bm3FqDLX.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-B3oqZR-c.js → maximize-2-CutKDynW.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-CJv-4uLD.js → modifiers.esm-C94MH21S.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-DpSeKStM.js → monitor-smartphone-wSLhNWq5.js} +1 -1
- package/dashboard/dist/assets/{provider-display-ChgG1v12.js → provider-display-CR8o_M3v.js} +1 -1
- package/dashboard/dist/assets/{react-CiQpGAt7.js → react-Cc1Ca2uE.js} +1 -1
- package/dashboard/dist/assets/{search-CR5Y7wkH.js → search-BXeDDAWS.js} +1 -1
- package/dashboard/dist/assets/{skills-api-CYV3Aau7.js → skills-api-DBP8kM7W.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-Cp2tEWCE.js → sortable.esm-Cd_ODlnX.js} +1 -1
- package/dashboard/dist/assets/{store-D579LpDz.js → store-lmptvFHG.js} +1 -1
- package/dashboard/dist/assets/{terminal-B1Afo2zN.js → terminal-965BMhR0.js} +1 -1
- package/dashboard/dist/assets/{trash-2-O7jz3Z3p.js → trash-2-wq5YeaAT.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-DpHf0WZG.js → use-community-stats-BCIgHdTZ.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-BVCtTqo4.js → use-limits-display-prefs-D0MB2ewv.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-C7mhlVII.js → use-ordered-list-DrQsh022.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-DhNppA5X.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-C3HNT8gg.js → use-session-efficiency-pref-BeBSADa-.js} +1 -1
- package/dashboard/dist/assets/{use-transform-Cn4pqfbJ.js → use-transform-fRpwp-G3.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-BAF6oNaO.js → use-usage-limits-C0Tk-ghm.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-DmofuCKt.js → useCurrency-BAZNRyKh.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-2HXHWZMK.js → useScrollLock-CU659jBG.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-CSI-l9pP.js → useTokenFormat-l0Xd62a5.js} +1 -1
- package/dashboard/dist/assets/{zap-BcxNvHjJ.js → zap-CWFkq16a.js} +1 -1
- package/dashboard/dist/index.html +4 -4
- 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/serve.js +11 -3
- package/src/commands/status.js +34 -0
- package/src/commands/sync.js +95 -1
- package/src/lib/diagnostics.js +10 -0
- package/src/lib/fs.js +79 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/qoder-limits.js +89 -34
- package/src/lib/rollout.js +226 -43
- package/src/lib/session-analytics.js +45 -5
- package/src/lib/usage-limits.js +93 -14
- package/dashboard/dist/assets/DialogDescription-BcNmkFVH.js +0 -1
- package/dashboard/dist/assets/LimitsPage-uQNPuFCp.js +0 -4
- package/dashboard/dist/assets/limits-providers-DXlRXQck.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-CWJDDawC.js +0 -1
package/src/lib/qoder-limits.js
CHANGED
|
@@ -11,12 +11,14 @@ const QODER_SITES = {
|
|
|
11
11
|
id: "international",
|
|
12
12
|
origin: "https://qoder.com",
|
|
13
13
|
usageUrl: "https://qoder.com/api/v2/me/usages/big_model_credits",
|
|
14
|
+
activityUrl: "https://openapi.qoder.sh/algo/api/v2/activity",
|
|
14
15
|
domains: new Set(["qoder.com", "www.qoder.com"]),
|
|
15
16
|
},
|
|
16
17
|
china: {
|
|
17
18
|
id: "china",
|
|
18
19
|
origin: "https://qoder.com.cn",
|
|
19
20
|
usageUrl: "https://qoder.com.cn/api/v2/me/usages/big_model_credits",
|
|
21
|
+
activityUrl: "https://openapi.qoder.com.cn/algo/api/v2/activity",
|
|
20
22
|
domains: new Set(["qoder.com.cn", "www.qoder.com.cn"]),
|
|
21
23
|
},
|
|
22
24
|
};
|
|
@@ -142,28 +144,33 @@ function qoderDataRoot({
|
|
|
142
144
|
home = os.homedir(),
|
|
143
145
|
env = process.env,
|
|
144
146
|
platform = process.platform,
|
|
147
|
+
appDir = "Qoder",
|
|
148
|
+
envPrefix = "QODER",
|
|
145
149
|
} = {}) {
|
|
146
|
-
|
|
147
|
-
|
|
150
|
+
const homeKey = `${envPrefix}_HOME`;
|
|
151
|
+
if (typeof env[homeKey] === "string" && env[homeKey].trim()) {
|
|
152
|
+
return path.resolve(env[homeKey].trim());
|
|
148
153
|
}
|
|
149
154
|
if (platform === "darwin") {
|
|
150
|
-
return path.join(home, "Library", "Application Support",
|
|
155
|
+
return path.join(home, "Library", "Application Support", appDir);
|
|
151
156
|
}
|
|
152
157
|
if (platform === "win32") {
|
|
153
|
-
return path.join(env.APPDATA || path.join(home, "AppData", "Roaming"),
|
|
158
|
+
return path.join(env.APPDATA || path.join(home, "AppData", "Roaming"), appDir);
|
|
154
159
|
}
|
|
155
|
-
return path.join(home, ".config",
|
|
160
|
+
return path.join(home, ".config", appDir);
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
function qoderRpcRequest(method, params = {}, {
|
|
159
164
|
home = os.homedir(),
|
|
160
165
|
env = process.env,
|
|
161
166
|
platform = process.platform,
|
|
167
|
+
appDir = "Qoder",
|
|
168
|
+
envPrefix = "QODER",
|
|
162
169
|
timeoutMs = 1500,
|
|
163
170
|
netModule = net,
|
|
164
171
|
} = {}) {
|
|
165
172
|
return new Promise((resolve, reject) => {
|
|
166
|
-
const infoPath = path.join(qoderDataRoot({ home, env, platform }), "SharedClientCache", ".info.json");
|
|
173
|
+
const infoPath = path.join(qoderDataRoot({ home, env, platform, appDir, envPrefix }), "SharedClientCache", ".info.json");
|
|
167
174
|
let info;
|
|
168
175
|
try {
|
|
169
176
|
info = JSON.parse(fs.readFileSync(infoPath, "utf8"));
|
|
@@ -302,17 +309,21 @@ function normalizeQoderActivityResponse(response, { nowMs = Date.now() } = {}) {
|
|
|
302
309
|
};
|
|
303
310
|
}
|
|
304
311
|
|
|
305
|
-
function qoderActivityCachePath({ home = os.homedir() } = {}) {
|
|
306
|
-
|
|
312
|
+
function qoderActivityCachePath({ home = os.homedir(), namespace } = {}) {
|
|
313
|
+
const file = namespace
|
|
314
|
+
? `qoder-${namespace}-activity-cache.json`
|
|
315
|
+
: QODER_ACTIVITY_CACHE_FILE;
|
|
316
|
+
return path.join(home, ".tokentracker", "tracker", file);
|
|
307
317
|
}
|
|
308
318
|
|
|
309
319
|
function readQoderActivityCache({
|
|
310
320
|
home = os.homedir(),
|
|
311
321
|
nowMs = Date.now(),
|
|
322
|
+
namespace,
|
|
312
323
|
maxAgeMs = QODER_ACTIVITY_CACHE_MAX_AGE_MS,
|
|
313
324
|
} = {}) {
|
|
314
325
|
try {
|
|
315
|
-
const parsed = JSON.parse(fs.readFileSync(qoderActivityCachePath({ home }), "utf8"));
|
|
326
|
+
const parsed = JSON.parse(fs.readFileSync(qoderActivityCachePath({ home, namespace }), "utf8"));
|
|
316
327
|
const window = parsed?.activity_window;
|
|
317
328
|
const cachedAtMs = Date.parse(parsed?.cached_at || "");
|
|
318
329
|
if (!window || !Number.isFinite(cachedAtMs) || cachedAtMs > nowMs + 60_000) return null;
|
|
@@ -336,9 +347,10 @@ function readQoderActivityCache({
|
|
|
336
347
|
function writeQoderActivityCache(window, {
|
|
337
348
|
home = os.homedir(),
|
|
338
349
|
nowMs = Date.now(),
|
|
350
|
+
namespace,
|
|
339
351
|
} = {}) {
|
|
340
352
|
if (!window) return;
|
|
341
|
-
const cachePath = qoderActivityCachePath({ home });
|
|
353
|
+
const cachePath = qoderActivityCachePath({ home, namespace });
|
|
342
354
|
const payload = {
|
|
343
355
|
activity_window: window,
|
|
344
356
|
cached_at: new Date(nowMs).toISOString(),
|
|
@@ -351,8 +363,11 @@ function writeQoderActivityCache(window, {
|
|
|
351
363
|
} catch (_error) {}
|
|
352
364
|
}
|
|
353
365
|
|
|
354
|
-
function qoderLimitsCachePath({ home = os.homedir() } = {}) {
|
|
355
|
-
|
|
366
|
+
function qoderLimitsCachePath({ home = os.homedir(), namespace } = {}) {
|
|
367
|
+
const file = namespace
|
|
368
|
+
? `qoder-${namespace}-usage-limits-cache.json`
|
|
369
|
+
: QODER_LIMITS_CACHE_FILE;
|
|
370
|
+
return path.join(home, ".tokentracker", "tracker", file);
|
|
356
371
|
}
|
|
357
372
|
|
|
358
373
|
function qoderCachedWindow(window, { cachedAtMs, nowMs } = {}) {
|
|
@@ -365,9 +380,10 @@ function qoderCachedWindow(window, { cachedAtMs, nowMs } = {}) {
|
|
|
365
380
|
function readQoderLimitsCache({
|
|
366
381
|
home = os.homedir(),
|
|
367
382
|
nowMs = Date.now(),
|
|
383
|
+
namespace,
|
|
368
384
|
} = {}) {
|
|
369
385
|
try {
|
|
370
|
-
const raw = JSON.parse(fs.readFileSync(qoderLimitsCachePath({ home }), "utf8"))?.qoder;
|
|
386
|
+
const raw = JSON.parse(fs.readFileSync(qoderLimitsCachePath({ home, namespace }), "utf8"))?.qoder;
|
|
371
387
|
const cachedAtMs = Date.parse(raw?.cached_at || "");
|
|
372
388
|
if (!Number.isFinite(cachedAtMs) || cachedAtMs > nowMs + 60_000) return null;
|
|
373
389
|
const primaryWindow = qoderCachedWindow(raw.primary_window, { cachedAtMs, nowMs });
|
|
@@ -398,9 +414,10 @@ function readQoderLimitsCache({
|
|
|
398
414
|
function writeQoderLimitsCache(limits, {
|
|
399
415
|
home = os.homedir(),
|
|
400
416
|
nowMs = Date.now(),
|
|
417
|
+
namespace,
|
|
401
418
|
} = {}) {
|
|
402
419
|
if (!limits?.configured || limits.error || (!limits.primary_window && !limits.secondary_window)) return;
|
|
403
|
-
const cachePath = qoderLimitsCachePath({ home });
|
|
420
|
+
const cachePath = qoderLimitsCachePath({ home, namespace });
|
|
404
421
|
const payload = {
|
|
405
422
|
qoder: {
|
|
406
423
|
plan_label: limits.plan_label || null,
|
|
@@ -484,7 +501,8 @@ function buildQoderActivityHeaders(authStatus, {
|
|
|
484
501
|
}
|
|
485
502
|
|
|
486
503
|
async function fetchQoderActivity(authStatus, fetchImpl = fetch, options = {}) {
|
|
487
|
-
const
|
|
504
|
+
const activityUrl = options.activityUrl || QODER_ACTIVITY_URL;
|
|
505
|
+
const response = await fetchImpl(activityUrl, {
|
|
488
506
|
method: "GET",
|
|
489
507
|
headers: buildQoderActivityHeaders(authStatus, options),
|
|
490
508
|
});
|
|
@@ -595,22 +613,26 @@ function readQoderLocalQuota({
|
|
|
595
613
|
home = os.homedir(),
|
|
596
614
|
platform = process.platform,
|
|
597
615
|
env = process.env,
|
|
616
|
+
appDir = "Qoder",
|
|
617
|
+
envPrefix = "QODER",
|
|
598
618
|
} = {}) {
|
|
619
|
+
const logRootKey = `${envPrefix}_LOG_ROOT`;
|
|
620
|
+
const homeKey = `${envPrefix}_HOME`;
|
|
599
621
|
const explicitLogRoot =
|
|
600
|
-
typeof env
|
|
601
|
-
? path.resolve(env.
|
|
622
|
+
typeof env[logRootKey] === "string" && env[logRootKey].trim()
|
|
623
|
+
? path.resolve(env[logRootKey].trim())
|
|
602
624
|
: null;
|
|
603
625
|
const configuredRoot =
|
|
604
|
-
typeof env
|
|
605
|
-
? path.resolve(env.
|
|
626
|
+
typeof env[homeKey] === "string" && env[homeKey].trim()
|
|
627
|
+
? path.resolve(env[homeKey].trim())
|
|
606
628
|
: null;
|
|
607
629
|
const logRoot = explicitLogRoot || (configuredRoot
|
|
608
630
|
? path.join(configuredRoot, "logs")
|
|
609
631
|
: platform === "darwin"
|
|
610
|
-
? path.join(home, "Library", "Application Support",
|
|
632
|
+
? path.join(home, "Library", "Application Support", appDir, "logs")
|
|
611
633
|
: platform === "win32"
|
|
612
|
-
? path.join(env.APPDATA || path.join(home, "AppData", "Roaming"),
|
|
613
|
-
: path.join(home, ".config",
|
|
634
|
+
? path.join(env.APPDATA || path.join(home, "AppData", "Roaming"), appDir, "logs")
|
|
635
|
+
: path.join(home, ".config", appDir, "logs"));
|
|
614
636
|
for (const logPath of listRecentQoderRendererLogs(logRoot)) {
|
|
615
637
|
try {
|
|
616
638
|
const fd = fs.openSync(logPath, "r");
|
|
@@ -640,6 +662,11 @@ async function fetchQoderLimits({
|
|
|
640
662
|
home = os.homedir(),
|
|
641
663
|
env = process.env,
|
|
642
664
|
platform = process.platform,
|
|
665
|
+
appDir = "Qoder",
|
|
666
|
+
envPrefix = "QODER",
|
|
667
|
+
site = QODER_SITES.international,
|
|
668
|
+
sitePinned = false,
|
|
669
|
+
cacheNamespace,
|
|
643
670
|
fetchImpl = fetch,
|
|
644
671
|
rpcRequest = qoderRpcRequest,
|
|
645
672
|
nowMs = Date.now(),
|
|
@@ -648,7 +675,7 @@ async function fetchQoderLimits({
|
|
|
648
675
|
let rpcAuth = null;
|
|
649
676
|
let activityWindow = null;
|
|
650
677
|
let activitySource = null;
|
|
651
|
-
const rpcOptions = { home, env, platform };
|
|
678
|
+
const rpcOptions = { home, env, platform, appDir, envPrefix };
|
|
652
679
|
// Qoder's shared client accepts only one reliable request at a time. Parallel
|
|
653
680
|
// connections intermittently time out even while the desktop app is running.
|
|
654
681
|
try {
|
|
@@ -660,10 +687,10 @@ async function fetchQoderLimits({
|
|
|
660
687
|
} catch (_error) {}
|
|
661
688
|
if (rpcAuth) {
|
|
662
689
|
try {
|
|
663
|
-
activityWindow = await fetchQoderActivity(rpcAuth, fetchImpl, { nowMs });
|
|
690
|
+
activityWindow = await fetchQoderActivity(rpcAuth, fetchImpl, { nowMs, activityUrl: site.activityUrl });
|
|
664
691
|
if (activityWindow) {
|
|
665
692
|
activitySource = "provider-api";
|
|
666
|
-
writeQoderActivityCache(activityWindow, { home, nowMs });
|
|
693
|
+
writeQoderActivityCache(activityWindow, { home, nowMs, namespace: cacheNamespace });
|
|
667
694
|
}
|
|
668
695
|
} catch (_error) {}
|
|
669
696
|
}
|
|
@@ -672,7 +699,7 @@ async function fetchQoderLimits({
|
|
|
672
699
|
// omission, not an authoritative deletion: an activity carries its own
|
|
673
700
|
// expiry, so the last observed window remains valid until reset_at.
|
|
674
701
|
if (!activityWindow) {
|
|
675
|
-
activityWindow = readQoderActivityCache({ home, nowMs });
|
|
702
|
+
activityWindow = readQoderActivityCache({ home, nowMs, namespace: cacheNamespace });
|
|
676
703
|
if (activityWindow) activitySource = "disk-cache";
|
|
677
704
|
}
|
|
678
705
|
if (rpcUsage) {
|
|
@@ -683,21 +710,32 @@ async function fetchQoderLimits({
|
|
|
683
710
|
rpcUsage.source = activityWindow
|
|
684
711
|
? `local-ipc+${activitySource === "disk-cache" ? "cached-activity" : "provider-api"}`
|
|
685
712
|
: "local-ipc";
|
|
686
|
-
writeQoderLimitsCache(rpcUsage, { home, nowMs });
|
|
713
|
+
writeQoderLimitsCache(rpcUsage, { home, nowMs, namespace: cacheNamespace });
|
|
687
714
|
return rpcUsage;
|
|
688
715
|
}
|
|
689
716
|
|
|
690
717
|
// Match the Antigravity last-good behavior: once the local service is
|
|
691
718
|
// unavailable, prefer a bounded disk snapshot before any provider fallback.
|
|
692
|
-
const cachedLimits = readQoderLimitsCache({ home, nowMs });
|
|
719
|
+
const cachedLimits = readQoderLimitsCache({ home, nowMs, namespace: cacheNamespace });
|
|
693
720
|
if (cachedLimits) {
|
|
694
721
|
if (activityWindow) cachedLimits.secondary_window = activityWindow;
|
|
695
722
|
return cachedLimits;
|
|
696
723
|
}
|
|
697
724
|
|
|
698
|
-
|
|
725
|
+
// Manual-cookie fallback reads an envPrefix-specific key: QODER_COOKIE for
|
|
726
|
+
// the international flow, QODER_CN_COOKIE for the CN flow — the two installs
|
|
727
|
+
// hold different sessions and must never share a cookie.
|
|
728
|
+
const cookieKey = `${envPrefix}_COOKIE`;
|
|
729
|
+
const manualCookie = typeof env[cookieKey] === "string" ? env[cookieKey].trim() : "";
|
|
730
|
+
// QODER_SITE is an international-flow knob. A pinned site (the CN fetch)
|
|
731
|
+
// must never be overridden by it — the CN flow always targets china.
|
|
732
|
+
const manualSite = sitePinned ? site : env.QODER_SITE ? siteFromEnv(env.QODER_SITE) : site;
|
|
699
733
|
const sessions = manualCookie
|
|
700
|
-
? [{
|
|
734
|
+
? [{
|
|
735
|
+
cookieHeader: manualCookie,
|
|
736
|
+
site: manualSite,
|
|
737
|
+
sourceLabel: cookieKey,
|
|
738
|
+
}]
|
|
701
739
|
: [];
|
|
702
740
|
|
|
703
741
|
let lastError = null;
|
|
@@ -709,7 +747,7 @@ async function fetchQoderLimits({
|
|
|
709
747
|
// Preserve that status as metadata, but match Qoder's Credits UI by
|
|
710
748
|
// rendering an empty 0/0 bucket as 0%, not a full red 100% bar.
|
|
711
749
|
const local = result.primary_window?.limit_credits === 0
|
|
712
|
-
? readQoderLocalQuota({ home, platform, env })
|
|
750
|
+
? readQoderLocalQuota({ home, platform, env, appDir, envPrefix })
|
|
713
751
|
: null;
|
|
714
752
|
if (local) {
|
|
715
753
|
result.quota_exceeded = local.quota_exceeded === true;
|
|
@@ -726,20 +764,20 @@ async function fetchQoderLimits({
|
|
|
726
764
|
cookie_source: session.sourceLabel,
|
|
727
765
|
site: session.site.id,
|
|
728
766
|
};
|
|
729
|
-
writeQoderLimitsCache(liveResult, { home, nowMs });
|
|
767
|
+
writeQoderLimitsCache(liveResult, { home, nowMs, namespace: cacheNamespace });
|
|
730
768
|
return liveResult;
|
|
731
769
|
} catch (error) {
|
|
732
770
|
lastError = error;
|
|
733
771
|
}
|
|
734
772
|
}
|
|
735
773
|
|
|
736
|
-
const local = readQoderLocalQuota({ home, platform, env });
|
|
774
|
+
const local = readQoderLocalQuota({ home, platform, env, appDir, envPrefix });
|
|
737
775
|
if (local) {
|
|
738
776
|
if (activityWindow) {
|
|
739
777
|
local.secondary_window = activityWindow;
|
|
740
778
|
local.source += activitySource === "disk-cache" ? "+cached-activity" : "+activity";
|
|
741
779
|
}
|
|
742
|
-
writeQoderLimitsCache(local, { home, nowMs });
|
|
780
|
+
writeQoderLimitsCache(local, { home, nowMs, namespace: cacheNamespace });
|
|
743
781
|
return local;
|
|
744
782
|
}
|
|
745
783
|
if (activityWindow) {
|
|
@@ -762,6 +800,22 @@ async function fetchQoderLimits({
|
|
|
762
800
|
return { configured: false };
|
|
763
801
|
}
|
|
764
802
|
|
|
803
|
+
// Qoder CN (国内版) — quota comes from qoder.com.cn and credentials live in the
|
|
804
|
+
// separate QoderCN data directory (its own app install). Same mechanics as the
|
|
805
|
+
// international fetch, but with the china site, the QoderCN data root (own
|
|
806
|
+
// QODER_CN_HOME / QODER_CN_DB_PATH / QODER_CN_LOG_ROOT overrides), and a
|
|
807
|
+
// dedicated cache namespace so the two editions never clobber each other.
|
|
808
|
+
function fetchQoderCnLimits(opts = {}) {
|
|
809
|
+
return fetchQoderLimits({
|
|
810
|
+
...opts,
|
|
811
|
+
appDir: "QoderCN",
|
|
812
|
+
envPrefix: "QODER_CN",
|
|
813
|
+
site: QODER_SITES.china,
|
|
814
|
+
sitePinned: true,
|
|
815
|
+
cacheNamespace: "cn",
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
|
|
765
819
|
module.exports = {
|
|
766
820
|
QODER_SITES,
|
|
767
821
|
normalizeQoderUsageResponse,
|
|
@@ -779,4 +833,5 @@ module.exports = {
|
|
|
779
833
|
parseQoderQuotaLog,
|
|
780
834
|
readQoderLocalQuota,
|
|
781
835
|
fetchQoderLimits,
|
|
836
|
+
fetchQoderCnLimits,
|
|
782
837
|
};
|