ccc-notifier 0.4.0 → 0.6.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.
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ aggregateNewTurn
4
+ } from "./chunk-D4D76RGQ.js";
5
+ import {
6
+ loadCursor,
7
+ logError,
8
+ sanitizeCursor
9
+ } from "./chunk-OOAC5ULQ.js";
10
+
11
+ // src/subagents.ts
12
+ import { promises as fs } from "fs";
13
+ import { join } from "path";
14
+ var MAX_AGENT_FILES = 200;
15
+ function emptyBuckets() {
16
+ return { input: 0, output: 0, cacheWrite5m: 0, cacheWrite1h: 0, cacheRead: 0 };
17
+ }
18
+ function addToModel(target, model, b) {
19
+ const cur = target[model] ?? emptyBuckets();
20
+ cur.input += b.input;
21
+ cur.output += b.output;
22
+ cur.cacheWrite5m += b.cacheWrite5m;
23
+ cur.cacheWrite1h += b.cacheWrite1h;
24
+ cur.cacheRead += b.cacheRead;
25
+ target[model] = cur;
26
+ }
27
+ function mergeUsage(target, src) {
28
+ for (const [model, b] of Object.entries(src)) addToModel(target, model, b);
29
+ }
30
+ function sameCursor(a, b) {
31
+ return a !== null && a.offset === b.offset && a.lastUuid === b.lastUuid && a.lastTs === b.lastTs && a.seenMessageKeys.length === b.seenMessageKeys.length && a.seenMessageKeys.every((key, i) => key === b.seenMessageKeys[i]);
32
+ }
33
+ function subagentsDirOf(mainTranscriptPath) {
34
+ const base = mainTranscriptPath.endsWith(".jsonl") ? mainTranscriptPath.slice(0, -".jsonl".length) : mainTranscriptPath;
35
+ return join(base, "subagents");
36
+ }
37
+ async function listAgentFiles(dir, entries, includeAllFiles) {
38
+ const files = entries.filter((e) => e.isFile() && e.name.startsWith("agent-") && e.name.endsWith(".jsonl")).map((e) => join(dir, e.name));
39
+ if (includeAllFiles || files.length <= MAX_AGENT_FILES) return files;
40
+ const withMtime = [];
41
+ for (const p of files) {
42
+ let mtime = 0;
43
+ try {
44
+ mtime = (await fs.stat(p)).mtimeMs;
45
+ } catch {
46
+ mtime = 0;
47
+ }
48
+ withMtime.push({ path: p, mtime });
49
+ }
50
+ withMtime.sort((a, b) => b.mtime - a.mtime);
51
+ return withMtime.slice(0, MAX_AGENT_FILES).map((x) => x.path);
52
+ }
53
+ async function collectSubagentUsage(mainTranscriptPath, opts = {}) {
54
+ const dir = subagentsDirOf(mainTranscriptPath);
55
+ let entries;
56
+ try {
57
+ entries = await fs.readdir(dir, { withFileTypes: true });
58
+ } catch (err) {
59
+ if (opts.strictRead && err.code !== "ENOENT") throw err;
60
+ return null;
61
+ }
62
+ const files = await listAgentFiles(dir, entries, opts.includeAllFiles === true);
63
+ const perModel = {};
64
+ let apiCalls = 0;
65
+ let agentFiles = 0;
66
+ const newCursors = [];
67
+ const groups = [];
68
+ let sessionId = "";
69
+ let cwd = null;
70
+ let gitBranch = null;
71
+ let lastTs = null;
72
+ const excluded = new Set(opts.excludeMessageKeys ?? []);
73
+ if (!opts.ignoreCursors) {
74
+ for (const filePath of files) {
75
+ const prior = sanitizeCursor(loadCursor(filePath));
76
+ for (const key of prior?.seenMessageKeys ?? []) excluded.add(key);
77
+ }
78
+ }
79
+ for (const filePath of files) {
80
+ try {
81
+ if (opts.strictRead) {
82
+ const file = await fs.open(filePath, "r");
83
+ await file.close();
84
+ }
85
+ const cursor = opts.ignoreCursors ? null : sanitizeCursor(loadCursor(filePath));
86
+ const agg = await aggregateNewTurn(filePath, cursor, {
87
+ excludeMessageKeys: excluded,
88
+ minTimestampMs: opts.minTimestampMs,
89
+ returnEmpty: true
90
+ });
91
+ if (agg === null) continue;
92
+ if (!sameCursor(cursor, agg.newCursor)) {
93
+ newCursors.push({ path: filePath, cursor: agg.newCursor });
94
+ }
95
+ for (const key of agg.messageKeys) excluded.add(key);
96
+ if (agg.apiCalls === 0) continue;
97
+ mergeUsage(perModel, agg.main);
98
+ mergeUsage(perModel, agg.sidechain);
99
+ apiCalls += agg.apiCalls;
100
+ agentFiles += 1;
101
+ const groupUsage = {};
102
+ mergeUsage(groupUsage, agg.main);
103
+ mergeUsage(groupUsage, agg.sidechain);
104
+ groups.push({
105
+ perModel: groupUsage,
106
+ apiCalls: agg.apiCalls,
107
+ firstTs: agg.firstTs,
108
+ lastTs: agg.lastTs
109
+ });
110
+ if (agg.sessionId) sessionId = agg.sessionId;
111
+ if (agg.cwd !== null) cwd = agg.cwd;
112
+ if (agg.gitBranch !== null) gitBranch = agg.gitBranch;
113
+ if (agg.lastTs !== null && (lastTs === null || agg.lastTs > lastTs)) lastTs = agg.lastTs;
114
+ } catch (err) {
115
+ if (opts.strictRead) throw err;
116
+ logError("subagents:file", err);
117
+ }
118
+ }
119
+ return { perModel, apiCalls, agentFiles, newCursors, groups, sessionId, cwd, gitBranch, lastTs };
120
+ }
121
+
122
+ export {
123
+ collectSubagentUsage
124
+ };
@@ -4,7 +4,7 @@ import {
4
4
  isMuted,
5
5
  readMuteState,
6
6
  writeMuteState
7
- } from "./chunk-26CISNOE.js";
7
+ } from "./chunk-OOAC5ULQ.js";
8
8
 
9
9
  // src/mute.ts
10
10
  function parseDuration(arg) {