@oh-my-pi/omp-stats 17.0.9 → 17.1.1
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/CHANGELOG.md +6 -0
- package/dist/types/db.d.ts +4 -3
- package/package.json +4 -4
- package/src/aggregator.ts +8 -4
- package/src/db.ts +16 -21
- package/src/parser.ts +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.1.0] - 2026-07-24
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed an issue where malformed persisted content blocks could abort stats ingestion for subsequent projects, and ensured pending full-session migrations are properly settled after successful backfills.
|
|
10
|
+
|
|
5
11
|
## [17.0.6] - 2026-07-20
|
|
6
12
|
|
|
7
13
|
### Changed
|
package/dist/types/db.d.ts
CHANGED
|
@@ -79,9 +79,10 @@ export declare function getMessageById(id: number): MessageStats | null;
|
|
|
79
79
|
* Get daily cost time series data for the last N days, broken down by model.
|
|
80
80
|
*/
|
|
81
81
|
export declare function getCostTimeSeries(days?: number, cutoff?: number | null): CostTimeSeriesPoint[];
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Settle every full-session backfill after a successful sync pass.
|
|
84
|
+
*/
|
|
85
|
+
export declare function markSessionBackfillsComplete(): void;
|
|
85
86
|
/**
|
|
86
87
|
* Insert user-message stats. Idempotent via UNIQUE(session_file, entry_id).
|
|
87
88
|
* The `WHERE NOT EXISTS` clause matches {@link insertMessageStats}: forks
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/omp-stats",
|
|
4
|
-
"version": "17.
|
|
4
|
+
"version": "17.1.1",
|
|
5
5
|
"description": "Local observability dashboard for pi AI usage statistics",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"fmt": "biome format --write ."
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@oh-my-pi/pi-ai": "17.
|
|
43
|
-
"@oh-my-pi/pi-catalog": "17.
|
|
44
|
-
"@oh-my-pi/pi-utils": "17.
|
|
42
|
+
"@oh-my-pi/pi-ai": "17.1.1",
|
|
43
|
+
"@oh-my-pi/pi-catalog": "17.1.1",
|
|
44
|
+
"@oh-my-pi/pi-utils": "17.1.1",
|
|
45
45
|
"@tailwindcss/node": "^4.3.2",
|
|
46
46
|
"chart.js": "^4.5.1",
|
|
47
47
|
"date-fns": "^4.4.0",
|
package/src/aggregator.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
insertMessageStats,
|
|
25
25
|
insertToolCalls,
|
|
26
26
|
insertUserMessageStats,
|
|
27
|
+
markSessionBackfillsComplete,
|
|
27
28
|
setFileOffset,
|
|
28
29
|
updateToolResults,
|
|
29
30
|
updateUserMessageLinks,
|
|
@@ -209,12 +210,15 @@ export async function syncAllSessions(opts?: SyncOptions): Promise<{ processed:
|
|
|
209
210
|
await initDb();
|
|
210
211
|
|
|
211
212
|
const files = await listAllSessionFiles();
|
|
212
|
-
if (files.length === 0) return { processed: 0, files: 0 };
|
|
213
|
-
|
|
214
213
|
let totalProcessed = 0;
|
|
215
214
|
let filesProcessed = 0;
|
|
216
215
|
let completed = 0;
|
|
217
216
|
let cursor = 0;
|
|
217
|
+
const finish = () => {
|
|
218
|
+
markSessionBackfillsComplete();
|
|
219
|
+
return { processed: totalProcessed, files: filesProcessed };
|
|
220
|
+
};
|
|
221
|
+
if (files.length === 0) return finish();
|
|
218
222
|
|
|
219
223
|
const report = (sessionFile: string) => {
|
|
220
224
|
completed++;
|
|
@@ -259,7 +263,7 @@ export async function syncAllSessions(opts?: SyncOptions): Promise<{ processed:
|
|
|
259
263
|
for (const sessionFile of files) {
|
|
260
264
|
await processFile(sessionFile, parseSessionFile);
|
|
261
265
|
}
|
|
262
|
-
return
|
|
266
|
+
return finish();
|
|
263
267
|
}
|
|
264
268
|
|
|
265
269
|
const poolSize = Math.min(files.length, requestedWorkers);
|
|
@@ -282,7 +286,7 @@ export async function syncAllSessions(opts?: SyncOptions): Promise<{ processed:
|
|
|
282
286
|
for (const handle of handles) handle.worker.terminate();
|
|
283
287
|
}
|
|
284
288
|
|
|
285
|
-
return
|
|
289
|
+
return finish();
|
|
286
290
|
}
|
|
287
291
|
|
|
288
292
|
const HOUR_MS = 60 * 60 * 1000;
|
package/src/db.ts
CHANGED
|
@@ -1079,28 +1079,23 @@ function backfillPriorityPremiumRequests(database: Database): void {
|
|
|
1079
1079
|
.run(PRIORITY_PREMIUM_REQUESTS_BACKFILL_KEY, BACKFILL_PENDING);
|
|
1080
1080
|
}
|
|
1081
1081
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
BACKFILL_COMPLETE,
|
|
1087
|
-
);
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
export function markUserMessagesBackfillComplete(): void {
|
|
1091
|
-
if (!db) return;
|
|
1092
|
-
db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(
|
|
1093
|
-
USER_MESSAGES_BACKFILL_KEY,
|
|
1094
|
-
BACKFILL_COMPLETE,
|
|
1095
|
-
);
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
export function markUserMessageLinksRepairComplete(): void {
|
|
1082
|
+
/**
|
|
1083
|
+
* Settle every full-session backfill after a successful sync pass.
|
|
1084
|
+
*/
|
|
1085
|
+
export function markSessionBackfillsComplete(): void {
|
|
1099
1086
|
if (!db) return;
|
|
1100
|
-
db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)")
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1087
|
+
const markComplete = db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)");
|
|
1088
|
+
const apply = db.transaction(() => {
|
|
1089
|
+
for (const key of [
|
|
1090
|
+
USER_MESSAGES_BACKFILL_KEY,
|
|
1091
|
+
TOOL_CALLS_BACKFILL_KEY,
|
|
1092
|
+
USER_MESSAGE_LINKS_REPAIR_KEY,
|
|
1093
|
+
PRIORITY_PREMIUM_REQUESTS_BACKFILL_KEY,
|
|
1094
|
+
]) {
|
|
1095
|
+
markComplete.run(key, BACKFILL_COMPLETE);
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
apply();
|
|
1104
1099
|
}
|
|
1105
1100
|
|
|
1106
1101
|
/**
|
package/src/parser.ts
CHANGED
|
@@ -240,7 +240,11 @@ function extractToolCalls(
|
|
|
240
240
|
|
|
241
241
|
const blocks = msg.content.filter(
|
|
242
242
|
(block): block is ToolCall =>
|
|
243
|
-
block
|
|
243
|
+
block !== null &&
|
|
244
|
+
typeof block === "object" &&
|
|
245
|
+
block.type === "toolCall" &&
|
|
246
|
+
typeof block.id === "string" &&
|
|
247
|
+
typeof block.name === "string",
|
|
244
248
|
);
|
|
245
249
|
if (blocks.length === 0) return [];
|
|
246
250
|
|
|
@@ -277,7 +281,9 @@ function extractToolResultLink(sessionFile: string, entry: SessionMessageEntry):
|
|
|
277
281
|
let resultChars = 0;
|
|
278
282
|
if (Array.isArray(msg.content)) {
|
|
279
283
|
for (const block of msg.content) {
|
|
280
|
-
if (block.type === "text" && typeof block.text === "string")
|
|
284
|
+
if (block && typeof block === "object" && block.type === "text" && typeof block.text === "string") {
|
|
285
|
+
resultChars += block.text.length;
|
|
286
|
+
}
|
|
281
287
|
}
|
|
282
288
|
}
|
|
283
289
|
return {
|