@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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/dist/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  4. package/dist/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  5. package/dist/client/assets/index-CUuLXODk.css +1 -0
  6. package/dist/client/assets/index-DB-VXWqy.js +55 -0
  7. package/dist/client/index.html +15 -0
  8. package/dist/core/Attribution.d.ts +59 -0
  9. package/dist/core/Attribution.js +108 -0
  10. package/dist/core/Attribution.js.map +1 -0
  11. package/dist/core/Balances.d.ts +27 -0
  12. package/dist/core/Balances.js +63 -0
  13. package/dist/core/Balances.js.map +1 -0
  14. package/dist/core/ClaudeLimits.d.ts +58 -0
  15. package/dist/core/ClaudeLimits.js +125 -0
  16. package/dist/core/ClaudeLimits.js.map +1 -0
  17. package/dist/core/ClaudeLimitsLive.d.ts +18 -0
  18. package/dist/core/ClaudeLimitsLive.js +36 -0
  19. package/dist/core/ClaudeLimitsLive.js.map +1 -0
  20. package/dist/core/ClaudeReader.d.ts +27 -0
  21. package/dist/core/ClaudeReader.js +154 -0
  22. package/dist/core/ClaudeReader.js.map +1 -0
  23. package/dist/core/CodexReader.d.ts +91 -0
  24. package/dist/core/CodexReader.js +365 -0
  25. package/dist/core/CodexReader.js.map +1 -0
  26. package/dist/core/Database.d.ts +4 -0
  27. package/dist/core/Database.js +65 -0
  28. package/dist/core/Database.js.map +1 -0
  29. package/dist/core/Ingest.d.ts +55 -0
  30. package/dist/core/Ingest.js +267 -0
  31. package/dist/core/Ingest.js.map +1 -0
  32. package/dist/core/Model.d.ts +160 -0
  33. package/dist/core/Model.js +125 -0
  34. package/dist/core/Model.js.map +1 -0
  35. package/dist/core/Pricing.d.ts +42 -0
  36. package/dist/core/Pricing.js +126 -0
  37. package/dist/core/Pricing.js.map +1 -0
  38. package/dist/core/Readers.d.ts +42 -0
  39. package/dist/core/Readers.js +18 -0
  40. package/dist/core/Readers.js.map +1 -0
  41. package/dist/core/Report.d.ts +53 -0
  42. package/dist/core/Report.js +270 -0
  43. package/dist/core/Report.js.map +1 -0
  44. package/dist/core/Store.d.ts +94 -0
  45. package/dist/core/Store.js +376 -0
  46. package/dist/core/Store.js.map +1 -0
  47. package/dist/core/Tickets.d.ts +42 -0
  48. package/dist/core/Tickets.js +125 -0
  49. package/dist/core/Tickets.js.map +1 -0
  50. package/dist/index.d.ts +14 -0
  51. package/dist/index.js +15 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/main.d.ts +2 -0
  54. package/dist/main.js +65 -0
  55. package/dist/main.js.map +1 -0
  56. package/dist/server/Api.d.ts +304 -0
  57. package/dist/server/Api.js +20 -0
  58. package/dist/server/Api.js.map +1 -0
  59. package/dist/server/Config.d.ts +27 -0
  60. package/dist/server/Config.js +49 -0
  61. package/dist/server/Config.js.map +1 -0
  62. package/dist/server/Handlers.d.ts +5 -0
  63. package/dist/server/Handlers.js +48 -0
  64. package/dist/server/Handlers.js.map +1 -0
  65. package/dist/server/HttpApplication.d.ts +7 -0
  66. package/dist/server/HttpApplication.js +70 -0
  67. package/dist/server/HttpApplication.js.map +1 -0
  68. package/dist/server/OwnerSession.d.ts +60 -0
  69. package/dist/server/OwnerSession.js +206 -0
  70. package/dist/server/OwnerSession.js.map +1 -0
  71. package/dist/server/Runtime.d.ts +57 -0
  72. package/dist/server/Runtime.js +85 -0
  73. package/dist/server/Runtime.js.map +1 -0
  74. package/dist/server/Server.d.ts +15 -0
  75. package/dist/server/Server.js +39 -0
  76. package/dist/server/Server.js.map +1 -0
  77. package/dist/shared/contracts.d.ts +309 -0
  78. package/dist/shared/contracts.js +105 -0
  79. package/dist/shared/contracts.js.map +1 -0
  80. package/dist/tsconfig.tsbuildinfo +1 -0
  81. package/package.json +68 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Opening the store file privately.
