tokentracker-cli 1.3.4 → 1.3.6
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 -5
- package/README.ja.md +3 -3
- package/README.ko.md +3 -3
- package/README.md +6 -5
- package/README.zh-CN.md +3 -3
- package/dashboard/dist/assets/{AchievementBadge-CKjtQp-U.js → AchievementBadge-CHDt55IP.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-DpOXrabE.js → AchievementsPage-DpbRHmb8.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-DiXyIoa0.js → AchievementsSection-Ck5CiX4P.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-CILTTkPF.js → BadgeDetailModal-BVPqlh0X.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-CrBHWiRi.js → BotAnimated-CVsv6kxT.js} +1 -1
- package/dashboard/dist/assets/{Card-DUItW3xI.js → Card-C9FjyAwh.js} +1 -1
- package/dashboard/dist/assets/{CloudCheckoutPage-C_t_PDlG.js → CloudCheckoutPage-ChkjId9l.js} +1 -1
- package/dashboard/dist/assets/{CloudPage-pvJUrhya.js → CloudPage-D1nrt3MG.js} +1 -1
- package/dashboard/dist/assets/{CloudUsageExport-DQbDwiyk.js → CloudUsageExport-jXdoiD0e.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-BvHilrbV.js → CommandPalette-DFnEeZzc.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-CizxEYv-.js → CommunityStatsModal-CZDS8gPA.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-h9nISVy7.js → ConfirmModal-DRhgBoGE.js} +1 -1
- package/dashboard/dist/assets/{Controls-BjdIonTX.js → Controls-BmT6-x_L.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-Bxryr4qp.js → DashboardPage-DfwwXZBo.js} +1 -1
- package/dashboard/dist/assets/{DateRangePopover-DpKh-0l8.js → DateRangePopover-DuqNLatr.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-DUJR9hJC.js → DevicePage-BtI0bWyg.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-BmVAmN-o.js → DialogClose-DjnC3P4V.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-yy_QHg7O.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-BsaIbN8k.js → DialogTitle-B9NNSz5p.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-BpSt2P_W.js → FadeIn-fsVQmJGa.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-Cc_VHB0R.js → HeaderGithubStar-Cg-f1iZc.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-WFd21iP9.js → HoverTooltip-DRIRlzXH.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-C_mUPSRB.js → IpCheckPage-DaAubaJQ.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DVR8ClZO.js → LandingPage-DjBGIjHk.js} +2 -2
- package/dashboard/dist/assets/LeaderboardPage-BkCXqhz7.js +2 -0
- package/dashboard/dist/assets/{LeaderboardProBadge-BjX4EFl2.js → LeaderboardProBadge-Ds-wiWcN.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfileModal-vbPMNn7W.js → LeaderboardProfileModal-X_rfSQT5.js} +4 -4
- package/dashboard/dist/assets/{LeaderboardProfilePage-CqClmuA-.js → LeaderboardProfilePage-rpl1Ozwj.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-BUvDBmgD.js +9 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-B6nDke_u.js → LocalOnlyNotice-LBH1DBrs.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-C28QVjd7.js → LoginCard-B0vRJEcg.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-Bx1btNhH.js → LoginModal-DdzgP43f.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DVOGNGYI.js → LoginPage-B4VCQxzn.js} +1 -1
- package/dashboard/dist/assets/{PetPage-nOKryTde.js → PetPage-Cmf6aXc9.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-Ch6NLnu6.js → PopoverPopup-BzfELnIC.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-BRY2br-g.js → ProviderIcon-ClHXNkQ0.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-ko1OQecQ.js → ResetPasswordPage-BeUpNt_6.js} +1 -1
- package/dashboard/dist/assets/{Select-Q8mS3_od.js → Select-BHEXdYHu.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-C1id8GDj.js → SelectItemText-aaQK_QIt.js} +1 -1
- package/dashboard/dist/assets/{SelfHostPage-Dzb2bDiM.js → SelfHostPage-ClNhKnlP.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-ewFwz29J.js → ServiceStatusPage-Byd_s6FS.js} +1 -1
- package/dashboard/dist/assets/SessionsPage-u3_b4PO7.js +3 -0
- package/dashboard/dist/assets/SettingsPage-DweCAbsy.js +2 -0
- package/dashboard/dist/assets/{SkillsPage-CcG77tqU.js → SkillsPage-Bb-PVfux.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-D2HDrbQR.js → TokenGalaxy-CubA3PXu.js} +1 -1
- package/dashboard/dist/assets/{TooltipPopup-C_bF2ZwO.js → TooltipPopup-DMliWhoK.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-BcyoN5ns.js → TrendMonitor-as8idaOy.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-BirK0U_N.js → WidgetsPage-DDPR4v0R.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-thsXBuw6.js → WrappedPage-zZzgYz6s.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-Bq8nj-JV.js → __vite-browser-external-DWN9Fb-H.js} +12 -12
- package/dashboard/dist/assets/__vite-browser-external-LpFw4zXh.css +1 -0
- package/dashboard/dist/assets/agent-logos-CoNhCm9-.js +1 -0
- package/dashboard/dist/assets/{arrow-left-c_NOSfoq.js → arrow-left-C5hDp7fc.js} +1 -1
- package/dashboard/dist/assets/{arrow-right-CP7drKCT.js → arrow-right-DEvMmSiA.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DQ6QfCpo.js → arrow-up-right-DxY0neJ4.js} +1 -1
- package/dashboard/dist/assets/{calendar-days-D_pxFQvT.js → calendar-days-bzh7-lab.js} +1 -1
- package/dashboard/dist/assets/{check-HAS31kTT.js → check-CSZvcC3X.js} +1 -1
- package/dashboard/dist/assets/{chevron-down-BIMuk6oN.js → chevron-down-CsiGcG0G.js} +1 -1
- package/dashboard/dist/assets/{cloud-checkout-COmDwrUT.js → cloud-checkout-z_eDVwm6.js} +1 -1
- package/dashboard/dist/assets/{composite-kLwqpqEk.js → composite-Cq0CmNAv.js} +1 -1
- package/dashboard/dist/assets/{download-9RgsZIiH.js → download-DqLpubIF.js} +1 -1
- package/dashboard/dist/assets/{external-link-C_gYjHGF.js → external-link-B8E8tbB3.js} +1 -1
- package/dashboard/dist/assets/{flame-BSsCvgT8.js → flame-CrJDlnro.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-N22Wqv1X.js → format-tokens-DPIGGOlt.js} +1 -1
- package/dashboard/dist/assets/gift-CCp4Hh50.js +1 -0
- package/dashboard/dist/assets/{host-mode-JzrhHX7F.js → host-mode-xzpMLcwP.js} +1 -1
- package/dashboard/dist/assets/{icons-pK99mIMv.js → icons-CbB9Y-eV.js} +1 -1
- package/dashboard/dist/assets/{index-BhviBroj.js → index-BiK5-tQp.js} +1 -1
- package/dashboard/dist/assets/{index-CmD84EJi.js → index-DX7nYrHb.js} +1 -1
- package/dashboard/dist/assets/{index-Dk43KNa6.js → index-zuC7FU-t.js} +1 -1
- package/dashboard/dist/assets/{info-D-RLFnSK.js → info-B3NKYwWG.js} +1 -1
- package/dashboard/dist/assets/{link-2-BiKXW9mV.js → link-2-BRc8d8M9.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-KVUaEKV2.js → loader-circle-Cm5Nsgp-.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-5NK8j3mT.js → modifiers.esm-CYLYBECB.js} +1 -1
- package/dashboard/dist/assets/module-BNnaKM__.js +3 -0
- package/dashboard/dist/assets/{monitor-smartphone-u9yr-_Es.js → monitor-smartphone--IZ4D84F.js} +1 -1
- package/dashboard/dist/assets/{play-D0MaDCOx.js → play-CUrMOLtq.js} +1 -1
- package/dashboard/dist/assets/{plus-DTJVtoAJ.js → plus-0hrCDl4r.js} +1 -1
- package/dashboard/dist/assets/{provider-display-YhtG0I9Y.js → provider-display-CNK0FN3Q.js} +1 -1
- package/dashboard/dist/assets/{react-gIVxucOB.js → react-CCNSVHEA.js} +1 -1
- package/dashboard/dist/assets/{safePolygon-C_sPnJ1L.js → safePolygon-DsolkMUX.js} +1 -1
- package/dashboard/dist/assets/{search-DdQRwFYV.js → search-DYmYb9xY.js} +1 -1
- package/dashboard/dist/assets/{server-M0rmbGWy.js → server-CYcqzdSW.js} +1 -1
- package/dashboard/dist/assets/{shield-check-9-sqQKdD.js → shield-check-5zABLIr6.js} +1 -1
- package/dashboard/dist/assets/{skills-api-B0qBWnuK.js → skills-api-CD-ojBGj.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-iqbm7x5D.js → sortable.esm-BCn2Ms6R.js} +1 -1
- package/dashboard/dist/assets/{store-DstuWd4a.js → store-B22kwLs0.js} +1 -1
- package/dashboard/dist/assets/{terminal-Dz0MxHNG.js → terminal-CTv-oTU2.js} +1 -1
- package/dashboard/dist/assets/{trash-DBMFOFVz.js → trash-C5MAh3GO.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-CacbAcUR.js → use-community-stats-BrgrxUD2.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-nBuQVZOR.js → use-latest-request-guard-B1_rbvgH.js} +1 -1
- package/dashboard/dist/assets/use-limits-display-prefs-BOk5f-HM.js +1 -0
- package/dashboard/dist/assets/{use-native-settings-EAqAFx2x.js → use-native-settings-B2gc_m9W.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-DGLjjU6Q.js → use-ordered-list-N3sGpJ0u.js} +1 -1
- package/dashboard/dist/assets/{use-pet-settings-C5MzWHE7.js → use-pet-settings-sWFXgR3m.js} +2 -2
- package/dashboard/dist/assets/use-reduced-motion-C3MBYVqm.js +1 -0
- package/dashboard/dist/assets/{use-transform-C25eCuz_.js → use-transform-BsaLmw46.js} +1 -1
- package/dashboard/dist/assets/use-usage-limits-wL7v-S0r.js +1 -0
- package/dashboard/dist/assets/{useCurrency-t3PYQi1l.js → useCurrency-CbCnjf-N.js} +1 -1
- package/dashboard/dist/assets/{usePositioner-C59mj6kG.js → usePositioner-MepYlL0A.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-C0xQsBGf.js → useTokenFormat-Bq-rwuKa.js} +1 -1
- package/dashboard/dist/assets/{zap-CyH0tvKM.js → zap-1ah5e11g.js} +1 -1
- package/dashboard/dist/index.html +14 -12
- package/dashboard/dist/ip-check.html +2 -2
- package/dashboard/dist/leaderboard.html +2 -2
- package/dashboard/dist/llms.txt +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +2 -2
- package/src/cli.js +1 -1
- package/src/commands/init.js +43 -1
- package/src/commands/status.js +27 -0
- package/src/commands/sync.js +54 -4
- package/src/commands/uninstall.js +19 -0
- package/src/lib/claude-config.js +47 -15
- package/src/lib/codex-rollout-parser.js +11 -3
- package/src/lib/command-runner.js +15 -1
- package/src/lib/cursor-config.js +2 -1
- package/src/lib/gemini-config.js +3 -2
- package/src/lib/local-api.js +13 -3
- package/src/lib/notify-command.js +38 -0
- package/src/lib/openclaw-session-plugin.js +47 -14
- package/src/lib/passive-mode.js +12 -0
- package/src/lib/pricing/curated-overrides.json +42 -5
- package/src/lib/pricing/matcher.js +14 -5
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +199 -44
- package/src/lib/session-analytics.js +653 -60
- package/src/lib/session-performance.js +24 -1
- package/src/lib/usage-limits.js +183 -20
- package/src/lib/zcode-limits.js +218 -18
- package/src/lib/zcode-reset-credits.js +66 -0
- package/dashboard/dist/assets/DialogDescription-C0Cgq67g.js +0 -1
- package/dashboard/dist/assets/LeaderboardPage-D8ydzVpn.js +0 -2
- package/dashboard/dist/assets/LimitsPage-DaRggHge.js +0 -8
- package/dashboard/dist/assets/SessionsPage-C6Y8kbnM.js +0 -3
- package/dashboard/dist/assets/SettingsPage-nztCXEPs.js +0 -2
- package/dashboard/dist/assets/__vite-browser-external-BwmeEfv7.css +0 -1
- package/dashboard/dist/assets/agent-logos-CK6SzhI7.js +0 -1
- package/dashboard/dist/assets/module-WSx8viXK.js +0 -3
- package/dashboard/dist/assets/use-limits-display-prefs-fqg8hs6b.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-DK2oZ2uC.js +0 -1
- package/dashboard/dist/assets/use-usage-limits-BhqnEai3.js +0 -1
package/src/lib/rollout.js
CHANGED
|
@@ -12,6 +12,7 @@ const { physicalJsonlRecords } = require("./jsonl-lines");
|
|
|
12
12
|
const { readSqliteJsonRows, readSqliteJsonRowsAsync } = require("./sqlite-reader");
|
|
13
13
|
const wsl = require("./wsl-probe");
|
|
14
14
|
const { resolveInstallPaths } = require("./install-resolver");
|
|
15
|
+
const { expandHome } = require("./scan-roots");
|
|
15
16
|
const {
|
|
16
17
|
consumeUsageDelta,
|
|
17
18
|
createUsageDeltaState,
|
|
@@ -1634,9 +1635,19 @@ async function resolveOpenclawSessionFiles(env = process.env, deps = {}) {
|
|
|
1634
1635
|
out.push(full);
|
|
1635
1636
|
}
|
|
1636
1637
|
}
|
|
1638
|
+
for (const dbPath of [
|
|
1639
|
+
path.join(agentsDir, agent.name, "agent", "openclaw-agent.sqlite"),
|
|
1640
|
+
path.join(agentsDir, agent.name, "openclaw-agent.sqlite"),
|
|
1641
|
+
]) {
|
|
1642
|
+
if ((await fs.stat(dbPath).catch(() => null))?.isFile()) {
|
|
1643
|
+
out.push(dbPath);
|
|
1644
|
+
break; // alternate layouts of one agent database, not two sources
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1637
1647
|
}
|
|
1638
1648
|
}
|
|
1639
|
-
|
|
1649
|
+
// Import archives must seed dedup before the corresponding SQLite history.
|
|
1650
|
+
out.sort((a, b) => Number(a.endsWith(".sqlite")) - Number(b.endsWith(".sqlite")) || a.localeCompare(b));
|
|
1640
1651
|
return out;
|
|
1641
1652
|
}
|
|
1642
1653
|
|
|
@@ -1699,6 +1710,67 @@ async function parseOpenclawIncremental({
|
|
|
1699
1710
|
const previousOffset = Number(prev?.offset || 0);
|
|
1700
1711
|
let accepted = null;
|
|
1701
1712
|
let openedRegularFile = false;
|
|
1713
|
+
if (filePath.endsWith(".sqlite")) {
|
|
1714
|
+
const sessionFilter = typeof entry?.sessionId === "string"
|
|
1715
|
+
? ` AND e.session_id = '${entry.sessionId.replace(/'/g, "''")}'` : "";
|
|
1716
|
+
const rows = await readSqliteJsonRowsAsync(filePath, `
|
|
1717
|
+
SELECT e.session_id, json_object(
|
|
1718
|
+
'type', json_extract(e.event_json, '$.type'),
|
|
1719
|
+
'id', json_extract(e.event_json, '$.id'),
|
|
1720
|
+
'timestamp', json_extract(e.event_json, '$.timestamp'),
|
|
1721
|
+
'message', json_object(
|
|
1722
|
+
'role', json_extract(e.event_json, '$.message.role'),
|
|
1723
|
+
'model', json_extract(e.event_json, '$.message.model'),
|
|
1724
|
+
'provider', json_extract(e.event_json, '$.message.provider'),
|
|
1725
|
+
'api', json_extract(e.event_json, '$.message.api'),
|
|
1726
|
+
'timestamp', json_extract(e.event_json, '$.message.timestamp'),
|
|
1727
|
+
'responseId', json_extract(e.event_json, '$.message.responseId'),
|
|
1728
|
+
'usage', json_extract(e.event_json, '$.message.usage')
|
|
1729
|
+
)) AS event
|
|
1730
|
+
FROM transcript_events e JOIN session_windows w ON w.session_id = e.session_id
|
|
1731
|
+
WHERE json_valid(e.event_json) AND COALESCE(w.reason, '') != 'compaction'
|
|
1732
|
+
AND json_extract(e.event_json, '$.message.role') = 'assistant'
|
|
1733
|
+
AND json_extract(e.event_json, '$.message.usage') IS NOT NULL
|
|
1734
|
+
${sessionFilter}
|
|
1735
|
+
ORDER BY e.session_id, e.seq
|
|
1736
|
+
`, { readOnly: true, throwOnReadFailure: true, label: "OpenClaw" });
|
|
1737
|
+
const sessions = new Map();
|
|
1738
|
+
for (const row of rows) {
|
|
1739
|
+
if (!sessions.has(row.session_id)) sessions.set(row.session_id, []);
|
|
1740
|
+
sessions.get(row.session_id).push(row.event);
|
|
1741
|
+
}
|
|
1742
|
+
for (const [sessionId, lines] of sessions) {
|
|
1743
|
+
const sessionCursorKey = `${key}#${sessionId}`;
|
|
1744
|
+
const prior = { ...(stagedFiles[sessionCursorKey]?.usageEvents || {}) };
|
|
1745
|
+
const dbDir = path.dirname(filePath);
|
|
1746
|
+
const agentDir = path.basename(dbDir) === "agent" && path.basename(path.dirname(path.dirname(dbDir))) === "agents"
|
|
1747
|
+
? path.dirname(dbDir) : dbDir;
|
|
1748
|
+
const agentPrefix = openclawCursorKey(`${agentDir}${path.sep}`);
|
|
1749
|
+
// SQLite imports retain transcript identities. Carry forward counts
|
|
1750
|
+
// from this session's legacy transcript, including reset archives.
|
|
1751
|
+
for (const [legacyPath, cursor] of Object.entries(stagedFiles)) {
|
|
1752
|
+
const basename = path.basename(legacyPath);
|
|
1753
|
+
if (cursor.provider !== "openclaw" || !openclawCursorKey(legacyPath).startsWith(agentPrefix)) continue;
|
|
1754
|
+
if (!basename.startsWith(`${sessionId}.jsonl`) &&
|
|
1755
|
+
!(basename.startsWith(`${sessionId}-topic-`) && basename.includes(".jsonl"))) continue;
|
|
1756
|
+
for (const [identity, count] of Object.entries(cursor.usageEvents || {})) {
|
|
1757
|
+
prior[identity] = Math.max(prior[identity] || 0, count);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
const result = await parseOpenclawSessionFile({
|
|
1761
|
+
lines, previousUsageEvents: prior, appendOnly: false,
|
|
1762
|
+
hourlyState, touchedBuckets, source: fileSource,
|
|
1763
|
+
});
|
|
1764
|
+
stagedFiles[sessionCursorKey] = {
|
|
1765
|
+
provider: "openclaw", usageEvents: result.usageEvents,
|
|
1766
|
+
hasRealUsage: Object.keys(result.usageEvents).length > 0,
|
|
1767
|
+
updatedAt: new Date().toISOString(),
|
|
1768
|
+
};
|
|
1769
|
+
eventsAggregated += result.eventsAggregated;
|
|
1770
|
+
}
|
|
1771
|
+
filesProcessed += 1;
|
|
1772
|
+
continue;
|
|
1773
|
+
}
|
|
1702
1774
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1703
1775
|
const fileHandle = await fs.open(filePath, "r").catch(() => null);
|
|
1704
1776
|
if (!fileHandle) break;
|
|
@@ -1913,6 +1985,7 @@ function mergeOpenclawAttemptBuckets(
|
|
|
1913
1985
|
}
|
|
1914
1986
|
|
|
1915
1987
|
async function parseOpenclawSessionFile({
|
|
1988
|
+
lines,
|
|
1916
1989
|
filePath,
|
|
1917
1990
|
fileHandle,
|
|
1918
1991
|
startOffset,
|
|
@@ -1928,7 +2001,8 @@ async function parseOpenclawSessionFile({
|
|
|
1928
2001
|
projectState,
|
|
1929
2002
|
projectTouchedBuckets,
|
|
1930
2003
|
}) {
|
|
1931
|
-
const st = fileHandle ? await fileHandle.stat() : await fs.stat(filePath);
|
|
2004
|
+
const st = lines ? { size: lines.length } : fileHandle ? await fileHandle.stat() : await fs.stat(filePath);
|
|
2005
|
+
startOffset = startOffset || 0;
|
|
1932
2006
|
const endOffset =
|
|
1933
2007
|
Number.isFinite(endOffsetLimit) && endOffsetLimit >= 0
|
|
1934
2008
|
? Math.min(st.size, endOffsetLimit)
|
|
@@ -1963,14 +2037,14 @@ async function parseOpenclawSessionFile({
|
|
|
1963
2037
|
};
|
|
1964
2038
|
}
|
|
1965
2039
|
|
|
1966
|
-
const stream = fssync.createReadStream(filePath, {
|
|
2040
|
+
const stream = lines ? null : fssync.createReadStream(filePath, {
|
|
1967
2041
|
fd: fileHandle?.fd,
|
|
1968
2042
|
autoClose: !fileHandle,
|
|
1969
2043
|
encoding: "utf8",
|
|
1970
2044
|
start: startOffset,
|
|
1971
2045
|
end: endOffset - 1,
|
|
1972
2046
|
});
|
|
1973
|
-
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
2047
|
+
const rl = lines || readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1974
2048
|
|
|
1975
2049
|
let eventsAggregated = 0;
|
|
1976
2050
|
const scanCounts = appendOnly ? { ...priorCounts } : {};
|
|
@@ -2069,8 +2143,8 @@ async function parseOpenclawSessionFile({
|
|
|
2069
2143
|
if (!collectOnly) eventsAggregated += 1;
|
|
2070
2144
|
}
|
|
2071
2145
|
|
|
2072
|
-
rl.close();
|
|
2073
|
-
if (!fileHandle) stream
|
|
2146
|
+
if (!lines) rl.close();
|
|
2147
|
+
if (!fileHandle) stream?.close?.();
|
|
2074
2148
|
return {
|
|
2075
2149
|
endOffset,
|
|
2076
2150
|
eventsAggregated,
|
|
@@ -5053,16 +5127,21 @@ function buildV2Sql(sessionTable, watermark = null) {
|
|
|
5053
5127
|
// sessionTable (which session table exists, session_v2 preferred over session
|
|
5054
5128
|
// via DESC). Returns null when the probe itself fails — callers treat that as
|
|
5055
5129
|
// v1-only, matching the pre-existing silent-degradation contract.
|
|
5056
|
-
function detectOpencodeMessageLayout(dbPath, sqliteOptions = {}) {
|
|
5130
|
+
function detectOpencodeMessageLayout(dbPath, sqliteOptions = {}, { propagateReadFailure = false } = {}) {
|
|
5057
5131
|
let rows;
|
|
5058
5132
|
try {
|
|
5059
5133
|
rows = readSqliteJsonRows(
|
|
5060
5134
|
dbPath,
|
|
5061
5135
|
`SELECT (SELECT 1 FROM session_message LIMIT 1) AS hasRows,
|
|
5062
5136
|
(SELECT name FROM sqlite_master WHERE name IN ('session','session_v2') ORDER BY name DESC LIMIT 1) AS sessionTable`,
|
|
5063
|
-
{ label: "OpenCode", timeout: 10_000, maxBuffer: 1024 * 1024, ...sqliteOptions },
|
|
5137
|
+
{ label: "OpenCode", timeout: 10_000, maxBuffer: 1024 * 1024, ...sqliteOptions, throwOnReadFailure: true },
|
|
5064
5138
|
);
|
|
5065
|
-
} catch (
|
|
5139
|
+
} catch (error) {
|
|
5140
|
+
// A missing session_message table is the normal v1-only miss; with
|
|
5141
|
+
// propagateReadFailure a real read failure must reach the caller — the
|
|
5142
|
+
// session scanner would otherwise skip the v2 generation silently and
|
|
5143
|
+
// cache a zero-token inventory over a readable-in-principle database.
|
|
5144
|
+
if (propagateReadFailure && !isMissingSqliteTableError(error)) throw error;
|
|
5066
5145
|
return null;
|
|
5067
5146
|
}
|
|
5068
5147
|
if (!Array.isArray(rows) || rows.length === 0) return null;
|
|
@@ -5082,7 +5161,17 @@ function opencodeMessageProvider(data) {
|
|
|
5082
5161
|
return data?.providerID || data?.model?.providerID || "";
|
|
5083
5162
|
}
|
|
5084
5163
|
|
|
5085
|
-
|
|
5164
|
+
// readSqliteJsonRows wraps its two backend errors into the thrown Error, so a
|
|
5165
|
+
// "table genuinely absent" miss and a "database unreadable" failure look the
|
|
5166
|
+
// same to a caller that only catches. Both backends spell a missing table
|
|
5167
|
+
// "no such table" — use that to keep the normal miss on the tolerant path
|
|
5168
|
+
// while real read failures can propagate.
|
|
5169
|
+
function isMissingSqliteTableError(error) {
|
|
5170
|
+
const messages = [error?.message, error?.cliError?.message, error?.nodeSqliteError?.message];
|
|
5171
|
+
return messages.some((m) => typeof m === "string" && m.includes("no such table"));
|
|
5172
|
+
}
|
|
5173
|
+
|
|
5174
|
+
function readOpencodeDbMessagesIncremental(dbPath, previousCursor = null, sqliteOptions = {}, { propagateReadFailure = false } = {}) {
|
|
5086
5175
|
if (!dbPath || !fssync.existsSync(dbPath)) return { messages: [], cursor: null };
|
|
5087
5176
|
|
|
5088
5177
|
const identity = opencodeDbIdentity(dbPath);
|
|
@@ -5149,7 +5238,11 @@ function readOpencodeDbMessagesIncremental(dbPath, previousCursor = null, sqlite
|
|
|
5149
5238
|
throwOnReadFailure: true,
|
|
5150
5239
|
});
|
|
5151
5240
|
return Array.isArray(rows) ? rows : [];
|
|
5152
|
-
} catch (
|
|
5241
|
+
} catch (error) {
|
|
5242
|
+
// A missing table is the normal "other generation's layout" miss; an
|
|
5243
|
+
// unreadable database is not — with propagateReadFailure the session
|
|
5244
|
+
// scan aborts instead of caching a zero-token inventory over it.
|
|
5245
|
+
if (propagateReadFailure && !isMissingSqliteTableError(error)) throw error;
|
|
5153
5246
|
return null; // query-level failure (e.g. wrong generation's table)
|
|
5154
5247
|
}
|
|
5155
5248
|
};
|
|
@@ -5227,7 +5320,7 @@ function readOpencodeDbMessagesIncremental(dbPath, previousCursor = null, sqlite
|
|
|
5227
5320
|
// baseline every database carries or degrades to), v2 runs only when the
|
|
5228
5321
|
// probe confirms session_message has rows — this avoids firing a JOIN
|
|
5229
5322
|
// against a non-existent session table on type-A databases.
|
|
5230
|
-
const probe = detectOpencodeMessageLayout(effectiveDbPath, sqliteOptions);
|
|
5323
|
+
const probe = detectOpencodeMessageLayout(effectiveDbPath, sqliteOptions, { propagateReadFailure });
|
|
5231
5324
|
const out = [];
|
|
5232
5325
|
const nextCursor = {
|
|
5233
5326
|
version: OPENCODE_DB_CURSOR_VERSION,
|
|
@@ -5267,8 +5360,8 @@ function readOpencodeDbMessagesIncremental(dbPath, previousCursor = null, sqlite
|
|
|
5267
5360
|
}
|
|
5268
5361
|
}
|
|
5269
5362
|
|
|
5270
|
-
function readOpencodeDbMessages(dbPath, sqliteOptions = {}) {
|
|
5271
|
-
return readOpencodeDbMessagesIncremental(dbPath, null, sqliteOptions).messages;
|
|
5363
|
+
function readOpencodeDbMessages(dbPath, sqliteOptions = {}, { propagateReadFailure = false } = {}) {
|
|
5364
|
+
return readOpencodeDbMessagesIncremental(dbPath, null, sqliteOptions, { propagateReadFailure }).messages;
|
|
5272
5365
|
}
|
|
5273
5366
|
|
|
5274
5367
|
// mimocode mirrors the user's Claude Code + claude-mem history into its own
|
|
@@ -5384,7 +5477,7 @@ const ZCODE_NATIVE_USAGE_COLUMNS = new Set([
|
|
|
5384
5477
|
"cache_read_input_tokens",
|
|
5385
5478
|
]);
|
|
5386
5479
|
|
|
5387
|
-
function detectZcodeNativeUsageLayout(dbPath, sqliteOptions = {}) {
|
|
5480
|
+
function detectZcodeNativeUsageLayout(dbPath, sqliteOptions = {}, { propagateReadFailure = false } = {}) {
|
|
5388
5481
|
if (!dbPath || !fssync.existsSync(dbPath)) return null;
|
|
5389
5482
|
let rows;
|
|
5390
5483
|
try {
|
|
@@ -5401,7 +5494,11 @@ function detectZcodeNativeUsageLayout(dbPath, sqliteOptions = {}) {
|
|
|
5401
5494
|
throwOnReadFailure: true,
|
|
5402
5495
|
},
|
|
5403
5496
|
);
|
|
5404
|
-
} catch (
|
|
5497
|
+
} catch (error) {
|
|
5498
|
+
// propagateReadFailure: an unreadable db is a failure, not "no native
|
|
5499
|
+
// layout" — the session scanner must skip the file, not fall back to an
|
|
5500
|
+
// empty legacy read and cache zero tokens.
|
|
5501
|
+
if (propagateReadFailure) throw error;
|
|
5405
5502
|
return null;
|
|
5406
5503
|
}
|
|
5407
5504
|
const modelUsageColumns = new Set(
|
|
@@ -5471,7 +5568,13 @@ function buildZcodeNativeUsageSql({ hasSessionDirectory }) {
|
|
|
5471
5568
|
ORDER BY mu.started_at ASC, mu.id ASC`;
|
|
5472
5569
|
}
|
|
5473
5570
|
|
|
5474
|
-
|
|
5571
|
+
// `propagateReadFailure` (off by default): when the native usage layout is
|
|
5572
|
+
// present but its row query fails, throw instead of returning null. The usage
|
|
5573
|
+
// queue treats that fallthrough as "no native table, use legacy" — correct for
|
|
5574
|
+
// its own resilience — but the session scanner must not cache a zero-token
|
|
5575
|
+
// inventory over a failed read (the browser then hides the sessions entirely
|
|
5576
|
+
// instead of reporting the db as skipped).
|
|
5577
|
+
function readZcodeNativeUsageMessages(dbPath, sqliteOptions = {}, { propagateReadFailure = false } = {}) {
|
|
5475
5578
|
let snapshot = null;
|
|
5476
5579
|
let effectiveDbPath = dbPath;
|
|
5477
5580
|
if (isUncPath(dbPath)) {
|
|
@@ -5486,7 +5589,7 @@ function readZcodeNativeUsageMessages(dbPath, sqliteOptions = {}) {
|
|
|
5486
5589
|
|
|
5487
5590
|
let rows;
|
|
5488
5591
|
try {
|
|
5489
|
-
const layout = detectZcodeNativeUsageLayout(effectiveDbPath, sqliteOptions);
|
|
5592
|
+
const layout = detectZcodeNativeUsageLayout(effectiveDbPath, sqliteOptions, { propagateReadFailure });
|
|
5490
5593
|
if (!layout) return null;
|
|
5491
5594
|
rows = readSqliteJsonRows(effectiveDbPath, buildZcodeNativeUsageSql(layout), {
|
|
5492
5595
|
label: "ZCode",
|
|
@@ -5495,7 +5598,8 @@ function readZcodeNativeUsageMessages(dbPath, sqliteOptions = {}) {
|
|
|
5495
5598
|
...sqliteOptions,
|
|
5496
5599
|
throwOnReadFailure: true,
|
|
5497
5600
|
});
|
|
5498
|
-
} catch (
|
|
5601
|
+
} catch (error) {
|
|
5602
|
+
if (propagateReadFailure) throw error;
|
|
5499
5603
|
return null;
|
|
5500
5604
|
} finally {
|
|
5501
5605
|
if (snapshot) snapshot.cleanup();
|
|
@@ -5547,10 +5651,10 @@ function readZcodeNativeUsageMessages(dbPath, sqliteOptions = {}) {
|
|
|
5547
5651
|
// table is authoritative from its first completed row onward, but it is not a
|
|
5548
5652
|
// historical backfill. Keep legacy rows before that boundary so merely adding
|
|
5549
5653
|
// the new table cannot make older usage disappear from TokenTracker.
|
|
5550
|
-
function readZcodeDbMessages(dbPath, sqliteOptions = {}) {
|
|
5654
|
+
function readZcodeDbMessages(dbPath, sqliteOptions = {}, { propagateReadFailure = false } = {}) {
|
|
5551
5655
|
if (!dbPath || !fssync.existsSync(dbPath)) return [];
|
|
5552
|
-
const nativeMessages = readZcodeNativeUsageMessages(dbPath, sqliteOptions);
|
|
5553
|
-
const legacyMessages = readOpencodeDbMessages(dbPath, sqliteOptions)
|
|
5656
|
+
const nativeMessages = readZcodeNativeUsageMessages(dbPath, sqliteOptions, { propagateReadFailure });
|
|
5657
|
+
const legacyMessages = readOpencodeDbMessages(dbPath, sqliteOptions, { propagateReadFailure })
|
|
5554
5658
|
.filter((message) => isZcodeNativeMessage(message.data))
|
|
5555
5659
|
.map(normalizeZcodeLegacyMessage);
|
|
5556
5660
|
if (nativeMessages === null) return legacyMessages;
|
|
@@ -10451,23 +10555,43 @@ async function parseCodebuddyIncremental({
|
|
|
10451
10555
|
// is accepted by workbuddySqliteUsageSnapshot.
|
|
10452
10556
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
10453
10557
|
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
|
|
10558
|
+
// WorkBuddy ships two regional builds that keep their data in sibling homes:
|
|
10559
|
+
// ~/.workbuddy — Tencent CN build (models: hy*/glm*/deepseek-*/auto)
|
|
10560
|
+
// ~/.workbuddy-ai — international build (models: gpt-5.6-*, gpt-6-*)
|
|
10561
|
+
// Both write byte-identical JSONL, so every reader below is shared and only
|
|
10562
|
+
// the home directory (and the bucket source key) differs. `variant` is also
|
|
10563
|
+
// the source key written into hourly buckets, so the two builds stay
|
|
10564
|
+
// separable in the dashboard and never share dedup state.
|
|
10565
|
+
const WORKBUDDY_VARIANTS = {
|
|
10566
|
+
workbuddy: { dir: ".workbuddy", envVar: "WORKBUDDY_HOME" },
|
|
10567
|
+
"workbuddy-ai": { dir: ".workbuddy-ai", envVar: "WORKBUDDY_AI_HOME" },
|
|
10568
|
+
};
|
|
10569
|
+
|
|
10570
|
+
function resolveWorkbuddyVariant(variant) {
|
|
10571
|
+
return Object.prototype.hasOwnProperty.call(WORKBUDDY_VARIANTS, variant)
|
|
10572
|
+
? variant
|
|
10573
|
+
: "workbuddy";
|
|
10574
|
+
}
|
|
10575
|
+
|
|
10576
|
+
function resolveWorkbuddyHome(env = process.env, variant = "workbuddy") {
|
|
10577
|
+
const spec = WORKBUDDY_VARIANTS[resolveWorkbuddyVariant(variant)];
|
|
10578
|
+
const home = env.HOME || env.USERPROFILE || require("node:os").homedir();
|
|
10579
|
+
const configured = expandHome(env[spec.envVar], home);
|
|
10580
|
+
if (configured) return configured;
|
|
10457
10581
|
if (process.platform === "win32") {
|
|
10458
10582
|
return pickWin32ProviderPath({
|
|
10459
10583
|
env,
|
|
10460
|
-
nativeValue: path.join(home,
|
|
10461
|
-
wslProviderDir:
|
|
10584
|
+
nativeValue: path.join(home, spec.dir),
|
|
10585
|
+
wslProviderDir: spec.dir,
|
|
10462
10586
|
});
|
|
10463
10587
|
}
|
|
10464
|
-
return path.join(home,
|
|
10588
|
+
return path.join(home, spec.dir);
|
|
10465
10589
|
}
|
|
10466
10590
|
|
|
10467
|
-
function resolveWorkbuddyDefaultModel(env = process.env) {
|
|
10591
|
+
function resolveWorkbuddyDefaultModel(env = process.env, variant = "workbuddy") {
|
|
10468
10592
|
const fallback = "auto";
|
|
10469
10593
|
try {
|
|
10470
|
-
const workbuddyHome = resolveWorkbuddyHome(env);
|
|
10594
|
+
const workbuddyHome = resolveWorkbuddyHome(env, variant);
|
|
10471
10595
|
if (!workbuddyHome) return fallback;
|
|
10472
10596
|
const settingsPath = path.join(workbuddyHome, "settings.json");
|
|
10473
10597
|
const raw = fssync.readFileSync(settingsPath, "utf8");
|
|
@@ -10487,8 +10611,8 @@ function resolveWorkbuddyDefaultModel(env = process.env) {
|
|
|
10487
10611
|
// ~/.workbuddy/traces/<pid>/trace_*.json. Those summaries are useful only when
|
|
10488
10612
|
// the detailed JSONL for a session is absent, so they are returned as typed
|
|
10489
10613
|
// entries and parsed after JSONL (JSONL remains authoritative when both exist).
|
|
10490
|
-
function resolveWorkbuddyProjectFiles(env = process.env) {
|
|
10491
|
-
const workbuddyHome = resolveWorkbuddyHome(env);
|
|
10614
|
+
function resolveWorkbuddyProjectFiles(env = process.env, variant = "workbuddy") {
|
|
10615
|
+
const workbuddyHome = resolveWorkbuddyHome(env, variant);
|
|
10492
10616
|
if (!workbuddyHome) return [];
|
|
10493
10617
|
const projectsDir = path.join(workbuddyHome, "projects");
|
|
10494
10618
|
const files = [];
|
|
@@ -10597,10 +10721,15 @@ async function parseWorkbuddyIncremental({
|
|
|
10597
10721
|
onProgress,
|
|
10598
10722
|
env,
|
|
10599
10723
|
defaultModel,
|
|
10724
|
+
source = "workbuddy",
|
|
10600
10725
|
} = {}) {
|
|
10601
10726
|
await ensureDir(path.dirname(queuePath));
|
|
10727
|
+
// `source` doubles as the variant selector and the bucket source key, so the
|
|
10728
|
+
// CN install (~/.workbuddy) and the international install (~/.workbuddy-ai)
|
|
10729
|
+
// get independent cursor namespaces and never share dedup state.
|
|
10730
|
+
const variant = resolveWorkbuddyVariant(source);
|
|
10602
10731
|
const workbuddyState =
|
|
10603
|
-
cursors
|
|
10732
|
+
cursors[variant] && typeof cursors[variant] === "object" ? cursors[variant] : {};
|
|
10604
10733
|
const seenIds = new Set(
|
|
10605
10734
|
Array.isArray(workbuddyState.seenIds) ? workbuddyState.seenIds : [],
|
|
10606
10735
|
);
|
|
@@ -10626,20 +10755,20 @@ async function parseWorkbuddyIncremental({
|
|
|
10626
10755
|
|
|
10627
10756
|
const allFiles = Array.isArray(projectFiles)
|
|
10628
10757
|
? projectFiles
|
|
10629
|
-
: resolveWorkbuddyProjectFiles(env || process.env);
|
|
10758
|
+
: resolveWorkbuddyProjectFiles(env || process.env, variant);
|
|
10630
10759
|
const jsonlFiles = allFiles.filter((entry) => typeof entry === "string");
|
|
10631
10760
|
const traceFiles = allFiles.filter(
|
|
10632
10761
|
(entry) => entry && typeof entry === "object" && typeof entry.path === "string" && entry.kind === "trace",
|
|
10633
10762
|
);
|
|
10634
10763
|
const files = [...jsonlFiles, ...traceFiles];
|
|
10635
|
-
const fallbackModel = defaultModel || resolveWorkbuddyDefaultModel(env || process.env);
|
|
10764
|
+
const fallbackModel = defaultModel || resolveWorkbuddyDefaultModel(env || process.env, variant);
|
|
10636
10765
|
|
|
10637
|
-
const workbuddyHome = resolveWorkbuddyHome(env || process.env);
|
|
10766
|
+
const workbuddyHome = resolveWorkbuddyHome(env || process.env, variant);
|
|
10638
10767
|
const dbPath = workbuddyHome ? path.join(workbuddyHome, "workbuddy.db") : null;
|
|
10639
10768
|
const dbExists = Boolean(dbPath && fssync.existsSync(dbPath));
|
|
10640
10769
|
|
|
10641
10770
|
if (files.length === 0 && !dbExists) {
|
|
10642
|
-
cursors
|
|
10771
|
+
cursors[variant] = {
|
|
10643
10772
|
...workbuddyState,
|
|
10644
10773
|
seenIds: Array.from(seenIds),
|
|
10645
10774
|
seenTraceIds: Array.from(seenTraceIds),
|
|
@@ -10786,9 +10915,9 @@ async function parseWorkbuddyIncremental({
|
|
|
10786
10915
|
conversation_count: 1,
|
|
10787
10916
|
};
|
|
10788
10917
|
|
|
10789
|
-
const bucket = getHourlyBucket(hourlyState,
|
|
10918
|
+
const bucket = getHourlyBucket(hourlyState, variant, model, bucketStart);
|
|
10790
10919
|
addTotals(bucket.totals, delta);
|
|
10791
|
-
touchedBuckets.add(bucketKey(
|
|
10920
|
+
touchedBuckets.add(bucketKey(variant, model, bucketStart));
|
|
10792
10921
|
seenIds.add(messageId);
|
|
10793
10922
|
// Keep legacy entry-id keys alongside the response id so upgrading a
|
|
10794
10923
|
// cursor cannot replay a row that an older build already counted.
|
|
@@ -10897,9 +11026,9 @@ async function parseWorkbuddyIncremental({
|
|
|
10897
11026
|
total_tokens: inputTokens + totalCached + totalOutput,
|
|
10898
11027
|
conversation_count: 1,
|
|
10899
11028
|
};
|
|
10900
|
-
const bucket = getHourlyBucket(hourlyState,
|
|
11029
|
+
const bucket = getHourlyBucket(hourlyState, variant, model, bucketStart);
|
|
10901
11030
|
addTotals(bucket.totals, delta);
|
|
10902
|
-
touchedBuckets.add(bucketKey(
|
|
11031
|
+
touchedBuckets.add(bucketKey(variant, model, bucketStart));
|
|
10903
11032
|
seenTraceIds.add(traceId);
|
|
10904
11033
|
tracedSessionIds.add(sessionId);
|
|
10905
11034
|
recordsProcessed++;
|
|
@@ -11032,9 +11161,9 @@ async function parseWorkbuddyIncremental({
|
|
|
11032
11161
|
conversation_count: Object.keys(previousTokens).every((key) => toNonNegativeInt(previousTokens[key]) === 0) || isReset ? 1 : 0,
|
|
11033
11162
|
};
|
|
11034
11163
|
|
|
11035
|
-
const bucket = getHourlyBucket(hourlyState,
|
|
11164
|
+
const bucket = getHourlyBucket(hourlyState, variant, model, bucketStart);
|
|
11036
11165
|
addTotals(bucket.totals, delta);
|
|
11037
|
-
touchedBuckets.add(bucketKey(
|
|
11166
|
+
touchedBuckets.add(bucketKey(variant, model, bucketStart));
|
|
11038
11167
|
sqliteSessions[sessionId] = {
|
|
11039
11168
|
used: usedNow,
|
|
11040
11169
|
updatedAt: updatedAtRaw,
|
|
@@ -11085,7 +11214,7 @@ async function parseWorkbuddyIncremental({
|
|
|
11085
11214
|
const updatedAt = new Date().toISOString();
|
|
11086
11215
|
hourlyState.updatedAt = updatedAt;
|
|
11087
11216
|
cursors.hourly = hourlyState;
|
|
11088
|
-
cursors
|
|
11217
|
+
cursors[variant] = {
|
|
11089
11218
|
...workbuddyState,
|
|
11090
11219
|
seenIds: cappedSeen,
|
|
11091
11220
|
seenTraceIds: cappedSeenTraceIds,
|
|
@@ -15972,6 +16101,27 @@ function resolvePiSessionFiles(env = process.env) {
|
|
|
15972
16101
|
} catch {
|
|
15973
16102
|
// ignore — return what we have
|
|
15974
16103
|
}
|
|
16104
|
+
// Multica launches pi headless with a dedicated flat session dir:
|
|
16105
|
+
// ~/.multica/pi-sessions/<session-id>.jsonl
|
|
16106
|
+
// Scan it too so orchestrated runs are counted alongside TUI sessions.
|
|
16107
|
+
try {
|
|
16108
|
+
const seen = new Set(files);
|
|
16109
|
+
const multicaSessionsDir = path.join(
|
|
16110
|
+
env.HOME || require("node:os").homedir(),
|
|
16111
|
+
".multica",
|
|
16112
|
+
"pi-sessions",
|
|
16113
|
+
);
|
|
16114
|
+
for (const entry of fssync.readdirSync(multicaSessionsDir, { withFileTypes: true })) {
|
|
16115
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
16116
|
+
const full = path.join(multicaSessionsDir, entry.name);
|
|
16117
|
+
if (!seen.has(full)) {
|
|
16118
|
+
seen.add(full);
|
|
16119
|
+
files.push(full);
|
|
16120
|
+
}
|
|
16121
|
+
}
|
|
16122
|
+
} catch {
|
|
16123
|
+
// ignore — no Multica install
|
|
16124
|
+
}
|
|
15975
16125
|
files.sort((a, b) => a.localeCompare(b));
|
|
15976
16126
|
return files;
|
|
15977
16127
|
}
|
|
@@ -24439,6 +24589,8 @@ module.exports = {
|
|
|
24439
24589
|
readOpencodeDbMessagesIncremental,
|
|
24440
24590
|
readMimoDbMessages,
|
|
24441
24591
|
readZcodeDbMessages,
|
|
24592
|
+
coerceEpochMs,
|
|
24593
|
+
normalizeOpencodeModelFields,
|
|
24442
24594
|
hasZcodeNativeUsageSchema,
|
|
24443
24595
|
resolveQoderDbPath,
|
|
24444
24596
|
resolveQoderDbPaths,
|
|
@@ -24473,6 +24625,7 @@ module.exports = {
|
|
|
24473
24625
|
parseGeminiIncremental,
|
|
24474
24626
|
parseOpencodeIncremental,
|
|
24475
24627
|
parseOpencodeDbIncremental,
|
|
24628
|
+
deriveOpencodeMessageFingerprint,
|
|
24476
24629
|
parseQoderDbIncremental,
|
|
24477
24630
|
openclawCursorKey,
|
|
24478
24631
|
parseOpenclawIncremental,
|
|
@@ -24514,6 +24667,8 @@ module.exports = {
|
|
|
24514
24667
|
resolveWorkbuddyProjectFiles,
|
|
24515
24668
|
resolveWorkbuddyDefaultModel,
|
|
24516
24669
|
parseWorkbuddyIncremental,
|
|
24670
|
+
WORKBUDDY_VARIANTS,
|
|
24671
|
+
resolveWorkbuddyVariant,
|
|
24517
24672
|
resolveKiroCliSessionFiles,
|
|
24518
24673
|
resolveKiroCliDbPath,
|
|
24519
24674
|
parseKiroCliIncremental,
|