ccc-notifier 0.6.2 → 0.7.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-O4VAP6RB.js} +2 -2
- package/dist/chunk-3M5UQK76.js +92 -0
- package/dist/{sweep-65D57PXU.js → chunk-6BXD6HU5.js} +275 -113
- package/dist/chunk-6IVSZ3S4.js +22 -0
- package/dist/{chunk-NV5UOHJA.js → chunk-JLDI4D5E.js} +5 -5
- package/dist/{chunk-T6Z2ZAN4.js → chunk-ML5H43NH.js} +1 -1
- package/dist/{chunk-6UZXXO5J.js → chunk-O2E4VTDR.js} +36 -9
- package/dist/{chunk-D4D76RGQ.js → chunk-OCKZZVOG.js} +353 -20
- package/dist/{chunk-27SJELD2.js → chunk-PXMXEFM7.js} +22 -107
- package/dist/{chunk-6HTETN26.js → chunk-Q45AAPJY.js} +18 -1
- package/dist/chunk-V4IS4FXH.js +454 -0
- package/dist/{chunk-OOAC5ULQ.js → chunk-WONMZ466.js} +184 -4
- package/dist/{chunk-OXXDZZPJ.js → chunk-X7U63BZG.js} +4 -6
- package/dist/{chunk-J5QAYTFE.js → chunk-XG73C6CC.js} +10 -1
- package/dist/cli.js +286 -29
- package/dist/{dashboard-VEKQ4TI3.js → dashboard-64XHDUVT.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-5CCM6MQL.js +41 -0
- package/dist/scan-UB6ORRZK.js +87 -0
- package/dist/{setup-I3JNGWZG.js → setup-VLMW433R.js} +5 -6
- package/dist/{subagent-store-US4TJJQR.js → subagent-store-USG3WJEB.js} +1 -1
- package/dist/sweep-DVOV4XIZ.js +20 -0
- package/dist/track-P6A5WDJZ.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-WCP7446C.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-OCKZZVOG.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-O2E4VTDR.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-XG73C6CC.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,12 @@ 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 ?? []);
|
|
442
591
|
return rec;
|
|
443
592
|
}
|
|
444
593
|
async function processCodexRolloutLocked(rolloutPath, table, fx, daysCutoff, dryRun, summary, opts = {}) {
|
|
@@ -490,7 +639,7 @@ async function discoverCodexSweepSource() {
|
|
|
490
639
|
if (!homeStat.isDirectory()) {
|
|
491
640
|
return { discovery: { rollouts: [], unreadableDirs: 1 } };
|
|
492
641
|
}
|
|
493
|
-
const sessionsRoot =
|
|
642
|
+
const sessionsRoot = join2(home, "sessions");
|
|
494
643
|
try {
|
|
495
644
|
await fsp.lstat(sessionsRoot);
|
|
496
645
|
} catch (err) {
|
|
@@ -499,10 +648,6 @@ async function discoverCodexSweepSource() {
|
|
|
499
648
|
}
|
|
500
649
|
return { discovery: await listCodexRollouts(sessionsRoot) };
|
|
501
650
|
}
|
|
502
|
-
function projectsRoot(override) {
|
|
503
|
-
if (override) return override;
|
|
504
|
-
return process.env.CCCN_CLAUDE_PROJECTS || join(homedir(), ".claude", "projects");
|
|
505
|
-
}
|
|
506
651
|
function parseSweepFlags(argv) {
|
|
507
652
|
const flags = {
|
|
508
653
|
dryRun: false,
|
|
@@ -532,7 +677,7 @@ function parseSweepFlags(argv) {
|
|
|
532
677
|
async function listProjectDirs(root) {
|
|
533
678
|
try {
|
|
534
679
|
const entries = await fsp.readdir(root, { withFileTypes: true });
|
|
535
|
-
return entries.filter((e) => e.isDirectory()).map((e) =>
|
|
680
|
+
return entries.filter((e) => e.isDirectory()).map((e) => join2(root, e.name));
|
|
536
681
|
} catch {
|
|
537
682
|
return null;
|
|
538
683
|
}
|
|
@@ -540,7 +685,7 @@ async function listProjectDirs(root) {
|
|
|
540
685
|
async function listTranscriptsStrict(projectDir) {
|
|
541
686
|
try {
|
|
542
687
|
const entries = await fsp.readdir(projectDir, { withFileTypes: true });
|
|
543
|
-
return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) =>
|
|
688
|
+
return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => join2(projectDir, e.name));
|
|
544
689
|
} catch {
|
|
545
690
|
return null;
|
|
546
691
|
}
|
|
@@ -587,7 +732,7 @@ function reportSourceFailure(summary, dryRun, scope, err, count = 1) {
|
|
|
587
732
|
}
|
|
588
733
|
logError(scope, err);
|
|
589
734
|
}
|
|
590
|
-
async function scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, dryRun, summary, progress) {
|
|
735
|
+
async function scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, dryRun, summary, progress, claudeRoots) {
|
|
591
736
|
const codexRollouts = codexSource?.discovery.rollouts.length ?? 0;
|
|
592
737
|
progress({
|
|
593
738
|
type: "scan-start",
|
|
@@ -611,7 +756,7 @@ async function scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, d
|
|
|
611
756
|
for (const mainPath of transcriptPaths) {
|
|
612
757
|
summary.transcripts += 1;
|
|
613
758
|
try {
|
|
614
|
-
await processTranscriptLocked(mainPath, table, fx, daysCutoff, dryRun, summary, {
|
|
759
|
+
await processTranscriptLocked(mainPath, table, fx, daysCutoff, dryRun, summary, claudeRoots, {
|
|
615
760
|
ignoreCursors: true,
|
|
616
761
|
strictRead: true
|
|
617
762
|
});
|
|
@@ -672,16 +817,29 @@ async function runSweep(argv, deps = {}) {
|
|
|
672
817
|
}
|
|
673
818
|
const flags = parsed.flags;
|
|
674
819
|
const progress = createSweepProgressReporter();
|
|
675
|
-
const
|
|
676
|
-
const
|
|
820
|
+
const claudeRoots = await claudeTranscriptRoots({ projectsOverride: flags.projects });
|
|
821
|
+
const cliRoot = claudeRoots.find((r) => r.surface === "cli");
|
|
822
|
+
const cliProjectDirs = await listProjectDirs(cliRoot.path);
|
|
677
823
|
const codexSource = await discoverCodexSweepSource();
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
824
|
+
const desktopProjectDirs = [];
|
|
825
|
+
for (const root of claudeRoots) {
|
|
826
|
+
if (root.surface !== "desktop") continue;
|
|
827
|
+
const dirs = await listProjectDirs(root.path);
|
|
828
|
+
if (dirs !== null) desktopProjectDirs.push(...dirs);
|
|
829
|
+
}
|
|
830
|
+
if (cliProjectDirs === null) {
|
|
831
|
+
const codexUsable = codexSource !== null && codexSource.discovery.unreadableDirs === 0;
|
|
832
|
+
if (!codexUsable && desktopProjectDirs.length === 0) {
|
|
833
|
+
console.log(`\u8D70\u67FB\u30EB\u30FC\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${cliRoot.path}`);
|
|
681
834
|
return 1;
|
|
682
835
|
}
|
|
683
|
-
|
|
836
|
+
const remaining = [
|
|
837
|
+
desktopProjectDirs.length > 0 ? "\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7" : null,
|
|
838
|
+
codexUsable ? "Codex" : null
|
|
839
|
+
].filter((v) => v !== null);
|
|
840
|
+
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
841
|
}
|
|
842
|
+
const projectDirs = cliProjectDirs === null && desktopProjectDirs.length === 0 ? null : [...cliProjectDirs ?? [], ...desktopProjectDirs];
|
|
685
843
|
progress({ type: "preparing", dryRun: flags.dryRun });
|
|
686
844
|
const cfg = flags.dryRun ? readConfigReadOnly((err) => {
|
|
687
845
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -708,7 +866,7 @@ async function runSweep(argv, deps = {}) {
|
|
|
708
866
|
const lockProvider = deps.lockProvider ?? (() => waitForDataLock());
|
|
709
867
|
if (flags.dryRun) {
|
|
710
868
|
try {
|
|
711
|
-
await scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, true, summary, progress);
|
|
869
|
+
await scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, true, summary, progress, claudeRoots);
|
|
712
870
|
} catch (err) {
|
|
713
871
|
reportSourceFailure(summary, true, "sweep:dry-run", err);
|
|
714
872
|
}
|
|
@@ -732,7 +890,7 @@ async function runSweep(argv, deps = {}) {
|
|
|
732
890
|
try {
|
|
733
891
|
invalidateCanonicalDashboards();
|
|
734
892
|
resetHistoryAndCursors();
|
|
735
|
-
await scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, false, summary, progress);
|
|
893
|
+
await scanAllSources(projectDirs, codexSource, table, fx, daysCutoff, false, summary, progress, claudeRoots);
|
|
736
894
|
try {
|
|
737
895
|
invalidateCanonicalDashboards();
|
|
738
896
|
if (summary.sourceFailures === 0 && cfg.dashboard.autoRegenerate) {
|
|
@@ -789,7 +947,11 @@ async function runSweep(argv, deps = {}) {
|
|
|
789
947
|
console.log("\u5C65\u6B74\u3068\u53D6\u308A\u8FBC\u307F\u4F4D\u7F6E\u3092\u5143JSONL\u304B\u3089\u5168\u518D\u751F\u6210\u3057\u307E\u3057\u305F");
|
|
790
948
|
return 0;
|
|
791
949
|
}
|
|
950
|
+
|
|
792
951
|
export {
|
|
793
|
-
|
|
794
|
-
|
|
952
|
+
normalizeCodexOriginator,
|
|
953
|
+
collectSubagentUsage,
|
|
954
|
+
splitIntoTurnDrafts,
|
|
955
|
+
attachSubagentGroups,
|
|
956
|
+
runSweep
|
|
795
957
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/surface.ts
|
|
4
|
+
function effectiveSurface(rec) {
|
|
5
|
+
return rec.surface ?? "cli";
|
|
6
|
+
}
|
|
7
|
+
var SURFACE_LABELS = {
|
|
8
|
+
cli: "CLI",
|
|
9
|
+
desktop: "\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7\u30A2\u30D7\u30EA",
|
|
10
|
+
vscode: "VS Code\u62E1\u5F35",
|
|
11
|
+
"claude-code": "Claude Code(Codex rollout)",
|
|
12
|
+
"chrome-extension": "Chrome\u62E1\u5F35",
|
|
13
|
+
other: "\u305D\u306E\u4ED6"
|
|
14
|
+
};
|
|
15
|
+
function surfaceLabel(surface) {
|
|
16
|
+
return SURFACE_LABELS[surface] ?? surface;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
effectiveSurface,
|
|
21
|
+
surfaceLabel
|
|
22
|
+
};
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-DGXUSPS4.js";
|
|
5
5
|
import {
|
|
6
6
|
formatSummary
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XG73C6CC.js";
|
|
8
8
|
|
|
9
9
|
// src/notify/os.ts
|
|
10
10
|
import { spawn } from "child_process";
|
|
@@ -181,10 +181,10 @@ function spawnNotifyChild(title, body) {
|
|
|
181
181
|
}
|
|
182
182
|
return { child: spawnLinuxNotify(title, body), swallowError: backend.swallowError };
|
|
183
183
|
}
|
|
184
|
-
async function notifyOS(record, cfg, todayUSD) {
|
|
184
|
+
async function notifyOS(record, cfg, todayUSD, summaryOverride) {
|
|
185
185
|
try {
|
|
186
186
|
if (!cfg?.notify?.os) return;
|
|
187
|
-
const { title, body } = formatSummary(record, cfg, todayUSD);
|
|
187
|
+
const { title, body } = summaryOverride ?? formatSummary(record, cfg, todayUSD);
|
|
188
188
|
if (process.env.CCCN_DRY_RUN === "1") {
|
|
189
189
|
writeDryRun("os", { title, body });
|
|
190
190
|
return;
|
|
@@ -221,12 +221,12 @@ async function notifyOS(record, cfg, todayUSD) {
|
|
|
221
221
|
|
|
222
222
|
// src/notify/slack.ts
|
|
223
223
|
var SEND_TIMEOUT_MS = 3e3;
|
|
224
|
-
async function notifySlack(record, cfg, todayUSD) {
|
|
224
|
+
async function notifySlack(record, cfg, todayUSD, summaryOverride) {
|
|
225
225
|
try {
|
|
226
226
|
const slackCfg = cfg?.notify?.slack;
|
|
227
227
|
const webhookUrl = slackCfg?.webhookUrl;
|
|
228
228
|
if (!webhookUrl) return;
|
|
229
|
-
const { title, body } = formatSummary(record, cfg, todayUSD);
|
|
229
|
+
const { title, body } = summaryOverride ?? formatSummary(record, cfg, todayUSD);
|
|
230
230
|
const line1 = body.split("\n")[0] ?? "";
|
|
231
231
|
const rawPrompt = record.prompt ?? "";
|
|
232
232
|
let promptText;
|