@linxiraos/pi-stats 1.0.9 → 1.1.0

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.
Files changed (86) hide show
  1. package/CHANGELOG.md +2 -278
  2. package/LICENSE +22 -0
  3. package/README.md +4 -2
  4. package/package.json +91 -93
  5. package/src/aggregator.ts +16 -4
  6. package/src/client/app/NavRail.tsx +1 -1
  7. package/src/client/data/formatters.ts +13 -0
  8. package/src/client/data/view-models.ts +3 -0
  9. package/src/client/routes/CostsRoute.tsx +28 -10
  10. package/src/client/routes/ErrorsRoute.tsx +5 -5
  11. package/src/client/routes/ModelsRoute.tsx +3 -2
  12. package/src/client/routes/OverviewRoute.tsx +6 -6
  13. package/src/client/routes/ProjectsRoute.tsx +7 -5
  14. package/src/client/routes/ProvidersRoute.tsx +30 -5
  15. package/src/client/routes/RequestsRoute.tsx +5 -5
  16. package/src/client/routes/ToolsRoute.tsx +23 -11
  17. package/src/client/ui/MetricCluster.tsx +7 -3
  18. package/src/client/ui/RequestDrawer.tsx +3 -3
  19. package/src/db.ts +127 -35
  20. package/src/embedded-client.generated.txt +0 -1
  21. package/src/index.ts +43 -19
  22. package/src/port-conflict.ts +13 -8
  23. package/src/server.ts +26 -14
  24. package/src/shared-types.ts +14 -1
  25. package/src/usage-windows.ts +104 -28
  26. package/dist/client/index.css +0 -1
  27. package/dist/client/index.html +0 -24
  28. package/dist/client/index.js +0 -257
  29. package/dist/client/styles.css +0 -1692
  30. package/dist/types/aggregator.d.ts +0 -98
  31. package/dist/types/client/App.d.ts +0 -1
  32. package/dist/types/client/api.d.ts +0 -22
  33. package/dist/types/client/app/AppLayout.d.ts +0 -16
  34. package/dist/types/client/app/NavRail.d.ts +0 -7
  35. package/dist/types/client/app/RangeControl.d.ts +0 -7
  36. package/dist/types/client/app/SyncButton.d.ts +0 -14
  37. package/dist/types/client/app/ThemeToggle.d.ts +0 -1
  38. package/dist/types/client/app/TopBar.d.ts +0 -15
  39. package/dist/types/client/app/routes.d.ts +0 -12
  40. package/dist/types/client/components/AgentTokenShare.d.ts +0 -5
  41. package/dist/types/client/components/chart-shared.d.ts +0 -173
  42. package/dist/types/client/components/models-table-shared.d.ts +0 -175
  43. package/dist/types/client/components/range-meta.d.ts +0 -21
  44. package/dist/types/client/data/charts.d.ts +0 -1
  45. package/dist/types/client/data/formatters.d.ts +0 -8
  46. package/dist/types/client/data/useHashRoute.d.ts +0 -8
  47. package/dist/types/client/data/useResource.d.ts +0 -13
  48. package/dist/types/client/data/view-models.d.ts +0 -74
  49. package/dist/types/client/index.d.ts +0 -2
  50. package/dist/types/client/routes/BehaviorRoute.d.ts +0 -7
  51. package/dist/types/client/routes/CostsRoute.d.ts +0 -7
  52. package/dist/types/client/routes/ErrorsRoute.d.ts +0 -8
  53. package/dist/types/client/routes/GainRoute.d.ts +0 -7
  54. package/dist/types/client/routes/ModelsRoute.d.ts +0 -7
  55. package/dist/types/client/routes/OverviewRoute.d.ts +0 -8
  56. package/dist/types/client/routes/ProjectsRoute.d.ts +0 -7
  57. package/dist/types/client/routes/ProvidersRoute.d.ts +0 -7
  58. package/dist/types/client/routes/RequestsRoute.d.ts +0 -8
  59. package/dist/types/client/routes/ToolsRoute.d.ts +0 -7
  60. package/dist/types/client/routes/index.d.ts +0 -10
  61. package/dist/types/client/types.d.ts +0 -63
  62. package/dist/types/client/ui/AsyncBoundary.d.ts +0 -12
  63. package/dist/types/client/ui/DataTable.d.ts +0 -17
  64. package/dist/types/client/ui/EmptyState.d.ts +0 -7
  65. package/dist/types/client/ui/ErrorState.d.ts +0 -6
  66. package/dist/types/client/ui/JsonBlock.d.ts +0 -7
  67. package/dist/types/client/ui/MetricCluster.d.ts +0 -5
  68. package/dist/types/client/ui/Panel.d.ts +0 -7
  69. package/dist/types/client/ui/RequestDrawer.d.ts +0 -5
  70. package/dist/types/client/ui/SegmentedControl.d.ts +0 -12
  71. package/dist/types/client/ui/Skeleton.d.ts +0 -8
  72. package/dist/types/client/ui/StatusPill.d.ts +0 -7
  73. package/dist/types/client/ui/index.d.ts +0 -11
  74. package/dist/types/client/useSystemTheme.d.ts +0 -11
  75. package/dist/types/db.d.ts +0 -159
  76. package/dist/types/embedded-client.d.ts +0 -18
  77. package/dist/types/gain-aggregator.d.ts +0 -26
  78. package/dist/types/index.d.ts +0 -7
  79. package/dist/types/parser.d.ts +0 -53
  80. package/dist/types/port-conflict.d.ts +0 -14
  81. package/dist/types/server.d.ts +0 -12
  82. package/dist/types/shared-types.d.ts +0 -408
  83. package/dist/types/sync-worker.d.ts +0 -31
  84. package/dist/types/types.d.ts +0 -164
  85. package/dist/types/usage-windows.d.ts +0 -45
  86. package/dist/types/user-metrics.d.ts +0 -81
