ccc-notifier 0.6.3 → 0.8.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/README.md +5 -0
- package/dist/{budget-V7CCMJHF.js → budget-U37CCG4Y.js} +2 -2
- package/dist/chunk-3M5UQK76.js +92 -0
- package/dist/chunk-6IVSZ3S4.js +22 -0
- package/dist/{sweep-CONNNBK7.js → chunk-C7QOYVGT.js} +324 -120
- package/dist/{chunk-PYU7CAK2.js → chunk-DSTGXYSY.js} +44 -11
- package/dist/{chunk-D4D76RGQ.js → chunk-GAOGTALW.js} +361 -20
- package/dist/{chunk-T6Z2ZAN4.js → chunk-ML5H43NH.js} +1 -1
- package/dist/{chunk-27SJELD2.js → chunk-PXMXEFM7.js} +22 -107
- package/dist/{chunk-6HTETN26.js → chunk-Q45AAPJY.js} +18 -1
- package/dist/{chunk-OXXDZZPJ.js → chunk-RE72XBXB.js} +4 -6
- package/dist/{chunk-NV5UOHJA.js → chunk-TTD3BGOL.js} +5 -5
- package/dist/{chunk-OOAC5ULQ.js → chunk-WONMZ466.js} +184 -4
- package/dist/{chunk-J5QAYTFE.js → chunk-XOGYLFX3.js} +12 -1
- package/dist/chunk-YH45W7TA.js +479 -0
- package/dist/cli.js +287 -30
- package/dist/{dashboard-O7LJLYM3.js → dashboard-YG4H3GLA.js} +6 -4
- package/dist/{history-DL4SG3PG.js → history-W34EJOVV.js} +5 -3
- package/dist/{mute-UIR5P5N5.js → mute-23BWQXS3.js} +2 -2
- package/dist/reset-cursors-UVQXFN5Y.js +44 -0
- package/dist/scan-QB2N2O4Q.js +94 -0
- package/dist/{setup-I3JNGWZG.js → setup-2QDA3OWE.js} +5 -6
- package/dist/{subagent-store-US4TJJQR.js → subagent-store-USG3WJEB.js} +1 -1
- package/dist/sweep-OLI2LIXJ.js +22 -0
- package/dist/track-TIINM4ZR.js +368 -0
- package/package.json +1 -1
- package/dist/chunk-HLJAJS2W.js +0 -55
- package/dist/chunk-HTYUYKFW.js +0 -21
- package/dist/chunk-OYD6H3ZZ.js +0 -124
- package/dist/track-RBLLR25M.js +0 -257
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
collectSubagentUsage
|
|
10
|
-
} from "./chunk-OYD6H3ZZ.js";
|
|
11
|
-
import {
|
|
3
|
+
addCountedCalls,
|
|
4
|
+
aggregateNewTurn,
|
|
5
|
+
anyOf,
|
|
6
|
+
callFingerprints,
|
|
7
|
+
claudeTranscriptRoots,
|
|
12
8
|
extractBucket,
|
|
13
9
|
getUsdJpy,
|
|
10
|
+
listCodexRollouts,
|
|
14
11
|
promptCandidate,
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
setCountedCalls,
|
|
13
|
+
splitIntoCodexTurnDrafts,
|
|
14
|
+
surfaceForClaudePath
|
|
15
|
+
} from "./chunk-GAOGTALW.js";
|
|
17
16
|
import {
|
|
17
|
+
codexHome,
|
|
18
18
|
computeCost,
|
|
19
19
|
loadPriceTable
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-Q45AAPJY.js";
|
|
21
21
|
import {
|
|
22
22
|
writeDashboardHtml
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-DGXUSPS4.js";
|
|
25
|
-
import {
|
|
26
|
-
formatJPY,
|
|
27
|
-
formatUSD,
|
|
28
|
-
modelDisplayName
|
|
29
|
-
} from "./chunk-J5QAYTFE.js";
|
|
23
|
+
} from "./chunk-DSTGXYSY.js";
|
|
30
24
|
import {
|
|
31
25
|
invalidateCanonicalDashboards,
|
|
32
26
|
makeFullDashboardState,
|
|
33
|
-
waitForDataLock,
|
|
34
27
|
writeFullDashboardStateAtomic
|
|
35
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-3M5UQK76.js";
|
|
29
|
+
import {
|
|
30
|
+
waitForDataLock
|
|
31
|
+
} from "./chunk-PXMXEFM7.js";
|
|
32
|
+
import {
|
|
33
|
+
formatJPY,
|
|
34
|
+
formatUSD,
|
|
35
|
+
modelDisplayName
|
|
36
|
+
} from "./chunk-XOGYLFX3.js";
|
|
36
37
|
import {
|
|
37
38
|
appendTurn,
|
|
38
39
|
loadCursor,
|
|
@@ -44,13 +45,146 @@ import {
|
|
|
44
45
|
resetHistoryAndCursors,
|
|
45
46
|
sanitizeCursor,
|
|
46
47
|
saveCursor
|
|
47
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-WONMZ466.js";
|
|
48
49
|
|
|
49
50
|
// src/sweep.ts
|
|
50
51
|
import { readFile } from "fs/promises";
|
|
51
52
|
import { promises as fsp } from "fs";
|
|
53
|
+
import { join as join2 } from "path";
|
|
54
|
+
|
|
55
|
+
// src/subagents.ts
|
|
56
|
+
import { promises as fs } from "fs";
|
|
52
57
|
import { join } from "path";
|
|
53
|
-
|
|
58
|
+
var MAX_AGENT_FILES = 200;
|
|
59
|
+
function emptyBuckets() {
|
|
60
|
+
return { input: 0, output: 0, cacheWrite5m: 0, cacheWrite1h: 0, cacheRead: 0 };
|
|
61
|
+
}
|
|
62
|
+
function addToModel(target, model, b) {
|
|
63
|
+
const cur = target[model] ?? emptyBuckets();
|
|
64
|
+
cur.input += b.input;
|
|
65
|
+
cur.output += b.output;
|
|
66
|
+
cur.cacheWrite5m += b.cacheWrite5m;
|
|
67
|
+
cur.cacheWrite1h += b.cacheWrite1h;
|
|
68
|
+
cur.cacheRead += b.cacheRead;
|
|
69
|
+
target[model] = cur;
|
|
70
|
+
}
|
|
71
|
+
function mergeUsage(target, src) {
|
|
72
|
+
for (const [model, b] of Object.entries(src)) addToModel(target, model, b);
|
|
73
|
+
}
|
|
74
|
+
function sameCursor(a, b) {
|
|
75
|
+
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]);
|
|
76
|
+
}
|
|
77
|
+
function subagentsDirOf(mainTranscriptPath) {
|
|
78
|
+
const base = mainTranscriptPath.endsWith(".jsonl") ? mainTranscriptPath.slice(0, -".jsonl".length) : mainTranscriptPath;
|
|
79
|
+
return join(base, "subagents");
|
|
80
|
+
}
|
|
81
|
+
async function listAgentFiles(dir, entries, includeAllFiles) {
|
|
82
|
+
const files = entries.filter((e) => e.isFile() && e.name.startsWith("agent-") && e.name.endsWith(".jsonl")).map((e) => join(dir, e.name));
|
|
83
|
+
if (includeAllFiles || files.length <= MAX_AGENT_FILES) return files;
|
|
84
|
+
const withMtime = [];
|
|
85
|
+
for (const p of files) {
|
|
86
|
+
let mtime = 0;
|
|
87
|
+
try {
|
|
88
|
+
mtime = (await fs.stat(p)).mtimeMs;
|
|
89
|
+
} catch {
|
|
90
|
+
mtime = 0;
|
|
91
|
+
}
|
|
92
|
+
withMtime.push({ path: p, mtime });
|
|
93
|
+
}
|
|
94
|
+
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
95
|
+
return withMtime.slice(0, MAX_AGENT_FILES).map((x) => x.path);
|
|
96
|
+
}
|
|
97
|
+
async function collectSubagentUsage(mainTranscriptPath, opts = {}) {
|
|
98
|
+
const dir = subagentsDirOf(mainTranscriptPath);
|
|
99
|
+
let entries;
|
|
100
|
+
try {
|
|
101
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
102
|
+
} catch (err) {
|
|
103
|
+
if (opts.strictRead && err.code !== "ENOENT") throw err;
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const files = await listAgentFiles(dir, entries, opts.includeAllFiles === true);
|
|
107
|
+
const perModel = {};
|
|
108
|
+
let apiCalls = 0;
|
|
109
|
+
let agentFiles = 0;
|
|
110
|
+
const newCursors = [];
|
|
111
|
+
const groups = [];
|
|
112
|
+
let sessionId = "";
|
|
113
|
+
let cwd = null;
|
|
114
|
+
let gitBranch = null;
|
|
115
|
+
let lastTs = null;
|
|
116
|
+
const readCursor = opts.readCursor ?? ((path) => sanitizeCursor(loadCursor(path)));
|
|
117
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
118
|
+
const excludeFilter = anyOf([excluded, opts.excludeMessageKeys]);
|
|
119
|
+
if (!opts.ignoreCursors) {
|
|
120
|
+
for (const filePath of files) {
|
|
121
|
+
const prior = readCursor(filePath);
|
|
122
|
+
for (const key of prior?.seenMessageKeys ?? []) excluded.add(key);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (const filePath of files) {
|
|
126
|
+
try {
|
|
127
|
+
if (opts.strictRead) {
|
|
128
|
+
const file = await fs.open(filePath, "r");
|
|
129
|
+
await file.close();
|
|
130
|
+
}
|
|
131
|
+
const cursor = opts.ignoreCursors ? null : readCursor(filePath);
|
|
132
|
+
const recovery = cursor === null && opts.recovery !== void 0 ? opts.recovery() : void 0;
|
|
133
|
+
const agg = await aggregateNewTurn(filePath, cursor, {
|
|
134
|
+
excludeMessageKeys: recovery?.excludeMessageKeys === void 0 ? excludeFilter : anyOf([excludeFilter, recovery.excludeMessageKeys]),
|
|
135
|
+
minTimestampMs: opts.minTimestampMs ?? recovery?.minTimestampMs ?? null,
|
|
136
|
+
returnEmpty: true
|
|
137
|
+
});
|
|
138
|
+
if (agg === null) continue;
|
|
139
|
+
if (!sameCursor(cursor, agg.newCursor)) {
|
|
140
|
+
newCursors.push({ path: filePath, cursor: agg.newCursor });
|
|
141
|
+
}
|
|
142
|
+
for (const key of agg.messageKeys) excluded.add(key);
|
|
143
|
+
if (agg.apiCalls === 0) continue;
|
|
144
|
+
mergeUsage(perModel, agg.main);
|
|
145
|
+
mergeUsage(perModel, agg.sidechain);
|
|
146
|
+
apiCalls += agg.apiCalls;
|
|
147
|
+
agentFiles += 1;
|
|
148
|
+
const groupUsage = {};
|
|
149
|
+
mergeUsage(groupUsage, agg.main);
|
|
150
|
+
mergeUsage(groupUsage, agg.sidechain);
|
|
151
|
+
groups.push({
|
|
152
|
+
perModel: groupUsage,
|
|
153
|
+
apiCalls: agg.apiCalls,
|
|
154
|
+
messageKeys: agg.messageKeys,
|
|
155
|
+
firstTs: agg.firstTs,
|
|
156
|
+
lastTs: agg.lastTs
|
|
157
|
+
});
|
|
158
|
+
if (agg.sessionId) sessionId = agg.sessionId;
|
|
159
|
+
if (agg.cwd !== null) cwd = agg.cwd;
|
|
160
|
+
if (agg.gitBranch !== null) gitBranch = agg.gitBranch;
|
|
161
|
+
if (agg.lastTs !== null && (lastTs === null || agg.lastTs > lastTs)) lastTs = agg.lastTs;
|
|
162
|
+
} catch (err) {
|
|
163
|
+
if (opts.strictRead) throw err;
|
|
164
|
+
logError("subagents:file", err);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { perModel, apiCalls, agentFiles, newCursors, groups, sessionId, cwd, gitBranch, lastTs };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/codex/originator.ts
|
|
171
|
+
var ORIGINATOR_TO_SURFACE = {
|
|
172
|
+
"codex-tui": "cli",
|
|
173
|
+
"codex_cli_rs": "cli",
|
|
174
|
+
"codex_exec": "cli",
|
|
175
|
+
"Codex Desktop": "desktop",
|
|
176
|
+
"codex_desktop": "desktop",
|
|
177
|
+
"codex_work_desktop": "desktop",
|
|
178
|
+
"codex_vscode": "vscode",
|
|
179
|
+
"Claude Code": "claude-code",
|
|
180
|
+
"codex-chrome-extension-sidepanel": "chrome-extension"
|
|
181
|
+
};
|
|
182
|
+
function normalizeCodexOriginator(originator) {
|
|
183
|
+
if (typeof originator !== "string" || originator.length === 0) return "other";
|
|
184
|
+
return ORIGINATOR_TO_SURFACE[originator] ?? "other";
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/sweep.ts
|
|
54
188
|
var NEWLINE = 10;
|
|
55
189
|
var MAX_SEEN_KEYS = 500;
|
|
56
190
|
var SYNTHETIC_MODEL = "<synthetic>";
|
|
@@ -97,11 +231,11 @@ function isRecord(v) {
|
|
|
97
231
|
function strOrNull(v) {
|
|
98
232
|
return typeof v === "string" ? v : null;
|
|
99
233
|
}
|
|
100
|
-
function
|
|
234
|
+
function emptyBuckets2() {
|
|
101
235
|
return { input: 0, output: 0, cacheWrite5m: 0, cacheWrite1h: 0, cacheRead: 0 };
|
|
102
236
|
}
|
|
103
|
-
function
|
|
104
|
-
const cur = target[model] ??
|
|
237
|
+
function addToModel2(target, model, b) {
|
|
238
|
+
const cur = target[model] ?? emptyBuckets2();
|
|
105
239
|
cur.input += b.input;
|
|
106
240
|
cur.output += b.output;
|
|
107
241
|
cur.cacheWrite5m += b.cacheWrite5m;
|
|
@@ -110,7 +244,7 @@ function addToModel(target, model, b) {
|
|
|
110
244
|
target[model] = cur;
|
|
111
245
|
}
|
|
112
246
|
function sumBuckets(usage) {
|
|
113
|
-
const total =
|
|
247
|
+
const total = emptyBuckets2();
|
|
114
248
|
for (const b of Object.values(usage)) {
|
|
115
249
|
total.input += b.input;
|
|
116
250
|
total.output += b.output;
|
|
@@ -151,11 +285,11 @@ async function readAll(path) {
|
|
|
151
285
|
return null;
|
|
152
286
|
}
|
|
153
287
|
}
|
|
154
|
-
async function splitIntoTurnDrafts(transcriptPath, cursor) {
|
|
288
|
+
async function splitIntoTurnDrafts(transcriptPath, cursor, opts = {}) {
|
|
155
289
|
const buffer = await readAll(transcriptPath);
|
|
156
290
|
if (buffer === null) {
|
|
157
291
|
const nc = cursor ?? { offset: 0, lastUuid: null, lastTs: null, seenMessageKeys: [] };
|
|
158
|
-
return { drafts: [], newCursor: nc, messageKeys: [] };
|
|
292
|
+
return { drafts: [], newCursor: nc, messageKeys: [], unreadable: true };
|
|
159
293
|
}
|
|
160
294
|
const fileSize = buffer.length;
|
|
161
295
|
let startOffset;
|
|
@@ -167,7 +301,7 @@ async function splitIntoTurnDrafts(transcriptPath, cursor) {
|
|
|
167
301
|
startOffset = 0;
|
|
168
302
|
rescan = cursor !== null;
|
|
169
303
|
}
|
|
170
|
-
const priorSeen = new Set(cursor?.seenMessageKeys ?? []);
|
|
304
|
+
const priorSeen = anyOf([new Set(cursor?.seenMessageKeys ?? []), opts.excludeMessageKeys]);
|
|
171
305
|
const tsFloor = cursor?.lastTs ?? null;
|
|
172
306
|
const drafts = [];
|
|
173
307
|
const runSeen = /* @__PURE__ */ new Set();
|
|
@@ -179,17 +313,20 @@ async function splitIntoTurnDrafts(transcriptPath, cursor) {
|
|
|
179
313
|
if (cur.pending.size === 0) return;
|
|
180
314
|
const mainPerModel = {};
|
|
181
315
|
const sidechainPerModel = {};
|
|
316
|
+
const draftKeys = [];
|
|
182
317
|
for (const [key, pm] of cur.pending) {
|
|
183
318
|
runSeen.add(key);
|
|
184
319
|
newKeys.push(key);
|
|
185
|
-
|
|
186
|
-
|
|
320
|
+
draftKeys.push(key);
|
|
321
|
+
if (pm.isSidechain) addToModel2(sidechainPerModel, pm.model, pm.bucket);
|
|
322
|
+
else addToModel2(mainPerModel, pm.model, pm.bucket);
|
|
187
323
|
}
|
|
188
324
|
drafts.push({
|
|
189
325
|
prompt: cur.prompt ?? "",
|
|
190
326
|
mainPerModel,
|
|
191
327
|
sidechainPerModel,
|
|
192
328
|
apiCalls: cur.pending.size,
|
|
329
|
+
messageKeys: draftKeys,
|
|
193
330
|
firstTs: cur.firstTs,
|
|
194
331
|
lastTs: cur.lastTs,
|
|
195
332
|
cwd: cur.cwd,
|
|
@@ -277,7 +414,7 @@ async function splitIntoTurnDrafts(transcriptPath, cursor) {
|
|
|
277
414
|
};
|
|
278
415
|
return { drafts, newCursor, messageKeys: newKeys };
|
|
279
416
|
}
|
|
280
|
-
function draftToRecord(draft, ts, table, fx) {
|
|
417
|
+
function draftToRecord(draft, ts, table, fx, surface) {
|
|
281
418
|
const breakdown = computeCost(draft.mainPerModel, draft.sidechainPerModel, table);
|
|
282
419
|
const sidechainHasModels = Object.keys(draft.sidechainPerModel).length > 0;
|
|
283
420
|
const rec = {
|
|
@@ -297,9 +434,11 @@ function draftToRecord(draft, ts, table, fx) {
|
|
|
297
434
|
fxRate: fx.rate,
|
|
298
435
|
fxSource: fx.source,
|
|
299
436
|
prompt: draft.prompt,
|
|
300
|
-
ingest: "sweep"
|
|
437
|
+
ingest: "sweep",
|
|
438
|
+
surface
|
|
301
439
|
};
|
|
302
440
|
if (breakdown.unknownModels.length > 0) rec.unknownModels = breakdown.unknownModels;
|
|
441
|
+
setCountedCalls(rec, callFingerprints(draft.messageKeys));
|
|
303
442
|
return rec;
|
|
304
443
|
}
|
|
305
444
|
function mergeUnknownModels(rec, extra) {
|
|
@@ -308,11 +447,73 @@ function mergeUnknownModels(rec, extra) {
|
|
|
308
447
|
for (const m of extra) if (!merged.includes(m)) merged.push(m);
|
|
309
448
|
rec.unknownModels = merged;
|
|
310
449
|
}
|
|
311
|
-
|
|
450
|
+
function attachSubagentGroups(records, sa, table, fx, surface, ingest) {
|
|
451
|
+
const parentRecords = [...records];
|
|
452
|
+
let addedUSD = 0;
|
|
453
|
+
for (const group of sa.groups) {
|
|
454
|
+
const saBreakdown = computeCost(group.perModel, {}, table);
|
|
455
|
+
addedUSD += saBreakdown.usd;
|
|
456
|
+
const saBlock = {
|
|
457
|
+
costUSD: saBreakdown.usd,
|
|
458
|
+
costByModel: { ...saBreakdown.byModel },
|
|
459
|
+
tokens: sumBuckets(group.perModel),
|
|
460
|
+
apiCalls: group.apiCalls,
|
|
461
|
+
agentFiles: 1
|
|
462
|
+
};
|
|
463
|
+
const groupMs = group.lastTs === null ? NaN : Date.parse(group.lastTs);
|
|
464
|
+
let target;
|
|
465
|
+
if (Number.isFinite(groupMs)) {
|
|
466
|
+
target = parentRecords.find((rec) => {
|
|
467
|
+
const recMs = Date.parse(rec.ts);
|
|
468
|
+
return Number.isFinite(recMs) && recMs >= groupMs;
|
|
469
|
+
});
|
|
470
|
+
} else {
|
|
471
|
+
target = parentRecords[parentRecords.length - 1];
|
|
472
|
+
}
|
|
473
|
+
if (target === void 0) {
|
|
474
|
+
target = {
|
|
475
|
+
schemaVersion: 1,
|
|
476
|
+
ts: group.lastTs ?? sa.lastTs ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
477
|
+
sessionId: sa.sessionId,
|
|
478
|
+
project: sa.cwd ?? "",
|
|
479
|
+
gitBranch: sa.gitBranch,
|
|
480
|
+
models: collectModels(group.perModel, {}),
|
|
481
|
+
tokens: emptyBuckets2(),
|
|
482
|
+
sidechainTokens: null,
|
|
483
|
+
apiCalls: 0,
|
|
484
|
+
costUSD: 0,
|
|
485
|
+
costByModel: {},
|
|
486
|
+
costJPY: 0,
|
|
487
|
+
fxRate: fx.rate,
|
|
488
|
+
fxSource: fx.source,
|
|
489
|
+
prompt: "",
|
|
490
|
+
ingest,
|
|
491
|
+
surface
|
|
492
|
+
};
|
|
493
|
+
records.push(target);
|
|
494
|
+
}
|
|
495
|
+
addCountedCalls(target, callFingerprints(group.messageKeys));
|
|
496
|
+
if (target.subagents === void 0) {
|
|
497
|
+
target.subagents = saBlock;
|
|
498
|
+
} else {
|
|
499
|
+
target.subagents.costUSD += saBlock.costUSD;
|
|
500
|
+
target.subagents.apiCalls += saBlock.apiCalls;
|
|
501
|
+
target.subagents.agentFiles += 1;
|
|
502
|
+
addToBuckets(target.subagents.tokens, saBlock.tokens);
|
|
503
|
+
for (const [model, usd] of Object.entries(saBlock.costByModel)) {
|
|
504
|
+
target.subagents.costByModel[model] = (target.subagents.costByModel[model] ?? 0) + usd;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
mergeUnknownModels(target, saBreakdown.unknownModels);
|
|
508
|
+
}
|
|
509
|
+
return addedUSD;
|
|
510
|
+
}
|
|
511
|
+
async function processTranscriptLocked(mainPath, table, fx, daysCutoff, dryRun, summary, roots, opts = {}) {
|
|
312
512
|
if (opts.strictRead) {
|
|
313
513
|
const file = await fsp.open(mainPath, "r");
|
|
314
514
|
await file.close();
|
|
315
515
|
}
|
|
516
|
+
const surface = surfaceForClaudePath(mainPath, roots);
|
|
316
517
|
const cursor = opts.ignoreCursors ? null : sanitizeCursor(loadCursor(mainPath));
|
|
317
518
|
const { drafts, newCursor, messageKeys } = await splitIntoTurnDrafts(mainPath, cursor);
|
|
318
519
|
const records = [];
|
|
@@ -322,7 +523,7 @@ async function processTranscriptLocked(mainPath, table, fx, daysCutoff, dryRun,
|
|
|
322
523
|
const tsMs = Date.parse(ts);
|
|
323
524
|
if (!Number.isFinite(tsMs) || tsMs < daysCutoff) continue;
|
|
324
525
|
}
|
|
325
|
-
records.push(draftToRecord(draft, ts, table, fx));
|
|
526
|
+
records.push(draftToRecord(draft, ts, table, fx, surface));
|
|
326
527
|
}
|
|
327
528
|
let sa = null;
|
|
328
529
|
try {
|
|
@@ -338,63 +539,8 @@ async function processTranscriptLocked(mainPath, table, fx, daysCutoff, dryRun,
|
|
|
338
539
|
logError("sweep:subagents", err);
|
|
339
540
|
sa = null;
|
|
340
541
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const parentRecords = [...records];
|
|
344
|
-
for (const group of sa.groups) {
|
|
345
|
-
const saBreakdown = computeCost(group.perModel, {}, table);
|
|
346
|
-
summary.subagentsUSD += saBreakdown.usd;
|
|
347
|
-
const saBlock = {
|
|
348
|
-
costUSD: saBreakdown.usd,
|
|
349
|
-
costByModel: { ...saBreakdown.byModel },
|
|
350
|
-
tokens: sumBuckets(group.perModel),
|
|
351
|
-
apiCalls: group.apiCalls,
|
|
352
|
-
agentFiles: 1
|
|
353
|
-
};
|
|
354
|
-
const groupMs = group.lastTs === null ? NaN : Date.parse(group.lastTs);
|
|
355
|
-
let target;
|
|
356
|
-
if (Number.isFinite(groupMs)) {
|
|
357
|
-
target = parentRecords.find((rec) => {
|
|
358
|
-
const recMs = Date.parse(rec.ts);
|
|
359
|
-
return Number.isFinite(recMs) && recMs >= groupMs;
|
|
360
|
-
});
|
|
361
|
-
} else {
|
|
362
|
-
target = parentRecords[parentRecords.length - 1];
|
|
363
|
-
}
|
|
364
|
-
if (target === void 0) {
|
|
365
|
-
target = {
|
|
366
|
-
schemaVersion: 1,
|
|
367
|
-
ts: group.lastTs ?? sa.lastTs ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
368
|
-
sessionId: sa.sessionId,
|
|
369
|
-
project: sa.cwd ?? "",
|
|
370
|
-
gitBranch: sa.gitBranch,
|
|
371
|
-
models: collectModels(group.perModel, {}),
|
|
372
|
-
tokens: emptyBuckets(),
|
|
373
|
-
sidechainTokens: null,
|
|
374
|
-
apiCalls: 0,
|
|
375
|
-
costUSD: 0,
|
|
376
|
-
costByModel: {},
|
|
377
|
-
costJPY: 0,
|
|
378
|
-
fxRate: fx.rate,
|
|
379
|
-
fxSource: fx.source,
|
|
380
|
-
prompt: "",
|
|
381
|
-
ingest: "sweep"
|
|
382
|
-
};
|
|
383
|
-
records.push(target);
|
|
384
|
-
}
|
|
385
|
-
if (target.subagents === void 0) {
|
|
386
|
-
target.subagents = saBlock;
|
|
387
|
-
} else {
|
|
388
|
-
target.subagents.costUSD += saBlock.costUSD;
|
|
389
|
-
target.subagents.apiCalls += saBlock.apiCalls;
|
|
390
|
-
target.subagents.agentFiles += 1;
|
|
391
|
-
addToBuckets(target.subagents.tokens, saBlock.tokens);
|
|
392
|
-
for (const [model, usd] of Object.entries(saBlock.costByModel)) {
|
|
393
|
-
target.subagents.costByModel[model] = (target.subagents.costByModel[model] ?? 0) + usd;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
mergeUnknownModels(target, saBreakdown.unknownModels);
|
|
397
|
-
}
|
|
542
|
+
if (sa !== null && sa.apiCalls > 0) {
|
|
543
|
+
summary.subagentsUSD += attachSubagentGroups(records, sa, table, fx, surface, "sweep");
|
|
398
544
|
summary.agentFiles += sa.agentFiles;
|
|
399
545
|
}
|
|
400
546
|
for (const rec of records) {
|
|
@@ -436,9 +582,47 @@ function codexDraftToRecord(draft, ts, table, fx) {
|
|
|
436
582
|
fxSource: fx.source,
|
|
437
583
|
prompt: draft.agg.prompt ?? "",
|
|
438
584
|
ingest: "sweep",
|
|
439
|
-
source: "codex"
|
|
585
|
+
source: "codex",
|
|
586
|
+
surface: normalizeCodexOriginator(draft.agg.originator)
|
|
440
587
|
};
|
|
588
|
+
if (typeof draft.agg.originator === "string") rec.originator = draft.agg.originator;
|
|
441
589
|
if (breakdown.unknownModels.length > 0) rec.unknownModels = breakdown.unknownModels;
|
|
590
|
+
setCountedCalls(rec, draft.agg.codexEventKeys ?? []);
|
|
591
|
+
return rec;
|
|
592
|
+
}
|
|
593
|
+
function codexChildDraftToRecord(draft, ts, table, fx, ingest) {
|
|
594
|
+
const main = draft.agg.main;
|
|
595
|
+
const breakdown = computeCost(main, {}, table);
|
|
596
|
+
const rec = {
|
|
597
|
+
schemaVersion: 1,
|
|
598
|
+
ts,
|
|
599
|
+
sessionId: draft.agg.sessionId,
|
|
600
|
+
project: draft.agg.cwd ?? "",
|
|
601
|
+
gitBranch: null,
|
|
602
|
+
models: collectModels(main, {}),
|
|
603
|
+
tokens: emptyBuckets2(),
|
|
604
|
+
sidechainTokens: null,
|
|
605
|
+
apiCalls: 0,
|
|
606
|
+
costUSD: 0,
|
|
607
|
+
costByModel: {},
|
|
608
|
+
costJPY: 0,
|
|
609
|
+
fxRate: fx.rate,
|
|
610
|
+
fxSource: fx.source,
|
|
611
|
+
prompt: "",
|
|
612
|
+
ingest,
|
|
613
|
+
source: "codex",
|
|
614
|
+
surface: normalizeCodexOriginator(draft.agg.originator),
|
|
615
|
+
subagents: {
|
|
616
|
+
costUSD: breakdown.usd,
|
|
617
|
+
costByModel: { ...breakdown.byModel },
|
|
618
|
+
tokens: sumBuckets(main),
|
|
619
|
+
apiCalls: draft.agg.apiCalls,
|
|
620
|
+
agentFiles: 1
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
if (typeof draft.agg.originator === "string") rec.originator = draft.agg.originator;
|
|
624
|
+
if (breakdown.unknownModels.length > 0) rec.unknownModels = breakdown.unknownModels;
|
|
625
|
+
setCountedCalls(rec, draft.agg.codexEventKeys ?? []);
|
|
442
626
|
return rec;
|
|
443
627
|
}
|
|
444
628
|
async function processCodexRolloutLocked(rolloutPath, table, fx, daysCutoff, dryRun, summary, opts = {}) {
|
|
@@ -449,21 +633,27 @@ async function processCodexRolloutLocked(rolloutPath, table, fx, daysCutoff, dry
|
|
|
449
633
|
const cursor = opts.ignoreCursors ? null : sanitizeCursor(loadCursor(rolloutPath));
|
|
450
634
|
const drafts = await splitIntoCodexTurnDrafts(rolloutPath, cursor);
|
|
451
635
|
if (drafts === null || drafts.length === 0) return;
|
|
452
|
-
|
|
636
|
+
const isChild = drafts[0].isSubagentRollout;
|
|
637
|
+
const isForkedChild = isChild && drafts[0].isForkedRollout;
|
|
453
638
|
const records = [];
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
639
|
+
if (!isForkedChild) {
|
|
640
|
+
for (const draft of drafts) {
|
|
641
|
+
const ts = draft.endTs ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
642
|
+
if (daysCutoff !== null) {
|
|
643
|
+
const tsMs = Date.parse(ts);
|
|
644
|
+
if (!Number.isFinite(tsMs) || tsMs < daysCutoff) continue;
|
|
645
|
+
}
|
|
646
|
+
records.push(
|
|
647
|
+
isChild ? codexChildDraftToRecord(draft, ts, table, fx, "sweep") : codexDraftToRecord(draft, ts, table, fx)
|
|
648
|
+
);
|
|
459
649
|
}
|
|
460
|
-
records.push(codexDraftToRecord(draft, ts, table, fx));
|
|
461
650
|
}
|
|
462
651
|
for (const rec of records) {
|
|
463
652
|
summary.newRecords += 1;
|
|
464
653
|
summary.totalUSD += rec.costUSD;
|
|
465
654
|
summary.codexRecords += 1;
|
|
466
655
|
summary.codexUSD += rec.costUSD;
|
|
656
|
+
summary.subagentsUSD += rec.subagents?.costUSD ?? 0;
|
|
467
657
|
if (rec.costByModel) {
|
|
468
658
|
for (const [m, c] of Object.entries(rec.costByModel)) {
|
|
469
659
|
summary.byModel[m] = (summary.byModel[m] ?? 0) + c;
|
|
@@ -490,7 +680,7 @@ async function discoverCodexSweepSource() {
|
|
|
490
680
|
if (!homeStat.isDirectory()) {
|
|
491
681
|
return { discovery: { rollouts: [], unreadableDirs: 1 } };
|
|
492
682
|
}
|
|
493
|
-
const sessionsRoot =
|
|
683
|
+
const sessionsRoot = join2(home, "sessions");
|
|
494
684
|
try {
|
|
495
685
|
await fsp.lstat(sessionsRoot);
|
|
496
686
|
} catch (err) {
|
|
@@ -499,10 +689,6 @@ async function discoverCodexSweepSource() {
|
|
|
499
689
|
}
|
|
500
690
|
return { discovery: await listCodexRollouts(sessionsRoot) };
|
|
501
691
|
}
|
|
502
|
-
function projectsRoot(override) {
|
|
503
|
-
if (override) return override;
|
|
504
|
-
return process.env.CCCN_CLAUDE_PROJECTS || join(homedir(), ".claude", "projects");
|
|
505
|
-
}
|
|
506
692
|
function parseSweepFlags(argv) {
|
|
507
693
|
const flags = {
|
|
508
694
|
dryRun: false,
|
|
@@ -532,7 +718,7 @@ function parseSweepFlags(argv) {
|
|
|
532
718
|
async function listProjectDirs(root) {
|
|
533
719
|
try {
|
|
534
720
|
const entries = await fsp.readdir(root, { withFileTypes: true });
|
|
535
|
-
return entries.filter((e) => e.isDirectory()).map((e) =>
|
|
721
|
+
return entries.filter((e) => e.isDirectory()).map((e) => join2(root, e.name));
|
|
536
722
|
} catch {
|
|
537
723
|
return null;
|
|
538
724
|
}
|
|
@@ -540,7 +726,7 @@ async function listProjectDirs(root) {
|
|
|
540
726
|
async function listTranscriptsStrict(projectDir) {
|
|
541
727
|
try {
|
|
542
728
|
const entries = await fsp.readdir(projectDir, { withFileTypes: true });
|
|
543
|
-
return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) =>
|
|
729
|
+
return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => join2(projectDir, e.name));
|
|
544
730
|
} catch {
|
|
545
731
|
return null;
|
|
546
732
|
}
|
|
@@ -587,7 +773,7 @@ function reportSourceFailure(summary, dryRun, scope, err, count = 1) {
|
|
|
587
773
|
}
|
|
588
774
|
logError(scope, err);
|
|
589
775
|
}
|
|
590
|
-
async function scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, dryRun, summary, progress) {
|
|
776
|
+
async function scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, dryRun, summary, progress, claudeRoots) {
|
|
591
777
|
const codexRollouts = codexSource?.discovery.rollouts.length ?? 0;
|
|
592
778
|
progress({
|
|
593
779
|
type: "scan-start",
|
|
@@ -611,7 +797,7 @@ async function scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, d
|
|
|
611
797
|
for (const mainPath of transcriptPaths) {
|
|
612
798
|
summary.transcripts += 1;
|
|
613
799
|
try {
|
|
614
|
-
await processTranscriptLocked(mainPath, table, fx, daysCutoff, dryRun, summary, {
|
|
800
|
+
await processTranscriptLocked(mainPath, table, fx, daysCutoff, dryRun, summary, claudeRoots, {
|
|
615
801
|
ignoreCursors: true,
|
|
616
802
|
strictRead: true
|
|
617
803
|
});
|
|
@@ -672,16 +858,29 @@ async function runSweep(argv, deps = {}) {
|
|
|
672
858
|
}
|
|
673
859
|
const flags = parsed.flags;
|
|
674
860
|
const progress = createSweepProgressReporter();
|
|
675
|
-
const
|
|
676
|
-
const
|
|
861
|
+
const claudeRoots = await claudeTranscriptRoots({ projectsOverride: flags.projects });
|
|
862
|
+
const cliRoot = claudeRoots.find((r) => r.surface === "cli");
|
|
863
|
+
const cliProjectDirs = await listProjectDirs(cliRoot.path);
|
|
677
864
|
const codexSource = await discoverCodexSweepSource();
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
865
|
+
const desktopProjectDirs = [];
|
|
866
|
+
for (const root of claudeRoots) {
|
|
867
|
+
if (root.surface !== "desktop") continue;
|
|
868
|
+
const dirs = await listProjectDirs(root.path);
|
|
869
|
+
if (dirs !== null) desktopProjectDirs.push(...dirs);
|
|
870
|
+
}
|
|
871
|
+
if (cliProjectDirs === null) {
|
|
872
|
+
const codexUsable = codexSource !== null && codexSource.discovery.unreadableDirs === 0;
|
|
873
|
+
if (!codexUsable && desktopProjectDirs.length === 0) {
|
|
874
|
+
console.log(`\u8D70\u67FB\u30EB\u30FC\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${cliRoot.path}`);
|
|
681
875
|
return 1;
|
|
682
876
|
}
|
|
683
|
-
|
|
877
|
+
const remaining = [
|
|
878
|
+
desktopProjectDirs.length > 0 ? "\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7" : null,
|
|
879
|
+
codexUsable ? "Codex" : null
|
|
880
|
+
].filter((v) => v !== null);
|
|
881
|
+
console.log(`Claude CLI \u306E\u8D70\u67FB\u30EB\u30FC\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${cliRoot.path}(${remaining.join(" / ")} \u306E\u307F\u8D70\u67FB\u3057\u307E\u3059)`);
|
|
684
882
|
}
|
|
883
|
+
const projectDirs = cliProjectDirs === null && desktopProjectDirs.length === 0 ? null : [...cliProjectDirs ?? [], ...desktopProjectDirs];
|
|
685
884
|
progress({ type: "preparing", dryRun: flags.dryRun });
|
|
686
885
|
const cfg = flags.dryRun ? readConfigReadOnly((err) => {
|
|
687
886
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -708,7 +907,7 @@ async function runSweep(argv, deps = {}) {
|
|
|
708
907
|
const lockProvider = deps.lockProvider ?? (() => waitForDataLock());
|
|
709
908
|
if (flags.dryRun) {
|
|
710
909
|
try {
|
|
711
|
-
await scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, true, summary, progress);
|
|
910
|
+
await scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, true, summary, progress, claudeRoots);
|
|
712
911
|
} catch (err) {
|
|
713
912
|
reportSourceFailure(summary, true, "sweep:dry-run", err);
|
|
714
913
|
}
|
|
@@ -732,7 +931,7 @@ async function runSweep(argv, deps = {}) {
|
|
|
732
931
|
try {
|
|
733
932
|
invalidateCanonicalDashboards();
|
|
734
933
|
resetHistoryAndCursors();
|
|
735
|
-
await scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, false, summary, progress);
|
|
934
|
+
await scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, false, summary, progress, claudeRoots);
|
|
736
935
|
try {
|
|
737
936
|
invalidateCanonicalDashboards();
|
|
738
937
|
if (summary.sourceFailures === 0 && cfg.dashboard.autoRegenerate) {
|
|
@@ -789,7 +988,12 @@ async function runSweep(argv, deps = {}) {
|
|
|
789
988
|
console.log("\u5C65\u6B74\u3068\u53D6\u308A\u8FBC\u307F\u4F4D\u7F6E\u3092\u5143JSONL\u304B\u3089\u5168\u518D\u751F\u6210\u3057\u307E\u3057\u305F");
|
|
790
989
|
return 0;
|
|
791
990
|
}
|
|
991
|
+
|
|
792
992
|
export {
|
|
793
|
-
|
|
794
|
-
|
|
993
|
+
normalizeCodexOriginator,
|
|
994
|
+
collectSubagentUsage,
|
|
995
|
+
splitIntoTurnDrafts,
|
|
996
|
+
attachSubagentGroups,
|
|
997
|
+
codexChildDraftToRecord,
|
|
998
|
+
runSweep
|
|
795
999
|
};
|