@knpkv/agent-usage 0.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.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/dist/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/client/assets/index-CUuLXODk.css +1 -0
- package/dist/client/assets/index-DB-VXWqy.js +55 -0
- package/dist/client/index.html +15 -0
- package/dist/core/Attribution.d.ts +59 -0
- package/dist/core/Attribution.js +108 -0
- package/dist/core/Attribution.js.map +1 -0
- package/dist/core/Balances.d.ts +27 -0
- package/dist/core/Balances.js +63 -0
- package/dist/core/Balances.js.map +1 -0
- package/dist/core/ClaudeLimits.d.ts +58 -0
- package/dist/core/ClaudeLimits.js +125 -0
- package/dist/core/ClaudeLimits.js.map +1 -0
- package/dist/core/ClaudeLimitsLive.d.ts +18 -0
- package/dist/core/ClaudeLimitsLive.js +36 -0
- package/dist/core/ClaudeLimitsLive.js.map +1 -0
- package/dist/core/ClaudeReader.d.ts +27 -0
- package/dist/core/ClaudeReader.js +154 -0
- package/dist/core/ClaudeReader.js.map +1 -0
- package/dist/core/CodexReader.d.ts +91 -0
- package/dist/core/CodexReader.js +365 -0
- package/dist/core/CodexReader.js.map +1 -0
- package/dist/core/Database.d.ts +4 -0
- package/dist/core/Database.js +65 -0
- package/dist/core/Database.js.map +1 -0
- package/dist/core/Ingest.d.ts +55 -0
- package/dist/core/Ingest.js +267 -0
- package/dist/core/Ingest.js.map +1 -0
- package/dist/core/Model.d.ts +160 -0
- package/dist/core/Model.js +125 -0
- package/dist/core/Model.js.map +1 -0
- package/dist/core/Pricing.d.ts +42 -0
- package/dist/core/Pricing.js +126 -0
- package/dist/core/Pricing.js.map +1 -0
- package/dist/core/Readers.d.ts +42 -0
- package/dist/core/Readers.js +18 -0
- package/dist/core/Readers.js.map +1 -0
- package/dist/core/Report.d.ts +53 -0
- package/dist/core/Report.js +270 -0
- package/dist/core/Report.js.map +1 -0
- package/dist/core/Store.d.ts +94 -0
- package/dist/core/Store.js +376 -0
- package/dist/core/Store.js.map +1 -0
- package/dist/core/Tickets.d.ts +42 -0
- package/dist/core/Tickets.js +125 -0
- package/dist/core/Tickets.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +65 -0
- package/dist/main.js.map +1 -0
- package/dist/server/Api.d.ts +304 -0
- package/dist/server/Api.js +20 -0
- package/dist/server/Api.js.map +1 -0
- package/dist/server/Config.d.ts +27 -0
- package/dist/server/Config.js +49 -0
- package/dist/server/Config.js.map +1 -0
- package/dist/server/Handlers.d.ts +5 -0
- package/dist/server/Handlers.js +48 -0
- package/dist/server/Handlers.js.map +1 -0
- package/dist/server/HttpApplication.d.ts +7 -0
- package/dist/server/HttpApplication.js +70 -0
- package/dist/server/HttpApplication.js.map +1 -0
- package/dist/server/OwnerSession.d.ts +60 -0
- package/dist/server/OwnerSession.js +206 -0
- package/dist/server/OwnerSession.js.map +1 -0
- package/dist/server/Runtime.d.ts +57 -0
- package/dist/server/Runtime.js +85 -0
- package/dist/server/Runtime.js.map +1 -0
- package/dist/server/Server.d.ts +15 -0
- package/dist/server/Server.js +39 -0
- package/dist/server/Server.js.map +1 -0
- package/dist/shared/contracts.d.ts +309 -0
- package/dist/shared/contracts.js +105 -0
- package/dist/shared/contracts.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-machine SQLite store: Usage Events, Ingest Cursors, Limit Snapshots, Balance Readings and
|
|
3
|
+
* cached ticket titles.
|
|
4
|
+
*
|
|
5
|
+
* **Mental model**
|
|
6
|
+
*
|
|
7
|
+
* - **Facts only** (ADR 0002). Rows hold token counts and Attribution Inputs; Bookings and costs are
|
|
8
|
+
* derived by callers. No column can hold prompt or response text.
|
|
9
|
+
* - **A chunk commits whole.** A file chunk's events, observations and the cursor past them land in
|
|
10
|
+
* one transaction, so a pass killed mid-backfill neither loses nor double-counts: the next pass
|
|
11
|
+
* resumes from the cursor, and event keys absorb any overlap.
|
|
12
|
+
* - **Every observation is kept; series are compressed when read.** Rollouts are read file by file,
|
|
13
|
+
* so a limit's history arrives interleaved; compressing at write time would lose confirmations a
|
|
14
|
+
* later file needs. Reads keep only the rows where a series' reading changed (Codex echoes one per
|
|
15
|
+
* request), plus each series' last observation, which is what "read … ago" reports.
|
|
16
|
+
* - **Usage is pre-aggregated into 15-minute buckets** by every dimension pricing and Booking need.
|
|
17
|
+
* Every time zone's offset is a multiple of 15 minutes, so callers roll buckets up into local
|
|
18
|
+
* hours, days and weeks without touching raw events.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
import { SqliteMigrator } from "@effect/sql-sqlite-node";
|
|
23
|
+
import { Context, Effect, Layer, Option, Schema } from "effect";
|
|
24
|
+
import { SqlClient } from "effect/sql";
|
|
25
|
+
import { Agent, AttributionInputs, BalanceKind, BalanceValue, Count, LimitReading, ObservationSource, Tokens, WindowMinutes } from "./Model.js";
|
|
26
|
+
import { LONG_PROMPT_TOKENS } from "./Pricing.js";
|
|
27
|
+
export class StoreError extends Schema.TaggedError()("StoreError", {
|
|
28
|
+
operation: Schema.String,
|
|
29
|
+
cause: Schema.Defect()
|
|
30
|
+
}) {
|
|
31
|
+
}
|
|
32
|
+
const storeError = (operation) => (cause) => new StoreError({ operation, cause });
|
|
33
|
+
/** How far a source file has been read, and the reader state to resume with (reader-owned JSON). */
|
|
34
|
+
export const Cursor = Schema.Struct({
|
|
35
|
+
/** Device and inode: a file that was replaced reads from 0 again. */
|
|
36
|
+
identity: Schema.String,
|
|
37
|
+
offset: Count,
|
|
38
|
+
state: Schema.String
|
|
39
|
+
});
|
|
40
|
+
/** Usage summed over one 15-minute bucket and every dimension that prices or books it. */
|
|
41
|
+
export const UsageGroup = Schema.Struct({
|
|
42
|
+
bucketStart: Schema.Int,
|
|
43
|
+
agent: Agent,
|
|
44
|
+
model: Schema.String,
|
|
45
|
+
fast: Schema.Boolean,
|
|
46
|
+
longPrompt: Schema.Boolean,
|
|
47
|
+
attribution: AttributionInputs,
|
|
48
|
+
requests: Count,
|
|
49
|
+
tokens: Tokens
|
|
50
|
+
});
|
|
51
|
+
export const TicketTitle = Schema.Struct({
|
|
52
|
+
key: Schema.String,
|
|
53
|
+
/** Null when the lookup ran and found no title. */
|
|
54
|
+
summary: Schema.NullOr(Schema.String),
|
|
55
|
+
fetchedAt: Schema.Int
|
|
56
|
+
});
|
|
57
|
+
export const BUCKET_MILLIS = 15 * 60 * 1000;
|
|
58
|
+
const EVENT_BATCH = 400;
|
|
59
|
+
/**
|
|
60
|
+
* A limit series' identity, as the report draws it: Codex windows by length (a plan change moves the
|
|
61
|
+
* weekly window between its primary and secondary slots), everything else by the provider's key.
|
|
62
|
+
* Compression must use the same identity, or a slot change hides a real transition.
|
|
63
|
+
*/
|
|
64
|
+
const seriesOf = (table) => `CASE WHEN ${table}source = 'codex-rollout' AND ${table}window_minutes IS NOT NULL` +
|
|
65
|
+
` THEN 'w' || ${table}window_minutes ELSE ${table}label END`;
|
|
66
|
+
const GroupRow = Schema.Struct({
|
|
67
|
+
bucket_start: Schema.Int,
|
|
68
|
+
agent: Agent,
|
|
69
|
+
model: Schema.String,
|
|
70
|
+
fast: Schema.Literals([0, 1]),
|
|
71
|
+
long_prompt: Schema.Literals([0, 1]),
|
|
72
|
+
cwd: Schema.String,
|
|
73
|
+
branch: Schema.String,
|
|
74
|
+
active_ticket: Schema.NullOr(Schema.String),
|
|
75
|
+
requests: Count,
|
|
76
|
+
input: Count,
|
|
77
|
+
output: Count,
|
|
78
|
+
reasoning: Count,
|
|
79
|
+
cache_read: Count,
|
|
80
|
+
cache_write_5m: Count,
|
|
81
|
+
cache_write_1h: Count
|
|
82
|
+
});
|
|
83
|
+
const SnapshotRow = Schema.Struct({
|
|
84
|
+
agent: Agent,
|
|
85
|
+
machine: Schema.String,
|
|
86
|
+
source: ObservationSource,
|
|
87
|
+
label: Schema.String,
|
|
88
|
+
window_minutes: WindowMinutes,
|
|
89
|
+
observed_at: Schema.Int,
|
|
90
|
+
reading: Schema.fromJsonString(LimitReading)
|
|
91
|
+
});
|
|
92
|
+
const BalanceRow = Schema.Struct({
|
|
93
|
+
kind: BalanceKind,
|
|
94
|
+
machine: Schema.String,
|
|
95
|
+
observed_at: Schema.Int,
|
|
96
|
+
value: Schema.fromJsonString(BalanceValue)
|
|
97
|
+
});
|
|
98
|
+
const CursorRow = Schema.Struct({ identity: Schema.String, offset: Count, state: Schema.String });
|
|
99
|
+
const TicketRow = Schema.Struct({ key: Schema.String, summary: Schema.NullOr(Schema.String), fetched_at: Schema.Int });
|
|
100
|
+
const ReadingJson = Schema.fromJsonString(LimitReading);
|
|
101
|
+
const BalanceJson = Schema.fromJsonString(BalanceValue);
|
|
102
|
+
const encodeReading = Schema.encodeSync(ReadingJson);
|
|
103
|
+
const encodeBalance = Schema.encodeSync(BalanceJson);
|
|
104
|
+
const migrations = SqliteMigrator.fromRecord({
|
|
105
|
+
"0001_usage": Effect.gen(function* () {
|
|
106
|
+
const sql = yield* SqlClient.SqlClient;
|
|
107
|
+
yield* sql `
|
|
108
|
+
CREATE TABLE usage_events (
|
|
109
|
+
agent TEXT NOT NULL,
|
|
110
|
+
dedupe_key TEXT NOT NULL,
|
|
111
|
+
machine TEXT NOT NULL,
|
|
112
|
+
session_id TEXT NOT NULL,
|
|
113
|
+
occurred_at INTEGER NOT NULL,
|
|
114
|
+
model TEXT NOT NULL,
|
|
115
|
+
fast INTEGER NOT NULL CHECK (fast IN (0, 1)),
|
|
116
|
+
input INTEGER NOT NULL,
|
|
117
|
+
output INTEGER NOT NULL,
|
|
118
|
+
reasoning INTEGER NOT NULL,
|
|
119
|
+
cache_read INTEGER NOT NULL,
|
|
120
|
+
cache_write_5m INTEGER NOT NULL,
|
|
121
|
+
cache_write_1h INTEGER NOT NULL,
|
|
122
|
+
cwd TEXT NOT NULL,
|
|
123
|
+
branch TEXT NOT NULL,
|
|
124
|
+
active_ticket TEXT,
|
|
125
|
+
PRIMARY KEY (agent, dedupe_key)
|
|
126
|
+
)
|
|
127
|
+
`;
|
|
128
|
+
yield* sql `CREATE INDEX usage_events_time ON usage_events (occurred_at)`;
|
|
129
|
+
yield* sql `
|
|
130
|
+
CREATE TABLE ingest_cursors (
|
|
131
|
+
agent TEXT NOT NULL,
|
|
132
|
+
file_key TEXT NOT NULL,
|
|
133
|
+
identity TEXT NOT NULL,
|
|
134
|
+
offset INTEGER NOT NULL,
|
|
135
|
+
state TEXT NOT NULL,
|
|
136
|
+
PRIMARY KEY (agent, file_key)
|
|
137
|
+
)
|
|
138
|
+
`;
|
|
139
|
+
yield* sql `
|
|
140
|
+
CREATE TABLE limit_snapshots (
|
|
141
|
+
agent TEXT NOT NULL,
|
|
142
|
+
machine TEXT NOT NULL,
|
|
143
|
+
source TEXT NOT NULL,
|
|
144
|
+
label TEXT NOT NULL,
|
|
145
|
+
window_minutes INTEGER,
|
|
146
|
+
observed_at INTEGER NOT NULL,
|
|
147
|
+
reading TEXT NOT NULL,
|
|
148
|
+
PRIMARY KEY (machine, agent, source, label, observed_at)
|
|
149
|
+
)
|
|
150
|
+
`;
|
|
151
|
+
yield* sql `CREATE INDEX limit_snapshots_time ON limit_snapshots (machine, observed_at)`;
|
|
152
|
+
yield* sql `
|
|
153
|
+
CREATE TABLE balance_readings (
|
|
154
|
+
kind TEXT NOT NULL,
|
|
155
|
+
machine TEXT NOT NULL,
|
|
156
|
+
observed_at INTEGER NOT NULL,
|
|
157
|
+
value TEXT NOT NULL,
|
|
158
|
+
PRIMARY KEY (kind, machine, observed_at)
|
|
159
|
+
)
|
|
160
|
+
`;
|
|
161
|
+
yield* sql `
|
|
162
|
+
CREATE TABLE tickets (
|
|
163
|
+
key TEXT PRIMARY KEY,
|
|
164
|
+
summary TEXT,
|
|
165
|
+
fetched_at INTEGER NOT NULL
|
|
166
|
+
)
|
|
167
|
+
`;
|
|
168
|
+
})
|
|
169
|
+
});
|
|
170
|
+
export class UsageStore extends Context.Service()("@knpkv/agent-usage/core/Store/UsageStore") {
|
|
171
|
+
static layer = Layer.effect(UsageStore, Effect.gen(function* () {
|
|
172
|
+
const sql = yield* SqlClient.SqlClient;
|
|
173
|
+
yield* SqliteMigrator.run({ loader: migrations }).pipe(Effect.mapError(storeError("migrate")));
|
|
174
|
+
const insertEvents = Effect.fnUntraced(function* (events) {
|
|
175
|
+
let added = 0;
|
|
176
|
+
for (let start = 0; start < events.length; start += EVENT_BATCH) {
|
|
177
|
+
const rows = events.slice(start, start + EVENT_BATCH).map((event) => ({
|
|
178
|
+
agent: event.agent,
|
|
179
|
+
dedupe_key: event.dedupeKey,
|
|
180
|
+
machine: event.machine,
|
|
181
|
+
session_id: event.sessionId,
|
|
182
|
+
occurred_at: event.occurredAt,
|
|
183
|
+
model: event.model,
|
|
184
|
+
fast: event.fast ? 1 : 0,
|
|
185
|
+
input: event.tokens.input,
|
|
186
|
+
output: event.tokens.output,
|
|
187
|
+
reasoning: event.tokens.reasoning,
|
|
188
|
+
cache_read: event.tokens.cacheRead,
|
|
189
|
+
cache_write_5m: event.tokens.cacheWrite5m,
|
|
190
|
+
cache_write_1h: event.tokens.cacheWrite1h,
|
|
191
|
+
cwd: event.attribution.cwd,
|
|
192
|
+
branch: event.attribution.branch,
|
|
193
|
+
active_ticket: event.attribution.activeTicket
|
|
194
|
+
}));
|
|
195
|
+
const inserted = yield* sql `
|
|
196
|
+
INSERT INTO usage_events ${sql.insert(rows)}
|
|
197
|
+
ON CONFLICT DO NOTHING
|
|
198
|
+
RETURNING dedupe_key
|
|
199
|
+
`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ dedupe_key: Schema.String })))));
|
|
200
|
+
added += inserted.length;
|
|
201
|
+
// A Claude request read again with larger counts (its final streamed usage) takes them;
|
|
202
|
+
// its time and attribution stay as first recorded. Codex keys are byte offsets: never.
|
|
203
|
+
const fresh = new Set(inserted.map((row) => row.dedupe_key));
|
|
204
|
+
for (const event of events.slice(start, start + EVENT_BATCH)) {
|
|
205
|
+
if (event.agent !== "claude" || fresh.has(event.dedupeKey))
|
|
206
|
+
continue;
|
|
207
|
+
yield* sql `
|
|
208
|
+
UPDATE usage_events SET
|
|
209
|
+
input = ${event.tokens.input}, output = ${event.tokens.output},
|
|
210
|
+
reasoning = ${event.tokens.reasoning}, cache_read = ${event.tokens.cacheRead},
|
|
211
|
+
cache_write_5m = ${event.tokens.cacheWrite5m}, cache_write_1h = ${event.tokens.cacheWrite1h}
|
|
212
|
+
WHERE agent = 'claude' AND dedupe_key = ${event.dedupeKey} AND output < ${event.tokens.output}
|
|
213
|
+
`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return added;
|
|
217
|
+
});
|
|
218
|
+
const insertSnapshots = Effect.fnUntraced(function* (snapshots) {
|
|
219
|
+
for (let start = 0; start < snapshots.length; start += EVENT_BATCH) {
|
|
220
|
+
const rows = snapshots.slice(start, start + EVENT_BATCH).map((snapshot) => ({
|
|
221
|
+
agent: snapshot.agent,
|
|
222
|
+
machine: snapshot.machine,
|
|
223
|
+
source: snapshot.source,
|
|
224
|
+
label: snapshot.label,
|
|
225
|
+
window_minutes: snapshot.windowMinutes,
|
|
226
|
+
observed_at: snapshot.observedAt,
|
|
227
|
+
reading: encodeReading(snapshot.reading)
|
|
228
|
+
}));
|
|
229
|
+
yield* sql `INSERT INTO limit_snapshots ${sql.insert(rows)} ON CONFLICT DO NOTHING`;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
const insertBalances = Effect.fnUntraced(function* (balances) {
|
|
233
|
+
for (let start = 0; start < balances.length; start += EVENT_BATCH) {
|
|
234
|
+
const rows = balances.slice(start, start + EVENT_BATCH).map((balance) => ({
|
|
235
|
+
kind: balance.kind,
|
|
236
|
+
machine: balance.machine,
|
|
237
|
+
observed_at: balance.observedAt,
|
|
238
|
+
value: encodeBalance(balance.value)
|
|
239
|
+
}));
|
|
240
|
+
yield* sql `INSERT INTO balance_readings ${sql.insert(rows)} ON CONFLICT DO NOTHING`;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
const recordObservations = Effect.fn("UsageStore.recordObservations")((snapshots, balances) => sql.withTransaction(Effect.andThen(insertSnapshots(snapshots), insertBalances(balances))).pipe(Effect.mapError(storeError("record-observations"))));
|
|
244
|
+
const commitChunk = Effect.fn("UsageStore.commitChunk")((chunk) => sql.withTransaction(Effect.gen(function* () {
|
|
245
|
+
const eventsAdded = yield* insertEvents(chunk.events);
|
|
246
|
+
yield* insertSnapshots(chunk.snapshots);
|
|
247
|
+
yield* insertBalances(chunk.balances);
|
|
248
|
+
yield* sql `
|
|
249
|
+
INSERT INTO ingest_cursors ${sql.insert({
|
|
250
|
+
agent: chunk.agent,
|
|
251
|
+
file_key: chunk.fileKey,
|
|
252
|
+
identity: chunk.cursor.identity,
|
|
253
|
+
offset: chunk.cursor.offset,
|
|
254
|
+
state: chunk.cursor.state
|
|
255
|
+
})}
|
|
256
|
+
ON CONFLICT (agent, file_key) DO UPDATE SET
|
|
257
|
+
identity = excluded.identity, offset = excluded.offset, state = excluded.state
|
|
258
|
+
`;
|
|
259
|
+
return { eventsAdded };
|
|
260
|
+
})).pipe(Effect.mapError(storeError("commit-chunk"))));
|
|
261
|
+
const cursor = Effect.fn("UsageStore.cursor")((agent, fileKey) => sql `SELECT identity, offset, state FROM ingest_cursors WHERE agent = ${agent} AND file_key = ${fileKey}`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(CursorRow))), Effect.map((rows) => Option.fromNullishOr(rows[0])), Effect.mapError(storeError("read-cursor"))));
|
|
262
|
+
const usageGroups = Effect.fn("UsageStore.usageGroups")((range) => sql `
|
|
263
|
+
SELECT
|
|
264
|
+
(occurred_at - occurred_at % ${BUCKET_MILLIS}) AS bucket_start,
|
|
265
|
+
agent, model, fast,
|
|
266
|
+
(input + cache_read + cache_write_5m + cache_write_1h) > ${LONG_PROMPT_TOKENS} AS long_prompt,
|
|
267
|
+
cwd, branch, active_ticket,
|
|
268
|
+
count(*) AS requests,
|
|
269
|
+
sum(input) AS input, sum(output) AS output, sum(reasoning) AS reasoning,
|
|
270
|
+
sum(cache_read) AS cache_read, sum(cache_write_5m) AS cache_write_5m, sum(cache_write_1h) AS cache_write_1h
|
|
271
|
+
FROM usage_events
|
|
272
|
+
WHERE machine = ${range.machine} AND occurred_at >= ${range.from} AND occurred_at < ${range.to}
|
|
273
|
+
-- Requests with and without cache traffic price apart: a model may lack a cache rate, and
|
|
274
|
+
-- that must leave only those requests unpriced, not the whole bucket.
|
|
275
|
+
GROUP BY bucket_start, agent, model, fast, long_prompt, cache_read > 0,
|
|
276
|
+
cache_write_5m + cache_write_1h > 0, cwd, branch, active_ticket
|
|
277
|
+
ORDER BY bucket_start, agent, model
|
|
278
|
+
`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(GroupRow))), Effect.map((rows) => rows.map((row) => ({
|
|
279
|
+
bucketStart: row.bucket_start,
|
|
280
|
+
agent: row.agent,
|
|
281
|
+
model: row.model,
|
|
282
|
+
fast: row.fast === 1,
|
|
283
|
+
longPrompt: row.long_prompt === 1,
|
|
284
|
+
attribution: { cwd: row.cwd, branch: row.branch, activeTicket: row.active_ticket },
|
|
285
|
+
requests: row.requests,
|
|
286
|
+
tokens: {
|
|
287
|
+
input: row.input,
|
|
288
|
+
output: row.output,
|
|
289
|
+
reasoning: row.reasoning,
|
|
290
|
+
cacheRead: row.cache_read,
|
|
291
|
+
cacheWrite5m: row.cache_write_5m,
|
|
292
|
+
cacheWrite1h: row.cache_write_1h
|
|
293
|
+
}
|
|
294
|
+
}))), Effect.mapError(storeError("usage-groups"))));
|
|
295
|
+
const places = Effect.fn("UsageStore.places")((machine) => sql `SELECT DISTINCT branch, cwd FROM usage_events WHERE machine = ${machine}`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ branch: Schema.String, cwd: Schema.String })))), Effect.mapError(storeError("places"))));
|
|
296
|
+
const limitSnapshots = Effect.fn("UsageStore.limitSnapshots")((range) => sql `
|
|
297
|
+
WITH in_range AS (
|
|
298
|
+
SELECT agent, machine, source, label, window_minutes, observed_at, reading, series,
|
|
299
|
+
coalesce(window_minutes, 'null') || ' ' || reading AS signature,
|
|
300
|
+
lag(coalesce(window_minutes, 'null') || ' ' || reading)
|
|
301
|
+
OVER (PARTITION BY agent, source, series ORDER BY observed_at) AS previous,
|
|
302
|
+
lag(observed_at) OVER (PARTITION BY agent, source, series ORDER BY observed_at) AS previous_at,
|
|
303
|
+
max(CASE WHEN label = '*' THEN observed_at END) OVER (
|
|
304
|
+
PARTITION BY agent, source ORDER BY observed_at ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
|
|
305
|
+
) AS failed_at,
|
|
306
|
+
max(CASE WHEN label <> '*' THEN observed_at END) OVER (
|
|
307
|
+
PARTITION BY agent, source ORDER BY observed_at ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
|
|
308
|
+
) AS succeeded_at,
|
|
309
|
+
row_number() OVER (PARTITION BY agent, source, series ORDER BY observed_at DESC) AS from_end
|
|
310
|
+
FROM (
|
|
311
|
+
SELECT *, ${sql.literal(seriesOf(""))} AS series FROM limit_snapshots
|
|
312
|
+
WHERE machine = ${range.machine} AND observed_at >= ${range.from} AND observed_at < ${range.to}
|
|
313
|
+
)
|
|
314
|
+
),
|
|
315
|
+
series_before AS (
|
|
316
|
+
SELECT agent, source, ${sql.literal(seriesOf(""))} AS series, max(observed_at) AS observed_at FROM limit_snapshots
|
|
317
|
+
WHERE machine = ${range.machine} AND observed_at < ${range.from}
|
|
318
|
+
GROUP BY agent, source, series
|
|
319
|
+
)
|
|
320
|
+
SELECT snapshot.agent, snapshot.machine, snapshot.source, snapshot.label, snapshot.window_minutes,
|
|
321
|
+
snapshot.observed_at, snapshot.reading
|
|
322
|
+
FROM series_before JOIN limit_snapshots AS snapshot
|
|
323
|
+
ON snapshot.machine = ${range.machine} AND snapshot.agent = series_before.agent
|
|
324
|
+
AND snapshot.source = series_before.source AND snapshot.observed_at = series_before.observed_at
|
|
325
|
+
AND ${sql.literal(seriesOf("snapshot."))} = series_before.series
|
|
326
|
+
UNION ALL
|
|
327
|
+
-- A row starts a new step when its reading changed, or when the other kind of reading (a
|
|
328
|
+
-- source-wide failure for a window, a window for a failure) came since this series' last row.
|
|
329
|
+
SELECT agent, machine, source, label, window_minutes, observed_at, reading FROM in_range
|
|
330
|
+
WHERE from_end = 1 OR previous IS NULL OR previous <> signature
|
|
331
|
+
OR (label <> '*' AND failed_at > previous_at)
|
|
332
|
+
OR (label = '*' AND succeeded_at > previous_at)
|
|
333
|
+
ORDER BY observed_at, agent, label
|
|
334
|
+
`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(SnapshotRow))), Effect.map((rows) => rows.map((row) => ({
|
|
335
|
+
agent: row.agent,
|
|
336
|
+
machine: row.machine,
|
|
337
|
+
source: row.source,
|
|
338
|
+
label: row.label,
|
|
339
|
+
windowMinutes: row.window_minutes,
|
|
340
|
+
observedAt: row.observed_at,
|
|
341
|
+
reading: row.reading
|
|
342
|
+
}))), Effect.mapError(storeError("limit-snapshots"))));
|
|
343
|
+
const latestBalances = Effect.fn("UsageStore.latestBalances")((machine) => sql `
|
|
344
|
+
SELECT kind, machine, observed_at, value FROM balance_readings AS outer_reading
|
|
345
|
+
WHERE machine = ${machine} AND observed_at = (
|
|
346
|
+
SELECT max(observed_at) FROM balance_readings AS inner_reading
|
|
347
|
+
WHERE inner_reading.kind = outer_reading.kind AND inner_reading.machine = outer_reading.machine
|
|
348
|
+
)
|
|
349
|
+
ORDER BY kind, machine
|
|
350
|
+
`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(BalanceRow))), Effect.map((rows) => rows.map((row) => ({
|
|
351
|
+
kind: row.kind,
|
|
352
|
+
machine: row.machine,
|
|
353
|
+
observedAt: row.observed_at,
|
|
354
|
+
value: row.value
|
|
355
|
+
}))), Effect.mapError(storeError("latest-balances"))));
|
|
356
|
+
const tickets = Effect.fn("UsageStore.tickets")((keys) => keys.length === 0
|
|
357
|
+
? Effect.succeed([])
|
|
358
|
+
: sql `SELECT key, summary, fetched_at FROM tickets WHERE ${sql.in("key", keys)}`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(TicketRow))), Effect.map((rows) => rows.map((row) => ({ key: row.key, summary: row.summary, fetchedAt: row.fetched_at }))), Effect.mapError(storeError("tickets"))));
|
|
359
|
+
const saveTicket = Effect.fn("UsageStore.saveTicket")((ticket) => sql `
|
|
360
|
+
INSERT INTO tickets ${sql.insert({ key: ticket.key, summary: ticket.summary, fetched_at: ticket.fetchedAt })}
|
|
361
|
+
ON CONFLICT (key) DO UPDATE SET summary = excluded.summary, fetched_at = excluded.fetched_at
|
|
362
|
+
`.pipe(Effect.asVoid, Effect.mapError(storeError("save-ticket"))));
|
|
363
|
+
return UsageStore.of({
|
|
364
|
+
cursor,
|
|
365
|
+
commitChunk,
|
|
366
|
+
recordObservations,
|
|
367
|
+
usageGroups,
|
|
368
|
+
places,
|
|
369
|
+
limitSnapshots,
|
|
370
|
+
latestBalances,
|
|
371
|
+
tickets,
|
|
372
|
+
saveTicket
|
|
373
|
+
});
|
|
374
|
+
}));
|
|
375
|
+
}
|
|
376
|
+
//# sourceMappingURL=Store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Store.js","sourceRoot":"","sources":["../../src/core/Store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jira titles for ticket Bookings, looked up with `acli` and cached in the store for a day.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Optional decoration.** A Booking is its key; a title only helps read it. Lookups run in the
|
|
7
|
+
* background after an ingest pass, so a slow or missing `acli` never delays a page.
|
|
8
|
+
* - **Three answers, never blank.** A title is Known, or Unknown because Jira has no such ticket
|
|
9
|
+
* (`NotFound`, cached like a title) or because no lookup has succeeded yet (`NotLookedUp`).
|
|
10
|
+
* - **A failed lookup caches nothing**, so the next pass tries again, and the failure is reported
|
|
11
|
+
* rather than swallowed.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { Effect } from "effect";
|
|
16
|
+
import { ChildProcessSpawner } from "effect/process";
|
|
17
|
+
import type { TicketTitleValue } from "./Model.js";
|
|
18
|
+
import { type StoreError, UsageStore } from "./Store.js";
|
|
19
|
+
declare const TicketLookupFailed_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
20
|
+
readonly _tag: "TicketLookupFailed";
|
|
21
|
+
} & Readonly<A>;
|
|
22
|
+
export declare class TicketLookupFailed extends TicketLookupFailed_base<{
|
|
23
|
+
readonly reason: string;
|
|
24
|
+
}> {
|
|
25
|
+
}
|
|
26
|
+
/** Finds the summaries of the given keys; a key Jira does not return is absent from the map. */
|
|
27
|
+
export type TicketSearch = (keys: ReadonlyArray<string>) => Effect.Effect<ReadonlyMap<string, string>, TicketLookupFailed>;
|
|
28
|
+
/** Looks up keys whose cached title is missing or older than a day. Returns the lookups that failed. */
|
|
29
|
+
export declare const refreshTicketTitles: (keys: ReadonlyArray<string>, search: TicketSearch) => Effect.Effect<ReadonlyArray<TicketLookupFailed>, StoreError, UsageStore>;
|
|
30
|
+
/** The cached title of each key. */
|
|
31
|
+
export declare const ticketTitles: (keys: ReadonlyArray<string>) => Effect.Effect<Readonly<Record<string, TicketTitleValue>>, StoreError, UsageStore>;
|
|
32
|
+
/**
|
|
33
|
+
* What one `acli` search said, from its exit code and stdout. A nonzero exit is a failed lookup even
|
|
34
|
+
* when it printed something: a failed search cannot show that a ticket does not exist.
|
|
35
|
+
*/
|
|
36
|
+
export declare const searchOutcome: (exitCode: number, stdout: string) => Effect.Effect<ReadonlyMap<string, string>, TicketLookupFailed>;
|
|
37
|
+
/**
|
|
38
|
+
* `acli jira workitem search` for a batch of keys. Keys reach the JQL only after the Booking
|
|
39
|
+
* pattern accepted them, so they cannot carry JQL syntax.
|
|
40
|
+
*/
|
|
41
|
+
export declare const acliTicketSearch: Effect.Effect<TicketSearch, never, ChildProcessSpawner.ChildProcessSpawner>;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jira titles for ticket Bookings, looked up with `acli` and cached in the store for a day.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Optional decoration.** A Booking is its key; a title only helps read it. Lookups run in the
|
|
7
|
+
* background after an ingest pass, so a slow or missing `acli` never delays a page.
|
|
8
|
+
* - **Three answers, never blank.** A title is Known, or Unknown because Jira has no such ticket
|
|
9
|
+
* (`NotFound`, cached like a title) or because no lookup has succeeded yet (`NotLookedUp`).
|
|
10
|
+
* - **A failed lookup caches nothing**, so the next pass tries again, and the failure is reported
|
|
11
|
+
* rather than swallowed.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { Clock, Data, Duration, Effect, Schema, Stream } from "effect";
|
|
16
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
17
|
+
import { UsageStore } from "./Store.js";
|
|
18
|
+
export class TicketLookupFailed extends Data.TaggedError("TicketLookupFailed") {
|
|
19
|
+
}
|
|
20
|
+
const ACLI_TIMEOUT = Duration.seconds(30);
|
|
21
|
+
const FRESH_FOR_MILLIS = 24 * 60 * 60 * 1000;
|
|
22
|
+
const KEYS_PER_SEARCH = 50;
|
|
23
|
+
/** Looks up keys whose cached title is missing or older than a day. Returns the lookups that failed. */
|
|
24
|
+
export const refreshTicketTitles = (keys, search) => Effect.gen(function* () {
|
|
25
|
+
const store = yield* UsageStore;
|
|
26
|
+
const now = yield* Clock.currentTimeMillis;
|
|
27
|
+
const cached = new Map((yield* store.tickets(keys)).map((ticket) => [ticket.key, ticket.fetchedAt]));
|
|
28
|
+
const stale = [...new Set(keys)].filter((key) => {
|
|
29
|
+
const fetchedAt = cached.get(key);
|
|
30
|
+
return fetchedAt === undefined || now - fetchedAt > FRESH_FOR_MILLIS;
|
|
31
|
+
});
|
|
32
|
+
const failures = [];
|
|
33
|
+
for (let start = 0; start < stale.length; start += KEYS_PER_SEARCH) {
|
|
34
|
+
const batch = stale.slice(start, start + KEYS_PER_SEARCH);
|
|
35
|
+
const found = yield* Effect.result(search(batch));
|
|
36
|
+
if (found._tag === "Success") {
|
|
37
|
+
for (const key of batch) {
|
|
38
|
+
yield* store.saveTicket({ key, summary: found.success.get(key) ?? null, fetchedAt: now });
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
// Jira refuses a whole search when one key in it does not exist. Asking key by key keeps one
|
|
43
|
+
// bad key from leaving the rest untitled; a key that still fails stays uncached and reported.
|
|
44
|
+
if (batch.length === 1) {
|
|
45
|
+
failures.push(found.failure);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
for (const key of batch) {
|
|
49
|
+
const single = yield* Effect.result(search([key]));
|
|
50
|
+
if (single._tag === "Failure")
|
|
51
|
+
failures.push(single.failure);
|
|
52
|
+
else
|
|
53
|
+
yield* store.saveTicket({ key, summary: single.success.get(key) ?? null, fetchedAt: now });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return failures;
|
|
57
|
+
});
|
|
58
|
+
/** The cached title of each key. */
|
|
59
|
+
export const ticketTitles = (keys) => Effect.gen(function* () {
|
|
60
|
+
const store = yield* UsageStore;
|
|
61
|
+
const cached = new Map((yield* store.tickets(keys)).map((ticket) => [ticket.key, ticket.summary]));
|
|
62
|
+
const titles = {};
|
|
63
|
+
for (const key of keys) {
|
|
64
|
+
const summary = cached.get(key);
|
|
65
|
+
titles[key] = summary === undefined
|
|
66
|
+
? { _tag: "Unknown", reason: "NotLookedUp" }
|
|
67
|
+
: summary === null
|
|
68
|
+
? { _tag: "Unknown", reason: "NotFound" }
|
|
69
|
+
: { _tag: "Known", summary };
|
|
70
|
+
}
|
|
71
|
+
return titles;
|
|
72
|
+
});
|
|
73
|
+
const SearchResult = Schema.fromJsonString(Schema.Array(Schema.Struct({
|
|
74
|
+
key: Schema.String,
|
|
75
|
+
fields: Schema.Struct({ summary: Schema.optionalKey(Schema.String) })
|
|
76
|
+
})));
|
|
77
|
+
const decodeSearch = Schema.decodeUnknownEffect(SearchResult);
|
|
78
|
+
/**
|
|
79
|
+
* What one `acli` search said, from its exit code and stdout. A nonzero exit is a failed lookup even
|
|
80
|
+
* when it printed something: a failed search cannot show that a ticket does not exist.
|
|
81
|
+
*/
|
|
82
|
+
export const searchOutcome = (exitCode, stdout) => exitCode !== 0
|
|
83
|
+
? Effect.fail(new TicketLookupFailed({ reason: `acli exited with status ${exitCode}` }))
|
|
84
|
+
: decodeSearch(stdout).pipe(Effect.mapError(() => new TicketLookupFailed({ reason: "acli returned an unreadable reply" })), Effect.map((items) => {
|
|
85
|
+
const found = new Map();
|
|
86
|
+
for (const item of items) {
|
|
87
|
+
if (item.fields.summary !== undefined)
|
|
88
|
+
found.set(item.key, item.fields.summary);
|
|
89
|
+
}
|
|
90
|
+
return found;
|
|
91
|
+
}));
|
|
92
|
+
/**
|
|
93
|
+
* `acli jira workitem search` for a batch of keys. Keys reach the JQL only after the Booking
|
|
94
|
+
* pattern accepted them, so they cannot carry JQL syntax.
|
|
95
|
+
*/
|
|
96
|
+
export const acliTicketSearch = Effect.gen(function* () {
|
|
97
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
98
|
+
const search = (keys) => Effect.scoped(Effect.gen(function* () {
|
|
99
|
+
const handle = yield* spawner.spawn(ChildProcess.make("acli", [
|
|
100
|
+
"jira",
|
|
101
|
+
"workitem",
|
|
102
|
+
"search",
|
|
103
|
+
"--jql",
|
|
104
|
+
`key in (${keys.join(",")})`,
|
|
105
|
+
"--fields",
|
|
106
|
+
"key,summary",
|
|
107
|
+
"--limit",
|
|
108
|
+
String(keys.length),
|
|
109
|
+
"--json"
|
|
110
|
+
]));
|
|
111
|
+
const [stdout, exitCode] = yield* Effect.all([Stream.mkString(Stream.decodeText(handle.stdout)), handle.exitCode], { concurrency: 2 });
|
|
112
|
+
return { stdout, exitCode };
|
|
113
|
+
})).pipe(
|
|
114
|
+
// A hung acli (waiting on a login, a dead network) must not hold up the work behind it.
|
|
115
|
+
Effect.timeoutOrElse({
|
|
116
|
+
duration: ACLI_TIMEOUT,
|
|
117
|
+
orElse: () => Effect.fail(new TicketLookupFailed({ reason: "acli did not answer within 30 seconds" }))
|
|
118
|
+
}), Effect.mapError((error) => error._tag === "TicketLookupFailed" ? error : new TicketLookupFailed({
|
|
119
|
+
reason: error.reason._tag === "NotFound"
|
|
120
|
+
? "ticket titles need acli, which is not installed"
|
|
121
|
+
: `acli could not run (${error.reason._tag})`
|
|
122
|
+
})), Effect.flatMap(({ exitCode, stdout }) => searchOutcome(exitCode, stdout)));
|
|
123
|
+
return search;
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=Tickets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tickets.js","sourceRoot":"","sources":["../../src/core/Tickets.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@knpkv/agent-usage` — Claude and Codex subscription usage on this Machine: per ticket, over
|
|
3
|
+
* time, and against subscription limits.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
export * as Attribution from "./core/Attribution.js";
|
|
8
|
+
export * as Ingest from "./core/Ingest.js";
|
|
9
|
+
export * as Model from "./core/Model.js";
|
|
10
|
+
export * as Pricing from "./core/Pricing.js";
|
|
11
|
+
export * as Report from "./core/Report.js";
|
|
12
|
+
export * as Store from "./core/Store.js";
|
|
13
|
+
export * as Api from "./server/Api.js";
|
|
14
|
+
export * as Server from "./server/Server.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@knpkv/agent-usage` — Claude and Codex subscription usage on this Machine: per ticket, over
|
|
3
|
+
* time, and against subscription limits.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
export * as Attribution from "./core/Attribution.js";
|
|
8
|
+
export * as Ingest from "./core/Ingest.js";
|
|
9
|
+
export * as Model from "./core/Model.js";
|
|
10
|
+
export * as Pricing from "./core/Pricing.js";
|
|
11
|
+
export * as Report from "./core/Report.js";
|
|
12
|
+
export * as Store from "./core/Store.js";
|
|
13
|
+
export * as Api from "./server/Api.js";
|
|
14
|
+
export * as Server from "./server/Server.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,WAAW,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAA;AAC1C,OAAO,KAAK,KAAK,MAAM,iBAAiB,CAAA;AACxC,OAAO,KAAK,OAAO,MAAM,mBAAmB,CAAA;AAC5C,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAA;AAC1C,OAAO,KAAK,KAAK,MAAM,iBAAiB,CAAA;AACxC,OAAO,KAAK,GAAG,MAAM,iBAAiB,CAAA;AACtC,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAA"}
|
package/dist/main.d.ts
ADDED