package/CHANGELOG.md CHANGED
@@ -2,287 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [1.0.9] - 2026-08-19
6
-
7
- ## [17.3.0] - 2026-08-13
8
-
9
- ### Added
10
-
11
- - Added cost-weighted `cacheSavings` metric alongside `cacheRate`, accounting for cache-read discounts and write premiums against equivalent uncached prompt costs.
12
-
13
- ### Fixed
14
-
15
- - Ensured the embedded dashboard archive is byte-reproducible by sorting entries and zeroing tar and gzip timestamps during compilation.
16
-
17
- ## [17.2.10] - 2026-08-06
5
+ ## [1.1.0] - 2026-08-25
18
6
 
19
7
  ### Changed
20
8
 
21
- - Optimized package dependencies by replacing `date-fns` with `@linxiraos/pi-utils/dates` and removing unused test dependencies.
22
-
23
- ## [17.2.9] - 2026-08-05
24
-
25
- ### Fixed
26
-
27
- - Restricted the stats dashboard to IPv4 loopback and removed wildcard CORS access to its API ([#7633](https://github.com/can1357/oh-my-pi/issues/7633)).
28
-
29
- ## [17.2.4] - 2026-08-01
30
-
31
- ### Fixed
32
-
33
- - Fixed provider usage window stats silently showing no data during SQLite contention by installing a five-second busy timeout on read-only agent database connections ([#7300](https://github.com/can1357/oh-my-pi/issues/7300)).
34
-
35
- ## [17.1.2] - 2026-07-24
36
-
37
- ### Added
38
-
39
- - Added a Providers dashboard section: per-provider totals, stacked token/cost burn over time, peak-burn-hours histogram, subscription-window insights (windows burned, estimated tokens per window, peak concurrent utilization, ideal account count, exhaustion events), and latest window utilization per account — window analytics read the auth broker's `/v1/usage/history` when a broker is configured (falling back to the local agent DB), since broker deployments record usage history on the broker host
40
-
41
- ## [17.1.0] - 2026-07-24
42
-
43
- ### Fixed
44
-
45
- - 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.
46
-
47
- ## [17.0.6] - 2026-07-20
48
-
49
- ### Changed
50
-
51
- - Clarified overview token accounting by separating uncached input from cache reads and showing the conversation-token total used by the agent breakdown.
52
-
53
- ## [17.0.5] - 2026-07-18
54
-
55
- ### Fixed
56
-
57
- - Fixed an EADDRINUSE error by properly reusing the live stats dashboard on the requested port and reclaiming stale listeners (#5970).
58
-
59
- ## [17.0.2] - 2026-07-17
60
-
61
- ### Fixed
62
-
63
- - Fixed the Recent Errors list to honor the selected dashboard time range before returning the newest 50 failures.
64
-
65
- ## [16.4.7] - 2026-07-12
66
-
67
- ### Fixed
68
-
69
- - Fixed a `SQLITE_CONSTRAINT_NOTNULL` crash (`messages.stop_reason`) aborting the entire session sync when a persisted assistant message lacks a `stopReason`. Malformed entries — missing stop reason, token counts, or message timestamp — are now coerced at the parser boundary, and entries with no usage or model attribution are skipped instead of failing the batch insert.
70
-
71
- ## [16.4.2] - 2026-07-10
72
-
73
- ### Fixed
74
-
75
- - Fixed a crash during stats synchronization on legacy session entries that lack a cost breakdown by falling back to catalog pricing when available.
76
-
77
- ## [16.3.9] - 2026-07-06
78
-
79
- ### Changed
80
-
81
- - Refined behavior metrics to significantly reduce false positives in profanity, yelling, and anguish detection by excluding technical terms (e.g., "dummy", "trash", "garbage"), neutral punctuation (e.g., dot runs), and single-word capitalization (e.g., filenames or environment variables).
82
- - Re-categorized frustration interjections (such as "ugh", "argh", and "grr") from profanity to anguish.
83
- - Improved negation and blame detection to exclude determiners (e.g., "no auto start") and compounds (e.g., "no-op") while adding support for phrases like "why did you" and "makes no sense".
84
- - Added sad emoticons as a signal for anguish while excluding code-like patterns.
85
- - Triggered a one-time automatic re-ingestion of sessions on the next database sync to apply the updated metrics.
86
-
87
- ## [16.3.7] - 2026-07-05
88
-
89
- ### Changed
90
-
91
- - Optimized session-entry lookup and file reading performance by caching file metadata to avoid repeated full-file scans.
92
-
93
- ## [16.3.1] - 2026-07-02
94
-
95
- ### Added
96
-
97
- - Added a Tools tab to the `omp stats` dashboard (`/#/tools`): per-tool call counts, error rates, result/argument payload sizes, per-model breakdown, and a stacked calls-over-time chart. Token and cost columns attribute each invoking turn's real provider usage evenly across that turn's tool calls. Existing databases re-parse sessions once on the next sync to backfill historical tool calls.
98
-
99
- ## [16.2.7] - 2026-06-30
100
-
101
- ### Fixed
102
-
103
- - Improved premium request calculation accuracy by correctly accounting for specific model families.
104
-
105
- ## [16.2.6] - 2026-06-29
106
-
107
- ### Fixed
108
-
109
- - Fixed application crashes and Bun aborts on macOS and when parsing large stats session files, including during `omp --smoke-test` runs, by utilizing a more resilient serial parser and lenient line scanner.
110
-
111
- ## [16.2.3] - 2026-06-28
112
-
113
- ### Added
114
-
115
- - Support for parsing named advisor transcripts using the `__advisor.<slug>.jsonl` naming convention.
116
-
117
- ## [16.2.0] - 2026-06-27
118
-
119
- ### Added
120
-
121
- - Added a Gain tab to the `omp stats` dashboard (`/#/gain`) to display snapcompact token-savings with project scoping from synced session folders.
122
-
123
- ## [16.1.17] - 2026-06-24
124
-
125
- ### Fixed
126
-
127
- - Stats sync counted the same provider request multiple times when a forked or branched session file copied the parent's entries verbatim. Inserts now skip rows whose `(entry_id, timestamp)` already exists under a different `session_file`, and a one-shot migration on the next `omp stats` run collapses any pre-existing duplicates ([#3370](https://github.com/can1357/oh-my-pi/issues/3370)).
128
-
129
- ## [16.1.15] - 2026-06-22
130
-
131
- ### Added
132
-
133
- - Added token usage breakdown by agent type (Main, Subagents, Advisor) to the overview dashboard
134
-
135
- ## [16.0.10] - 2026-06-18
136
-
137
- ### Changed
138
-
139
- - Updated description of moderated content categories to use more inclusive terminology
140
-
141
- ### Fixed
142
-
143
- - Wide data tables (Requests, Errors, Overview, Projects) overflowed the page horizontally at narrow-desktop widths (768-1023px): the `.stats-table-desktop-only` wrapper used for mobile-card tables lacked the `overflow-x: auto` containment that `.stats-table-container` already has. They now scroll within their own bounds instead of spilling the page body.
144
-
145
- ## [16.0.5] - 2026-06-17
146
-
147
- ### Added
148
-
149
- - New Projects view summarizing usage, cost, and reliability per project folder (backed by the existing `/api/stats/folders` endpoint).
150
- - System-aware light/dark theme toggle — follows the OS by default, and an explicit choice persists across reloads.
151
-
152
- ### Changed
153
-
154
- - Redesigned the local stats dashboard with an OMP-themed product shell, dedicated per-section views, accessible loading/empty/error states, and flicker-free navigation between screens and time ranges.
155
-
156
- ### Fixed
157
-
158
- - The 1h time-range chart rendered an empty/single-point line; it now buckets at 5-minute granularity for a real trend.
159
-
160
- ## [15.13.3] - 2026-06-15
161
-
162
- ### Changed
163
-
164
- - Renamed `__omp_stats_sync_worker` to `__omp_worker_stats_sync`.
165
-
166
- ## [15.13.1] - 2026-06-15
167
-
168
- ### Fixed
169
-
170
- - Dropped `git` from the profanity list so normal repository mentions no longer count as profanity
171
-
172
- ## [15.12.4] - 2026-06-13
173
-
174
- ### Fixed
175
-
176
- - Fixed the stats dashboard's SQLite init never setting `PRAGMA busy_timeout`, so a concurrent `omp` startup hitting WAL recovery could crash `initDb()` with `SQLITE_BUSY` instead of waiting through it. The busy handler is now installed before `PRAGMA journal_mode=WAL` ([#2421](https://github.com/can1357/oh-my-pi/issues/2421)).
177
-
178
- ## [15.11.0] - 2026-06-10
179
-
180
- ### Added
181
-
182
- - Added support for prebuilt npm bundle mode via `PI_BUNDLED`, allowing the stats server to use an embedded dashboard bundle in packaged CLI distributions
183
-
184
- ### Fixed
185
-
186
- - Fixed handling of legacy `embedded-client.generated.txt` placeholder content so it is treated as missing archive instead of being decoded into invalid bytes
187
- - Fixed ENOENT handling while scanning dashboard source/build directories so missing `client/` or `dist/client` trees no longer crash startup
188
-
189
- ## [15.10.11] - 2026-06-10
190
-
191
- ### Changed
192
-
193
- - Bundled-model lookups (`getBundledModel`, `GeneratedProvider`) now import from the new `@linxiraos/pi-catalog` package instead of the `@linxiraos/pi-ai` barrel, which no longer re-exports catalog values
194
- - The session-sync worker re-enters the host CLI entry (`workerHostEntry()` + `__omp_stats_sync_worker` argv selector) when running inside omp — source, npm bundle, or compiled binary — and keeps loading its own `sync-worker.ts` module directly for standalone `omp-stats`, bun test, and SDK hosts
195
-
196
- ## [15.1.6] - 2026-05-19
197
-
198
- ### Fixed
199
-
200
- - Fixed `omp stats` crashing on first session sync in published `omp-{linux,darwin,windows}-*` binaries with `BuildMessage: ModuleNotFound resolving "./packages/stats/src/sync-worker.ts"`; the release build script now lists the stats sync, browser tab, and JS eval workers as explicit `--compile` entrypoints so Bun emits them into bunfs, matching the dev build script and the AGENTS.md worker spawn contract. ([#1150](https://github.com/can1357/oh-my-pi/issues/1150))
201
-
202
- ## [15.1.0] - 2026-05-15
203
-
204
- ### Fixed
205
-
206
- - Fixed incremental `parseSessionFile(path, fromOffset)` losing the active service tier when resuming past a `service_tier_change` entry, so priority OpenAI replies appended after the offset are now credited with `premiumRequests: 1` (regression introduced by 13f59162e which stopped folding priority-tier into per-message premium counts)
207
-
208
- ## [15.0.1] - 2026-05-14
209
-
210
- ### Breaking Changes
211
-
212
- - Raised the minimum required Bun version to >=1.3.14 in package metadata
213
-
214
- ### Changed
215
-
216
- - Changed the "Premium Reqs" dashboard card to also include OpenAI priority service-tier requests (`serviceTier: "priority"`), counting each as 1 premium request alongside GitHub Copilot premium calls. Pre-existing sessions are backfilled on the next `omp stats` run: a one-shot `premium_requests_priority_v1` sentinel wipes `file_offsets` so every session re-parses, and `insertMessageStats` now `UPSERT`s `premium_requests` (other columns untouched) using the `service_tier_change` entries already in the session log to retroactively credit priority traffic.
217
-
218
- ## [14.9.9] - 2026-05-12
219
-
220
- ### Added
221
-
222
- - Added separate input-token and output-token totals to the overview dashboard cards.
223
-
224
- ### Fixed
225
-
226
- - Fixed `omp stats` in compiled binaries by using the serial sync path instead of spawning a raw file-asset worker that cannot import bundled parser code.
227
- - Fixed behavior backfills after failed compiled-binary sync attempts by marking the backfill sentinel only after a successful full sync.
228
-
229
- ## [14.9.7] - 2026-05-12
230
-
231
- ### Breaking Changes
232
-
233
- - Broke backward compatibility of behavior stats fields by replacing `yellingSentences`/`dramaRuns` with `yelling`/`anguish` and adding `negation`, `repetition`, `blame` in query result types and persisted `user_messages` schema
234
-
235
- ### Added
236
-
237
- - Added `SyncOptions` to `syncAllSessions` with `onProgress` and `workers` to optionally show per-file sync progress and tune parser concurrency
238
- - Added new frustration behavior metrics (`negation`, `repetition`, `blame`) plus a `frustration` aggregate in behavior charts, model tables, and summary cards
239
-
240
- ### Changed
241
-
242
- - Changed sync ingestion to parse session files through a worker pool while applying parsed results and database writes on the main thread
243
- - Changed behavior analysis to strip code blocks, XML/URLs, quoted lines, and placeholders before scoring and to suppress signals on long structured messages
244
- - Changed dashboard metrics labels and totals to the new signal names, including replacing the old three-signal totals with `yelling`, `profanity`, `anguish`, and `frustration`
245
- - Changed sync output to print a live terminal progress indicator while processing session files
246
-
247
- ### Fixed
248
-
249
- - Fixed user-message attribution so assistant model/provider links are backfilled during incremental sync instead of being left unknown
250
- - Fixed word-boundary regex handling in profanity detection so matching now works as intended in normal prose
251
-
252
- ## [14.9.5] - 2026-05-12
253
-
254
- ### Added
255
-
256
- - Added time range selection options (1h, 24h, 7d, 30d, 90d, All) to the dashboard header and bound them to reloading statistics for the selected window
257
- - Added a **Behavior** dashboard page that tracks user yelling (CAPS), profanity, and dramatic punctuation (`!!!` / `???`) per day, with by-model comparisons mirroring the cost page
258
- - Added a per-model behavior table to the **Behavior** page mirroring the Models table: sortable rows of CAPS / profanity / drama hits per model with sparkline trend and an expandable per-model breakdown chart
259
- - Added optional `range` query parameter support on stats endpoints to retrieve metrics scoped to a requested time window
260
-
261
- ### Changed
262
-
263
- - Changed the Costs dashboard summary to report totals, average per day, and top model for the selected time range instead of a fixed 30-day window and removed the previous-30-day trend comparison
264
- - Changed behavior metrics ingestion to compute yelling from user message sentence-level uppercase ratios, filtering out short uppercase fragments so the behavior data is attributed to messages more accurately
265
- - Removed per-chart 14/30/90 day pickers on Costs and Behavior pages so every page obeys the single time-range selector in the header
266
- - Changed dashboard and stats queries to return data from the selected time window instead of always using all-time aggregates
267
- - Changed the default displayed range in the UI/API to last 24h
268
- - Added support for returning all data when `range=all` is requested
269
-
270
- ### Fixed
271
-
272
- - Fixed handling of unknown `range` values by falling back to the last 24h instead of returning unscoped data
273
- - Fixed `omp stats` failing to build the client on globally-installed installs by promoting `tailwindcss` from `devDependencies` to `dependencies` (the client build runs at runtime)
274
-
275
- ## [14.5.4] - 2026-04-28
276
-
277
- ### Fixed
278
-
279
- - Fixed GPT cost reporting by deriving missing OpenAI Codex costs from the model catalog and backfilling existing zero-cost rows.
280
-
281
- ## [13.6.0] - 2026-03-03
282
-
283
- ### Fixed
284
-
285
- - Include subtask session files in usage stats ([#250](https://github.com/can1357/oh-my-pi/issues/250))
9
+ - 同步上游 OMP v18.0.3 / v18.0.4(内部运行时与构建改进,无独立用户可见变更)。
286
10
 
287
11
  ## [1.0.0] - 2026-08-13
288
12
 
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Can Bölük
4
+ Copyright (c) 2026 Stencil Labs, Inc.
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md CHANGED
@@ -17,10 +17,12 @@ Local observability dashboard for AI usage statistics.
17
17
  | Cache Rate | `cache_read / (input + cache_read) * 100` |
18
18
  | Cache Savings | `(uncached prompt cost - actual prompt cost) / uncached prompt cost * 100` |
19
19
  | Error Rate | `count(stopReason=error) / total_calls * 100` |
20
- | Total Cost | Sum of `usage.cost.total` |
20
+ | API-equivalent estimate | Sum of token usage priced with the matching public API rate card |
21
21
  | Avg Latency | Mean of `duration` |
22
22
  | TTFT | Mean of `ttft` (time to first token) |
23
23
 
24
+ Subscription-backed models use matching public API prices when an exact public model exists; these values estimate API-equivalent usage rather than the user's bill. Subscription-only models without a public price are reported as N/A and excluded from dollar totals.
25
+
24
26
  ## Usage
25
27
 
26
28
  ### Via CLI
@@ -72,7 +74,7 @@ console.log(stats.byModel[0].avgTokensPerSecond);
72
74
 
73
75
  The web dashboard provides:
74
76
 
75
- - Overall metrics cards (requests, cost, cache rate, cache savings, error rate, duration, tokens/s)
77
+ - Overall metrics cards (requests, API-equivalent estimate, cache rate, cache savings, error rate, duration, tokens/s)
76
78
  - Time series chart showing requests and errors over time
77
79
  - Per-model breakdown table
78
80
  - Per-folder breakdown table
package/package.json CHANGED
@@ -1,94 +1,92 @@
1
1
  {
2
- "type": "module",
3
- "name": "@linxiraos/pi-stats",
4
- "version": "1.0.9",
5
- "description": "Local observability dashboard for pi AI usage statistics",
6
- "homepage": "https://linxira-os.github.io",
7
- "author": "Can Boluk",
8
- "license": "MIT",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/Linxira-OS/linxira-zeta.git",
12
- "directory": "packages/stats"
13
- },
14
- "bugs": {
15
- "url": "https://github.com/can1357/oh-my-pi/issues"
16
- },
17
- "keywords": [
18
- "ai",
19
- "observability",
20
- "metrics",
21
- "dashboard",
22
- "llm",
23
- "statistics"
24
- ],
25
- "main": "./src/index.ts",
26
- "types": "./dist/types/index.d.ts",
27
- "bin": {
28
- "omp-stats": "./src/index.ts"
29
- },
30
- "scripts": {
31
- "gen:stats": "bun scripts/generate-client-bundle.ts --generate",
32
- "gen:stats:reset": "bun scripts/generate-client-bundle.ts --reset",
33
- "build": "bun run build.ts",
34
- "dev": "bun run src/index.ts",
35
- "check": "biome check . && bun run check:types",
36
- "check:types": "tsgo -p tsconfig.json --noEmit && tsgo -p tsconfig.client.json --noEmit",
37
- "lint": "biome lint .",
38
- "fix": "biome check --write --unsafe .",
39
- "fmt": "biome format --write ."
40
- },
41
- "dependencies": {
42
- "@linxiraos/pi-ai": "1.0.9",
43
- "@linxiraos/pi-catalog": "1.0.9",
44
- "@linxiraos/pi-utils": "1.0.9",
45
- "@tailwindcss/node": "^4.3.2",
46
- "chart.js": "^4.5.1",
47
- "lucide-react": "^1.24.0",
48
- "react": "19.2.7",
49
- "react-chartjs-2": "^5.3.1",
50
- "react-dom": "19.2.7",
51
- "tailwindcss": "^4.3.2"
52
- },
53
- "devDependencies": {
54
- "@types/bun": "^1.3.14",
55
- "@types/react": "^19.2.17",
56
- "@types/react-dom": "^19.2.3",
57
- "postcss": "^8.5.16"
58
- },
59
- "engines": {
60
- "bun": ">=1.3.14"
61
- },
62
- "files": [
63
- "src",
64
- "build.ts",
65
- "tailwind.config.js",
66
- "README.md",
67
- "CHANGELOG.md",
68
- "dist/types",
69
- "dist/client"
70
- ],
71
- "exports": {
72
- ".": {
73
- "types": "./dist/types/index.d.ts",
74
- "import": "./src/index.ts"
75
- },
76
- "./*": {
77
- "types": "./dist/types/*.d.ts",
78
- "import": "./src/*.ts"
79
- },
80
- "./client": {
81
- "types": "./dist/types/client/index.d.ts",
82
- "import": "./src/client/index.tsx"
83
- },
84
- "./client/*": {
85
- "types": "./dist/types/client/*.d.ts",
86
- "import": "./src/client/*.ts"
87
- },
88
- "./client/components/*": {
89
- "types": "./dist/types/client/components/*.d.ts",
90
- "import": "./src/client/components/*.ts"
91
- },
92
- "./*.js": "./src/*.ts"
93
- }
94
- }
2
+ "type": "module",
3
+ "name": "@linxiraos/pi-stats",
4
+ "version": "1.1.0",
5
+ "description": "Local observability dashboard for pi AI usage statistics",
6
+ "homepage": "https://linxira-os.github.io/zeta/",
7
+ "author": "Can Boluk",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Linxira-OS/linxira-zeta.git",
12
+ "directory": "packages/stats"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/Linxira-OS/linxira-zeta/issues"
16
+ },
17
+ "keywords": [
18
+ "ai",
19
+ "observability",
20
+ "metrics",
21
+ "dashboard",
22
+ "llm",
23
+ "statistics"
24
+ ],
25
+ "main": "./src/index.ts",
26
+ "types": "./src/index.ts",
27
+ "bin": {
28
+ "omp-stats": "./src/index.ts"
29
+ },
30
+ "scripts": {
31
+ "gen:stats": "bun scripts/generate-client-bundle.ts --generate",
32
+ "gen:stats:reset": "bun scripts/generate-client-bundle.ts --reset",
33
+ "build": "bun run build.ts",
34
+ "dev": "bun run src/index.ts",
35
+ "check": "biome check . && bun run check:types",
36
+ "check:types": "tsgo -p tsconfig.json --noEmit && tsgo -p tsconfig.client.json --noEmit",
37
+ "lint": "biome lint .",
38
+ "fix": "biome check --write --unsafe .",
39
+ "fmt": "biome format --write ."
40
+ },
41
+ "dependencies": {
42
+ "@linxiraos/pi-ai": "catalog:",
43
+ "@linxiraos/pi-catalog": "catalog:",
44
+ "@linxiraos/pi-utils": "catalog:",
45
+ "@tailwindcss/node": "catalog:",
46
+ "chart.js": "catalog:",
47
+ "lucide-react": "catalog:",
48
+ "react": "catalog:",
49
+ "react-chartjs-2": "catalog:",
50
+ "react-dom": "catalog:",
51
+ "tailwindcss": "catalog:"
52
+ },
53
+ "devDependencies": {
54
+ "@types/bun": "catalog:",
55
+ "@types/react": "catalog:",
56
+ "@types/react-dom": "catalog:",
57
+ "postcss": "catalog:"
58
+ },
59
+ "engines": {
60
+ "bun": ">=1.3.14"
61
+ },
62
+ "files": [
63
+ "src",
64
+ "build.ts",
65
+ "tailwind.config.js",
66
+ "README.md",
67
+ "CHANGELOG.md"
68
+ ],
69
+ "exports": {
70
+ ".": {
71
+ "types": "./src/index.ts",
72
+ "import": "./src/index.ts"
73
+ },
74
+ "./*": {
75
+ "types": "./src/*.ts",
76
+ "import": "./src/*.ts"
77
+ },
78
+ "./client": {
79
+ "types": "./src/client/index.tsx",
80
+ "import": "./src/client/index.tsx"
81
+ },
82
+ "./client/*": {
83
+ "types": "./src/client/*.ts",
84
+ "import": "./src/client/*.ts"
85
+ },
86
+ "./client/components/*": {
87
+ "types": "./src/client/components/*.ts",
88
+ "import": "./src/client/components/*.ts"
89
+ },
90
+ "./*.js": "./src/*.ts"
91
+ }
92
+ }
package/src/aggregator.ts CHANGED
@@ -42,12 +42,13 @@ import type { SyncWorkerRequest, SyncWorkerResponse } from "./sync-worker";
42
42
  import type {
43
43
  BehaviorDashboardStats,
44
44
  DashboardStats,
45
+ FolderStats,
45
46
  MessageStats,
46
47
  ProviderDashboardStats,
47
48
  RequestDetails,
48
49
  ToolDashboardStats,
49
50
  } from "./types";
50
- import { computeUsageWindowStats, fetchUsageSnapshots } from "./usage-windows";
51
+ import { computeUsageWindowStats, fetchUsageData } from "./usage-windows";
51
52
 
52
53
  /**
53
54
  * Apply a freshly parsed result to the database. Runs entirely on the
@@ -455,6 +456,13 @@ export async function getCostDashboardStats(range?: string | null): Promise<Pick
455
456
  costSeries: getCostTimeSeries(costSeriesDays, cutoff),
456
457
  };
457
458
  }
459
+
460
+ export async function getFolderStats(range?: string | null): Promise<FolderStats[]> {
461
+ await initDb();
462
+ const { cutoff } = getTimeRangeConfig(range);
463
+ return getStatsByFolder(cutoff ?? undefined);
464
+ }
465
+
458
466
  export async function getRecentRequests(limit?: number): Promise<MessageStats[]> {
459
467
  await initDb();
460
468
  return dbGetRecentRequests(limit);
@@ -521,14 +529,18 @@ export async function getToolDashboardStats(range?: string | null): Promise<Tool
521
529
  * Get the providers dashboard payload: per-provider totals, peak-burn-hours
522
530
  * histogram, provider token time series, and subscription-window analytics
523
531
  * (utilization series + insights) derived from recorded usage-limit snapshots.
532
+ *
533
+ * Window token estimates use broker-held fleet token burn when a broker is
534
+ * configured — the window fractions cover every install sharing the broker's
535
+ * credentials, so dividing them into local-only tokens would undercount.
524
536
  */
525
537
  export async function getProviderDashboardStats(range?: string | null): Promise<ProviderDashboardStats> {
526
538
  await initDb();
527
539
  const { modelSeriesDays, modelSeriesBucketMs, cutoff } = getTimeRangeConfig(range);
528
540
  const providers = getStatsByProvider(cutoff ?? undefined);
529
- const tokensByProvider = new Map(providers.map(p => [p.provider, p.totalTokens]));
530
- const snapshots = await fetchUsageSnapshots(cutoff ?? 0);
531
- const { usageSeries, windowInsights } = computeUsageWindowStats(snapshots, tokensByProvider);
541
+ const usage = await fetchUsageData(cutoff ?? 0);
542
+ const tokensByProvider = usage.fleetTokensByProvider ?? new Map(providers.map(p => [p.provider, p.totalTokens]));
543
+ const { usageSeries, windowInsights } = computeUsageWindowStats(usage.rows, tokensByProvider);
532
544
  return {
533
545
  providers,
534
546
  hourly: getProviderHourlyBurn(cutoff ?? undefined),
@@ -37,7 +37,7 @@ export function NavRail({ activeSection, onSectionChange, className = "" }: NavR
37
37
  </nav>
38
38
 
39
39
  <div className="stats-nav-rail-footer">
40
- <span className="stats-version-tag">OMP Stats v1.0.0</span>
40
+ <span className="stats-version-tag">Zeta Stats</span>
41
41
  </div>
42
42
  </aside>
43
43
  );
@@ -1,4 +1,5 @@
1
1
  import { formatDistanceToNow } from "@linxiraos/pi-utils/dates";
2
+ import type { MessageStats } from "../types";
2
3
 
3
4
  export function formatInteger(value: number): string {
4
5
  return value.toLocaleString();
@@ -17,6 +18,18 @@ export function formatCost(value: number, digits?: number): string {
17
18
  })}`;
18
19
  }
19
20
 
21
+ /** Format an API-equivalent estimate, using N/A when all usage is unpriced. */
22
+ export function formatEstimatedCost(value: number, unpricedRequests: number, digits?: number): string {
23
+ return value === 0 && unpricedRequests > 0 ? "N/A" : formatCost(value, digits);
24
+ }
25
+
26
+ /** Format one request's cost, distinguishing unpriced SuperGrok usage from free usage. */
27
+ export function formatMessageCost(message: Pick<MessageStats, "provider" | "usage">, digits?: number): string {
28
+ const unpricedRequests =
29
+ message.provider === "xai-oauth" && message.usage.totalTokens > 0 && message.usage.cost.total === 0 ? 1 : 0;
30
+ return formatEstimatedCost(message.usage.cost.total, unpricedRequests, digits);
31
+ }
32
+
20
33
  export function formatPercent(value: number, digits = 1): string {
21
34
  return `${(value * 100).toFixed(digits)}%`;
22
35
  }
@@ -74,6 +74,7 @@ export function buildAgentTokenShare(stats: AgentTypeStats[]): AgentTokenShareVi
74
74
 
75
75
  export interface CostSummaryView {
76
76
  totalCost: number;
77
+ unpricedRequests: number;
77
78
  avgDailyCost: number;
78
79
  topModelName: string;
79
80
  topModelCost: number;
@@ -111,6 +112,7 @@ export interface FolderRowView extends FolderStats {
111
112
 
112
113
  export function buildCostSummary(costSeries: CostTimeSeriesPoint[]): CostSummaryView {
113
114
  const totalCost = costSeries.reduce((sum, p) => sum + p.cost, 0);
115
+ const unpricedRequests = costSeries.reduce((sum, point) => sum + point.unpricedRequests, 0);
114
116
  const dayBuckets = new Set(costSeries.map(p => p.timestamp)).size;
115
117
  const avgDailyCost = dayBuckets > 0 ? totalCost / dayBuckets : 0;
116
118
 
@@ -130,6 +132,7 @@ export function buildCostSummary(costSeries: CostTimeSeriesPoint[]): CostSummary
130
132
 
131
133
  return {
132
134
  totalCost,
135
+ unpricedRequests,
133
136
  avgDailyCost,
134
137
  topModelName,
135
138
  topModelCost,