@oh-my-pi/omp-stats 17.2.5 → 17.2.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/dist/types/aggregator.d.ts +8 -0
- package/package.json +4 -4
- package/src/aggregator.ts +25 -1
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { BehaviorDashboardStats, DashboardStats, MessageStats, ProviderDashboardStats, RequestDetails, ToolDashboardStats } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Serialize stats ingestion and archive reconciliation across processes.
|
|
4
|
+
* The lock covers file discovery, parsing, and the final SQLite write so a
|
|
5
|
+
* parse result for a session moved by GC can never commit after cleanup.
|
|
6
|
+
* The native lock is owned by an operating-system primitive, so an interrupted
|
|
7
|
+
* owner is released automatically and a live owner is never displaced.
|
|
8
|
+
*/
|
|
9
|
+
export declare function withStatsSyncLock<T>(dbPath: string, fn: () => Promise<T>): Promise<T>;
|
|
2
10
|
/**
|
|
3
11
|
* Progress event emitted after each session file is fully processed.
|
|
4
12
|
* `current` is the number of files completed (skipped + parsed),
|
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.2.
|
|
4
|
+
"version": "17.2.6",
|
|
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.2.
|
|
43
|
-
"@oh-my-pi/pi-catalog": "17.2.
|
|
44
|
-
"@oh-my-pi/pi-utils": "17.2.
|
|
42
|
+
"@oh-my-pi/pi-ai": "17.2.6",
|
|
43
|
+
"@oh-my-pi/pi-catalog": "17.2.6",
|
|
44
|
+
"@oh-my-pi/pi-utils": "17.2.6",
|
|
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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { getStatsDbPath, workerHostEntry } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { withFileLock } from "@oh-my-pi/pi-utils/file-lock";
|
|
3
5
|
import {
|
|
4
6
|
getRecentErrors as dbGetRecentErrors,
|
|
5
7
|
getRecentRequests as dbGetRecentRequests,
|
|
@@ -48,6 +50,24 @@ import type {
|
|
|
48
50
|
} from "./types";
|
|
49
51
|
import { computeUsageWindowStats, fetchUsageSnapshots } from "./usage-windows";
|
|
50
52
|
|
|
53
|
+
const STATS_SYNC_LOCK_RETRY_MS = 25;
|
|
54
|
+
const STATS_SYNC_LOCK_WAIT_MS = 60 * 60 * 1000;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Serialize stats ingestion and archive reconciliation across processes.
|
|
58
|
+
* The lock covers file discovery, parsing, and the final SQLite write so a
|
|
59
|
+
* parse result for a session moved by GC can never commit after cleanup.
|
|
60
|
+
* The native lock is owned by an operating-system primitive, so an interrupted
|
|
61
|
+
* owner is released automatically and a live owner is never displaced.
|
|
62
|
+
*/
|
|
63
|
+
export async function withStatsSyncLock<T>(dbPath: string, fn: () => Promise<T>): Promise<T> {
|
|
64
|
+
await fs.promises.mkdir(path.dirname(dbPath), { recursive: true });
|
|
65
|
+
return await withFileLock(`${dbPath}.sync`, fn, {
|
|
66
|
+
retryDelayMs: STATS_SYNC_LOCK_RETRY_MS,
|
|
67
|
+
retries: Math.ceil(STATS_SYNC_LOCK_WAIT_MS / STATS_SYNC_LOCK_RETRY_MS),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
51
71
|
/**
|
|
52
72
|
* Apply a freshly parsed result to the database. Runs entirely on the
|
|
53
73
|
* main thread so the single SQLite handle owns every write.
|
|
@@ -218,6 +238,10 @@ export async function smokeTestSyncWorker({ timeoutMs = 5_000 }: { timeoutMs?: n
|
|
|
218
238
|
* bar walks at a steady rate).
|
|
219
239
|
*/
|
|
220
240
|
export async function syncAllSessions(opts?: SyncOptions): Promise<{ processed: number; files: number }> {
|
|
241
|
+
return withStatsSyncLock(getStatsDbPath(), () => syncAllSessionsLocked(opts));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function syncAllSessionsLocked(opts?: SyncOptions): Promise<{ processed: number; files: number }> {
|
|
221
245
|
await initDb();
|
|
222
246
|
|
|
223
247
|
const files = await listAllSessionFiles();
|