3
+ *
4
+ * **Mental model**
5
+ *
6
+ * - **Owner-only, or not at all.** The store directory must be `0700` (created so when missing) and
7
+ * the database and its WAL/SHM files are kept `0600`. A directory others can read is refused
8
+ * rather than silently tightened: it was set that way by someone.
9
+ * - **No symlinks at the store.** A symlinked directory or database file could point the store
10
+ * anywhere; it is refused. Ancestors are not checked: a symlinked home is ordinary.
11
+ * - **POSIX only.** Without a mode check that means something, the store fails closed.
12
+ *
13
+ * @module
14
+ */
15
+ import { SqliteClient } from "@effect/sql-sqlite-node";
16
+ import { Effect, FileSystem, Layer, Path, Schema } from "effect";
17
+ import { StoreError, UsageStore } from "./Store.js";
18
+ const DATABASE_FILE = "usage.db";
19
+ const fail = (operation, cause) => Effect.fail(new StoreError({ operation, cause }));
20
+ /** `readlink` on something that is not a link fails with EINVAL; nothing else means "not a link". */
21
+ const isNotALink = Schema.is(Schema.Struct({ code: Schema.Literal("EINVAL") }));
22
+ const refuseSymlink = (fs, path) => fs.readLink(path).pipe(Effect.matchEffect({
23
+ // Not a link, or not there: both are fine. Any other failure (ELOOP, EIO) fails closed.
24
+ onFailure: (error) => error.reason._tag === "NotFound" || isNotALink(error.cause)
25
+ ? Effect.void
26
+ : fail("secure.readlink", error),
27
+ onSuccess: (target) => fail("secure.symlink", { path, target })
28
+ }));
29
+ const prepareDirectory = Effect.fnUntraced(function* (directory) {
30
+ const fs = yield* FileSystem.FileSystem;
31
+ const path = yield* Path.Path;
32
+ if (path.sep !== "/")
33
+ return yield* fail("secure.platform", { sep: path.sep });
34
+ yield* refuseSymlink(fs, directory);
35
+ const exists = yield* fs.exists(directory).pipe(Effect.mapError((cause) => new StoreError({ operation: "secure.exists", cause })));
36
+ if (!exists) {
37
+ yield* fs.makeDirectory(directory, { recursive: true, mode: 0o700 }).pipe(Effect.andThen(fs.chmod(directory, 0o700)), Effect.mapError((cause) => new StoreError({ operation: "secure.create-directory", cause })));
38
+ }
39
+ const info = yield* fs.stat(directory).pipe(Effect.mapError((cause) => new StoreError({ operation: "secure.stat", cause })));
40
+ if (info.type !== "Directory")
41
+ return yield* fail("secure.directory-type", { directory, type: info.type });
42
+ if ((info.mode & 0o777) !== 0o700) {
43
+ return yield* fail("secure.directory-mode", { directory, mode: (info.mode & 0o777).toString(8) });
44
+ }
45
+ const filename = path.join(directory, DATABASE_FILE);
46
+ const files = [filename, `${filename}-wal`, `${filename}-shm`];
47
+ yield* Effect.forEach(files, (file) => refuseSymlink(fs, file), { discard: true });
48
+ return { filename, files };
49
+ });
50
+ const restrictFiles = Effect.fnUntraced(function* (files) {
51
+ const fs = yield* FileSystem.FileSystem;
52
+ for (const file of files) {
53
+ yield* refuseSymlink(fs, file);
54
+ const exists = yield* fs.exists(file).pipe(Effect.mapError((cause) => new StoreError({ operation: "secure.exists", cause })));
55
+ if (exists) {
56
+ yield* fs.chmod(file, 0o600).pipe(Effect.mapError((cause) => new StoreError({ operation: "secure.chmod", cause })));
57
+ }
58
+ }
59
+ });
60
+ /** The store, opened privately in `directory` (`usage.db`), migrated and ready. */
61
+ export const databaseLayer = (directory) => Layer.unwrap(Effect.gen(function* () {
62
+ const { filename, files } = yield* prepareDirectory(directory);
63
+ return UsageStore.layer.pipe(Layer.provide(SqliteClient.layer({ filename })), Layer.tap(() => restrictFiles(files)));
64
+ }));
65
+ //# sourceMappingURL=Database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.js","sourceRoot":"","sources":["../../src/core/Database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * One ingest pass: every transcript and rollout read from its Ingest Cursor to the last complete
3
+ * line, committed chunk by chunk.
4
+ *
5
+ * **Mental model**
6
+ *
7
+ * - **Append-only files, byte cursors.** A cursor holds a file's identity (device + inode), the byte
8
+ * offset after its last complete line, and the reader's carried state. A file whose identity
9
+ * changed or that shrank is read again from the start; event keys absorb the overlap.
10
+ * - **A line is read once its newline lands.** A half-written final line stays unread until the
11
+ * next pass, so a live session is never booked from a fragment.
12
+ * - **Nothing is passed over quietly.** An unreadable file or a missing source root is reported in
13
+ * the Ingest Status; only a store failure fails the pass.
14
+ *
15
+ * @module
16
+ */
17
+ import { Effect, FileSystem, Path } from "effect";
18
+ import { type SkipCounts } from "./Readers.js";
19
+ import { type StoreError, UsageStore } from "./Store.js";
20
+ /** Where this Machine's agents keep their sessions, and the Machine's name. */
21
+ export interface SourceRoots {
22
+ /** `<claude config>/projects` */
23
+ readonly claudeProjects: string;
24
+ /** `<CODEX_HOME>`: rollouts live in `sessions/`, and Codex moves archived ones to `archived_sessions/`. */
25
+ readonly codexHome: string;
26
+ readonly machine: string;
27
+ }
28
+ export interface UnreadableFile {
29
+ /** Path relative to the source root; `.` is the root itself. */
30
+ readonly fileKey: string;
31
+ readonly reason: string;
32
+ }
33
+ /** What one pass found in one Agent's source root. */
34
+ export interface SourceStatus {
35
+ readonly rootMissing: boolean;
36
+ readonly filesScanned: number;
37
+ readonly filesRead: number;
38
+ readonly eventsAdded: number;
39
+ readonly skipped: SkipCounts;
40
+ readonly unreadable: ReadonlyArray<UnreadableFile>;
41
+ }
42
+ export interface IngestStatus {
43
+ readonly startedAt: number;
44
+ readonly finishedAt: number;
45
+ readonly claude: SourceStatus;
46
+ readonly codex: SourceStatus;
47
+ }
48
+ export interface IngestOptions {
49
+ /** How many bytes to read and commit at a time; bounds memory during a backfill. */
50
+ readonly chunkBytes?: number;
51
+ /** Lines longer than this are skipped and counted rather than held in memory. */
52
+ readonly maxLineBytes?: number;
53
+ }
54
+ /** Reads everything appended since the last pass in both sources. Fails only when the store does. */
55
+ export declare const ingestOnce: (roots: SourceRoots, options?: IngestOptions) => Effect.Effect<IngestStatus, StoreError, UsageStore | FileSystem.FileSystem | Path.Path>;
@@ -0,0 +1,267 @@
1
+ /**
2
+ * One ingest pass: every transcript and rollout read from its Ingest Cursor to the last complete
3
+ * line, committed chunk by chunk.
4
+ *
5
+ * **Mental model**
6
+ *
7
+ * - **Append-only files, byte cursors.** A cursor holds a file's identity (device + inode), the byte
8
+ * offset after its last complete line, and the reader's carried state. A file whose identity
9
+ * changed or that shrank is read again from the start; event keys absorb the overlap.
10
+ * - **A line is read once its newline lands.** A half-written final line stays unread until the
11
+ * next pass, so a live session is never booked from a fragment.
12
+ * - **Nothing is passed over quietly.** An unreadable file or a missing source root is reported in
13
+ * the Ingest Status; only a store failure fails the pass.
14
+ *
15
+ * @module
16
+ */
17
+ import { Clock, Effect, FileSystem, Option, Path, Schema } from "effect";
18
+ import { ClaudeReaderState, initialClaudeState, readClaude } from "./ClaudeReader.js";
19
+ import { CodexReaderState, initialCodexState, readCodex } from "./CodexReader.js";
20
+ import { countSkip, mergeSkips, noSkips } from "./Readers.js";
21
+ import { UsageStore } from "./Store.js";
22
+ const DEFAULT_CHUNK_BYTES = 8 * 1024 * 1024;
23
+ /** Far above any usage-bearing line; only huge pasted tool output or images come near it. */
24
+ const DEFAULT_MAX_LINE_BYTES = 32 * 1024 * 1024;
25
+ const NEWLINE = 10;
26
+ const ClaudeStateJson = Schema.fromJsonString(ClaudeReaderState);
27
+ const CodexStateJson = Schema.fromJsonString(CodexReaderState);
28
+ /** `<project>/<session>.jsonl`, and `<project>/<session>/subagents/<agent>.jsonl` booked to the session. */
29
+ const claudeSource = {
30
+ agent: "claude",
31
+ initial: initialClaudeState,
32
+ decodeState: Schema.decodeUnknownOption(ClaudeStateJson),
33
+ encodeState: Schema.encodeSync(ClaudeStateJson),
34
+ read: readClaude,
35
+ descend: (segments) => segments.length <= 2 || (segments.length === 3 && segments[2] === "subagents"),
36
+ select: (paths, path) => paths.flatMap((fileKey) => {
37
+ if (!fileKey.endsWith(".jsonl"))
38
+ return [];
39
+ const segments = fileKey.split(path.sep);
40
+ if (segments.length === 2)
41
+ return [{ fileKey, sessionId: path.basename(fileKey, ".jsonl") }];
42
+ if (segments.length === 4 && segments[2] === "subagents" && segments[1] !== undefined) {
43
+ return [{ fileKey, sessionId: segments[1] }];
44
+ }
45
+ return [];
46
+ })
47
+ };
48
+ const ROLLOUT_ID = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/iu;
49
+ const CODEX_ROLLOUT_DIRECTORIES = new Set(["sessions", "archived_sessions"]);
50
+ /**
51
+ * `sessions/YYYY/MM/DD/rollout-<time>-<session uuid>.jsonl`, and archived rollouts below
52
+ * `archived_sessions/`. An archived rollout keeps its session id and byte offsets, so its requests
53
+ * deduplicate against the ones read before it moved.
54
+ */
55
+ const codexSource = {
56
+ agent: "codex",
57
+ initial: initialCodexState,
58
+ decodeState: Schema.decodeUnknownOption(CodexStateJson),
59
+ encodeState: Schema.encodeSync(CodexStateJson),
60
+ read: readCodex,
61
+ descend: (segments) => CODEX_ROLLOUT_DIRECTORIES.has(segments[0] ?? "") && segments.length <= 4,
62
+ select: (paths, path) => paths.flatMap((fileKey) => {
63
+ const name = path.basename(fileKey);
64
+ const top = fileKey.split(path.sep)[0] ?? "";
65
+ if (!CODEX_ROLLOUT_DIRECTORIES.has(top) || !name.startsWith("rollout-") || !name.endsWith(".jsonl"))
66
+ return [];
67
+ return [{ fileKey, sessionId: ROLLOUT_ID.exec(name)?.[1] ?? path.basename(name, ".jsonl") }];
68
+ })
69
+ };
70
+ const decoder = new TextDecoder();
71
+ /**
72
+ * Splits complete, newline-terminated bytes into lines with their absolute byte offsets. Lines longer
73
+ * than `maxLineBytes` are counted and left undecoded.
74
+ */
75
+ const splitLines = (bytes, startOffset, maxLineBytes) => {
76
+ const lines = [];
77
+ let oversized = 0;
78
+ let lineStart = 0;
79
+ for (let index = 0; index <= bytes.length; index++) {
80
+ if (index === bytes.length || bytes[index] === NEWLINE) {
81
+ if (index - lineStart > maxLineBytes)
82
+ oversized++;
83
+ else if (index > lineStart) {
84
+ lines.push({ offset: startOffset + lineStart, text: decoder.decode(bytes.subarray(lineStart, index)) });
85
+ }
86
+ lineStart = index + 1;
87
+ }
88
+ }
89
+ return { lines, oversized };
90
+ };
91
+ const concatAll = (pieces) => {
92
+ if (pieces.length === 1 && pieces[0] !== undefined)
93
+ return pieces[0];
94
+ const joined = new Uint8Array(pieces.reduce((length, piece) => length + piece.length, 0));
95
+ let at = 0;
96
+ for (const piece of pieces) {
97
+ joined.set(piece, at);
98
+ at += piece.length;
99
+ }
100
+ return joined;
101
+ };
102
+ const unchanged = { read: false, eventsAdded: 0, skipped: noSkips };
103
+ const ingestFile = (source, root, machine, listed, chunkBytes, maxLineBytes) => Effect.scoped(Effect.gen(function* () {
104
+ const fs = yield* FileSystem.FileSystem;
105
+ const path = yield* Path.Path;
106
+ const store = yield* UsageStore;
107
+ const absolute = path.join(root, listed.fileKey);
108
+ const info = yield* fs.stat(absolute);
109
+ if (info.type !== "File")
110
+ return unchanged;
111
+ const size = Number(info.size);
112
+ const identity = `${info.dev}:${Option.getOrElse(info.ino, () => 0)}`;
113
+ const saved = yield* store.cursor(source.agent, listed.fileKey);
114
+ const resumed = Option.flatMap(Option.filter(saved, (cursor) => cursor.identity === identity && cursor.offset <= size), (cursor) => Option.map(source.decodeState(cursor.state), (state) => ({ offset: cursor.offset, state })));
115
+ let offset = Option.match(resumed, { onNone: () => 0, onSome: (value) => value.offset });
116
+ let state = Option.match(resumed, { onNone: () => source.initial, onSome: (value) => value.state });
117
+ if (offset === size)
118
+ return unchanged;
119
+ const file = { fileKey: listed.fileKey, machine, sessionId: listed.sessionId };
120
+ const handle = yield* fs.open(absolute);
121
+ yield* handle.seek(BigInt(offset), "start");
122
+ let eventsAdded = 0;
123
+ let skipped = noSkips;
124
+ // Bytes after `offset` with no newline yet, kept as pieces so a long line is copied once.
125
+ let pending = [];
126
+ let pendingLength = 0;
127
+ // Inside a line longer than `maxLineBytes`: its bytes are dropped until its newline arrives.
128
+ let discarding = false;
129
+ let readPosition = offset;
130
+ const commit = (next, result) => store.commitChunk({
131
+ agent: source.agent,
132
+ fileKey: listed.fileKey,
133
+ cursor: { identity, offset: next, state: source.encodeState(result.state) },
134
+ events: result.events,
135
+ snapshots: result.snapshots,
136
+ balances: result.balances
137
+ });
138
+ while (readPosition < size) {
139
+ const chunk = yield* handle.readAlloc(Math.min(chunkBytes, size - readPosition));
140
+ if (Option.isNone(chunk))
141
+ break;
142
+ const bytes = chunk.value;
143
+ const chunkStart = readPosition;
144
+ readPosition += bytes.length;
145
+ let start = 0;
146
+ if (discarding) {
147
+ const end = bytes.indexOf(NEWLINE);
148
+ if (end < 0)
149
+ continue;
150
+ // The oversized line ends here: count it once and move the cursor past it.
151
+ discarding = false;
152
+ start = end + 1;
153
+ offset = chunkStart + start;
154
+ skipped = countSkip(skipped, "oversizedLine");
155
+ yield* commit(offset, { events: [], snapshots: [], balances: [], skipped: noSkips, state });
156
+ }
157
+ const rest = bytes.subarray(start);
158
+ const lastNewline = rest.lastIndexOf(NEWLINE);
159
+ if (lastNewline < 0) {
160
+ pending.push(rest);
161
+ pendingLength += rest.length;
162
+ if (pendingLength > maxLineBytes) {
163
+ discarding = true;
164
+ pending = [];
165
+ pendingLength = 0;
166
+ }
167
+ continue;
168
+ }
169
+ const complete = concatAll([...pending, rest.subarray(0, lastNewline)]);
170
+ const split = splitLines(complete, offset, maxLineBytes);
171
+ const result = source.read(file, split.lines, state);
172
+ for (let count = 0; count < split.oversized; count++)
173
+ skipped = countSkip(skipped, "oversizedLine");
174
+ const next = offset + complete.length + 1;
175
+ const committed = yield* commit(next, result);
176
+ eventsAdded += committed.eventsAdded;
177
+ skipped = mergeSkips(skipped, result.skipped);
178
+ state = result.state;
179
+ offset = next;
180
+ const tail = rest.subarray(lastNewline + 1);
181
+ pending = tail.length === 0 ? [] : [tail];
182
+ pendingLength = tail.length;
183
+ }
184
+ return { read: true, eventsAdded, skipped };
185
+ }));
186
+ const emptyStatus = (rootMissing) => ({
187
+ rootMissing,
188
+ filesScanned: 0,
189
+ filesRead: 0,
190
+ eventsAdded: 0,
191
+ skipped: noSkips,
192
+ unreadable: []
193
+ });
194
+ /**
195
+ * Every `.jsonl` path under the root, listing one directory at a time so that one directory this
196
+ * user cannot read (a container's, owned by another uid) is reported on its own instead of
197
+ * hiding the rest of the root.
198
+ */
199
+ const walk = (root, descend) => Effect.gen(function* () {
200
+ const fs = yield* FileSystem.FileSystem;
201
+ const path = yield* Path.Path;
202
+ const files = [];
203
+ const unreadable = [];
204
+ const pending = [[]];
205
+ for (let segments = pending.shift(); segments !== undefined; segments = pending.shift()) {
206
+ const relative = segments.join(path.sep);
207
+ const listing = yield* Effect.result(fs.readDirectory(path.join(root, relative)));
208
+ if (listing._tag === "Failure") {
209
+ const reason = listing.failure.reason._tag;
210
+ if (segments.length === 0 && reason === "NotFound") {
211
+ const missing = { rootMissing: true, files: [], unreadable: [] };
212
+ return missing;
213
+ }
214
+ if (reason !== "NotFound")
215
+ unreadable.push({ fileKey: relative === "" ? "." : relative, reason });
216
+ continue;
217
+ }
218
+ for (const name of [...listing.success].sort()) {
219
+ const child = [...segments, name];
220
+ if (name.endsWith(".jsonl")) {
221
+ files.push(child.join(path.sep));
222
+ continue;
223
+ }
224
+ if (!descend(child))
225
+ continue;
226
+ const info = yield* Effect.result(fs.stat(path.join(root, ...child)));
227
+ if (info._tag === "Failure") {
228
+ unreadable.push({ fileKey: child.join(path.sep), reason: info.failure.reason._tag });
229
+ }
230
+ else if (info.success.type === "Directory")
231
+ pending.push(child);
232
+ }
233
+ }
234
+ const found = { rootMissing: false, files: files.sort(), unreadable };
235
+ return found;
236
+ });
237
+ const ingestSource = (source, root, machine, chunkBytes, maxLineBytes) => Effect.gen(function* () {
238
+ const path = yield* Path.Path;
239
+ const walked = yield* walk(root, source.descend);
240
+ if (walked.rootMissing)
241
+ return emptyStatus(true);
242
+ const files = source.select(walked.files, path);
243
+ let status = { ...emptyStatus(false), filesScanned: files.length, unreadable: walked.unreadable };
244
+ for (const listed of files) {
245
+ const outcome = yield* ingestFile(source, root, machine, listed, chunkBytes, maxLineBytes).pipe(Effect.catchTag("PlatformError", (error) => Effect.succeed({ unreadable: { fileKey: listed.fileKey, reason: error.reason._tag } })));
246
+ status = "unreadable" in outcome
247
+ ? { ...status, unreadable: [...status.unreadable, outcome.unreadable] }
248
+ : {
249
+ ...status,
250
+ filesRead: status.filesRead + (outcome.read ? 1 : 0),
251
+ eventsAdded: status.eventsAdded + outcome.eventsAdded,
252
+ skipped: mergeSkips(status.skipped, outcome.skipped)
253
+ };
254
+ }
255
+ return status;
256
+ });
257
+ /** Reads everything appended since the last pass in both sources. Fails only when the store does. */
258
+ export const ingestOnce = (roots, options = {}) => Effect.gen(function* () {
259
+ const chunkBytes = options.chunkBytes ?? DEFAULT_CHUNK_BYTES;
260
+ const maxLineBytes = options.maxLineBytes ?? DEFAULT_MAX_LINE_BYTES;
261
+ const startedAt = yield* Clock.currentTimeMillis;
262
+ const claude = yield* ingestSource(claudeSource, roots.claudeProjects, roots.machine, chunkBytes, maxLineBytes);
263
+ const codex = yield* ingestSource(codexSource, roots.codexHome, roots.machine, chunkBytes, maxLineBytes);
264
+ const finishedAt = yield* Clock.currentTimeMillis;
265
+ return { startedAt, finishedAt, claude, codex };
266
+ });
267
+ //# sourceMappingURL=Ingest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Ingest.js","sourceRoot":"","sources":["../../src/core/Ingest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * The facts a store records: Usage Events, Limit Snapshots and Balance Readings.
3
+ *
4
+ * **Mental model**
5
+ *
6
+ * - **Facts, not conclusions.** A Usage Event carries token counts and Attribution Inputs; its
7
+ * Booking and API-Equivalent Cost are derived when read (ADR 0002).
8
+ * - **Unknown is a value.** A limit or balance that could not be read is stored as `Unknown` with a
9
+ * reason, so a gap on a graph is a gap and never a zero.
10
+ *
11
+ * @module
12
+ */
13
+ import { Schema } from "effect";
14
+ /** The closed set of coding-agent runtimes whose usage is recorded. */
15
+ export declare const Agent: Schema.Literals<readonly ["claude", "codex"]>;
16
+ export type Agent = typeof Agent.Type;
17
+ /** A token count: a whole number, never negative. */
18
+ export declare const Count: Schema.Int;
19
+ /**
20
+ * One request's token counts, split the way pricing needs them.
21
+ *
22
+ * `input` is uncached input only, for both agents: Codex reports cache hits inside its input count
23
+ * and the readers subtract them, so the columns mean the same thing whichever agent wrote them.
24
+ * Codex reports one cache-write count; it is stored as `cacheWrite5m`. `output` excludes
25
+ * `reasoning` for both agents (Codex counts reasoning inside its output figure, Claude reports no
26
+ * split), so the six fields add up to every token the request moved.
27
+ */
28
+ export declare const Tokens: Schema.Struct<{
29
+ readonly input: Schema.Int;
30
+ readonly output: Schema.Int;
31
+ readonly reasoning: Schema.Int;
32
+ readonly cacheRead: Schema.Int;
33
+ readonly cacheWrite5m: Schema.Int;
34
+ readonly cacheWrite1h: Schema.Int;
35
+ }>;
36
+ export type Tokens = typeof Tokens.Type;
37
+ /** Every token a request moved, the sum the "tokens" measure plots. */
38
+ export declare const totalTokens: (tokens: Tokens) => number;
39
+ /** The facts captured with a Usage Event that decide its Booking. */
40
+ export declare const AttributionInputs: Schema.Struct<{
41
+ readonly cwd: Schema.String;
42
+ readonly branch: Schema.String;
43
+ readonly activeTicket: Schema.NullOr<Schema.String>;
44
+ }>;
45
+ export type AttributionInputs = typeof AttributionInputs.Type;
46
+ /**
47
+ * One model request's token counts on one Machine. `dedupeKey` is unique per Agent: Claude's
48
+ * message id + request id, Codex's session id + the line's byte offset in its rollout.
49
+ */
50
+ export declare const UsageEvent: Schema.Struct<{
51
+ readonly agent: Schema.Literals<readonly ["claude", "codex"]>;
52
+ readonly dedupeKey: Schema.NonEmptyString;
53
+ readonly machine: Schema.NonEmptyString;
54
+ readonly sessionId: Schema.NonEmptyString;
55
+ readonly occurredAt: Schema.Int;
56
+ readonly model: Schema.NonEmptyString;
57
+ /** Claude's fast mode: the same model billed at premium rates. */
58
+ readonly fast: Schema.Boolean;
59
+ readonly tokens: Schema.Struct<{
60
+ readonly input: Schema.Int;
61
+ readonly output: Schema.Int;
62
+ readonly reasoning: Schema.Int;
63
+ readonly cacheRead: Schema.Int;
64
+ readonly cacheWrite5m: Schema.Int;
65
+ readonly cacheWrite1h: Schema.Int;
66
+ }>;
67
+ readonly attribution: Schema.Struct<{
68
+ readonly cwd: Schema.String;
69
+ readonly branch: Schema.String;
70
+ readonly activeTicket: Schema.NullOr<Schema.String>;
71
+ }>;
72
+ }>;
73
+ export type UsageEvent = typeof UsageEvent.Type;
74
+ /** Why a limit or balance could not be read. */
75
+ export declare const UnknownReason: Schema.Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
76
+ export type UnknownReason = typeof UnknownReason.Type;
77
+ /** Where a Limit Snapshot or Balance Reading came from. */
78
+ export declare const ObservationSource: Schema.Literals<readonly ["claude-oauth-usage", "codex-rollout"]>;
79
+ export type ObservationSource = typeof ObservationSource.Type;
80
+ /** A window's length in minutes, or Unknown for a provider key whose window nobody published. */
81
+ export declare const WindowMinutes: Schema.NullOr<Schema.Int>;
82
+ export type WindowMinutes = typeof WindowMinutes.Type;
83
+ export declare const LimitReading: Schema.Union<readonly [Schema.TaggedStruct<"Known", {
84
+ readonly usedPercent: Schema.Finite;
85
+ /** Epoch milliseconds; null when the provider gave no reset time. */
86
+ readonly resetsAt: Schema.NullOr<Schema.Int>;
87
+ }>, Schema.TaggedStruct<"Unknown", {
88
+ readonly reason: Schema.Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
89
+ }>]>;
90
+ export type LimitReading = typeof LimitReading.Type;
91
+ /**
92
+ * One observation of a Limit Window. `label` is the provider's own key (`five_hour`, `seven_day`,
93
+ * Codex's `primary`/`secondary`); an Unknown reading for a whole source uses label `*`.
94
+ */
95
+ export declare const LimitSnapshot: Schema.Struct<{
96
+ readonly agent: Schema.Literals<readonly ["claude", "codex"]>;
97
+ readonly machine: Schema.NonEmptyString;
98
+ readonly source: Schema.Literals<readonly ["claude-oauth-usage", "codex-rollout"]>;
99
+ readonly label: Schema.NonEmptyString;
100
+ readonly windowMinutes: Schema.NullOr<Schema.Int>;
101
+ readonly observedAt: Schema.Int;
102
+ readonly reading: Schema.Union<readonly [Schema.TaggedStruct<"Known", {
103
+ readonly usedPercent: Schema.Finite;
104
+ /** Epoch milliseconds; null when the provider gave no reset time. */
105
+ readonly resetsAt: Schema.NullOr<Schema.Int>;
106
+ }>, Schema.TaggedStruct<"Unknown", {
107
+ readonly reason: Schema.Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
108
+ }>]>;
109
+ }>;
110
+ export type LimitSnapshot = typeof LimitSnapshot.Type;
111
+ export declare const Balance: Schema.Union<readonly [Schema.TaggedStruct<"Amount", {
112
+ readonly leftMinor: Schema.Finite;
113
+ readonly limitMinor: Schema.Finite;
114
+ readonly decimals: Schema.Int;
115
+ readonly currency: Schema.String;
116
+ }>, Schema.TaggedStruct<"Credits", {
117
+ readonly credits: Schema.Finite;
118
+ }>, Schema.TaggedStruct<"Unlimited", {}>, Schema.TaggedStruct<"Exhausted", {}>, Schema.TaggedStruct<"Disabled", {}>]>;
119
+ export type Balance = typeof Balance.Type;
120
+ export declare const BalanceValue: Schema.Union<readonly [Schema.TaggedStruct<"Known", {
121
+ readonly balance: Schema.Union<readonly [Schema.TaggedStruct<"Amount", {
122
+ readonly leftMinor: Schema.Finite;
123
+ readonly limitMinor: Schema.Finite;
124
+ readonly decimals: Schema.Int;
125
+ readonly currency: Schema.String;
126
+ }>, Schema.TaggedStruct<"Credits", {
127
+ readonly credits: Schema.Finite;
128
+ }>, Schema.TaggedStruct<"Unlimited", {}>, Schema.TaggedStruct<"Exhausted", {}>, Schema.TaggedStruct<"Disabled", {}>]>;
129
+ }>, Schema.TaggedStruct<"Unknown", {
130
+ readonly reason: Schema.Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
131
+ }>]>;
132
+ export type BalanceValue = typeof BalanceValue.Type;
133
+ /** Which balance a reading is of. */
134
+ export declare const BalanceKind: Schema.Literals<readonly ["claude-extra-usage", "codex-credits"]>;
135
+ export type BalanceKind = typeof BalanceKind.Type;
136
+ export declare const BalanceReading: Schema.Struct<{
137
+ readonly kind: Schema.Literals<readonly ["claude-extra-usage", "codex-credits"]>;
138
+ readonly machine: Schema.NonEmptyString;
139
+ readonly observedAt: Schema.Int;
140
+ readonly value: Schema.Union<readonly [Schema.TaggedStruct<"Known", {
141
+ readonly balance: Schema.Union<readonly [Schema.TaggedStruct<"Amount", {
142
+ readonly leftMinor: Schema.Finite;
143
+ readonly limitMinor: Schema.Finite;
144
+ readonly decimals: Schema.Int;
145
+ readonly currency: Schema.String;
146
+ }>, Schema.TaggedStruct<"Credits", {
147
+ readonly credits: Schema.Finite;
148
+ }>, Schema.TaggedStruct<"Unlimited", {}>, Schema.TaggedStruct<"Exhausted", {}>, Schema.TaggedStruct<"Disabled", {}>]>;
149
+ }>, Schema.TaggedStruct<"Unknown", {
150
+ readonly reason: Schema.Literals<readonly ["NoAuth", "AuthExpired", "NotSupported", "Fetch", "Parse", "NoData"]>;
151
+ }>]>;
152
+ }>;
153
+ export type BalanceReading = typeof BalanceReading.Type;
154
+ /** A ticket's Jira title, or why there is none. */
155
+ export declare const TicketTitleValue: Schema.Union<readonly [Schema.TaggedStruct<"Known", {
156
+ readonly summary: Schema.String;
157
+ }>, Schema.TaggedStruct<"Unknown", {
158
+ readonly reason: Schema.Literals<readonly ["NotFound", "NotLookedUp"]>;
159
+ }>]>;
160
+ export type TicketTitleValue = typeof TicketTitleValue.Type;