nilometer 0.0.0-stage → 0.2.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.
- package/CHANGELOG.md +68 -0
- package/LICENSE +21 -0
- package/README.md +464 -2
- package/core/pricing/prices.json +348 -0
- package/core/schema/001_ingestion.sql +57 -0
- package/core/schema/002_line_count.sql +7 -0
- package/core/schema/003_derived.sql +70 -0
- package/core/schema/004_requests_dedup.sql +50 -0
- package/core/schema/005_repositories.sql +25 -0
- package/core/schema/006_status_readings.sql +33 -0
- package/core/schema/007_pricing.sql +95 -0
- package/core/schema/008_interruptions.sql +263 -0
- package/core/schema/009_headroom.sql +84 -0
- package/core/schema/010_attribution.sql +106 -0
- package/core/schema/011_projections.sql +83 -0
- package/core/schema/012_unsplit_cache_writes.sql +49 -0
- package/core/schema/013_repository_revalidation.sql +104 -0
- package/core/schema/014_observed_readings.sql +204 -0
- package/core/schema/015_response_observations.sql +127 -0
- package/core/schema/016_windows_repository_paths.sql +41 -0
- package/core/schema/017_unattributed_usage_once.sql +47 -0
- package/core/schema/018_request_time_index.sql +21 -0
- package/core/schema/019_dedup_earliest_of_equals.sql +61 -0
- package/core/schema/020_limit_quota_fields.sql +131 -0
- package/core/schema/021_plan_periods.sql +121 -0
- package/core/schema/022_attachment_walk.sql +223 -0
- package/core/schema/023_auto_continuations.sql +129 -0
- package/core/schema/024_counted_limit_groups.sql +29 -0
- package/core/schema/025_model_fallback.sql +77 -0
- package/core/schema/026_changed_readings.sql +106 -0
- package/core/schema/027_weekly_crossings.sql +32 -0
- package/core/schema/028_stale_readings.sql +196 -0
- package/core/schema/029_lockout_sessions.sql +68 -0
- package/core/schema/030_repository_aliases.sql +81 -0
- package/core/schema/031_cte_cursor_budget.sql +153 -0
- package/core/schema/032_stored_views.sql +233 -0
- package/dist/cli/main.cli.js +39 -0
- package/dist/cli/program.js +881 -0
- package/dist/core/db/database.js +182 -0
- package/dist/core/db/fingerprint.js +71 -0
- package/dist/core/ingest/attribution.js +131 -0
- package/dist/core/ingest/classify.js +382 -0
- package/dist/core/ingest/command.js +95 -0
- package/dist/core/ingest/derive.js +219 -0
- package/dist/core/ingest/discover.js +126 -0
- package/dist/core/ingest/ingest.js +160 -0
- package/dist/core/ingest/reader.js +89 -0
- package/dist/core/ingest/reset-time.js +97 -0
- package/dist/core/ingest/results.js +201 -0
- package/dist/core/ingest/spool.js +165 -0
- package/dist/core/ingest/store.js +99 -0
- package/dist/core/ingest/stored-views.js +65 -0
- package/dist/core/install/install.js +185 -0
- package/dist/core/install/locations.js +89 -0
- package/dist/core/install/private-files.js +259 -0
- package/dist/core/install/record.js +103 -0
- package/dist/core/plans/plan-prices.js +128 -0
- package/dist/core/plans/repository-aliases.js +136 -0
- package/dist/core/pricing/prices.js +147 -0
- package/dist/core/settings/settings-file.js +215 -0
- package/dist/core/settings/statusline.js +240 -0
- package/dist/core/util/commands.js +91 -0
- package/dist/core/util/node-version.js +58 -0
- package/dist/core/util/open-page.js +69 -0
- package/dist/core/util/paths.js +36 -0
- package/dist/core/verify/compare.js +77 -0
- package/dist/core/verify/verify.js +220 -0
- package/dist/core/view/server.js +142 -0
- package/dist/viewer/explain.js +574 -0
- package/dist/viewer/format.js +150 -0
- package/dist/viewer/html-charts.js +1292 -0
- package/dist/viewer/html-contract.js +2 -0
- package/dist/viewer/html-data.js +718 -0
- package/dist/viewer/html.js +1717 -0
- package/dist/viewer/queries.js +128 -0
- package/dist/viewer/render.js +602 -0
- package/dist/viewer/report.js +120 -0
- package/dist/viewer/save.js +110 -0
- package/docs/requirements.md +113 -0
- package/hooks/statusline.sh +104 -0
- package/package.json +61 -4
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
{
|
|
2
|
+
"comment": "Anthropic API list prices, USD per million tokens. Every row was read from source_url on verified_on. effective_from 0000-01-01 means in effect since release as far as can be verified (D-020). A price change adds a row; never edit a past row. See the pricing-table skill.",
|
|
3
|
+
"rows": [
|
|
4
|
+
{
|
|
5
|
+
"model_id": "claude-fable-5-1",
|
|
6
|
+
"effective_from": "0000-01-01",
|
|
7
|
+
"input": 10,
|
|
8
|
+
"output": 50,
|
|
9
|
+
"cache_write_5m": 12.5,
|
|
10
|
+
"cache_write_1h": 20,
|
|
11
|
+
"cache_read": 0.25,
|
|
12
|
+
"fast": null,
|
|
13
|
+
"standard_rate_max_input_tokens": null,
|
|
14
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
15
|
+
"verified_on": "2026-09-13"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"model_id": "claude-fable-5",
|
|
19
|
+
"effective_from": "0000-01-01",
|
|
20
|
+
"input": 10,
|
|
21
|
+
"output": 50,
|
|
22
|
+
"cache_write_5m": 12.5,
|
|
23
|
+
"cache_write_1h": 20,
|
|
24
|
+
"cache_read": 1,
|
|
25
|
+
"fast": null,
|
|
26
|
+
"standard_rate_max_input_tokens": null,
|
|
27
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
28
|
+
"verified_on": "2026-09-13"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"model_id": "claude-mythos-5-1",
|
|
32
|
+
"effective_from": "0000-01-01",
|
|
33
|
+
"input": 10,
|
|
34
|
+
"output": 50,
|
|
35
|
+
"cache_write_5m": 12.5,
|
|
36
|
+
"cache_write_1h": 20,
|
|
37
|
+
"cache_read": 0.25,
|
|
38
|
+
"fast": null,
|
|
39
|
+
"standard_rate_max_input_tokens": null,
|
|
40
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
41
|
+
"verified_on": "2026-09-24"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"model_id": "claude-mythos-5",
|
|
45
|
+
"effective_from": "0000-01-01",
|
|
46
|
+
"input": 10,
|
|
47
|
+
"output": 50,
|
|
48
|
+
"cache_write_5m": 12.5,
|
|
49
|
+
"cache_write_1h": 20,
|
|
50
|
+
"cache_read": 1,
|
|
51
|
+
"fast": null,
|
|
52
|
+
"standard_rate_max_input_tokens": null,
|
|
53
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
54
|
+
"verified_on": "2026-09-24"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"model_id": "claude-opus-5-5",
|
|
58
|
+
"effective_from": "0000-01-01",
|
|
59
|
+
"input": 4,
|
|
60
|
+
"output": 20,
|
|
61
|
+
"cache_write_5m": 5,
|
|
62
|
+
"cache_write_1h": 8,
|
|
63
|
+
"cache_read": 0.2,
|
|
64
|
+
"fast": {
|
|
65
|
+
"input": 8,
|
|
66
|
+
"output": 40,
|
|
67
|
+
"cache_write_5m": 10,
|
|
68
|
+
"cache_write_1h": 16,
|
|
69
|
+
"cache_read": 0.4
|
|
70
|
+
},
|
|
71
|
+
"standard_rate_max_input_tokens": null,
|
|
72
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
73
|
+
"verified_on": "2026-09-24"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"model_id": "claude-opus-5",
|
|
77
|
+
"effective_from": "0000-01-01",
|
|
78
|
+
"input": 5,
|
|
79
|
+
"output": 25,
|
|
80
|
+
"cache_write_5m": 6.25,
|
|
81
|
+
"cache_write_1h": 10,
|
|
82
|
+
"cache_read": 0.5,
|
|
83
|
+
"fast": {
|
|
84
|
+
"input": 10,
|
|
85
|
+
"output": 50,
|
|
86
|
+
"cache_write_5m": 12.5,
|
|
87
|
+
"cache_write_1h": 20,
|
|
88
|
+
"cache_read": 1
|
|
89
|
+
},
|
|
90
|
+
"standard_rate_max_input_tokens": null,
|
|
91
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
92
|
+
"verified_on": "2026-09-13"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"model_id": "claude-opus-4-8",
|
|
96
|
+
"effective_from": "0000-01-01",
|
|
97
|
+
"input": 5,
|
|
98
|
+
"output": 25,
|
|
99
|
+
"cache_write_5m": 6.25,
|
|
100
|
+
"cache_write_1h": 10,
|
|
101
|
+
"cache_read": 0.5,
|
|
102
|
+
"fast": {
|
|
103
|
+
"input": 10,
|
|
104
|
+
"output": 50,
|
|
105
|
+
"cache_write_5m": 12.5,
|
|
106
|
+
"cache_write_1h": 20,
|
|
107
|
+
"cache_read": 1
|
|
108
|
+
},
|
|
109
|
+
"standard_rate_max_input_tokens": null,
|
|
110
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
111
|
+
"verified_on": "2026-09-13"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"model_id": "claude-opus-4-7",
|
|
115
|
+
"effective_from": "0000-01-01",
|
|
116
|
+
"input": 5,
|
|
117
|
+
"output": 25,
|
|
118
|
+
"cache_write_5m": 6.25,
|
|
119
|
+
"cache_write_1h": 10,
|
|
120
|
+
"cache_read": 0.5,
|
|
121
|
+
"fast": null,
|
|
122
|
+
"standard_rate_max_input_tokens": null,
|
|
123
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
124
|
+
"verified_on": "2026-09-13"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"model_id": "claude-opus-4-6",
|
|
128
|
+
"effective_from": "0000-01-01",
|
|
129
|
+
"input": 5,
|
|
130
|
+
"output": 25,
|
|
131
|
+
"cache_write_5m": 6.25,
|
|
132
|
+
"cache_write_1h": 10,
|
|
133
|
+
"cache_read": 0.5,
|
|
134
|
+
"fast": null,
|
|
135
|
+
"standard_rate_max_input_tokens": null,
|
|
136
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
137
|
+
"verified_on": "2026-09-13"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"model_id": "claude-opus-4-5",
|
|
141
|
+
"effective_from": "0000-01-01",
|
|
142
|
+
"input": 5,
|
|
143
|
+
"output": 25,
|
|
144
|
+
"cache_write_5m": 6.25,
|
|
145
|
+
"cache_write_1h": 10,
|
|
146
|
+
"cache_read": 0.5,
|
|
147
|
+
"fast": null,
|
|
148
|
+
"standard_rate_max_input_tokens": 200000,
|
|
149
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
150
|
+
"verified_on": "2026-09-24"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"model_id": "claude-opus-4-5-20251101",
|
|
154
|
+
"effective_from": "0000-01-01",
|
|
155
|
+
"input": 5,
|
|
156
|
+
"output": 25,
|
|
157
|
+
"cache_write_5m": 6.25,
|
|
158
|
+
"cache_write_1h": 10,
|
|
159
|
+
"cache_read": 0.5,
|
|
160
|
+
"fast": null,
|
|
161
|
+
"standard_rate_max_input_tokens": 200000,
|
|
162
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
163
|
+
"verified_on": "2026-09-24"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"model_id": "claude-opus-4-1",
|
|
167
|
+
"effective_from": "0000-01-01",
|
|
168
|
+
"input": 15,
|
|
169
|
+
"output": 75,
|
|
170
|
+
"cache_write_5m": 18.75,
|
|
171
|
+
"cache_write_1h": 30,
|
|
172
|
+
"cache_read": 1.5,
|
|
173
|
+
"fast": null,
|
|
174
|
+
"standard_rate_max_input_tokens": 200000,
|
|
175
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
176
|
+
"verified_on": "2026-09-24"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"model_id": "claude-opus-4-1-20250805",
|
|
180
|
+
"effective_from": "0000-01-01",
|
|
181
|
+
"input": 15,
|
|
182
|
+
"output": 75,
|
|
183
|
+
"cache_write_5m": 18.75,
|
|
184
|
+
"cache_write_1h": 30,
|
|
185
|
+
"cache_read": 1.5,
|
|
186
|
+
"fast": null,
|
|
187
|
+
"standard_rate_max_input_tokens": 200000,
|
|
188
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
189
|
+
"verified_on": "2026-09-24"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"model_id": "claude-opus-4-0",
|
|
193
|
+
"effective_from": "0000-01-01",
|
|
194
|
+
"input": 15,
|
|
195
|
+
"output": 75,
|
|
196
|
+
"cache_write_5m": 18.75,
|
|
197
|
+
"cache_write_1h": 30,
|
|
198
|
+
"cache_read": 1.5,
|
|
199
|
+
"fast": null,
|
|
200
|
+
"standard_rate_max_input_tokens": 200000,
|
|
201
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
202
|
+
"verified_on": "2026-09-24"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"model_id": "claude-opus-4-20250514",
|
|
206
|
+
"effective_from": "0000-01-01",
|
|
207
|
+
"input": 15,
|
|
208
|
+
"output": 75,
|
|
209
|
+
"cache_write_5m": 18.75,
|
|
210
|
+
"cache_write_1h": 30,
|
|
211
|
+
"cache_read": 1.5,
|
|
212
|
+
"fast": null,
|
|
213
|
+
"standard_rate_max_input_tokens": 200000,
|
|
214
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
215
|
+
"verified_on": "2026-09-24"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"model_id": "claude-sonnet-5-5",
|
|
219
|
+
"effective_from": "0000-01-01",
|
|
220
|
+
"input": 2,
|
|
221
|
+
"output": 10,
|
|
222
|
+
"cache_write_5m": 2.5,
|
|
223
|
+
"cache_write_1h": 4,
|
|
224
|
+
"cache_read": 0.2,
|
|
225
|
+
"fast": null,
|
|
226
|
+
"standard_rate_max_input_tokens": null,
|
|
227
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
228
|
+
"verified_on": "2026-10-04"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"model_id": "claude-sonnet-5",
|
|
232
|
+
"effective_from": "0000-01-01",
|
|
233
|
+
"input": 2,
|
|
234
|
+
"output": 10,
|
|
235
|
+
"cache_write_5m": 2.5,
|
|
236
|
+
"cache_write_1h": 4,
|
|
237
|
+
"cache_read": 0.2,
|
|
238
|
+
"fast": null,
|
|
239
|
+
"standard_rate_max_input_tokens": null,
|
|
240
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
241
|
+
"verified_on": "2026-09-13"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"model_id": "claude-sonnet-4-6",
|
|
245
|
+
"effective_from": "0000-01-01",
|
|
246
|
+
"input": 3,
|
|
247
|
+
"output": 15,
|
|
248
|
+
"cache_write_5m": 3.75,
|
|
249
|
+
"cache_write_1h": 6,
|
|
250
|
+
"cache_read": 0.3,
|
|
251
|
+
"fast": null,
|
|
252
|
+
"standard_rate_max_input_tokens": null,
|
|
253
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
254
|
+
"verified_on": "2026-09-13"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"model_id": "claude-sonnet-4-5",
|
|
258
|
+
"effective_from": "0000-01-01",
|
|
259
|
+
"input": 3,
|
|
260
|
+
"output": 15,
|
|
261
|
+
"cache_write_5m": 3.75,
|
|
262
|
+
"cache_write_1h": 6,
|
|
263
|
+
"cache_read": 0.3,
|
|
264
|
+
"fast": null,
|
|
265
|
+
"standard_rate_max_input_tokens": 200000,
|
|
266
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
267
|
+
"verified_on": "2026-09-24"
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"model_id": "claude-sonnet-4-5-20250929",
|
|
271
|
+
"effective_from": "0000-01-01",
|
|
272
|
+
"input": 3,
|
|
273
|
+
"output": 15,
|
|
274
|
+
"cache_write_5m": 3.75,
|
|
275
|
+
"cache_write_1h": 6,
|
|
276
|
+
"cache_read": 0.3,
|
|
277
|
+
"fast": null,
|
|
278
|
+
"standard_rate_max_input_tokens": 200000,
|
|
279
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
280
|
+
"verified_on": "2026-09-24"
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"model_id": "claude-sonnet-4-0",
|
|
284
|
+
"effective_from": "0000-01-01",
|
|
285
|
+
"input": 3,
|
|
286
|
+
"output": 15,
|
|
287
|
+
"cache_write_5m": 3.75,
|
|
288
|
+
"cache_write_1h": 6,
|
|
289
|
+
"cache_read": 0.3,
|
|
290
|
+
"fast": null,
|
|
291
|
+
"standard_rate_max_input_tokens": 200000,
|
|
292
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
293
|
+
"verified_on": "2026-09-24"
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"model_id": "claude-sonnet-4-20250514",
|
|
297
|
+
"effective_from": "0000-01-01",
|
|
298
|
+
"input": 3,
|
|
299
|
+
"output": 15,
|
|
300
|
+
"cache_write_5m": 3.75,
|
|
301
|
+
"cache_write_1h": 6,
|
|
302
|
+
"cache_read": 0.3,
|
|
303
|
+
"fast": null,
|
|
304
|
+
"standard_rate_max_input_tokens": 200000,
|
|
305
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
306
|
+
"verified_on": "2026-09-24"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"model_id": "claude-haiku-4-5-20251001",
|
|
310
|
+
"effective_from": "0000-01-01",
|
|
311
|
+
"input": 1,
|
|
312
|
+
"output": 5,
|
|
313
|
+
"cache_write_5m": 1.25,
|
|
314
|
+
"cache_write_1h": 2,
|
|
315
|
+
"cache_read": 0.1,
|
|
316
|
+
"fast": null,
|
|
317
|
+
"standard_rate_max_input_tokens": 200000,
|
|
318
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
319
|
+
"verified_on": "2026-09-13"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"model_id": "claude-haiku-4-5",
|
|
323
|
+
"effective_from": "0000-01-01",
|
|
324
|
+
"input": 1,
|
|
325
|
+
"output": 5,
|
|
326
|
+
"cache_write_5m": 1.25,
|
|
327
|
+
"cache_write_1h": 2,
|
|
328
|
+
"cache_read": 0.1,
|
|
329
|
+
"fast": null,
|
|
330
|
+
"standard_rate_max_input_tokens": 200000,
|
|
331
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
332
|
+
"verified_on": "2026-09-13"
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"model_id": "claude-3-5-haiku-20241022",
|
|
336
|
+
"effective_from": "0000-01-01",
|
|
337
|
+
"input": 0.8,
|
|
338
|
+
"output": 4,
|
|
339
|
+
"cache_write_5m": 1,
|
|
340
|
+
"cache_write_1h": 1.6,
|
|
341
|
+
"cache_read": 0.08,
|
|
342
|
+
"fast": null,
|
|
343
|
+
"standard_rate_max_input_tokens": 200000,
|
|
344
|
+
"source_url": "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
345
|
+
"verified_on": "2026-09-24"
|
|
346
|
+
}
|
|
347
|
+
]
|
|
348
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
-- 001_ingestion: raw storage for session logs and the status line spool.
|
|
2
|
+
--
|
|
3
|
+
-- Implements D-002 (store every raw line; everything else is derived and rebuildable) and D-003
|
|
4
|
+
-- (incremental reading by byte offset of the last complete line). Rows in raw_lines are never
|
|
5
|
+
-- updated or deleted: a log file rewritten by Claude Code keeps its earlier lines here, because
|
|
6
|
+
-- those requests happened (fixtures/README.md, cumulative runs).
|
|
7
|
+
--
|
|
8
|
+
-- Column comments: "source:" names the log, payload, or filesystem value a column holds;
|
|
9
|
+
-- "derived:" marks values computed by ingestion.
|
|
10
|
+
|
|
11
|
+
CREATE TABLE ingest_runs (
|
|
12
|
+
id INTEGER PRIMARY KEY, -- derived: run identity, increasing
|
|
13
|
+
started_at TEXT NOT NULL, -- derived: ISO-8601 UTC time the run began
|
|
14
|
+
finished_at TEXT, -- derived: ISO-8601 UTC time the run ended; NULL while running or after a crash
|
|
15
|
+
mode TEXT NOT NULL, -- derived: 'incremental' reads from stored offsets; 'full' rereads every file
|
|
16
|
+
CHECK (mode IN ('incremental', 'full'))
|
|
17
|
+
) STRICT;
|
|
18
|
+
|
|
19
|
+
CREATE TABLE source_files (
|
|
20
|
+
id INTEGER PRIMARY KEY, -- derived: file identity
|
|
21
|
+
kind TEXT NOT NULL, -- derived: 'log' for session JSONL, 'spool' for the status line spool
|
|
22
|
+
root TEXT NOT NULL, -- source: discovery root (a CLAUDE_CONFIG_DIR entry, ~/.claude, or the data dir)
|
|
23
|
+
relative_path TEXT NOT NULL, -- source: path below the root, e.g. projects/<encoded>/<session>.jsonl
|
|
24
|
+
byte_offset INTEGER NOT NULL, -- derived: position just past the last complete line consumed (D-003)
|
|
25
|
+
size INTEGER NOT NULL, -- source: file size in bytes at the last read (stat)
|
|
26
|
+
inode INTEGER, -- source: inode at the last read (stat); NULL where the platform has none
|
|
27
|
+
first_run_id INTEGER NOT NULL REFERENCES ingest_runs (id), -- derived: run that first saw the file
|
|
28
|
+
last_run_id INTEGER NOT NULL REFERENCES ingest_runs (id), -- derived: run that last read the file
|
|
29
|
+
UNIQUE (root, relative_path),
|
|
30
|
+
CHECK (kind IN ('log', 'spool')),
|
|
31
|
+
CHECK (byte_offset >= 0 AND byte_offset <= size)
|
|
32
|
+
) STRICT;
|
|
33
|
+
|
|
34
|
+
CREATE TABLE raw_lines (
|
|
35
|
+
id INTEGER PRIMARY KEY, -- derived: row identity; not an ordering (use file, first run, line)
|
|
36
|
+
source_file_id INTEGER NOT NULL REFERENCES source_files (id), -- derived: file the line came from
|
|
37
|
+
line_number INTEGER NOT NULL, -- source: 1-based line number where the line was first seen
|
|
38
|
+
byte_offset INTEGER NOT NULL, -- source: byte position of the line's first byte when first seen
|
|
39
|
+
content_hash TEXT NOT NULL, -- derived: SHA-256 of bytes, lowercase hex; with the file, the line's identity
|
|
40
|
+
bytes BLOB NOT NULL, -- source: the exact line bytes, without the terminating newline
|
|
41
|
+
first_run_id INTEGER NOT NULL REFERENCES ingest_runs (id), -- derived: run that first stored the line
|
|
42
|
+
UNIQUE (source_file_id, content_hash),
|
|
43
|
+
CHECK (line_number >= 1),
|
|
44
|
+
CHECK (byte_offset >= 0),
|
|
45
|
+
CHECK (length(content_hash) = 64)
|
|
46
|
+
) STRICT;
|
|
47
|
+
|
|
48
|
+
-- The canonical order every derived list uses: file, then first run, then line.
|
|
49
|
+
CREATE INDEX raw_lines_order ON raw_lines (source_file_id, first_run_id, line_number);
|
|
50
|
+
|
|
51
|
+
CREATE TABLE ingest_report (
|
|
52
|
+
id INTEGER PRIMARY KEY, -- derived: report row identity
|
|
53
|
+
run_id INTEGER NOT NULL REFERENCES ingest_runs (id), -- derived: run that found the problem
|
|
54
|
+
source_file_id INTEGER REFERENCES source_files (id), -- derived: file involved; NULL for run-level problems
|
|
55
|
+
problem TEXT NOT NULL, -- derived: problem code, e.g. 'file_rewritten', 'file_unreadable'
|
|
56
|
+
detail TEXT -- derived: human-readable detail; never a full log line
|
|
57
|
+
) STRICT;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- 002_line_count: remember how many complete lines precede each file's stored byte offset.
|
|
2
|
+
--
|
|
3
|
+
-- Incremental reads start mid-file (D-003). Without this count, the first new line couldn't be
|
|
4
|
+
-- given its true line number, and fixtures/README.md defines a line's number as where it was
|
|
5
|
+
-- first seen. Reset to 0 together with byte_offset when a rewrite is detected.
|
|
6
|
+
|
|
7
|
+
ALTER TABLE source_files ADD COLUMN line_count INTEGER NOT NULL DEFAULT 0 CHECK (line_count >= 0); -- derived: complete lines before byte_offset
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
-- 003_derived: tables derived from raw_lines by core/ingest/derive.ts.
|
|
2
|
+
--
|
|
3
|
+
-- Everything here is rebuildable (D-002): deleting these rows and deriving again from raw_lines
|
|
4
|
+
-- must produce identical contents. derive_meta records the parser version that produced them, so
|
|
5
|
+
-- a parser change triggers a rebuild instead of mixing old and new interpretations.
|
|
6
|
+
--
|
|
7
|
+
-- Classification and field rules: fixtures/README.md (D-001, D-004, D-007, D-019).
|
|
8
|
+
-- The token tables are not STRICT on purpose: a JSON number that isn't an integer is stored as
|
|
9
|
+
-- written rather than rejected, matching "a token value counts if it's a JSON number".
|
|
10
|
+
|
|
11
|
+
CREATE TABLE derive_meta (
|
|
12
|
+
key TEXT PRIMARY KEY, -- derived: setting name, e.g. 'parser_version'
|
|
13
|
+
value TEXT NOT NULL -- derived: setting value
|
|
14
|
+
) STRICT;
|
|
15
|
+
|
|
16
|
+
CREATE TABLE parsed_lines (
|
|
17
|
+
raw_line_id INTEGER PRIMARY KEY REFERENCES raw_lines (id), -- derived: the line these fields came from
|
|
18
|
+
class TEXT NOT NULL, -- derived: classification, first matching rule wins
|
|
19
|
+
session_id TEXT, -- source: sessionId ('<missing>' if absent or not a string); NULL when malformed
|
|
20
|
+
type TEXT, -- source: type ('<missing>' if absent or not a string); NULL when malformed
|
|
21
|
+
subtype TEXT, -- source: subtype when it's a string, else NULL
|
|
22
|
+
timestamp_raw TEXT, -- source: timestamp as JSON text of its value; NULL when absent or malformed
|
|
23
|
+
timestamp_utc TEXT, -- derived: timestamp normalized to UTC with milliseconds; NULL when it doesn't parse
|
|
24
|
+
cwd TEXT, -- source: cwd when it's a string, else NULL
|
|
25
|
+
git_branch TEXT, -- source: gitBranch when it's a string, else NULL
|
|
26
|
+
cc_version TEXT, -- source: version (Claude Code version) when it's a string, else NULL
|
|
27
|
+
is_sidechain INTEGER NOT NULL, -- source: isSidechain === true (1) or anything else (0)
|
|
28
|
+
CHECK (class IN ('malformed', 'limit_hit', 'api_error', 'synthetic_other', 'request', 'retry_notice', 'ignored_type')),
|
|
29
|
+
CHECK (is_sidechain IN (0, 1))
|
|
30
|
+
) STRICT;
|
|
31
|
+
|
|
32
|
+
CREATE TABLE requests (
|
|
33
|
+
raw_line_id INTEGER PRIMARY KEY REFERENCES raw_lines (id), -- derived: the request line
|
|
34
|
+
dedup_key TEXT, -- derived: D-001 key; NULL when the line has neither ID (unkeyed)
|
|
35
|
+
message_id TEXT, -- source: message.id when it's a string
|
|
36
|
+
request_id TEXT, -- source: requestId when it's a string
|
|
37
|
+
model TEXT NOT NULL, -- source: message.model ('<missing>' if absent or not a string)
|
|
38
|
+
input_tokens NUMERIC NOT NULL, -- source: message.usage.input_tokens (0 when not a number)
|
|
39
|
+
output_tokens NUMERIC NOT NULL, -- source: message.usage.output_tokens (0 when not a number)
|
|
40
|
+
cache_read_tokens NUMERIC NOT NULL, -- source: message.usage.cache_read_input_tokens (0 when not a number)
|
|
41
|
+
cache_write_5m_tokens NUMERIC, -- source: message.usage.cache_creation.ephemeral_5m_input_tokens; NULL without the split object
|
|
42
|
+
cache_write_1h_tokens NUMERIC, -- source: message.usage.cache_creation.ephemeral_1h_input_tokens; NULL without the split object
|
|
43
|
+
cache_write_unsplit_tokens NUMERIC, -- source: message.usage.cache_creation_input_tokens; only when the split object is absent
|
|
44
|
+
speed TEXT -- source: message.usage.speed when it's a string
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- Requests grouped for dedup: each key's candidates are read together.
|
|
48
|
+
CREATE INDEX requests_dedup_key ON requests (dedup_key);
|
|
49
|
+
|
|
50
|
+
CREATE TABLE events (
|
|
51
|
+
raw_line_id INTEGER PRIMARY KEY REFERENCES raw_lines (id), -- derived: the event line
|
|
52
|
+
class TEXT NOT NULL, -- derived: 'limit_hit', 'api_error', 'synthetic_other', or 'retry_notice'
|
|
53
|
+
error TEXT, -- source: error when it's a string
|
|
54
|
+
api_error_status NUMERIC, -- source: apiErrorStatus, or error.status for retry notices
|
|
55
|
+
window TEXT, -- derived: 'five_hour' or 'seven_day' parsed from limit-hit text (D-004)
|
|
56
|
+
reset_text TEXT, -- derived: text after 'resets ' in limit-hit text (D-004)
|
|
57
|
+
unknown_error_key TEXT, -- derived: report label for an api_error's unrecognized or missing error
|
|
58
|
+
retry_rate_limits_present INTEGER NOT NULL, -- derived: 1 when a retry notice's error.rateLimits is present and not null (D-019)
|
|
59
|
+
CHECK (class IN ('limit_hit', 'api_error', 'synthetic_other', 'retry_notice'))
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
CREATE TABLE line_problems (
|
|
63
|
+
id INTEGER PRIMARY KEY, -- derived: row identity
|
|
64
|
+
raw_line_id INTEGER NOT NULL REFERENCES raw_lines (id), -- derived: the line with the problem
|
|
65
|
+
problem TEXT NOT NULL, -- derived: 'missing_field', 'unparsed_timestamp', or 'non_message_iteration'
|
|
66
|
+
detail TEXT, -- derived: the field name, raw timestamp JSON, or iteration type
|
|
67
|
+
CHECK (problem IN ('missing_field', 'unparsed_timestamp', 'non_message_iteration'))
|
|
68
|
+
) STRICT;
|
|
69
|
+
|
|
70
|
+
CREATE INDEX line_problems_line ON line_problems (raw_line_id);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
-- 004_requests_dedup: the deduplicated request set, as a view over derived rows (D-001).
|
|
2
|
+
--
|
|
3
|
+
-- One API response is written as several cumulative log lines. Each dedup key keeps one winner:
|
|
4
|
+
-- largest output_tokens, then the non-sidechain copy, then the later line in (file, first run, line)
|
|
5
|
+
-- order. Unkeyed requests (neither message.id nor requestId) pass through as themselves.
|
|
6
|
+
-- Rows carry their source position so every number built on this view can be traced to a line.
|
|
7
|
+
|
|
8
|
+
-- Observed: one row per distinct API response (winners) plus every unkeyed request line.
|
|
9
|
+
CREATE VIEW requests_dedup AS
|
|
10
|
+
WITH ranked AS (
|
|
11
|
+
SELECT
|
|
12
|
+
r.raw_line_id,
|
|
13
|
+
r.dedup_key,
|
|
14
|
+
r.message_id,
|
|
15
|
+
r.request_id,
|
|
16
|
+
r.model,
|
|
17
|
+
r.input_tokens,
|
|
18
|
+
r.output_tokens,
|
|
19
|
+
r.cache_read_tokens,
|
|
20
|
+
r.cache_write_5m_tokens,
|
|
21
|
+
r.cache_write_1h_tokens,
|
|
22
|
+
r.cache_write_unsplit_tokens,
|
|
23
|
+
r.speed,
|
|
24
|
+
p.session_id,
|
|
25
|
+
p.timestamp_raw,
|
|
26
|
+
p.timestamp_utc,
|
|
27
|
+
p.cwd,
|
|
28
|
+
p.is_sidechain,
|
|
29
|
+
f.root,
|
|
30
|
+
f.relative_path,
|
|
31
|
+
l.first_run_id,
|
|
32
|
+
l.line_number,
|
|
33
|
+
ROW_NUMBER() OVER (
|
|
34
|
+
PARTITION BY r.dedup_key
|
|
35
|
+
ORDER BY r.output_tokens DESC, p.is_sidechain ASC,
|
|
36
|
+
f.root DESC, f.relative_path DESC, l.first_run_id DESC, l.line_number DESC
|
|
37
|
+
) AS candidate_rank
|
|
38
|
+
FROM requests r
|
|
39
|
+
JOIN parsed_lines p ON p.raw_line_id = r.raw_line_id
|
|
40
|
+
JOIN raw_lines l ON l.id = r.raw_line_id
|
|
41
|
+
JOIN source_files f ON f.id = l.source_file_id
|
|
42
|
+
)
|
|
43
|
+
SELECT
|
|
44
|
+
raw_line_id, dedup_key, message_id, request_id, model,
|
|
45
|
+
input_tokens, output_tokens, cache_read_tokens,
|
|
46
|
+
cache_write_5m_tokens, cache_write_1h_tokens, cache_write_unsplit_tokens, speed,
|
|
47
|
+
session_id, timestamp_raw, timestamp_utc, cwd, is_sidechain,
|
|
48
|
+
root, relative_path, first_run_id, line_number
|
|
49
|
+
FROM ranked
|
|
50
|
+
WHERE dedup_key IS NULL OR candidate_rank = 1;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- 005_repositories: which repository each working directory belongs to (D-010).
|
|
2
|
+
--
|
|
3
|
+
-- Filled by core/ingest/attribution.ts, once per distinct cwd, using git's common directory, so
|
|
4
|
+
-- worktrees and subdirectories of one repository share a repo_root. Unlike the derived tables in
|
|
5
|
+
-- 003, this depends on the filesystem at resolution time (a directory can disappear later), so it
|
|
6
|
+
-- is a cache and is not part of the rebuild-identical set.
|
|
7
|
+
|
|
8
|
+
CREATE TABLE repositories (
|
|
9
|
+
cwd TEXT PRIMARY KEY, -- source: cwd from log lines
|
|
10
|
+
kind TEXT NOT NULL, -- derived: 'repo', 'not_git' (git found no repository), or 'missing' (path gone)
|
|
11
|
+
repo_root TEXT, -- derived: parent of git's common dir (the dir itself for bare repos); NULL unless kind = 'repo'
|
|
12
|
+
resolved_at TEXT NOT NULL, -- derived: ISO-8601 UTC time of resolution
|
|
13
|
+
CHECK (kind IN ('repo', 'not_git', 'missing')),
|
|
14
|
+
CHECK ((kind = 'repo') = (repo_root IS NOT NULL))
|
|
15
|
+
) STRICT;
|
|
16
|
+
|
|
17
|
+
-- Observed: each request's repository, or its working directory when it has none.
|
|
18
|
+
CREATE VIEW request_repositories AS
|
|
19
|
+
SELECT
|
|
20
|
+
d.raw_line_id,
|
|
21
|
+
d.cwd,
|
|
22
|
+
COALESCE(r.kind, 'unresolved') AS repo_kind,
|
|
23
|
+
COALESCE(r.repo_root, d.cwd) AS repository
|
|
24
|
+
FROM requests_dedup d
|
|
25
|
+
LEFT JOIN repositories r ON r.cwd = d.cwd;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- 006_status_readings: status line readings decoded from the hook's spool (D-008, D-009, D-018).
|
|
2
|
+
--
|
|
3
|
+
-- Spool lines are stored in raw_lines like log lines (source_files.kind = 'spool') and derived
|
|
4
|
+
-- here instead of into parsed_lines. Values are stored as written and flagged, never clamped: an
|
|
5
|
+
-- out-of-range percentage is evidence of a payload problem, not something to repair.
|
|
6
|
+
-- Rebuildable from raw_lines, like the tables in 003.
|
|
7
|
+
|
|
8
|
+
CREATE TABLE status_readings (
|
|
9
|
+
raw_line_id INTEGER PRIMARY KEY REFERENCES raw_lines (id), -- derived: the spool line
|
|
10
|
+
status TEXT NOT NULL, -- derived: 'ok', 'malformed_line', 'malformed_payload_encoding', or 'malformed_payload'
|
|
11
|
+
captured_at_s NUMERIC, -- source: spool captured_at_s (epoch seconds, from the hook's clock)
|
|
12
|
+
hook_version NUMERIC, -- source: spool hook_version
|
|
13
|
+
session_id TEXT, -- source: payload session_id
|
|
14
|
+
transcript_path TEXT, -- source: payload transcript_path; links the reading to its session log
|
|
15
|
+
model_id TEXT, -- source: payload model.id
|
|
16
|
+
cc_version TEXT, -- source: payload version (Claude Code version)
|
|
17
|
+
cost_total_usd NUMERIC, -- source: payload cost.total_cost_usd; a cross-check only, never a source (CLAUDE.md)
|
|
18
|
+
has_rate_limits INTEGER NOT NULL, -- derived: 1 when payload rate_limits is an object
|
|
19
|
+
CHECK (status IN ('ok', 'malformed_line', 'malformed_payload_encoding', 'malformed_payload')),
|
|
20
|
+
CHECK (has_rate_limits IN (0, 1))
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE TABLE rate_limit_windows (
|
|
24
|
+
raw_line_id INTEGER NOT NULL REFERENCES status_readings (raw_line_id), -- derived: the reading
|
|
25
|
+
window TEXT NOT NULL, -- source: key under payload rate_limits, e.g. 'five_hour'
|
|
26
|
+
used_percentage NUMERIC, -- source: rate_limits.<window>.used_percentage when it's a number
|
|
27
|
+
resets_at NUMERIC, -- source: rate_limits.<window>.resets_at when it's a number (epoch seconds)
|
|
28
|
+
validity TEXT NOT NULL, -- derived: 'valid' or the first problem found
|
|
29
|
+
PRIMARY KEY (raw_line_id, window),
|
|
30
|
+
CHECK (validity IN ('valid', 'invalid_percentage', 'invalid_epoch_in_percentage', 'invalid_resets_at', 'unknown_window'))
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CREATE INDEX rate_limit_windows_window ON rate_limit_windows (window, validity);
|