tokentracker-cli 1.5.2 → 1.5.3
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-B5kmt0VD.js → AchievementBadge-uuDGO9Mq.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-C0FlkOo0.js → AchievementsPage-BCTVT7Jo.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-CRcp80Pp.js → AchievementsSection-B6fJSJSr.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-DAFjGbKW.js → BadgeDetailModal-FjUN__9G.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-CO0bbimB.js → BotAnimated-B-KqY_Wr.js} +1 -1
- package/dashboard/dist/assets/{Card-Xhr5exz8.js → Card-DS8T8piP.js} +1 -1
- package/dashboard/dist/assets/{CloudCheckoutPage-DsQ7FmsI.js → CloudCheckoutPage-_0UjsFPu.js} +1 -1
- package/dashboard/dist/assets/{CloudPage-DkSexu5V.js → CloudPage-BstgVBaV.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-Bm0huQdj.js → CommandPalette-DtVsYZgc.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-BsL8GN8Q.js → CommunityStatsModal-DTpEJqV3.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-_WPp292g.js → ConfirmModal-Lfdh2vr7.js} +1 -1
- package/dashboard/dist/assets/{Controls-ktzn68KM.js → Controls-DpwQ7pkq.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-DUFWejkE.js +1 -0
- package/dashboard/dist/assets/{DateRangePopover-DWr4zIzF.js → DateRangePopover-CrWgrzT2.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-CBa8Mk47.js → DevicePage-DbK5KWJS.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-CZ5jzgtb.js → DialogClose-Dd2B_o03.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-CLy0arbt.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-Dr0N6q15.js → DialogTitle-3JQBVha3.js} +1 -1
- package/dashboard/dist/assets/{ElectricLogo-sYqgrO6-.js → ElectricLogo-LYAqG8pG.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-Qq-hcrZW.js → FadeIn-DhDvUor7.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-B77zSv7i.js → HeaderGithubStar-9f1XmgYC.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-lRDaUmoW.js → HoverTooltip-BXhHAkP7.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-B6AvHZjA.js → IpCheckPage-ChPdDeMl.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DgzOjf3x.js → LandingPage-DnJ7Y9zw.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardPage-BtjjtDMC.js → LeaderboardPage-BF9isLzu.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProBadge-B2wNTUTU.js → LeaderboardProBadge-BM4JKgkC.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfileModal-DJJckwHh.js → LeaderboardProfileModal-DfZABOpN.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-DJdxByl2.js → LeaderboardProfilePage-BaZ38iT5.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-C7LbG3LX.js +6 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-B2lVqf_H.js → LocalOnlyNotice-DvhlYoqQ.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-i0gg1yby.js → LoginCard-g_Gbnzhs.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-B-loLNZM.js → LoginModal-DkJrI6b7.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-D_swI75j.js → LoginPage-CqCKSo0n.js} +1 -1
- package/dashboard/dist/assets/{PetPage-DDpKvpjG.js → PetPage-DbF7ROsj.js} +2 -2
- package/dashboard/dist/assets/{PopoverPopup-Gja4MyEK.js → PopoverPopup-4WuH41ZT.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-DOPj7tAZ.js → ProviderIcon-6j0A2KD9.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-BVADLk89.js → ResetPasswordPage-DBd-iwuM.js} +1 -1
- package/dashboard/dist/assets/{Select-D-38tN-E.js → Select-BBEpLeiG.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-jN61ukxk.js → SelectItemText-Cn_U_rrO.js} +1 -1
- package/dashboard/dist/assets/{SelfHostPage-k7AkGpl1.js → SelfHostPage-2WZc6Rml.js} +1 -1
- package/dashboard/dist/assets/ServiceStatusPage-B-g4MDl0.js +1 -0
- package/dashboard/dist/assets/{SessionsPage-BymccEEz.js → SessionsPage-CWS0iQ1L.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-DfnMCAuA.js → SettingsPage-rXNr96i-.js} +3 -3
- package/dashboard/dist/assets/{SkillsPage-YyIHIaHN.js → SkillsPage-CudTvbZL.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-BLcvZuQ9.js → TokenGalaxy-BRKrbTsJ.js} +1 -1
- package/dashboard/dist/assets/{TooltipPopup-3E8kyVzz.js → TooltipPopup-CsLKXhWC.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-ByKAdXSF.js → TrendMonitor-8onUTbkk.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-BgEVIIzL.js → WidgetsPage-CcwE0lz8.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-HjbX8QaA.js → WrappedPage-Cj9LLYqP.js} +1 -1
- package/dashboard/dist/assets/__vite-browser-external-07djSGmy.js +45 -0
- package/dashboard/dist/assets/__vite-browser-external-BDldtsD2.css +1 -0
- package/dashboard/dist/assets/{agent-logos-BVF2siPQ.js → agent-logos-ChBA1nAH.js} +1 -1
- package/dashboard/dist/assets/{arrow-left-CYO24PGP.js → arrow-left-Bb5mfoPc.js} +1 -1
- package/dashboard/dist/assets/{arrow-right-Xg9PZYhw.js → arrow-right-D1Ti2XUI.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-B7ah6Qv-.js → arrow-up-right-B_CjA_y8.js} +1 -1
- package/dashboard/dist/assets/{calendar-days-BV_iG938.js → calendar-days-DdfrAqie.js} +1 -1
- package/dashboard/dist/assets/{check--EZH8RTp.js → check-29zGw1MK.js} +1 -1
- package/dashboard/dist/assets/{chevron-down-BzGTlk0-.js → chevron-down-DAzlFX7g.js} +1 -1
- package/dashboard/dist/assets/{cloud-checkout-BgyxPHEh.js → cloud-checkout-C2SeCjFM.js} +1 -1
- package/dashboard/dist/assets/{composite-C0KrmLEK.js → composite-DIf735Hq.js} +1 -1
- package/dashboard/dist/assets/{copy-BtucSK2U.js → copy-BOuOM0HQ.js} +1 -1
- package/dashboard/dist/assets/{download-CWHq03Y2.js → download-50tQRpGe.js} +1 -1
- package/dashboard/dist/assets/{external-link-DCTXF5VZ.js → external-link-BhlQClVe.js} +1 -1
- package/dashboard/dist/assets/{flame-BE5TsqVF.js → flame-CeGfA-0L.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-ThNIMWnZ.js → format-tokens-Cq7YiAkJ.js} +1 -1
- package/dashboard/dist/assets/{gift-DgaN9s4-.js → gift-Do-UFhiT.js} +1 -1
- package/dashboard/dist/assets/{icons-B4r5EiDC.js → icons-C6EZ_HmP.js} +1 -1
- package/dashboard/dist/assets/{index-CKhcCLNC.js → index-BFrbVqsm.js} +1 -1
- package/dashboard/dist/assets/{index-DqCtgU0G.js → index-BWhbBAd9.js} +1 -1
- package/dashboard/dist/assets/{index-fNKH4U7j.js → index-D9kwYWLX.js} +1 -1
- package/dashboard/dist/assets/{index-DFF9xo6f.js → index-c0crpe9Z.js} +1 -1
- package/dashboard/dist/assets/{info-B4YzRR4y.js → info-B4MXmsNE.js} +1 -1
- package/dashboard/dist/assets/{link-2-DzbUbaBw.js → link-2-DuWCslzl.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-B9c7D-7v.js → loader-circle-C2I3NZAO.js} +1 -1
- package/dashboard/dist/assets/{local-usage-transfer-btuk_b0Y.js → local-usage-transfer-BQasvsUF.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-BjrU-Xz7.js → modifiers.esm-DbeNS6FT.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-Bwn4tb4q.js → monitor-smartphone-C6Y4ktsc.js} +1 -1
- package/dashboard/dist/assets/{play-DIFOwGIQ.js → play-CwgcDJC2.js} +1 -1
- package/dashboard/dist/assets/{plus-BpBqnwVg.js → plus-B796wsUO.js} +1 -1
- package/dashboard/dist/assets/{provider-display-DSrs4u7T.js → provider-display-Cw26RFDm.js} +1 -1
- package/dashboard/dist/assets/{react-BgG7_MY0.js → react-d7Bk1-_K.js} +1 -1
- package/dashboard/dist/assets/{safePolygon-BUGWIDOM.js → safePolygon-CDE0dNv_.js} +1 -1
- package/dashboard/dist/assets/{search-aHrmBmtZ.js → search-DtKvsdRs.js} +1 -1
- package/dashboard/dist/assets/{server-DOEvwlEU.js → server-gAURHUyz.js} +1 -1
- package/dashboard/dist/assets/{shield-check-BPTnLhsT.js → shield-check-DFVdDTwh.js} +1 -1
- package/dashboard/dist/assets/{skills-api-C052Mwez.js → skills-api-2rdPUrJI.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-DaZKhjhu.js → sortable.esm-DFlfwnJr.js} +1 -1
- package/dashboard/dist/assets/{store-Cq30Pofw.js → store-BKPCk_de.js} +1 -1
- package/dashboard/dist/assets/{terminal-CoSDSkE7.js → terminal-BtWFakvS.js} +1 -1
- package/dashboard/dist/assets/{trash-BAmt-kPj.js → trash-bpr9zTAM.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-Bxq0kBhm.js → use-community-stats-DMnGgLne.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-DmIQDlvk.js → use-latest-request-guard-BSBlJJHe.js} +1 -1
- package/dashboard/dist/assets/use-limits-display-prefs-eYlvnUKn.js +1 -0
- package/dashboard/dist/assets/{use-native-settings-s_CIC8Bb.js → use-native-settings-OthqiF6g.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-BHhykAbt.js → use-ordered-list-DpAtr0R-.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-DF0grwHs.js +1 -0
- package/dashboard/dist/assets/{use-transform-CVeMFlS6.js → use-transform-BRz9dz_2.js} +1 -1
- package/dashboard/dist/assets/use-usage-limits-CwmhudAX.js +1 -0
- package/dashboard/dist/assets/{useCurrency-DilqL2Iv.js → useCurrency-CAA-tQ27.js} +1 -1
- package/dashboard/dist/assets/{usePositioner-BcnySw8a.js → usePositioner-Cj2DPmwM.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-CvKW46WP.js → useTokenFormat-Bf5-f7Hx.js} +1 -1
- package/dashboard/dist/assets/{zap-D7VRNTwk.js → zap-B8Fg6PaC.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/ip-check.html +2 -2
- package/dashboard/dist/leaderboard.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +1 -1
- package/src/commands/serve.js +22 -13
- package/src/lib/deepseek-status.js +157 -0
- package/src/lib/local-api.js +44 -4
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/provider-status.js +15 -2
- package/src/lib/qoder-limits.js +17 -2
- package/src/lib/usage-limits.js +181 -39
- package/src/lib/workbuddy-limits.js +191 -18
- package/src/lib/zcode-limits.js +143 -35
- package/dashboard/dist/assets/DashboardPage-CdNXi3Im.js +0 -1
- package/dashboard/dist/assets/DialogDescription-kCZ4gN09.js +0 -1
- package/dashboard/dist/assets/LimitsPage-gSBHSBi_.js +0 -6
- package/dashboard/dist/assets/ServiceStatusPage-D_Fa_pYz.js +0 -1
- package/dashboard/dist/assets/__vite-browser-external-Br-KAXh6.css +0 -1
- package/dashboard/dist/assets/__vite-browser-external-D_cMeuqI.js +0 -45
- package/dashboard/dist/assets/use-limits-display-prefs-CyAn1fme.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-BmFyFE3S.js +0 -1
- package/dashboard/dist/assets/use-usage-limits-DRZMI20u.js +0 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// DeepSeek service-status probe.
|
|
2
|
+
//
|
|
3
|
+
// status.deepseek.com is a Flashcat(快猫)-hosted Next.js app. Unlike Statuspage.io
|
|
4
|
+
// it exposes no public JSON API and sends no CORS headers, so a browser cannot
|
|
5
|
+
// read it directly. The page state is, however, inlined in the server render as
|
|
6
|
+
// React Server Component "flight" data: the client script pushes JSON chunks via
|
|
7
|
+
// self.__next_f.push([1, "<json string>"]). We concatenate those chunks and read
|
|
8
|
+
// the single machine-readable signal — the live `active_changes` array.
|
|
9
|
+
//
|
|
10
|
+
// Semantics (Flashcat change status -> Statuspage.io indicator):
|
|
11
|
+
// operational -> none
|
|
12
|
+
// degraded / degraded_performance / maintenance -> minor
|
|
13
|
+
// partial_outage -> major
|
|
14
|
+
// major_outage / full_outage -> critical
|
|
15
|
+
// An empty active_changes array means everything is operational. Anything we
|
|
16
|
+
// cannot parse throws, so the caller falls back instead of inventing "all good".
|
|
17
|
+
|
|
18
|
+
const DEEPSEEK_STATUS_URL = "https://status.deepseek.com/";
|
|
19
|
+
|
|
20
|
+
const RSC_PUSH_RE = /self\.__next_f\.push\(\[1,("(?:[^"\\]|\\.)*")\]\)/g;
|
|
21
|
+
|
|
22
|
+
const CHANGE_STATUS_TO_INDICATOR = {
|
|
23
|
+
operational: "none",
|
|
24
|
+
degraded: "minor",
|
|
25
|
+
degraded_performance: "minor",
|
|
26
|
+
maintenance: "minor",
|
|
27
|
+
under_maintenance: "minor",
|
|
28
|
+
partial_outage: "major",
|
|
29
|
+
major_outage: "critical",
|
|
30
|
+
full_outage: "critical",
|
|
31
|
+
outage: "critical",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const INDICATOR_RANK = { none: 0, minor: 1, major: 2, critical: 3 };
|
|
35
|
+
|
|
36
|
+
function extractFlight(html) {
|
|
37
|
+
if (typeof html !== "string" || html.length === 0) {
|
|
38
|
+
throw new Error("DeepSeek status: expected an HTML document");
|
|
39
|
+
}
|
|
40
|
+
let flight = "";
|
|
41
|
+
let chunks = 0;
|
|
42
|
+
RSC_PUSH_RE.lastIndex = 0;
|
|
43
|
+
let match;
|
|
44
|
+
while ((match = RSC_PUSH_RE.exec(html))) {
|
|
45
|
+
try {
|
|
46
|
+
flight += JSON.parse(match[1]);
|
|
47
|
+
} catch (_error) {
|
|
48
|
+
throw new Error("DeepSeek status: malformed RSC chunk");
|
|
49
|
+
}
|
|
50
|
+
chunks += 1;
|
|
51
|
+
}
|
|
52
|
+
if (chunks === 0) {
|
|
53
|
+
throw new Error("DeepSeek status: no RSC data found");
|
|
54
|
+
}
|
|
55
|
+
return flight;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Return the balanced JSON array that starts at `start` (a '[' in flight).
|
|
59
|
+
function sliceJsonArray(flight, start) {
|
|
60
|
+
let depth = 0;
|
|
61
|
+
let inString = false;
|
|
62
|
+
let escaped = false;
|
|
63
|
+
for (let i = start; i < flight.length; i += 1) {
|
|
64
|
+
const ch = flight[i];
|
|
65
|
+
if (inString) {
|
|
66
|
+
if (escaped) escaped = false;
|
|
67
|
+
else if (ch === "\\") escaped = true;
|
|
68
|
+
else if (ch === '"') inString = false;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (ch === '"') inString = true;
|
|
72
|
+
else if (ch === "[") depth += 1;
|
|
73
|
+
else if (ch === "]") {
|
|
74
|
+
depth -= 1;
|
|
75
|
+
if (depth === 0) return flight.slice(start, i + 1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw new Error("DeepSeek status: unterminated active_changes array");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function readUpdatedAt(flight) {
|
|
82
|
+
const match = /"initialDataUpdatedAt":(\d{10,})/.exec(flight);
|
|
83
|
+
if (!match) return undefined;
|
|
84
|
+
const ms = Number(match[1]);
|
|
85
|
+
return Number.isFinite(ms) ? new Date(ms).toISOString() : undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Parse the status.deepseek.com HTML into a Statuspage.io-shaped payload:
|
|
90
|
+
* { status: { indicator, description }, page: { updated_at } }
|
|
91
|
+
* Throws when the page structure is not understood, so callers fall back
|
|
92
|
+
* instead of reporting a false "operational".
|
|
93
|
+
*/
|
|
94
|
+
function parseDeepSeekStatusPage(html) {
|
|
95
|
+
const flight = extractFlight(html);
|
|
96
|
+
const marker = '"active_changes":';
|
|
97
|
+
const markerIndex = flight.indexOf(marker);
|
|
98
|
+
if (markerIndex === -1) {
|
|
99
|
+
throw new Error("DeepSeek status: active_changes not found");
|
|
100
|
+
}
|
|
101
|
+
// The value must be an array. If upstream ever reports null / an object /
|
|
102
|
+
// anything else, refuse to guess a healthier-looking array further along the
|
|
103
|
+
// document — throwing keeps the caller on its last-good / "unknown" path
|
|
104
|
+
// instead of inventing a false "operational".
|
|
105
|
+
let arrayStart = markerIndex + marker.length;
|
|
106
|
+
while (arrayStart < flight.length && /\s/.test(flight[arrayStart])) arrayStart += 1;
|
|
107
|
+
if (flight[arrayStart] !== "[") {
|
|
108
|
+
throw new Error("DeepSeek status: active_changes is not an array");
|
|
109
|
+
}
|
|
110
|
+
let changes;
|
|
111
|
+
try {
|
|
112
|
+
changes = JSON.parse(sliceJsonArray(flight, arrayStart));
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw new Error("DeepSeek status: cannot parse active_changes (" + error.message + ")");
|
|
115
|
+
}
|
|
116
|
+
if (!Array.isArray(changes)) {
|
|
117
|
+
throw new Error("DeepSeek status: active_changes is not an array");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const updatedAt = readUpdatedAt(flight);
|
|
121
|
+
if (changes.length === 0) {
|
|
122
|
+
return { status: { indicator: "none", description: "" }, page: { updated_at: updatedAt } };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let worstIndicator = "none";
|
|
126
|
+
let worstRank = -1;
|
|
127
|
+
let descriptions = [];
|
|
128
|
+
for (const change of changes) {
|
|
129
|
+
if (!change || typeof change !== "object") continue;
|
|
130
|
+
const rawStatus = String(change.status || change.change_status || change.impact || "").toLowerCase();
|
|
131
|
+
const indicator = CHANGE_STATUS_TO_INDICATOR[rawStatus] || "minor";
|
|
132
|
+
const rank = INDICATOR_RANK[indicator];
|
|
133
|
+
const description = typeof change.name === "string" ? change.name
|
|
134
|
+
: typeof change.title === "string" ? change.title
|
|
135
|
+
: typeof change.description === "string" ? change.description
|
|
136
|
+
: "";
|
|
137
|
+
if (rank > worstRank) {
|
|
138
|
+
worstRank = rank;
|
|
139
|
+
worstIndicator = indicator;
|
|
140
|
+
descriptions = description ? [description] : [];
|
|
141
|
+
} else if (rank === worstRank && description) {
|
|
142
|
+
descriptions.push(description);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// A non-empty array with no readable change is still "something is wrong".
|
|
146
|
+
if (worstRank < 0) worstIndicator = "minor";
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
status: { indicator: worstIndicator, description: descriptions.join("; ") },
|
|
150
|
+
page: { updated_at: updatedAt },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = {
|
|
155
|
+
parseDeepSeekStatusPage,
|
|
156
|
+
DEEPSEEK_STATUS_URL,
|
|
157
|
+
};
|
package/src/lib/local-api.js
CHANGED
|
@@ -20,6 +20,7 @@ const { createCloudDeviceTokenStore } = require("./cloud-device-token");
|
|
|
20
20
|
const { parseBearerToken } = require("./bearer-token");
|
|
21
21
|
const { writeJsonAtomicSync } = require("./atomic-json");
|
|
22
22
|
const { createLocalUsageTransfer, MAX_BYTES: LOCAL_USAGE_MAX_BYTES } = require("./local-usage-transfer");
|
|
23
|
+
const { fetchProviderServiceStatus } = require("./provider-status");
|
|
23
24
|
|
|
24
25
|
const SYNC_TIMEOUT_MS = 120_000;
|
|
25
26
|
// A failed account-view request should not stall every dashboard refresh
|
|
@@ -1677,6 +1678,32 @@ function createLocalApiHandler({ queuePath, serverVersion = null, trackerDataDir
|
|
|
1677
1678
|
|
|
1678
1679
|
return async function handleLocalApi(req, res, url) {
|
|
1679
1680
|
const p = url.pathname;
|
|
1681
|
+
// status.deepseek.com has no public JSON API or CORS, so the dashboard
|
|
1682
|
+
// cannot probe it like the other providers. Relay it through the local app:
|
|
1683
|
+
// fetchProviderServiceStatus("deepseek") fetches + parses the server-rendered
|
|
1684
|
+
// page (with a 5-minute cache and 6s timeout in provider-status.js) and we
|
|
1685
|
+
// return a Statuspage.io-shaped payload the existing dashboard parser reads.
|
|
1686
|
+
if (p === "/api/deepseek-status") {
|
|
1687
|
+
const method = String(req.method || "GET").toUpperCase();
|
|
1688
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
1689
|
+
json(res, { error: "Method Not Allowed" }, 405);
|
|
1690
|
+
return true;
|
|
1691
|
+
}
|
|
1692
|
+
try {
|
|
1693
|
+
const reading = await fetchProviderServiceStatus("deepseek");
|
|
1694
|
+
if (!reading) {
|
|
1695
|
+
json(res, { error: "DeepSeek status unavailable" }, 502);
|
|
1696
|
+
return true;
|
|
1697
|
+
}
|
|
1698
|
+
json(res, {
|
|
1699
|
+
page: { updated_at: reading.updated_at },
|
|
1700
|
+
status: { indicator: reading.indicator, description: reading.description || "" },
|
|
1701
|
+
});
|
|
1702
|
+
} catch (error) {
|
|
1703
|
+
json(res, { error: `DeepSeek status probe error: ${error?.message || error}` }, 502);
|
|
1704
|
+
}
|
|
1705
|
+
return true;
|
|
1706
|
+
}
|
|
1680
1707
|
if (p === "/api/local-usage") {
|
|
1681
1708
|
if (!isAuthorizedLocalMutation(req)) { json(res, { code: "unauthorized" }, 401); return true; }
|
|
1682
1709
|
try {
|
|
@@ -2718,7 +2745,7 @@ function createLocalApiHandler({ queuePath, serverVersion = null, trackerDataDir
|
|
|
2718
2745
|
const src = row.source || "unknown";
|
|
2719
2746
|
const mdl = row.model || "unknown";
|
|
2720
2747
|
if (!bySource.has(src))
|
|
2721
|
-
bySource.set(src, { source: src, source_scope: getSourceScope(src), totals: { total_tokens: 0, billable_total_tokens: 0, input_tokens: 0, output_tokens: 0, cached_input_tokens: 0, cache_creation_input_tokens: 0, reasoning_output_tokens: 0, total_cost_usd: "0" }, models: new Map() });
|
|
2748
|
+
bySource.set(src, { source: src, source_scope: getSourceScope(src), totals: { total_tokens: 0, billable_total_tokens: 0, input_tokens: 0, output_tokens: 0, cached_input_tokens: 0, cache_creation_input_tokens: 0, reasoning_output_tokens: 0, conversation_count: 0, total_cost_usd: "0" }, models: new Map() });
|
|
2722
2749
|
const sa = bySource.get(src);
|
|
2723
2750
|
sa.totals.total_tokens += row.total_tokens || 0;
|
|
2724
2751
|
sa.totals.billable_total_tokens += row.billable_total_tokens ?? row.total_tokens ?? 0;
|
|
@@ -2727,8 +2754,12 @@ function createLocalApiHandler({ queuePath, serverVersion = null, trackerDataDir
|
|
|
2727
2754
|
sa.totals.cached_input_tokens += row.cached_input_tokens || 0;
|
|
2728
2755
|
sa.totals.cache_creation_input_tokens += row.cache_creation_input_tokens || 0;
|
|
2729
2756
|
sa.totals.reasoning_output_tokens += row.reasoning_output_tokens || 0;
|
|
2757
|
+
// Request-only sources (#948: credit-billed transcripts that report no
|
|
2758
|
+
// tokens) carry their activity solely as conversation_count — thread it
|
|
2759
|
+
// through so the dashboard can surface "0 tokens / N requests".
|
|
2760
|
+
sa.totals.conversation_count += row.conversation_count || 0;
|
|
2730
2761
|
if (!sa.models.has(mdl))
|
|
2731
|
-
sa.models.set(mdl, { model: mdl, model_id: mdl, pricing: getModelPricingInfo(mdl, { source: src }), totals: { total_tokens: 0, billable_total_tokens: 0, input_tokens: 0, output_tokens: 0, cached_input_tokens: 0, cache_creation_input_tokens: 0, reasoning_output_tokens: 0, total_cost_usd: "0" } });
|
|
2762
|
+
sa.models.set(mdl, { model: mdl, model_id: mdl, pricing: getModelPricingInfo(mdl, { source: src }), totals: { total_tokens: 0, billable_total_tokens: 0, input_tokens: 0, output_tokens: 0, cached_input_tokens: 0, cache_creation_input_tokens: 0, reasoning_output_tokens: 0, conversation_count: 0, total_cost_usd: "0" } });
|
|
2732
2763
|
const ma = sa.models.get(mdl);
|
|
2733
2764
|
const reportedCost = ["grok", "cline"].includes(src) && Number(row.total_cost_usd) > 0;
|
|
2734
2765
|
const costSource = reportedCost ? "provider_reported" : "model_pricing";
|
|
@@ -2740,6 +2771,7 @@ function createLocalApiHandler({ queuePath, serverVersion = null, trackerDataDir
|
|
|
2740
2771
|
ma.totals.cached_input_tokens += row.cached_input_tokens || 0;
|
|
2741
2772
|
ma.totals.cache_creation_input_tokens += row.cache_creation_input_tokens || 0;
|
|
2742
2773
|
ma.totals.reasoning_output_tokens += row.reasoning_output_tokens || 0;
|
|
2774
|
+
ma.totals.conversation_count += row.conversation_count || 0;
|
|
2743
2775
|
// Price each row, exactly as aggregateByDay does for the hero (#653).
|
|
2744
2776
|
// computeRowCost is not linear for models with a Fast tier or
|
|
2745
2777
|
// long-context rates: priority_* / long_context_* mark a subset of the
|
|
@@ -3672,7 +3704,7 @@ function createLocalApiHandler({ queuePath, serverVersion = null, trackerDataDir
|
|
|
3672
3704
|
|
|
3673
3705
|
// --- usage-limits ---
|
|
3674
3706
|
if (p === "/functions/tokentracker-usage-limits") {
|
|
3675
|
-
const { getUsageLimits, resetUsageLimitsCache } = require("./usage-limits");
|
|
3707
|
+
const { getUsageLimits, resetUsageLimitsCache, normalizeHiddenProviders } = require("./usage-limits");
|
|
3676
3708
|
try {
|
|
3677
3709
|
// Devin quota is opt-in (Settings > Usage & Limits > Providers). An
|
|
3678
3710
|
// explicit devin=1 without local authentication is rejected before any
|
|
@@ -3683,7 +3715,14 @@ function createLocalApiHandler({ queuePath, serverVersion = null, trackerDataDir
|
|
|
3683
3715
|
const localAuthorized = isAuthorizedLocalMutation(req);
|
|
3684
3716
|
const devinParam = url.searchParams.get("devin");
|
|
3685
3717
|
const devinEnabled = devinParam === "1" || devinParam === "true";
|
|
3686
|
-
|
|
3718
|
+
// Provider visibility gate (#956): hidden=<csv> names provider rows to
|
|
3719
|
+
// skip entirely — no upstream call, no credential read. Like devin=1,
|
|
3720
|
+
// an explicit list is honored only together with the loopback auth
|
|
3721
|
+
// header (letting an unauthenticated caller scope the fetch would let
|
|
3722
|
+
// it pick exactly which credentials to leave alone). Unknown ids are
|
|
3723
|
+
// dropped by the normalizer, so the gate only ever narrows.
|
|
3724
|
+
const hiddenProviders = normalizeHiddenProviders(url.searchParams.get("hidden"));
|
|
3725
|
+
if ((devinEnabled || hiddenProviders) && !localAuthorized) {
|
|
3687
3726
|
json(res, { error: "Unauthorized" }, 401);
|
|
3688
3727
|
return true;
|
|
3689
3728
|
}
|
|
@@ -3701,6 +3740,7 @@ function createLocalApiHandler({ queuePath, serverVersion = null, trackerDataDir
|
|
|
3701
3740
|
// cooldown) — an explicit user refresh should hit upstream.
|
|
3702
3741
|
forceRefresh,
|
|
3703
3742
|
devinEnabled,
|
|
3743
|
+
hiddenProviders,
|
|
3704
3744
|
});
|
|
3705
3745
|
json(res, data);
|
|
3706
3746
|
} catch (e) {
|