tokentracker-cli 1.4.11 → 1.4.12
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 +5 -4
- package/README.ja.md +5 -4
- package/README.ko.md +5 -4
- package/README.md +6 -4
- package/README.zh-CN.md +5 -4
- package/dashboard/dist/assets/{AchievementBadge-DALWiLQQ.js → AchievementBadge-BwrL59nq.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-CjWobmn1.js → AchievementsPage-DG0dvG3d.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-CTryGWII.js → AchievementsSection-NrjlZjZK.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-D4lMxewA.js → BadgeDetailModal-BjgBAb-0.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-CsLNf9WH.js → BotAnimated-D7gMmkaM.js} +1 -1
- package/dashboard/dist/assets/{Card-BOnNTrmF.js → Card-CgvwIS5W.js} +1 -1
- package/dashboard/dist/assets/{CloudCheckoutPage-CrY15xqT.js → CloudCheckoutPage-DKDpT0HI.js} +1 -1
- package/dashboard/dist/assets/{CloudPage-hNSCMzVG.js → CloudPage-BOfqPAAH.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-CE4UfPXk.js → CommandPalette-BfRKy_dn.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-CLUegC6z.js → CommunityStatsModal-BcVSszit.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-D3wwfiYB.js → ConfirmModal-Be4EVpPT.js} +1 -1
- package/dashboard/dist/assets/{Controls-B_ta_8zw.js → Controls-vrG8LLBe.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-RevVPZFt.js → DashboardPage-DvhF72XX.js} +1 -1
- package/dashboard/dist/assets/{DateRangePopover-CYud1FKu.js → DateRangePopover-CZVMdPEq.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-D_eSLfM3.js → DevicePage-BeNIjTqa.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-Bu1ct4DA.js → DialogClose-BaNPZ3Bu.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-LHQ-SC4e.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-BJxM52J1.js → DialogTitle-B3IwNXro.js} +1 -1
- package/dashboard/dist/assets/{ElectricLogo-DYVZebgD.js → ElectricLogo-DC-03HL_.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-CVaq5ALh.js → FadeIn-DhUnSf2V.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-BCwYSguq.js → HeaderGithubStar-BiWRLl7V.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-YsG-TdOX.js → HoverTooltip-DLPiBqDu.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-C0qiyZXr.js → IpCheckPage-DKjg0V9L.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-BlRJWwvx.js → LandingPage-XQuTnBMQ.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardPage-DcCCKL2q.js → LeaderboardPage-xAbo5mzo.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProBadge-I_oW0KCc.js → LeaderboardProBadge-mMiEeaYS.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfileModal-DM5Vc7MB.js → LeaderboardProfileModal-B7q0Kk6B.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-DnYBBGNe.js → LeaderboardProfilePage-Bru_6wgX.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-Cyy3PI8p.js → LimitsPage-DQH6ziz4.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-083BoEX9.js → LocalOnlyNotice-B-lxih4W.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-CN1db2Rj.js → LoginCard-ChNsRYE2.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-Dfi7_ory.js → LoginModal-CRw3PgMi.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-BzMdiYlx.js → LoginPage-CVsbYn-h.js} +1 -1
- package/dashboard/dist/assets/{PetPage-CsE95lfJ.js → PetPage-BwEnQLLY.js} +2 -2
- package/dashboard/dist/assets/{PopoverPopup-U7DTTTE0.js → PopoverPopup-CDOtxSL8.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-CnLfT5d9.js → ProviderIcon-Dsu1p6gG.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-De2RzCj-.js → ResetPasswordPage-BzpaSORs.js} +1 -1
- package/dashboard/dist/assets/{Select-BS5MWHdB.js → Select-62UH-yLs.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-D3i379K-.js → SelectItemText-aTexKmQ0.js} +1 -1
- package/dashboard/dist/assets/{SelfHostPage-CTL0dnp5.js → SelfHostPage-CmhV0dms.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-BmEvOk3B.js → ServiceStatusPage-CLWO33w1.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-DIog3JA_.js → SessionsPage-BNCDKb58.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-Cgpiwh-C.js → SettingsPage-DybHncPO.js} +3 -3
- package/dashboard/dist/assets/{SkillsPage-DpagshiB.js → SkillsPage-BV5R3VhT.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-Do1U_-E3.js → TokenGalaxy-mNNAo6Ai.js} +1 -1
- package/dashboard/dist/assets/{TooltipPopup-MOAnEqf3.js → TooltipPopup-D00SVpLP.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-B55RUbQm.js → TrendMonitor-aNIpzAO-.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-CehbGk-6.js → WidgetsPage-COjZIMo8.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-Cqt-U0Zx.js → WrappedPage-af-bDIfB.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-CR0vo15z.js → __vite-browser-external-KHpR2q-A.js} +2 -2
- package/dashboard/dist/assets/{agent-logos-DuW-DWAl.js → agent-logos-D9Uq5b6y.js} +1 -1
- package/dashboard/dist/assets/{arrow-left-DIvQjuji.js → arrow-left-Pt53BsA4.js} +1 -1
- package/dashboard/dist/assets/{arrow-right-BURLycHb.js → arrow-right-stBCZPot.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-Bl1QMMkY.js → arrow-up-right-ltUBOD-o.js} +1 -1
- package/dashboard/dist/assets/{calendar-days-CmIWJC3A.js → calendar-days-BMEtkPFp.js} +1 -1
- package/dashboard/dist/assets/{check-DB2ZTWor.js → check-ax5wQf5Q.js} +1 -1
- package/dashboard/dist/assets/{chevron-down-BflN3NJt.js → chevron-down-gS62rNQt.js} +1 -1
- package/dashboard/dist/assets/{cloud-checkout-BugQolSC.js → cloud-checkout-DDtdbz7C.js} +1 -1
- package/dashboard/dist/assets/{composite-DJsqXsjA.js → composite-DVm8byy8.js} +1 -1
- package/dashboard/dist/assets/{copy-CtB-GPN2.js → copy-VbASSQO1.js} +1 -1
- package/dashboard/dist/assets/{download-x5Botpqc.js → download-D1g6OCRI.js} +1 -1
- package/dashboard/dist/assets/{external-link-DH9sRkJW.js → external-link-CGUQrKDL.js} +1 -1
- package/dashboard/dist/assets/{flame-DEcsokoL.js → flame-Dlvc-qIH.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-BH0jc1MG.js → format-tokens-CB1QfZsj.js} +1 -1
- package/dashboard/dist/assets/{gift-BZk9muk9.js → gift-DydflDeL.js} +1 -1
- package/dashboard/dist/assets/{icons-BMf9-bfS.js → icons-B_xYF8_-.js} +1 -1
- package/dashboard/dist/assets/{index-axJYocSc.js → index-BRNltZbP.js} +1 -1
- package/dashboard/dist/assets/{index-BEtrik3j.js → index-ByXXZlPn.js} +1 -1
- package/dashboard/dist/assets/{index-Cm-9apOR.js → index-CuLrp2D2.js} +1 -1
- package/dashboard/dist/assets/{index-TErx-xPb.js → index-cidqWI7C.js} +1 -1
- package/dashboard/dist/assets/{info-DWdPAxu8.js → info-D6-LIywq.js} +1 -1
- package/dashboard/dist/assets/{link-2-c3ZZd-w-.js → link-2-BbuXAF5y.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-BKdtxfky.js → loader-circle-DAeCHAyj.js} +1 -1
- package/dashboard/dist/assets/{local-usage-transfer-BFTJyQl3.js → local-usage-transfer-BejRwzi4.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-C3yIfOiP.js → modifiers.esm-CV2S03sv.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-DbdL7Mei.js → monitor-smartphone-Dj_u5s6h.js} +1 -1
- package/dashboard/dist/assets/{play-qtWt2ArI.js → play-DrcSicai.js} +1 -1
- package/dashboard/dist/assets/{plus-C_-__5vi.js → plus-BwYQW7nf.js} +1 -1
- package/dashboard/dist/assets/{provider-display-IKHnhvaj.js → provider-display-Bgfy-2rn.js} +2 -2
- package/dashboard/dist/assets/{react-H6DfeMUm.js → react-BXby2gfP.js} +1 -1
- package/dashboard/dist/assets/{safePolygon-OEQJQTVa.js → safePolygon-B8DY85mD.js} +1 -1
- package/dashboard/dist/assets/{search-MZE76qNU.js → search-SCMosATz.js} +1 -1
- package/dashboard/dist/assets/{server-CUWYLMjG.js → server-DoD96f86.js} +1 -1
- package/dashboard/dist/assets/{shield-check-lA0Ja0RB.js → shield-check-CESJcEp7.js} +1 -1
- package/dashboard/dist/assets/{skills-api-CJJIPvSe.js → skills-api-DE71Ylhd.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-DTnvt1IN.js → sortable.esm-EgVv2jPt.js} +1 -1
- package/dashboard/dist/assets/{store-CC0YKrxA.js → store-mMQPBMoh.js} +1 -1
- package/dashboard/dist/assets/{terminal-yc2l_-3m.js → terminal-DxNM-IG2.js} +1 -1
- package/dashboard/dist/assets/{trash-Dl9f-o2h.js → trash-rywOpT2_.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-CZCKWCO7.js → use-community-stats-BQw06Twe.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-DTrOu0gD.js → use-latest-request-guard-BvouGm-c.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-DGqP231e.js → use-limits-display-prefs-C5_9TDSy.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-BEATnxB6.js → use-native-settings-BxuddjkP.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-C_HTFP5C.js → use-ordered-list-Cwqc5XA8.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-DU3FvZwM.js +1 -0
- package/dashboard/dist/assets/{use-transform-DM0QGq_O.js → use-transform-Dl-SvALB.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-BnjBG2fM.js → use-usage-limits-BqmHYYc3.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-xQrRFokb.js → useCurrency-B1XK_Z0N.js} +1 -1
- package/dashboard/dist/assets/{usePositioner-DkVdsypM.js → usePositioner-BvqDMGZo.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-CvA_kIPe.js → useTokenFormat-pPWJeWRe.js} +1 -1
- package/dashboard/dist/assets/{zap-BJNS4i_u.js → zap-DKYjQi1B.js} +1 -1
- package/dashboard/dist/index.html +13 -11
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/llms.txt +3 -2
- package/dashboard/dist/share.html +1 -1
- package/package.json +2 -2
- package/src/commands/init.js +1 -0
- package/src/commands/status.js +10 -0
- package/src/commands/sync.js +24 -0
- package/src/lib/pricing/curated-overrides.json +66 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +277 -0
- package/src/lib/trae-cn-config.js +36 -20
- package/dashboard/dist/assets/DialogDescription-GUHQVNGf.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-CfCWjE-a.js +0 -1
package/src/lib/rollout.js
CHANGED
|
@@ -14107,6 +14107,279 @@ async function parseLmstudioIncremental({
|
|
|
14107
14107
|
return { recordsProcessed, eventsAggregated, bucketsQueued };
|
|
14108
14108
|
}
|
|
14109
14109
|
|
|
14110
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
14111
|
+
// OpenSquilla
|
|
14112
|
+
//
|
|
14113
|
+
// Read only the finalized usage ledger's model rows. Transcript entries and
|
|
14114
|
+
// message content are not needed for token attribution and are never selected.
|
|
14115
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
14116
|
+
|
|
14117
|
+
const OPENSQUILLA_SOURCE = "opensquilla";
|
|
14118
|
+
|
|
14119
|
+
function resolveOpenSquillaPath(value, home) {
|
|
14120
|
+
const raw = typeof value === "string" ? value.trim() : "";
|
|
14121
|
+
if (!raw) return null;
|
|
14122
|
+
if (raw === "~") return path.resolve(home);
|
|
14123
|
+
if (raw.startsWith("~/") || raw.startsWith("~\\")) {
|
|
14124
|
+
return path.resolve(home, raw.slice(2));
|
|
14125
|
+
}
|
|
14126
|
+
return path.resolve(raw);
|
|
14127
|
+
}
|
|
14128
|
+
|
|
14129
|
+
function resolveOpenSquillaDbPaths(env = process.env, deps = {}) {
|
|
14130
|
+
const home = deps.home || env.HOME || env.USERPROFILE || os.homedir();
|
|
14131
|
+
const platform = deps.platform || process.platform;
|
|
14132
|
+
const dbOverride = resolveOpenSquillaPath(env.TOKENTRACKER_OPENSQUILLA_DB, home);
|
|
14133
|
+
if (dbOverride) return [dbOverride];
|
|
14134
|
+
|
|
14135
|
+
const gatewayStateDir = resolveOpenSquillaPath(env.OPENSQUILLA_GATEWAY_STATE_DIR, home);
|
|
14136
|
+
if (gatewayStateDir) return [path.join(gatewayStateDir, "sessions.db")];
|
|
14137
|
+
|
|
14138
|
+
const stateHome = resolveOpenSquillaPath(env.OPENSQUILLA_STATE_DIR, home);
|
|
14139
|
+
if (stateHome) return [path.join(stateHome, "state", "sessions.db")];
|
|
14140
|
+
|
|
14141
|
+
const profileHomeOverride = typeof env.OPENSQUILLA_HOME === "string"
|
|
14142
|
+
? env.OPENSQUILLA_HOME.trim()
|
|
14143
|
+
: "";
|
|
14144
|
+
const profileOverride = typeof env.OPENSQUILLA_PROFILE === "string"
|
|
14145
|
+
? env.OPENSQUILLA_PROFILE.trim()
|
|
14146
|
+
: "";
|
|
14147
|
+
if (profileHomeOverride || profileOverride) {
|
|
14148
|
+
const profilesRoot = resolveOpenSquillaPath(
|
|
14149
|
+
profileHomeOverride || path.join(home, ".opensquilla", "profiles"),
|
|
14150
|
+
home,
|
|
14151
|
+
);
|
|
14152
|
+
const profile = profileOverride || "default";
|
|
14153
|
+
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/i.test(profile)) return [];
|
|
14154
|
+
return [path.join(profilesRoot, profile, "state", "sessions.db")];
|
|
14155
|
+
}
|
|
14156
|
+
|
|
14157
|
+
const paths = [path.join(home, ".opensquilla", "state", "sessions.db")];
|
|
14158
|
+
let userDataRoot;
|
|
14159
|
+
if (platform === "win32") {
|
|
14160
|
+
userDataRoot = env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
14161
|
+
} else if (platform === "darwin") {
|
|
14162
|
+
userDataRoot = path.join(home, "Library", "Application Support");
|
|
14163
|
+
} else {
|
|
14164
|
+
userDataRoot = env.XDG_CONFIG_HOME || path.join(home, ".config");
|
|
14165
|
+
}
|
|
14166
|
+
paths.unshift(path.join(
|
|
14167
|
+
userDataRoot,
|
|
14168
|
+
"@opensquilla",
|
|
14169
|
+
"desktop-electron",
|
|
14170
|
+
"opensquilla",
|
|
14171
|
+
"state",
|
|
14172
|
+
"sessions.db",
|
|
14173
|
+
));
|
|
14174
|
+
|
|
14175
|
+
const seen = new Set();
|
|
14176
|
+
return paths.filter((dbPath) => {
|
|
14177
|
+
const resolved = path.resolve(dbPath);
|
|
14178
|
+
const key = platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
14179
|
+
if (seen.has(key)) return false;
|
|
14180
|
+
seen.add(key);
|
|
14181
|
+
return true;
|
|
14182
|
+
});
|
|
14183
|
+
}
|
|
14184
|
+
|
|
14185
|
+
async function readOpenSquillaUsageRows(dbPath, sqliteOptions = {}) {
|
|
14186
|
+
if (!dbPath || !fssync.existsSync(dbPath)) return [];
|
|
14187
|
+
const options = {
|
|
14188
|
+
label: "OpenSquilla",
|
|
14189
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
14190
|
+
timeout: 30_000,
|
|
14191
|
+
...sqliteOptions,
|
|
14192
|
+
readOnly: true,
|
|
14193
|
+
throwOnReadFailure: true,
|
|
14194
|
+
};
|
|
14195
|
+
const tables = new Set(
|
|
14196
|
+
(await readSqliteJsonRowsAsync(
|
|
14197
|
+
dbPath,
|
|
14198
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name IN ('usage_events','usage_event_items')",
|
|
14199
|
+
options,
|
|
14200
|
+
)).map((row) => row?.name).filter(Boolean),
|
|
14201
|
+
);
|
|
14202
|
+
if (!tables.has("usage_events") || !tables.has("usage_event_items")) return [];
|
|
14203
|
+
const sql = [
|
|
14204
|
+
"SELECT e.event_id, e.completed_at_ms, i.ordinal,",
|
|
14205
|
+
"COALESCE(i.model, e.model) AS model, i.input_tokens, i.output_tokens,",
|
|
14206
|
+
"i.reasoning_tokens, i.cache_read_tokens, i.cache_write_tokens, i.total_tokens",
|
|
14207
|
+
"FROM usage_events AS e",
|
|
14208
|
+
"JOIN usage_event_items AS i ON i.event_id = e.event_id",
|
|
14209
|
+
"WHERE e.status = 'finalized' AND e.completed_at_ms IS NOT NULL",
|
|
14210
|
+
"ORDER BY e.completed_at_ms, e.event_id, i.ordinal",
|
|
14211
|
+
].join(" ");
|
|
14212
|
+
return readSqliteJsonRowsAsync(dbPath, sql, options);
|
|
14213
|
+
}
|
|
14214
|
+
|
|
14215
|
+
function normalizeOpenSquillaUsageRow(row) {
|
|
14216
|
+
const eventId = typeof row?.event_id === "string" ? row.event_id.trim() : "";
|
|
14217
|
+
if (!eventId || row?.ordinal == null) return null;
|
|
14218
|
+
|
|
14219
|
+
// OpenSquilla stores input as a cache-inclusive total and reasoning as an
|
|
14220
|
+
// output subset; normalize both before writing TokenTracker queue rows.
|
|
14221
|
+
const totals = normalizeLocalStudioTokens({
|
|
14222
|
+
input_tokens: row.input_tokens,
|
|
14223
|
+
output_tokens: row.output_tokens,
|
|
14224
|
+
reasoning_tokens: row.reasoning_tokens,
|
|
14225
|
+
cached_tokens: row.cache_read_tokens,
|
|
14226
|
+
cache_creation_input_tokens: row.cache_write_tokens,
|
|
14227
|
+
total_tokens: row.total_tokens,
|
|
14228
|
+
});
|
|
14229
|
+
const timestampMs = coerceEpochMs(row.completed_at_ms);
|
|
14230
|
+
const bucketStart = timestampMs
|
|
14231
|
+
? toUtcHalfHourStart(new Date(timestampMs).toISOString())
|
|
14232
|
+
: null;
|
|
14233
|
+
if (!totals || !bucketStart) return null;
|
|
14234
|
+
|
|
14235
|
+
return {
|
|
14236
|
+
key: "opensquilla:" + eventId + ":" + String(toNonNegativeInt(row.ordinal)),
|
|
14237
|
+
model: normalizeModelInput(row.model) || DEFAULT_MODEL,
|
|
14238
|
+
bucketStart,
|
|
14239
|
+
totals,
|
|
14240
|
+
};
|
|
14241
|
+
}
|
|
14242
|
+
|
|
14243
|
+
async function parseOpenSquillaIncremental({
|
|
14244
|
+
dbPaths,
|
|
14245
|
+
cursors,
|
|
14246
|
+
queuePath,
|
|
14247
|
+
onProgress,
|
|
14248
|
+
env,
|
|
14249
|
+
sqliteOptions,
|
|
14250
|
+
} = {}) {
|
|
14251
|
+
await ensureDir(path.dirname(queuePath));
|
|
14252
|
+
const priorState = cursors.opensquilla && typeof cursors.opensquilla === "object"
|
|
14253
|
+
? cursors.opensquilla
|
|
14254
|
+
: {};
|
|
14255
|
+
// Copy before mutate so a failed queue append leaves committed cursors intact.
|
|
14256
|
+
const messages = priorState.messages && typeof priorState.messages === "object"
|
|
14257
|
+
? { ...priorState.messages }
|
|
14258
|
+
: {};
|
|
14259
|
+
const fingerprints = priorState.fingerprints && typeof priorState.fingerprints === "object"
|
|
14260
|
+
? { ...priorState.fingerprints }
|
|
14261
|
+
: {};
|
|
14262
|
+
const requestedPaths = Array.isArray(dbPaths) && dbPaths.length
|
|
14263
|
+
? dbPaths
|
|
14264
|
+
: resolveOpenSquillaDbPaths(env || process.env);
|
|
14265
|
+
const seenPaths = new Set();
|
|
14266
|
+
const availablePaths = requestedPaths.filter((dbPath) => {
|
|
14267
|
+
if (typeof dbPath !== "string" || !dbPath.trim() || !fssync.existsSync(dbPath)) return false;
|
|
14268
|
+
const resolved = path.resolve(dbPath);
|
|
14269
|
+
const key = process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
14270
|
+
if (seenPaths.has(key)) return false;
|
|
14271
|
+
seenPaths.add(key);
|
|
14272
|
+
return true;
|
|
14273
|
+
});
|
|
14274
|
+
// No currently readable DB is treated as temporary unavailability — keep
|
|
14275
|
+
// prior observations rather than wiping totals.
|
|
14276
|
+
if (availablePaths.length === 0) {
|
|
14277
|
+
cursors.opensquilla = { ...priorState, messages, fingerprints, updatedAt: new Date().toISOString() };
|
|
14278
|
+
return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
14279
|
+
}
|
|
14280
|
+
|
|
14281
|
+
const pathFingerprints = new Map();
|
|
14282
|
+
let databaseChanged = false;
|
|
14283
|
+
const currentPathKeys = new Set();
|
|
14284
|
+
for (const dbPath of availablePaths) {
|
|
14285
|
+
const key = path.resolve(dbPath);
|
|
14286
|
+
currentPathKeys.add(key);
|
|
14287
|
+
const fingerprint = sqliteSidecarFingerprint(dbPath);
|
|
14288
|
+
pathFingerprints.set(key, fingerprint);
|
|
14289
|
+
if (!fingerprints[key] || !sameSqliteFingerprint(fingerprint, fingerprints[key])) {
|
|
14290
|
+
databaseChanged = true;
|
|
14291
|
+
}
|
|
14292
|
+
}
|
|
14293
|
+
// A removed Desktop/CLI DB must also trigger reconciliation so orphaned
|
|
14294
|
+
// observations can be retracted (fingerprint-only checks would miss this).
|
|
14295
|
+
for (const key of Object.keys(fingerprints)) {
|
|
14296
|
+
if (!currentPathKeys.has(key)) {
|
|
14297
|
+
databaseChanged = true;
|
|
14298
|
+
break;
|
|
14299
|
+
}
|
|
14300
|
+
}
|
|
14301
|
+
if (!databaseChanged) {
|
|
14302
|
+
return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
14303
|
+
}
|
|
14304
|
+
|
|
14305
|
+
// The Desktop profile is first in the default path list. If a migrated CLI
|
|
14306
|
+
// profile retains the same event IDs, prefer the active Desktop copy and
|
|
14307
|
+
// count each model row only once.
|
|
14308
|
+
const rowsByEventItem = new Map();
|
|
14309
|
+
for (const dbPath of availablePaths) {
|
|
14310
|
+
const dbRows = await readOpenSquillaUsageRows(dbPath, sqliteOptions);
|
|
14311
|
+
for (const row of dbRows) {
|
|
14312
|
+
const eventId = typeof row?.event_id === "string" ? row.event_id.trim() : "";
|
|
14313
|
+
if (!eventId || row?.ordinal == null) continue;
|
|
14314
|
+
const key = eventId + ":" + String(toNonNegativeInt(row.ordinal));
|
|
14315
|
+
if (!rowsByEventItem.has(key)) rowsByEventItem.set(key, row);
|
|
14316
|
+
}
|
|
14317
|
+
}
|
|
14318
|
+
const rows = Array.from(rowsByEventItem.values());
|
|
14319
|
+
// structuredClone so a failed enqueueTouchedBuckets cannot leave mutated
|
|
14320
|
+
// totals committed while opensquilla.messages stays unchanged (retry would
|
|
14321
|
+
// subtract again). Same pattern as TRAE / session reconcilers.
|
|
14322
|
+
const hourlyState = structuredClone(normalizeHourlyState(cursors?.hourly));
|
|
14323
|
+
const touchedBuckets = new Set();
|
|
14324
|
+
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
14325
|
+
let eventsAggregated = 0;
|
|
14326
|
+
const currentKeys = new Set();
|
|
14327
|
+
for (let index = 0; index < rows.length; index++) {
|
|
14328
|
+
const event = normalizeOpenSquillaUsageRow(rows[index]);
|
|
14329
|
+
if (event) currentKeys.add(event.key);
|
|
14330
|
+
if (event && reconcilePassiveUsageEvent({
|
|
14331
|
+
event,
|
|
14332
|
+
source: OPENSQUILLA_SOURCE,
|
|
14333
|
+
messages,
|
|
14334
|
+
hourlyState,
|
|
14335
|
+
touchedBuckets,
|
|
14336
|
+
})) eventsAggregated += 1;
|
|
14337
|
+
if (cb) {
|
|
14338
|
+
cb({
|
|
14339
|
+
index: index + 1,
|
|
14340
|
+
total: rows.length,
|
|
14341
|
+
recordsProcessed: index + 1,
|
|
14342
|
+
eventsAggregated,
|
|
14343
|
+
bucketsQueued: touchedBuckets.size,
|
|
14344
|
+
});
|
|
14345
|
+
}
|
|
14346
|
+
}
|
|
14347
|
+
|
|
14348
|
+
// Retract observations that disappeared with a removed database (or were
|
|
14349
|
+
// deleted from the remaining DB). Overlapping event IDs keep Desktop-first
|
|
14350
|
+
// totals via reconcilePassiveUsageEvent above.
|
|
14351
|
+
for (const key of Object.keys(messages)) {
|
|
14352
|
+
if (!key.startsWith("opensquilla:") || currentKeys.has(key)) continue;
|
|
14353
|
+
const previous = messages[key];
|
|
14354
|
+
if (previous?.model && previous?.bucketStart && previous?.totals) {
|
|
14355
|
+
const oldBucket = getHourlyBucket(
|
|
14356
|
+
hourlyState,
|
|
14357
|
+
OPENSQUILLA_SOURCE,
|
|
14358
|
+
previous.model,
|
|
14359
|
+
previous.bucketStart,
|
|
14360
|
+
);
|
|
14361
|
+
subtractTotals(oldBucket.totals, previous.totals);
|
|
14362
|
+
touchedBuckets.add(bucketKey(OPENSQUILLA_SOURCE, previous.model, previous.bucketStart));
|
|
14363
|
+
}
|
|
14364
|
+
delete messages[key];
|
|
14365
|
+
eventsAggregated += 1;
|
|
14366
|
+
}
|
|
14367
|
+
|
|
14368
|
+
const nextFingerprints = {};
|
|
14369
|
+
for (const dbPath of availablePaths) {
|
|
14370
|
+
const key = path.resolve(dbPath);
|
|
14371
|
+
const before = pathFingerprints.get(key);
|
|
14372
|
+
const after = sqliteSidecarFingerprint(dbPath);
|
|
14373
|
+
nextFingerprints[key] = sameSqliteFingerprint(before, after) ? after : before;
|
|
14374
|
+
}
|
|
14375
|
+
const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
|
|
14376
|
+
const updatedAt = new Date().toISOString();
|
|
14377
|
+
hourlyState.updatedAt = updatedAt;
|
|
14378
|
+
cursors.hourly = hourlyState;
|
|
14379
|
+
cursors.opensquilla = { messages, fingerprints: nextFingerprints, updatedAt };
|
|
14380
|
+
return { recordsProcessed: rows.length, eventsAggregated, bucketsQueued };
|
|
14381
|
+
}
|
|
14382
|
+
|
|
14110
14383
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
14111
14384
|
// Unsloth Studio
|
|
14112
14385
|
//
|
|
@@ -25865,6 +26138,10 @@ module.exports = {
|
|
|
25865
26138
|
normalizeLocalStudioTokens,
|
|
25866
26139
|
readLmstudioFileRecords,
|
|
25867
26140
|
parseLmstudioIncremental,
|
|
26141
|
+
resolveOpenSquillaDbPaths,
|
|
26142
|
+
readOpenSquillaUsageRows,
|
|
26143
|
+
normalizeOpenSquillaUsageRow,
|
|
26144
|
+
parseOpenSquillaIncremental,
|
|
25868
26145
|
resolveUnslothDbPath,
|
|
25869
26146
|
readUnslothUsageRows,
|
|
25870
26147
|
normalizeUnslothUsageRow,
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* TRAE Work CN (国内版) config + usage fetch. Phase A: macOS + Windows + test-injected
|
|
5
|
-
* paths only. Resolves the CN
|
|
5
|
+
* paths only. Resolves the CN app data directory (TRAE Work CN or the classic
|
|
6
|
+
* Trae CN client), reads the `iCubeAuthInfo://icube.cloudide`
|
|
6
7
|
* blob from User/globalStorage/storage.json (plaintext JSON or Base64 tc v5),
|
|
7
8
|
* and talks to the TRAE CN usage API. JWTs / refresh tokens are never
|
|
8
9
|
* persisted, never logged, and never put into error messages; a
|
|
@@ -31,7 +32,7 @@ function isTraeCnUsageEnabled(env = process.env) {
|
|
|
31
32
|
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
32
33
|
return normalized === "1" || normalized === "true";
|
|
33
34
|
}
|
|
34
|
-
const
|
|
35
|
+
const TRAE_CN_APP_DIRS = ["TRAE SOLO CN", "Trae CN"];
|
|
35
36
|
const TRAE_CN_AUTH_KEY = "iCubeAuthInfo://icube.cloudide";
|
|
36
37
|
|
|
37
38
|
const TRAE_CN_USAGE_URL = "https://api.trae.cn/trae/api/v1/pay/query_user_usage_group_by_session";
|
|
@@ -77,30 +78,46 @@ const TRAE_CN_KG = Buffer.from([
|
|
|
77
78
|
// Path resolution
|
|
78
79
|
// ---------------------------------------------------------------------------
|
|
79
80
|
|
|
80
|
-
function
|
|
81
|
-
const override = env[TRAE_CN_HOME_ENV];
|
|
82
|
-
if (typeof override === "string" && override.trim()) {
|
|
83
|
-
return path.resolve(override.trim());
|
|
84
|
-
}
|
|
81
|
+
function traeCnDataParent({ env = process.env, home = os.homedir(), platform = process.platform } = {}) {
|
|
85
82
|
if (platform === "darwin") {
|
|
86
|
-
return path.join(home, "Library", "Application Support"
|
|
83
|
+
return path.join(home, "Library", "Application Support");
|
|
87
84
|
}
|
|
88
85
|
if (platform === "win32") {
|
|
89
|
-
// Windows keeps the CN
|
|
90
|
-
// macOS Application Support layout (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
: path.join(home, "AppData", "Roaming");
|
|
95
|
-
return path.join(appData, TRAE_CN_APP_DIR);
|
|
86
|
+
// Windows keeps the CN app under the roaming profile, mirroring the
|
|
87
|
+
// macOS Application Support layout (<parent>/<app dir>/User/...).
|
|
88
|
+
return typeof env.APPDATA === "string" && env.APPDATA.trim()
|
|
89
|
+
? env.APPDATA.trim()
|
|
90
|
+
: path.join(home, "AppData", "Roaming");
|
|
96
91
|
}
|
|
97
|
-
// No verified CN
|
|
92
|
+
// No verified CN app-data layout on other platforms yet.
|
|
98
93
|
return null;
|
|
99
94
|
}
|
|
100
95
|
|
|
101
96
|
function resolveTraeCnStoragePath(options = {}) {
|
|
102
|
-
const
|
|
103
|
-
|
|
97
|
+
const env = options.env ?? process.env;
|
|
98
|
+
const override = env[TRAE_CN_HOME_ENV];
|
|
99
|
+
if (typeof override === "string" && override.trim()) {
|
|
100
|
+
return path.join(path.resolve(override.trim()), "User", "globalStorage", "storage.json");
|
|
101
|
+
}
|
|
102
|
+
const parent = traeCnDataParent({ env, home: options.home ?? os.homedir(), platform: options.platform ?? process.platform });
|
|
103
|
+
if (!parent) return null;
|
|
104
|
+
for (const dir of TRAE_CN_APP_DIRS) {
|
|
105
|
+
const candidate = path.join(parent, dir, "User", "globalStorage", "storage.json");
|
|
106
|
+
try {
|
|
107
|
+
// Any successful stat means this client dir is the discovery hit —
|
|
108
|
+
// including a non-file path (e.g. a directory named storage.json).
|
|
109
|
+
// Hand that to the reader so EISDIR surfaces as TRAE_CN_STORAGE_UNREADABLE
|
|
110
|
+
// instead of silently skipping to the next client or reporting absent.
|
|
111
|
+
fs.statSync(candidate);
|
|
112
|
+
return candidate;
|
|
113
|
+
} catch (err) {
|
|
114
|
+
// Permission failures must reach the reader's diagnostics, not
|
|
115
|
+
// masquerade as an absent installation and silently drop an entire
|
|
116
|
+
// usage history (same discovery policy as trae-db.js).
|
|
117
|
+
if (err.code !== "ENOENT" && err.code !== "ENOTDIR") return candidate;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
104
121
|
}
|
|
105
122
|
|
|
106
123
|
// ---------------------------------------------------------------------------
|
|
@@ -524,7 +541,7 @@ module.exports = {
|
|
|
524
541
|
TRAE_CN_HOME_ENV,
|
|
525
542
|
TRAE_CN_USAGE_ENV,
|
|
526
543
|
isTraeCnUsageEnabled,
|
|
527
|
-
|
|
544
|
+
TRAE_CN_APP_DIRS,
|
|
528
545
|
TRAE_CN_AUTH_KEY,
|
|
529
546
|
TRAE_CN_USAGE_URL,
|
|
530
547
|
TRAE_CN_USAGE_TYPE,
|
|
@@ -532,7 +549,6 @@ module.exports = {
|
|
|
532
549
|
TRAE_CN_USAGE_MAX_PAGES,
|
|
533
550
|
TRAE_CN_USAGE_DELAY_MS,
|
|
534
551
|
TRAE_CN_MAGIC,
|
|
535
|
-
resolveTraeCnHome,
|
|
536
552
|
resolveTraeCnStoragePath,
|
|
537
553
|
traeCnHardcodedKdfSecret,
|
|
538
554
|
deriveTraeCnKeyIv,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as n,J as a}from"./__vite-browser-external-CR0vo15z.js";import{u as p}from"./DialogTitle-BJxM52J1.js";import{u as c}from"./composite-DJsqXsjA.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,i=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as o}from"./__vite-browser-external-CR0vo15z.js";import{h as r,i as t,p as s}from"./react-H6DfeMUm.js";function i(){!r.current&&t();const[e]=o.useState(s.current);return e}export{i as u};
|