tokentracker-cli 0.84.6 → 0.84.7
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 +10 -8
- package/README.ja.md +9 -7
- package/README.ko.md +9 -7
- package/README.md +12 -10
- package/README.zh-CN.md +9 -7
- package/dashboard/dist/assets/{AchievementBadge-CKIvD3lG.js → AchievementBadge-DAPsTMsx.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-CZWCUiS5.js → AchievementsPage-B9oXfXvm.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-BSk_fR8P.js → AchievementsSection-DI_LTZb3.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-CjHkcmuq.js → BadgeDetailModal--WCrJWVa.js} +1 -1
- package/dashboard/dist/assets/Card-BzbSN3EC.js +1 -0
- package/dashboard/dist/assets/{ClawdAnimated-Cl6gmF7u.js → ClawdAnimated-Dgxae1At.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-B0rcodcF.js → CommandPalette-BiZBYV5A.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-BtlTJbf4.js → CommunityStatsModal-C6jZx10_.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-tJCWVZQH.js → ConfirmModal-lD-mHMvO.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-Dtxq8dzI.js → DashboardPage-DCPNoiRG.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-9USuEsRu.js → DevicePage-DiF2IOjJ.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-DxHdqMcl.js → DialogClose-Crx2EFnI.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-CHsHo924.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-C1ZeQgf2.js → DialogTitle-amHXttsp.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-Cx4jLl9G.js → FadeIn-CIhF_9PP.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-BOyuGkoA.js → HeaderGithubStar-D6Ph2GuY.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-BDhqMR3L.js → HoverTooltip-BS8eO6tj.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-DTvA9Wmx.js → IpCheckPage-Cz1Je6WS.js} +1 -1
- package/dashboard/dist/assets/LandingPage-DqeXJfwn.js +2 -0
- package/dashboard/dist/assets/{LeaderboardAvatar-3ibNg6If.js → LeaderboardAvatar-D09yLoVA.js} +1 -1
- package/dashboard/dist/assets/LeaderboardPage-BetpFetS.js +2 -0
- package/dashboard/dist/assets/{LeaderboardProfileModal-DeGbJtvi.js → LeaderboardProfileModal-UW3KnANZ.js} +4 -4
- package/dashboard/dist/assets/{LeaderboardProfilePage-DEPzO6hW.js → LeaderboardProfilePage-Chd_2AVY.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-uhpNteXJ.js +4 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-C76LAT_3.js → LocalOnlyNotice-CpFFX8ro.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-DEyCJT67.js → LoginCard-DkZpGnVu.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-CueMQgAZ.js → LoginModal-CALv6jkD.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-CwqBb5Fd.js → LoginPage-BZuXHYAT.js} +1 -1
- package/dashboard/dist/assets/PetPage-C1LuDyYr.js +1 -0
- package/dashboard/dist/assets/{PopoverPopup-Dt9VY2VQ.js → PopoverPopup-CluVhrMr.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-CHx4ohfx.js → ProviderIcon-D3YKjVKb.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DB6R7fjf.js → ResetPasswordPage-CzN50nX5.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-BINOpXIY.js → SegmentedControl-DFOAcEKU.js} +1 -1
- package/dashboard/dist/assets/{Select-DcYXcsKv.js → Select-B3W_ihs_.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-BDv2Kb3x.js → SelectItemText-FXRtXnFV.js} +1 -1
- package/dashboard/dist/assets/ServiceStatusPage-BItZqH7q.js +1 -0
- package/dashboard/dist/assets/{SessionsPage-cL_w0ytB.js → SessionsPage-DVQU-hTH.js} +2 -2
- package/dashboard/dist/assets/SettingsPage-ByvrwOi6.js +1 -0
- package/dashboard/dist/assets/SkillsPage-DrGr5Qij.js +1 -0
- package/dashboard/dist/assets/{TokenGalaxy-BHq4YSv2.js → TokenGalaxy-ClF_n9od.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-akXaaKb6.js → ToolbarRootContext-B82hGhAf.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-DPhFXzQc.js → TrendMonitor-DkWm7_1z.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-Nf0UEz7s.js → WidgetsPage-DCY4pyR2.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-D4zOSsqD.js → WrappedPage-yGQ4DLqj.js} +1 -1
- package/dashboard/dist/assets/__vite-browser-external-BdRrPLk6.js +1483 -0
- package/dashboard/dist/assets/__vite-browser-external-hXV_ZYTG.css +1 -0
- package/dashboard/dist/assets/{agent-logos-Bja3VCAa.js → agent-logos-D5SHGOxB.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-CzcCNJJ2.js → arrow-up-right-CbMcAcss.js} +1 -1
- package/dashboard/dist/assets/{check-DUHSZCYn.js → check-Dyzh7lTz.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-jLvhaNUy.js → chevron-right-DT06ZIKC.js} +1 -1
- package/dashboard/dist/assets/{copy-ChyH-ynx.js → copy-B3Kt3Wrf.js} +1 -1
- package/dashboard/dist/assets/{download-CgwvXrUe.js → download-B3rOytqI.js} +1 -1
- package/dashboard/dist/assets/external-link-BamTP0Ks.js +1 -0
- package/dashboard/dist/assets/{flame-ysf1F8uB.js → flame-msy2h1ms.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DAa2cubl.js → format-tokens-8b1OUM_8.js} +1 -1
- package/dashboard/dist/assets/{icons-DccoWQq9.js → icons-C2NR2_Tf.js} +1 -1
- package/dashboard/dist/assets/{index-kkQrDRat.js → index-C8sDJzUY.js} +1 -1
- package/dashboard/dist/assets/{index-CxExExXh.js → index-CvTfpiCF.js} +1 -1
- package/dashboard/dist/assets/{index-CAycNPQf.js → index-x3vFJQFt.js} +1 -1
- package/dashboard/dist/assets/{info-DWQQIq9M.js → info-C9j23aGq.js} +1 -1
- package/dashboard/dist/assets/limits-providers-ySB9Ci5l.js +1 -0
- package/dashboard/dist/assets/{link-2-CKeVVq4P.js → link-2-rrcyTT6S.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-Dw0tu6IN.js → loader-circle-x9x9I8Hv.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-BMU0YzEF.js → maximize-2-7TWFb2k7.js} +1 -1
- package/dashboard/dist/assets/{provider-display-Cf9I-VLS.js → provider-display-JbSwpvxU.js} +1 -1
- package/dashboard/dist/assets/{react-BhbcISKy.js → react-DK5clHtY.js} +1 -1
- package/dashboard/dist/assets/{search-BoY8EW1z.js → search-DukXRvEV.js} +1 -1
- package/dashboard/dist/assets/{skills-api-KVH21GJw.js → skills-api-BUFJuwiR.js} +1 -1
- package/dashboard/dist/assets/{store-BL7rSajG.js → store-BTh7bv09.js} +1 -1
- package/dashboard/dist/assets/{terminal-CeMuITdm.js → terminal-CcUqLtn1.js} +1 -1
- package/dashboard/dist/assets/{trash-2-jVUCKqrR.js → trash-2-Bq7hnvNq.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-uWK0fE6l.js → use-community-stats-qTpx6ugG.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-DWsF4ShM.js → use-limits-display-prefs-Di_J4eVv.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-SEbqWDvc.js → use-native-settings-CIqpv9Z3.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-D4UhrPvw.js → use-ordered-list-OwogSXT7.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-5QGGPoom.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-DbysyUTJ.js → use-session-efficiency-pref-CjAvMh7u.js} +1 -1
- package/dashboard/dist/assets/{use-transform-DaEssGaZ.js → use-transform-KZLdt-NZ.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-CmmQ34Ov.js → use-usage-limits-Tbifa037.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-DvBEn4rx.js → useCurrency-D2td-bkn.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-QozsdFng.js → useScrollLock-Cvyfyezc.js} +1 -1
- package/dashboard/dist/assets/useTokenFormat-DYIUS6IH.js +1 -0
- package/dashboard/dist/assets/{zap-Dp_aJ1q6.js → zap-B19mEed5.js} +1 -1
- package/dashboard/dist/brand-logos/qoder.svg +5 -0
- package/dashboard/dist/index.html +18 -15
- package/dashboard/dist/ip-check.html +2 -2
- package/dashboard/dist/leaderboard.html +2 -2
- package/dashboard/dist/llms.txt +3 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +2 -2
- package/src/commands/init.js +1 -0
- package/src/commands/status.js +17 -0
- package/src/commands/sync.js +146 -126
- package/src/lib/local-api.js +4 -1
- package/src/lib/pet-packages.js +44 -2
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/provider-status.js +104 -0
- package/src/lib/qoder-limits.js +985 -0
- package/src/lib/rollout.js +385 -0
- package/src/lib/usage-limits.js +32 -2
- package/dashboard/dist/assets/Card-D6hdzwvI.js +0 -1
- package/dashboard/dist/assets/DialogDescription-DgUSe6A1.js +0 -1
- package/dashboard/dist/assets/LandingPage-k1271lWV.js +0 -2
- package/dashboard/dist/assets/LeaderboardPage-Byww5UpT.js +0 -2
- package/dashboard/dist/assets/LimitsPage-DPtqL0PS.js +0 -4
- package/dashboard/dist/assets/PetPage--xQAjW4_.js +0 -1
- package/dashboard/dist/assets/SettingsPage-CHGA0bQS.js +0 -1
- package/dashboard/dist/assets/SkillsPage-I29SqkXO.js +0 -1
- package/dashboard/dist/assets/__vite-browser-external-UqlSqI-1.js +0 -1465
- package/dashboard/dist/assets/__vite-browser-external-WKilYZnq.css +0 -1
- package/dashboard/dist/assets/limits-providers-B_18X9Rp.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-C3UrOh55.js +0 -1
- package/dashboard/dist/assets/useTokenFormat-BTLrL7Mi.js +0 -1
package/src/lib/rollout.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require("node:fs/promises");
|
|
2
2
|
const fssync = require("node:fs");
|
|
3
|
+
const os = require("node:os");
|
|
3
4
|
const path = require("node:path");
|
|
4
5
|
const readline = require("node:readline");
|
|
5
6
|
|
|
@@ -2904,6 +2905,15 @@ function normalizeOpencodeState(raw) {
|
|
|
2904
2905
|
};
|
|
2905
2906
|
}
|
|
2906
2907
|
|
|
2908
|
+
function normalizeQoderState(raw) {
|
|
2909
|
+
const state = raw && typeof raw === "object" ? raw : {};
|
|
2910
|
+
const messages = state.messages && typeof state.messages === "object" ? state.messages : {};
|
|
2911
|
+
return {
|
|
2912
|
+
messages,
|
|
2913
|
+
updatedAt: typeof state.updatedAt === "string" ? state.updatedAt : null,
|
|
2914
|
+
};
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2907
2917
|
function normalizeMessageKeyPart(value) {
|
|
2908
2918
|
if (typeof value !== "string") return "";
|
|
2909
2919
|
return value.trim();
|
|
@@ -2997,6 +3007,33 @@ function addTotals(target, delta) {
|
|
|
2997
3007
|
target.conversation_count += delta.conversation_count || 0;
|
|
2998
3008
|
}
|
|
2999
3009
|
|
|
3010
|
+
function subtractTotals(target, totals) {
|
|
3011
|
+
target.input_tokens = Math.max(0, target.input_tokens - (totals.input_tokens || 0));
|
|
3012
|
+
target.cached_input_tokens = Math.max(
|
|
3013
|
+
0,
|
|
3014
|
+
target.cached_input_tokens - (totals.cached_input_tokens || 0),
|
|
3015
|
+
);
|
|
3016
|
+
target.cache_creation_input_tokens = Math.max(
|
|
3017
|
+
0,
|
|
3018
|
+
target.cache_creation_input_tokens - (totals.cache_creation_input_tokens || 0),
|
|
3019
|
+
);
|
|
3020
|
+
target.output_tokens = Math.max(0, target.output_tokens - (totals.output_tokens || 0));
|
|
3021
|
+
target.reasoning_output_tokens = Math.max(
|
|
3022
|
+
0,
|
|
3023
|
+
target.reasoning_output_tokens - (totals.reasoning_output_tokens || 0),
|
|
3024
|
+
);
|
|
3025
|
+
target.total_tokens = Math.max(0, target.total_tokens - (totals.total_tokens || 0));
|
|
3026
|
+
target.billable_total_tokens = Math.max(
|
|
3027
|
+
0,
|
|
3028
|
+
target.billable_total_tokens -
|
|
3029
|
+
(totals.billable_total_tokens ?? totals.total_tokens ?? 0),
|
|
3030
|
+
);
|
|
3031
|
+
target.conversation_count = Math.max(
|
|
3032
|
+
0,
|
|
3033
|
+
target.conversation_count - (totals.conversation_count || 0),
|
|
3034
|
+
);
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3000
3037
|
function totalsKey(totals) {
|
|
3001
3038
|
return [
|
|
3002
3039
|
totals.input_tokens || 0,
|
|
@@ -4040,6 +4077,349 @@ async function parseOpencodeDbIncremental({
|
|
|
4040
4077
|
return { messagesProcessed, eventsAggregated, bucketsQueued, projectBucketsQueued };
|
|
4041
4078
|
}
|
|
4042
4079
|
|
|
4080
|
+
const QODER_USAGE_SQL = `
|
|
4081
|
+
SELECT
|
|
4082
|
+
cm.rowid AS row_id,
|
|
4083
|
+
cm.id,
|
|
4084
|
+
cm.session_id,
|
|
4085
|
+
cm.request_id,
|
|
4086
|
+
cm.token_info,
|
|
4087
|
+
cm.model_info,
|
|
4088
|
+
cm.gmt_create,
|
|
4089
|
+
cr.extra AS record_extra,
|
|
4090
|
+
cs.preferred_model_info,
|
|
4091
|
+
cs.project_uri,
|
|
4092
|
+
cs.project_name
|
|
4093
|
+
FROM chat_message AS cm
|
|
4094
|
+
LEFT JOIN chat_record AS cr ON cr.request_id = cm.request_id
|
|
4095
|
+
LEFT JOIN chat_session AS cs ON cs.session_id = cm.session_id
|
|
4096
|
+
WHERE cm.role = 'assistant'
|
|
4097
|
+
AND cm.token_info IS NOT NULL
|
|
4098
|
+
AND trim(cm.token_info) NOT IN ('', '{}')
|
|
4099
|
+
ORDER BY cm.gmt_create, cm.rowid
|
|
4100
|
+
`;
|
|
4101
|
+
|
|
4102
|
+
function resolveQoderDbPath({
|
|
4103
|
+
home = os.homedir(),
|
|
4104
|
+
env = process.env,
|
|
4105
|
+
platform = process.platform,
|
|
4106
|
+
} = {}) {
|
|
4107
|
+
if (typeof env.QODER_DB_PATH === "string" && env.QODER_DB_PATH.trim()) {
|
|
4108
|
+
return path.resolve(env.QODER_DB_PATH.trim());
|
|
4109
|
+
}
|
|
4110
|
+
let root;
|
|
4111
|
+
if (typeof env.QODER_HOME === "string" && env.QODER_HOME.trim()) {
|
|
4112
|
+
root = path.resolve(env.QODER_HOME.trim());
|
|
4113
|
+
} else if (platform === "darwin") {
|
|
4114
|
+
root = path.join(home, "Library", "Application Support", "Qoder");
|
|
4115
|
+
} else if (platform === "win32") {
|
|
4116
|
+
root = path.join(
|
|
4117
|
+
env.APPDATA || path.join(home, "AppData", "Roaming"),
|
|
4118
|
+
"Qoder",
|
|
4119
|
+
);
|
|
4120
|
+
} else {
|
|
4121
|
+
root = path.join(home, ".config", "Qoder");
|
|
4122
|
+
}
|
|
4123
|
+
return path.join(root, "SharedClientCache", "cache", "db", "local.db");
|
|
4124
|
+
}
|
|
4125
|
+
|
|
4126
|
+
function resolveQoderDbPaths({
|
|
4127
|
+
home = os.homedir(),
|
|
4128
|
+
env = process.env,
|
|
4129
|
+
platform = process.platform,
|
|
4130
|
+
deps = {},
|
|
4131
|
+
} = {}) {
|
|
4132
|
+
const nativeValue = resolveQoderDbPath({ home, env, platform });
|
|
4133
|
+
if (platform !== "win32" || env.QODER_DB_PATH || env.QODER_HOME) {
|
|
4134
|
+
return { native: nativeValue, wsl: null };
|
|
4135
|
+
}
|
|
4136
|
+
const existsSync = deps.existsSync || fssync.existsSync;
|
|
4137
|
+
const native = wsl.shouldProbeNative(env) && existsSync(nativeValue) ? nativeValue : null;
|
|
4138
|
+
const discoverWslHome = deps.discoverWslHome || wsl.discoverWslHome;
|
|
4139
|
+
const wslRoot = wsl.shouldProbeWsl(env)
|
|
4140
|
+
? discoverWslHome(".config/Qoder", { ...deps, env })
|
|
4141
|
+
: null;
|
|
4142
|
+
const wslValue = wslRoot
|
|
4143
|
+
? path.join(wslRoot, "SharedClientCache", "cache", "db", "local.db")
|
|
4144
|
+
: null;
|
|
4145
|
+
const wslDb = wslValue && existsSync(wslValue) ? wslValue : null;
|
|
4146
|
+
return wsl.resolveAllWin32Paths({
|
|
4147
|
+
nativeValue: native,
|
|
4148
|
+
wslValue: wslDb,
|
|
4149
|
+
env,
|
|
4150
|
+
platform: "win32",
|
|
4151
|
+
});
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
async function readQoderDbMessages(dbPath, sqliteOptions = {}) {
|
|
4155
|
+
if (!dbPath || !fssync.existsSync(dbPath)) return [];
|
|
4156
|
+
return readSqliteJsonRowsAsync(dbPath, QODER_USAGE_SQL, {
|
|
4157
|
+
label: "Qoder",
|
|
4158
|
+
...sqliteOptions,
|
|
4159
|
+
});
|
|
4160
|
+
}
|
|
4161
|
+
|
|
4162
|
+
function parseJsonObject(value) {
|
|
4163
|
+
if (!value) return null;
|
|
4164
|
+
if (value && typeof value === "object" && !Buffer.isBuffer(value)) return value;
|
|
4165
|
+
try {
|
|
4166
|
+
const parsed = JSON.parse(String(value));
|
|
4167
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
4168
|
+
} catch (_error) {
|
|
4169
|
+
return null;
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
|
|
4173
|
+
function normalizeQoderTokens(tokenInfo) {
|
|
4174
|
+
const tokens = parseJsonObject(tokenInfo);
|
|
4175
|
+
if (!tokens) return null;
|
|
4176
|
+
const prompt = Number(tokens.prompt_tokens);
|
|
4177
|
+
const cached = Number(tokens.cached_tokens || 0);
|
|
4178
|
+
const completion = Number(tokens.completion_tokens);
|
|
4179
|
+
if (
|
|
4180
|
+
!Number.isFinite(prompt) ||
|
|
4181
|
+
!Number.isFinite(cached) ||
|
|
4182
|
+
!Number.isFinite(completion) ||
|
|
4183
|
+
prompt < 0 ||
|
|
4184
|
+
cached < 0 ||
|
|
4185
|
+
completion < 0
|
|
4186
|
+
) {
|
|
4187
|
+
return null;
|
|
4188
|
+
}
|
|
4189
|
+
// Qoder's prompt_tokens already includes cached_tokens. Keep cached input in
|
|
4190
|
+
// its own column and report only the remainder as ordinary input, otherwise
|
|
4191
|
+
// cached context is counted twice in dashboards and cost calculations.
|
|
4192
|
+
const input = Math.max(0, Math.trunc(prompt) - Math.trunc(cached));
|
|
4193
|
+
const cachedInput = Math.min(Math.trunc(prompt), Math.trunc(cached));
|
|
4194
|
+
const output = Math.trunc(completion);
|
|
4195
|
+
return {
|
|
4196
|
+
input_tokens: input,
|
|
4197
|
+
cached_input_tokens: cachedInput,
|
|
4198
|
+
cache_creation_input_tokens: 0,
|
|
4199
|
+
output_tokens: output,
|
|
4200
|
+
reasoning_output_tokens: 0,
|
|
4201
|
+
total_tokens: Math.trunc(prompt) + output,
|
|
4202
|
+
billable_total_tokens: Math.trunc(prompt) + output,
|
|
4203
|
+
};
|
|
4204
|
+
}
|
|
4205
|
+
|
|
4206
|
+
function qoderModelFromRow(row) {
|
|
4207
|
+
const direct = parseJsonObject(row?.model_info);
|
|
4208
|
+
const recordExtra = parseJsonObject(row?.record_extra);
|
|
4209
|
+
const preferred = parseJsonObject(row?.preferred_model_info);
|
|
4210
|
+
return (
|
|
4211
|
+
normalizeModelInput(direct?.model_key || direct?.modelKey) ||
|
|
4212
|
+
normalizeModelInput(recordExtra?.modelConfig?.key || recordExtra?.model_config?.key) ||
|
|
4213
|
+
normalizeModelInput(
|
|
4214
|
+
preferred?.model_key ||
|
|
4215
|
+
preferred?.modelKey ||
|
|
4216
|
+
preferred?.preferred_model ||
|
|
4217
|
+
preferred?.preferredModel,
|
|
4218
|
+
) ||
|
|
4219
|
+
"qoder-agent"
|
|
4220
|
+
);
|
|
4221
|
+
}
|
|
4222
|
+
|
|
4223
|
+
function qoderMessageKey(row) {
|
|
4224
|
+
const id = normalizeMessageKeyPart(row?.id);
|
|
4225
|
+
const sessionId = normalizeMessageKeyPart(row?.session_id);
|
|
4226
|
+
if (sessionId && id) return `${sessionId}|${id}`;
|
|
4227
|
+
if (id) return id;
|
|
4228
|
+
const rowId = row?.row_id;
|
|
4229
|
+
return rowId === null || rowId === undefined ? null : `row:${rowId}`;
|
|
4230
|
+
}
|
|
4231
|
+
|
|
4232
|
+
function qoderProjectPath(row) {
|
|
4233
|
+
const raw = typeof row?.project_uri === "string" ? row.project_uri.trim() : "";
|
|
4234
|
+
if (!raw) return null;
|
|
4235
|
+
if (!raw.startsWith("file://")) return raw;
|
|
4236
|
+
try {
|
|
4237
|
+
return decodeURIComponent(new URL(raw).pathname);
|
|
4238
|
+
} catch (_error) {
|
|
4239
|
+
return null;
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
|
|
4243
|
+
async function parseQoderDbIncremental({
|
|
4244
|
+
dbMessages,
|
|
4245
|
+
cursors,
|
|
4246
|
+
queuePath,
|
|
4247
|
+
projectQueuePath,
|
|
4248
|
+
onProgress,
|
|
4249
|
+
publicRepoResolver,
|
|
4250
|
+
} = {}) {
|
|
4251
|
+
await ensureDir(path.dirname(queuePath));
|
|
4252
|
+
const rows = Array.isArray(dbMessages) ? dbMessages : [];
|
|
4253
|
+
const hourlyState = normalizeHourlyState(cursors?.hourly);
|
|
4254
|
+
const qoderState = normalizeQoderState(cursors?.qoder);
|
|
4255
|
+
const touchedBuckets = new Set();
|
|
4256
|
+
const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
|
|
4257
|
+
const projectState = projectEnabled ? normalizeProjectState(cursors?.projectHourly) : null;
|
|
4258
|
+
const projectTouchedBuckets = projectEnabled ? new Set() : null;
|
|
4259
|
+
const projectMetaCache = projectEnabled ? new Map() : null;
|
|
4260
|
+
const publicRepoCache = projectEnabled ? new Map() : null;
|
|
4261
|
+
const requestOwners = new Map();
|
|
4262
|
+
for (const row of rows) {
|
|
4263
|
+
const messageKey = qoderMessageKey(row);
|
|
4264
|
+
if (
|
|
4265
|
+
!messageKey ||
|
|
4266
|
+
!normalizeQoderTokens(row?.token_info) ||
|
|
4267
|
+
!coerceEpochMs(row?.gmt_create)
|
|
4268
|
+
) {
|
|
4269
|
+
continue;
|
|
4270
|
+
}
|
|
4271
|
+
const requestKey =
|
|
4272
|
+
normalizeMessageKeyPart(row?.request_id) ||
|
|
4273
|
+
normalizeMessageKeyPart(row?.session_id) ||
|
|
4274
|
+
messageKey;
|
|
4275
|
+
if (!requestOwners.has(requestKey)) requestOwners.set(requestKey, messageKey);
|
|
4276
|
+
}
|
|
4277
|
+
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
4278
|
+
let messagesProcessed = 0;
|
|
4279
|
+
let eventsAggregated = 0;
|
|
4280
|
+
|
|
4281
|
+
for (let index = 0; index < rows.length; index++) {
|
|
4282
|
+
const row = rows[index];
|
|
4283
|
+
const messageKey = qoderMessageKey(row);
|
|
4284
|
+
const currentBase = normalizeQoderTokens(row?.token_info);
|
|
4285
|
+
const timestampMs = coerceEpochMs(row?.gmt_create);
|
|
4286
|
+
const bucketStart = timestampMs
|
|
4287
|
+
? toUtcHalfHourStart(new Date(timestampMs).toISOString())
|
|
4288
|
+
: null;
|
|
4289
|
+
if (!messageKey || !currentBase || !bucketStart) {
|
|
4290
|
+
messagesProcessed += 1;
|
|
4291
|
+
continue;
|
|
4292
|
+
}
|
|
4293
|
+
|
|
4294
|
+
const requestKey =
|
|
4295
|
+
normalizeMessageKeyPart(row?.request_id) ||
|
|
4296
|
+
normalizeMessageKeyPart(row?.session_id) ||
|
|
4297
|
+
messageKey;
|
|
4298
|
+
const previous = qoderState.messages[messageKey];
|
|
4299
|
+
// Recompute request ownership from the complete ordered DB snapshot on
|
|
4300
|
+
// every pass. Qoder can attach token_info to an earlier assistant row only
|
|
4301
|
+
// after a later row was already counted; retaining the old per-message
|
|
4302
|
+
// owner in that case inflates one request to two conversations.
|
|
4303
|
+
const conversationCount = requestOwners.get(requestKey) === messageKey ? 1 : 0;
|
|
4304
|
+
|
|
4305
|
+
const currentTotals = {
|
|
4306
|
+
...currentBase,
|
|
4307
|
+
conversation_count: conversationCount,
|
|
4308
|
+
};
|
|
4309
|
+
const model = qoderModelFromRow(row);
|
|
4310
|
+
let projectKey = null;
|
|
4311
|
+
let projectRef = null;
|
|
4312
|
+
if (projectEnabled) {
|
|
4313
|
+
const projectPath = qoderProjectPath(row);
|
|
4314
|
+
if (projectPath) {
|
|
4315
|
+
const context = await resolveProjectContextForPath({
|
|
4316
|
+
startDir: projectPath,
|
|
4317
|
+
projectMetaCache,
|
|
4318
|
+
publicRepoCache,
|
|
4319
|
+
publicRepoResolver,
|
|
4320
|
+
projectState,
|
|
4321
|
+
});
|
|
4322
|
+
projectKey = context?.projectKey || null;
|
|
4323
|
+
projectRef = context?.projectRef || null;
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
|
|
4327
|
+
const previousTotals =
|
|
4328
|
+
previous?.totals && typeof previous.totals === "object" ? previous.totals : null;
|
|
4329
|
+
const unchanged =
|
|
4330
|
+
previousTotals &&
|
|
4331
|
+
totalsKey(previousTotals) === totalsKey(currentTotals) &&
|
|
4332
|
+
previous.bucketStart === bucketStart &&
|
|
4333
|
+
previous.model === model &&
|
|
4334
|
+
(previous.projectKey || null) === projectKey;
|
|
4335
|
+
if (!unchanged) {
|
|
4336
|
+
if (previousTotals && previous.bucketStart && previous.model) {
|
|
4337
|
+
const oldBucket = getHourlyBucket(
|
|
4338
|
+
hourlyState,
|
|
4339
|
+
"qoder",
|
|
4340
|
+
previous.model,
|
|
4341
|
+
previous.bucketStart,
|
|
4342
|
+
);
|
|
4343
|
+
subtractTotals(oldBucket.totals, previousTotals);
|
|
4344
|
+
touchedBuckets.add(bucketKey("qoder", previous.model, previous.bucketStart));
|
|
4345
|
+
if (projectEnabled && previous.projectKey) {
|
|
4346
|
+
const oldProjectBucket = getProjectBucket(
|
|
4347
|
+
projectState,
|
|
4348
|
+
previous.projectKey,
|
|
4349
|
+
"qoder",
|
|
4350
|
+
previous.bucketStart,
|
|
4351
|
+
previous.projectRef || null,
|
|
4352
|
+
);
|
|
4353
|
+
subtractTotals(oldProjectBucket.totals, previousTotals);
|
|
4354
|
+
projectTouchedBuckets.add(
|
|
4355
|
+
projectBucketKey(previous.projectKey, "qoder", previous.bucketStart),
|
|
4356
|
+
);
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
|
|
4360
|
+
const bucket = getHourlyBucket(hourlyState, "qoder", model, bucketStart);
|
|
4361
|
+
addTotals(bucket.totals, currentTotals);
|
|
4362
|
+
touchedBuckets.add(bucketKey("qoder", model, bucketStart));
|
|
4363
|
+
if (projectEnabled && projectKey) {
|
|
4364
|
+
const projectBucket = getProjectBucket(
|
|
4365
|
+
projectState,
|
|
4366
|
+
projectKey,
|
|
4367
|
+
"qoder",
|
|
4368
|
+
bucketStart,
|
|
4369
|
+
projectRef,
|
|
4370
|
+
);
|
|
4371
|
+
addTotals(projectBucket.totals, currentTotals);
|
|
4372
|
+
projectTouchedBuckets.add(projectBucketKey(projectKey, "qoder", bucketStart));
|
|
4373
|
+
}
|
|
4374
|
+
qoderState.messages[messageKey] = {
|
|
4375
|
+
totals: currentTotals,
|
|
4376
|
+
conversationCount,
|
|
4377
|
+
requestKey,
|
|
4378
|
+
bucketStart,
|
|
4379
|
+
model,
|
|
4380
|
+
projectKey,
|
|
4381
|
+
projectRef,
|
|
4382
|
+
updatedAt: new Date().toISOString(),
|
|
4383
|
+
};
|
|
4384
|
+
eventsAggregated += 1;
|
|
4385
|
+
}
|
|
4386
|
+
|
|
4387
|
+
messagesProcessed += 1;
|
|
4388
|
+
if (cb) {
|
|
4389
|
+
cb({
|
|
4390
|
+
index: index + 1,
|
|
4391
|
+
total: rows.length,
|
|
4392
|
+
messagesProcessed,
|
|
4393
|
+
eventsAggregated,
|
|
4394
|
+
bucketsQueued: touchedBuckets.size,
|
|
4395
|
+
});
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
|
|
4399
|
+
const bucketsQueued = await enqueueTouchedBuckets({
|
|
4400
|
+
queuePath,
|
|
4401
|
+
hourlyState,
|
|
4402
|
+
touchedBuckets,
|
|
4403
|
+
});
|
|
4404
|
+
const projectBucketsQueued = projectEnabled
|
|
4405
|
+
? await enqueueTouchedProjectBuckets({
|
|
4406
|
+
projectQueuePath,
|
|
4407
|
+
projectState,
|
|
4408
|
+
projectTouchedBuckets,
|
|
4409
|
+
})
|
|
4410
|
+
: 0;
|
|
4411
|
+
const updatedAt = new Date().toISOString();
|
|
4412
|
+
hourlyState.updatedAt = updatedAt;
|
|
4413
|
+
qoderState.updatedAt = updatedAt;
|
|
4414
|
+
cursors.hourly = hourlyState;
|
|
4415
|
+
cursors.qoder = qoderState;
|
|
4416
|
+
if (projectState) {
|
|
4417
|
+
projectState.updatedAt = updatedAt;
|
|
4418
|
+
cursors.projectHourly = projectState;
|
|
4419
|
+
}
|
|
4420
|
+
return { messagesProcessed, eventsAggregated, bucketsQueued, projectBucketsQueued };
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4043
4423
|
// Resolve project context from DB message (no file path available)
|
|
4044
4424
|
async function resolveProjectContextForDb({
|
|
4045
4425
|
msg,
|
|
@@ -13854,6 +14234,9 @@ module.exports = {
|
|
|
13854
14234
|
readOpencodeDbMessages,
|
|
13855
14235
|
readMimoDbMessages,
|
|
13856
14236
|
readZcodeDbMessages,
|
|
14237
|
+
resolveQoderDbPath,
|
|
14238
|
+
resolveQoderDbPaths,
|
|
14239
|
+
readQoderDbMessages,
|
|
13857
14240
|
resolveKiroDbPath,
|
|
13858
14241
|
resolveKiroJsonlPath,
|
|
13859
14242
|
resolveHermesPath,
|
|
@@ -13878,6 +14261,7 @@ module.exports = {
|
|
|
13878
14261
|
parseGeminiIncremental,
|
|
13879
14262
|
parseOpencodeIncremental,
|
|
13880
14263
|
parseOpencodeDbIncremental,
|
|
14264
|
+
parseQoderDbIncremental,
|
|
13881
14265
|
openclawCursorKey,
|
|
13882
14266
|
parseOpenclawIncremental,
|
|
13883
14267
|
parseCursorApiIncremental,
|
|
@@ -13965,6 +14349,7 @@ module.exports = {
|
|
|
13965
14349
|
// Exposed for regression tests covering cache-token accounting.
|
|
13966
14350
|
normalizeGeminiTokens,
|
|
13967
14351
|
normalizeOpencodeTokens,
|
|
14352
|
+
normalizeQoderTokens,
|
|
13968
14353
|
sameGeminiTotals,
|
|
13969
14354
|
diffGeminiTotals,
|
|
13970
14355
|
// Exposed so the queue-repair migration can mutate cursors state in the
|
package/src/lib/usage-limits.js
CHANGED
|
@@ -27,6 +27,8 @@ const {
|
|
|
27
27
|
const { fetchGrokLimits } = require("./grok-limits");
|
|
28
28
|
const { fetchZcodeLimits } = require("./zcode-limits");
|
|
29
29
|
const { fetchOpencodeGoLimits } = require("./opencode-go-limits");
|
|
30
|
+
const { fetchQoderLimits } = require("./qoder-limits");
|
|
31
|
+
const { fetchProviderServiceStatus } = require("./provider-status");
|
|
30
32
|
const { readSqliteJsonRows, readSqliteJsonRowsAsync } = require("./sqlite-reader");
|
|
31
33
|
|
|
32
34
|
const execFileAsync = promisify(cp.execFile);
|
|
@@ -2909,7 +2911,7 @@ function toTitleCase(s) {
|
|
|
2909
2911
|
// leading brand word and Title Case the rest.
|
|
2910
2912
|
function normalizePlanLabel(raw, brand) {
|
|
2911
2913
|
if (raw == null) return null;
|
|
2912
|
-
let s = String(raw).trim();
|
|
2914
|
+
let s = String(raw).replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
2913
2915
|
if (!s) return null;
|
|
2914
2916
|
const lower = s.toLowerCase();
|
|
2915
2917
|
if (["free", "none", "unknown"].includes(lower)) return null;
|
|
@@ -3052,7 +3054,7 @@ async function fetchUsageLimitsUncached({
|
|
|
3052
3054
|
: null;
|
|
3053
3055
|
|
|
3054
3056
|
const providerFetch = withFetchTimeout(fetchImpl, providerTimeoutMs);
|
|
3055
|
-
const [claudeResult, codexResult, cursor, kimi, gemini, kiro, antigravity, copilot, grok, zcode, opencodeGo] = await Promise.all([
|
|
3057
|
+
const [claudeResult, codexResult, cursor, kimi, gemini, kiro, antigravity, copilot, grok, zcode, opencodeGo, qoder, claudeServiceStatus] = await Promise.all([
|
|
3056
3058
|
claudeToken && !freshClaudeCache && !claudeRetryAtMs
|
|
3057
3059
|
? withProviderTimeout(fetchClaudeUsageLimits(claudeToken, { fetchImpl: providerFetch, maxAttempts: 1 }), "Claude", providerTimeoutMs).then(
|
|
3058
3060
|
(value) => ({ status: "fulfilled", value }),
|
|
@@ -3088,6 +3090,23 @@ async function fetchUsageLimitsUncached({
|
|
|
3088
3090
|
// OPENCODE_GO_AUTH_COOKIE is set. See src/lib/opencode-go-limits.js.
|
|
3089
3091
|
withProviderTimeout(fetchOpencodeGoLimits({ home, env, fetchImpl: providerFetch }), "OpenCode Go", providerTimeoutMs)
|
|
3090
3092
|
.catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
|
|
3093
|
+
withProviderTimeout(
|
|
3094
|
+
fetchQoderLimits({
|
|
3095
|
+
home,
|
|
3096
|
+
env,
|
|
3097
|
+
platform,
|
|
3098
|
+
securityRunner,
|
|
3099
|
+
fetchImpl: providerFetch,
|
|
3100
|
+
}),
|
|
3101
|
+
"Qoder",
|
|
3102
|
+
providerTimeoutMs,
|
|
3103
|
+
).catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
|
|
3104
|
+
// Public status-page probe (fail-soft, own 5-min cache in provider-status.js).
|
|
3105
|
+
// Only probed for configured accounts — without a token the Claude section
|
|
3106
|
+
// never renders, so the reading would have nowhere to go.
|
|
3107
|
+
claudeToken
|
|
3108
|
+
? fetchProviderServiceStatus("claude", { fetchImpl, nowMs })
|
|
3109
|
+
: Promise.resolve(null),
|
|
3091
3110
|
]);
|
|
3092
3111
|
|
|
3093
3112
|
let claude;
|
|
@@ -3154,6 +3173,15 @@ async function fetchUsageLimitsUncached({
|
|
|
3154
3173
|
}
|
|
3155
3174
|
}
|
|
3156
3175
|
|
|
3176
|
+
// Attach the service-status reading AFTER assembly so it rides on every path
|
|
3177
|
+
// (live, fresh-cache, stale-cache, error) but never gets persisted by
|
|
3178
|
+
// writeClaudeLimitsCache above — a disk cache must not resurrect an incident
|
|
3179
|
+
// banner hours later. Only active incidents ship; "none" is omitted so the
|
|
3180
|
+
// client renders nothing in the happy path.
|
|
3181
|
+
if (claude.configured && claudeServiceStatus && claudeServiceStatus.indicator !== "none") {
|
|
3182
|
+
claude.service_status = claudeServiceStatus;
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3157
3185
|
let codex;
|
|
3158
3186
|
if (!codexToken) {
|
|
3159
3187
|
codex = { configured: false };
|
|
@@ -3212,6 +3240,7 @@ async function fetchUsageLimitsUncached({
|
|
|
3212
3240
|
grok: withPlanLabel(grok, null, "Grok"),
|
|
3213
3241
|
zcode: withPlanLabel(zcode, zcode.plan_label, "ZCode"),
|
|
3214
3242
|
opencodeGo: withPlanLabel(opencodeGo, opencodeGo?.plan_label, "OpenCode Go"),
|
|
3243
|
+
qoder: withPlanLabel(qoder, qoder?.plan_label, "Qoder"),
|
|
3215
3244
|
};
|
|
3216
3245
|
|
|
3217
3246
|
for (const [providerName, provider] of Object.entries(data)) {
|
|
@@ -3270,4 +3299,5 @@ module.exports = {
|
|
|
3270
3299
|
fetchGrokLimits,
|
|
3271
3300
|
fetchZcodeLimits,
|
|
3272
3301
|
fetchOpencodeGoLimits,
|
|
3302
|
+
fetchQoderLimits,
|
|
3273
3303
|
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{j as r,d as e}from"./__vite-browser-external-UqlSqI-1.js";function n({children:i,title:a,subtitle:o,className:s="",bodyClassName:d=""}){return r.jsxs("div",{className:e("rounded-xl border border-oai-gray-200 dark:border-oai-gray-800 bg-white dark:bg-oai-gray-900 transition-colors duration-200",s),children:[(a||o)&&r.jsxs("div",{className:"px-5 py-4 border-b border-oai-gray-200 dark:border-oai-gray-800 transition-colors duration-200",children:[a&&r.jsx("h3",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide transition-colors duration-200",children:a}),o&&r.jsx("p",{className:"text-sm text-oai-gray-500 dark:text-oai-gray-300 mt-1 transition-colors duration-200",children:o})]}),r.jsx("div",{className:`p-5 ${d}`,children:i})]})}export{n as C};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as n,z as a}from"./__vite-browser-external-UqlSqI-1.js";import{u as p}from"./DialogTitle-C1ZeQgf2.js";import{u as c}from"./useScrollLock-QozsdFng.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,{store:i}=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};
|