ccc-notifier 0.3.0 → 0.5.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 +11 -7
- package/dist/{budget-KKTFYAXH.js → budget-TBWVHUKA.js} +1 -1
- package/dist/chunk-5PH7PPD6.js +1077 -0
- package/dist/{chunk-4RZ6OGTD.js → chunk-DDUK4EWQ.js} +1 -1
- package/dist/chunk-ENGUOLTD.js +92 -0
- package/dist/chunk-HLJAJS2W.js +55 -0
- package/dist/{chunk-LHKBGA5K.js → chunk-LVMKY6JB.js} +304 -29
- package/dist/chunk-O2AR4PXU.js +82 -0
- package/dist/chunk-O34L3NSI.js +172 -0
- package/dist/{chunk-QX5KIRSU.js → chunk-QWBQN4XY.js} +183 -54
- package/dist/{chunk-7KVOQ4UZ.js → chunk-S3XKO7MY.js} +235 -108
- package/dist/cli.js +382 -73
- package/dist/{dashboard-TH3NOVT3.js → dashboard-6F2QNUJT.js} +4 -2
- package/dist/history-7KN26LJH.js +162 -0
- package/dist/{mute-EPLYH67P.js → mute-MPX3K7QN.js} +2 -2
- package/dist/{setup-PTO5N46B.js → setup-IXPMVWOE.js} +2 -2
- package/dist/subagent-store-A6MIN22X.js +31 -0
- package/dist/{sweep-25EZAOGC.js → sweep-UIONM4UA.js} +143 -126
- package/dist/track-KEZON6KI.js +245 -0
- package/package.json +1 -1
- package/dist/chunk-DSV75EF7.js +0 -357
- package/dist/chunk-ECADO26T.js +0 -316
- package/dist/history-DLFYQJFM.js +0 -114
- package/dist/track-PXFSGMGL.js +0 -184
|
@@ -3,10 +3,12 @@ import {
|
|
|
3
3
|
browserOpenPlan,
|
|
4
4
|
runDashboard,
|
|
5
5
|
writeDashboardHtml
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QWBQN4XY.js";
|
|
7
7
|
import "./chunk-DGXUSPS4.js";
|
|
8
8
|
import "./chunk-J5QAYTFE.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-ENGUOLTD.js";
|
|
10
|
+
import "./chunk-O34L3NSI.js";
|
|
11
|
+
import "./chunk-5PH7PPD6.js";
|
|
10
12
|
export {
|
|
11
13
|
browserOpenPlan,
|
|
12
14
|
runDashboard,
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
invalidateCanonicalDashboards
|
|
4
|
+
} from "./chunk-ENGUOLTD.js";
|
|
5
|
+
import {
|
|
6
|
+
waitForDataLock
|
|
7
|
+
} from "./chunk-O34L3NSI.js";
|
|
8
|
+
import {
|
|
9
|
+
paths
|
|
10
|
+
} from "./chunk-5PH7PPD6.js";
|
|
11
|
+
|
|
12
|
+
// src/history.ts
|
|
13
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, rmSync } from "fs";
|
|
14
|
+
import { createHash } from "crypto";
|
|
15
|
+
import * as p from "@clack/prompts";
|
|
16
|
+
function parseFlags(argv) {
|
|
17
|
+
let days = null;
|
|
18
|
+
let yes = false;
|
|
19
|
+
for (let i = 0; i < argv.length; i++) {
|
|
20
|
+
const a = argv[i];
|
|
21
|
+
if (a === "--yes" || a === "-y") {
|
|
22
|
+
yes = true;
|
|
23
|
+
} else if (a === "--days") {
|
|
24
|
+
const n = Number.parseInt(argv[i + 1] ?? "", 10);
|
|
25
|
+
if (Number.isFinite(n) && n > 0) days = n;
|
|
26
|
+
i++;
|
|
27
|
+
} else if (a.startsWith("--days=")) {
|
|
28
|
+
const n = Number.parseInt(a.slice("--days=".length), 10);
|
|
29
|
+
if (Number.isFinite(n) && n > 0) days = n;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { days, yes };
|
|
33
|
+
}
|
|
34
|
+
function readLines(file) {
|
|
35
|
+
const raw = readFileSync(file, "utf8");
|
|
36
|
+
const lines = [];
|
|
37
|
+
for (const line of raw.split("\n")) {
|
|
38
|
+
if (!line.trim()) continue;
|
|
39
|
+
let rec = null;
|
|
40
|
+
try {
|
|
41
|
+
rec = JSON.parse(line);
|
|
42
|
+
} catch {
|
|
43
|
+
rec = null;
|
|
44
|
+
}
|
|
45
|
+
lines.push({ raw: line, rec });
|
|
46
|
+
}
|
|
47
|
+
return lines;
|
|
48
|
+
}
|
|
49
|
+
function isTargeted(rec, cutoffMs) {
|
|
50
|
+
if (cutoffMs === null) return true;
|
|
51
|
+
const ts = Date.parse(rec.ts);
|
|
52
|
+
if (!Number.isFinite(ts)) return false;
|
|
53
|
+
return ts < cutoffMs;
|
|
54
|
+
}
|
|
55
|
+
function atomicWrite(file, content) {
|
|
56
|
+
const tmp = `${file}.tmp`;
|
|
57
|
+
writeFileSync(tmp, content, "utf8");
|
|
58
|
+
renameSync(tmp, file);
|
|
59
|
+
}
|
|
60
|
+
function collectTargets(lines, sub, cutoff) {
|
|
61
|
+
const targetSet = /* @__PURE__ */ new Set();
|
|
62
|
+
for (let i = 0; i < lines.length; i++) {
|
|
63
|
+
const rec = lines[i].rec;
|
|
64
|
+
if (!rec || !isTargeted(rec, cutoff)) continue;
|
|
65
|
+
if (sub === "redact" && !(typeof rec.prompt === "string" && rec.prompt.length > 0)) continue;
|
|
66
|
+
targetSet.add(i);
|
|
67
|
+
}
|
|
68
|
+
return targetSet;
|
|
69
|
+
}
|
|
70
|
+
function targetFingerprint(lines, targets) {
|
|
71
|
+
const hash = createHash("sha256");
|
|
72
|
+
hash.update(`count:${targets.size}
|
|
73
|
+
`);
|
|
74
|
+
for (const i of [...targets].sort((a, b) => a - b)) {
|
|
75
|
+
hash.update(`${i}:${lines[i]?.raw ?? ""}
|
|
76
|
+
`);
|
|
77
|
+
}
|
|
78
|
+
return hash.digest("hex");
|
|
79
|
+
}
|
|
80
|
+
async function runHistory(argv, deps = {}) {
|
|
81
|
+
const [sub, ...rest] = argv;
|
|
82
|
+
if (sub !== "clear" && sub !== "redact") {
|
|
83
|
+
console.error(
|
|
84
|
+
"\u4F7F\u3044\u65B9 / Usage: ccc-notifier history <clear|redact> [--days N] [--yes]\n clear \u2026 \u30EC\u30B3\u30FC\u30C9\u3054\u3068\u524A\u9664(\u30C1\u30E3\u30FC\u30C8\u30FB\u96C6\u8A08\u304B\u3089\u3082\u6D88\u3048\u308B) / delete records\n redact \u2026 \u30D7\u30ED\u30F3\u30D7\u30C8\u5168\u6587\u3060\u3051\u6D88\u3059(\u30B3\u30B9\u30C8\u30FB\u30C1\u30E3\u30FC\u30C8\u306F\u6B8B\u3059) / strip prompts only"
|
|
85
|
+
);
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
const flags = parseFlags(rest);
|
|
89
|
+
const file = paths().historyFile;
|
|
90
|
+
let lines = existsSync(file) ? readLines(file) : [];
|
|
91
|
+
const cutoff = flags.days !== null ? Date.now() - flags.days * 864e5 : null;
|
|
92
|
+
const scope = flags.days !== null ? `${flags.days}\u65E5\u3088\u308A\u524D` : "\u5168\u671F\u9593";
|
|
93
|
+
let targetSet = collectTargets(lines, sub, cutoff);
|
|
94
|
+
const initialFingerprint = targetFingerprint(lines, targetSet);
|
|
95
|
+
const action = sub === "clear" ? "\u30EC\u30B3\u30FC\u30C9\u3054\u3068\u524A\u9664" : "\u30D7\u30ED\u30F3\u30D7\u30C8\u5168\u6587\u3092\u6D88\u53BB";
|
|
96
|
+
if (targetSet.size > 0 && !flags.yes) {
|
|
97
|
+
const confirmed = await (deps.confirm ?? p.confirm)({
|
|
98
|
+
message: `${scope}\u306E\u5C65\u6B74 ${targetSet.size} \u4EF6\u3092${action}\u3057\u307E\u3059\u3002\u5143\u306B\u623B\u305B\u307E\u305B\u3093\u3002\u3088\u308D\u3057\u3044\u3067\u3059\u304B?`,
|
|
99
|
+
initialValue: false
|
|
100
|
+
});
|
|
101
|
+
if (p.isCancel(confirmed) || !confirmed) {
|
|
102
|
+
p.cancel("\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F");
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const lock = await waitForDataLock();
|
|
107
|
+
if (lock === null) {
|
|
108
|
+
console.error("\u5C65\u6B74\u306E\u66F4\u65B0\u30ED\u30C3\u30AF\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u5F8C\u3067\u3082\u3046\u4E00\u5EA6\u304A\u8A66\u3057\u304F\u3060\u3055\u3044 / history lock is busy");
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
lines = existsSync(file) ? readLines(file) : [];
|
|
113
|
+
targetSet = collectTargets(lines, sub, cutoff);
|
|
114
|
+
if (!flags.yes && targetFingerprint(lines, targetSet) !== initialFingerprint) {
|
|
115
|
+
console.error(
|
|
116
|
+
"\u78BA\u8A8D\u4E2D\u306B\u5C65\u6B74\u304C\u5909\u66F4\u3055\u308C\u305F\u305F\u3081\u51E6\u7406\u3057\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u3082\u3046\u4E00\u5EA6\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044 / history changed; retry"
|
|
117
|
+
);
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
invalidateCanonicalDashboards();
|
|
121
|
+
if (!existsSync(file)) {
|
|
122
|
+
console.log("\u5C65\u6B74\u304C\u3042\u308A\u307E\u305B\u3093(history.jsonl \u306F\u672A\u4F5C\u6210\u3067\u3059)\u3002");
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
if (targetSet.size === 0) {
|
|
126
|
+
console.log(`\u5BFE\u8C61\u304C\u3042\u308A\u307E\u305B\u3093(${scope})\u3002`);
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
if (sub === "clear") {
|
|
131
|
+
const kept = lines.filter((_, i) => !targetSet.has(i)).map((l) => l.raw);
|
|
132
|
+
if (kept.length === 0) {
|
|
133
|
+
rmSync(file, { force: true });
|
|
134
|
+
} else {
|
|
135
|
+
atomicWrite(file, kept.join("\n") + "\n");
|
|
136
|
+
}
|
|
137
|
+
console.log(`\u5C65\u6B74 ${targetSet.size} \u4EF6\u3092\u524A\u9664\u3057\u307E\u3057\u305F(${scope})\u3002`);
|
|
138
|
+
} else {
|
|
139
|
+
const out = lines.map((l, i) => {
|
|
140
|
+
if (!targetSet.has(i) || l.rec === null) return l.raw;
|
|
141
|
+
return JSON.stringify({ ...l.rec, prompt: "" });
|
|
142
|
+
});
|
|
143
|
+
atomicWrite(file, out.join("\n") + "\n");
|
|
144
|
+
console.log(
|
|
145
|
+
`\u30D7\u30ED\u30F3\u30D7\u30C8\u5168\u6587\u3092 ${targetSet.size} \u4EF6\u6D88\u53BB\u3057\u307E\u3057\u305F(${scope}\u3002\u30B3\u30B9\u30C8\u96C6\u8A08\u30FB\u30C1\u30E3\u30FC\u30C8\u306F\u4FDD\u6301\u3055\u308C\u307E\u3059)\u3002`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.error(
|
|
150
|
+
`\u5C65\u6B74\u306E\u66F4\u65B0\u306B\u5931\u6557\u3057\u307E\u3057\u305F / failed to update history: ${err instanceof Error ? err.message : String(err)}`
|
|
151
|
+
);
|
|
152
|
+
return 1;
|
|
153
|
+
}
|
|
154
|
+
invalidateCanonicalDashboards();
|
|
155
|
+
return 0;
|
|
156
|
+
} finally {
|
|
157
|
+
lock.release();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export {
|
|
161
|
+
runHistory
|
|
162
|
+
};
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
matchesMarker,
|
|
4
4
|
runInit,
|
|
5
5
|
runUninstall
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-S3XKO7MY.js";
|
|
7
7
|
import "./chunk-NV5UOHJA.js";
|
|
8
8
|
import "./chunk-DGXUSPS4.js";
|
|
9
9
|
import "./chunk-HTYUYKFW.js";
|
|
10
10
|
import "./chunk-J5QAYTFE.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-5PH7PPD6.js";
|
|
12
12
|
export {
|
|
13
13
|
matchesMarker,
|
|
14
14
|
runInit,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
acquireCodexActivityLock,
|
|
4
|
+
closeCodexRootContext,
|
|
5
|
+
codexActivityProjectionKey,
|
|
6
|
+
handleCodexSubagentHook,
|
|
7
|
+
handleCodexUserPromptSubmitHook,
|
|
8
|
+
normalizeAgentType,
|
|
9
|
+
openCodexRootContext,
|
|
10
|
+
projectCodexSubagentActivity,
|
|
11
|
+
readCodexSubagentActivity,
|
|
12
|
+
recordCodexSubagentEvent,
|
|
13
|
+
recordCodexSubagentPayload,
|
|
14
|
+
reduceCodexSubagentActivity,
|
|
15
|
+
validateCodexSubagentPayload
|
|
16
|
+
} from "./chunk-5PH7PPD6.js";
|
|
17
|
+
export {
|
|
18
|
+
acquireCodexActivityLock,
|
|
19
|
+
closeCodexRootContext,
|
|
20
|
+
codexActivityProjectionKey,
|
|
21
|
+
handleCodexSubagentHook,
|
|
22
|
+
handleCodexUserPromptSubmitHook,
|
|
23
|
+
normalizeAgentType,
|
|
24
|
+
openCodexRootContext,
|
|
25
|
+
projectCodexSubagentActivity,
|
|
26
|
+
readCodexSubagentActivity,
|
|
27
|
+
recordCodexSubagentEvent,
|
|
28
|
+
recordCodexSubagentPayload,
|
|
29
|
+
reduceCodexSubagentActivity,
|
|
30
|
+
validateCodexSubagentPayload
|
|
31
|
+
};
|
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
listCodexRollouts
|
|
4
|
+
} from "./chunk-HLJAJS2W.js";
|
|
2
5
|
import {
|
|
3
6
|
codexHome,
|
|
4
7
|
detectCodex
|
|
5
8
|
} from "./chunk-HTYUYKFW.js";
|
|
6
9
|
import {
|
|
7
|
-
collectSubagentUsage
|
|
8
|
-
|
|
9
|
-
} from "./chunk-DSV75EF7.js";
|
|
10
|
+
collectSubagentUsage
|
|
11
|
+
} from "./chunk-O2AR4PXU.js";
|
|
10
12
|
import {
|
|
11
13
|
computeCost,
|
|
12
14
|
extractBucket,
|
|
13
15
|
getUsdJpy,
|
|
14
16
|
loadPriceTable,
|
|
15
|
-
promptCandidate
|
|
16
|
-
|
|
17
|
+
promptCandidate,
|
|
18
|
+
splitIntoCodexTurnDrafts
|
|
19
|
+
} from "./chunk-LVMKY6JB.js";
|
|
17
20
|
import {
|
|
18
21
|
formatJPY,
|
|
19
22
|
formatUSD,
|
|
20
23
|
modelDisplayName
|
|
21
24
|
} from "./chunk-J5QAYTFE.js";
|
|
25
|
+
import {
|
|
26
|
+
waitForDataLock
|
|
27
|
+
} from "./chunk-O34L3NSI.js";
|
|
22
28
|
import {
|
|
23
29
|
appendTurn,
|
|
24
30
|
loadCursor,
|
|
@@ -27,7 +33,7 @@ import {
|
|
|
27
33
|
readConfig,
|
|
28
34
|
sanitizeCursor,
|
|
29
35
|
saveCursor
|
|
30
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-5PH7PPD6.js";
|
|
31
37
|
|
|
32
38
|
// src/sweep.ts
|
|
33
39
|
import { readFile } from "fs/promises";
|
|
@@ -258,99 +264,91 @@ function mergeUnknownModels(rec, extra) {
|
|
|
258
264
|
for (const m of extra) if (!merged.includes(m)) merged.push(m);
|
|
259
265
|
rec.unknownModels = merged;
|
|
260
266
|
}
|
|
261
|
-
async function processTranscript(mainPath, table, fx, daysCutoff, dryRun, summary) {
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (daysCutoff !== null) {
|
|
268
|
-
const tsMs = Date.parse(ts);
|
|
269
|
-
if (!Number.isFinite(tsMs) || tsMs < daysCutoff) continue;
|
|
270
|
-
}
|
|
271
|
-
records.push(draftToRecord(draft, ts, table, fx));
|
|
267
|
+
async function processTranscript(mainPath, table, fx, daysCutoff, dryRun, summary, lockProvider) {
|
|
268
|
+
const lock = dryRun ? null : await lockProvider();
|
|
269
|
+
if (!dryRun && lock === null) {
|
|
270
|
+
logError("sweep:data-lock", new Error(`data lock timeout: ${mainPath}`));
|
|
271
|
+
summary.lockTimeouts += 1;
|
|
272
|
+
return;
|
|
272
273
|
}
|
|
273
|
-
let sa = null;
|
|
274
274
|
try {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
summary.subagentsUSD += saBreakdown.usd;
|
|
284
|
-
const saBlock = {
|
|
285
|
-
costUSD: saBreakdown.usd,
|
|
286
|
-
costByModel: saBreakdown.byModel,
|
|
287
|
-
tokens: sumBuckets(sa.perModel),
|
|
288
|
-
apiCalls: sa.apiCalls,
|
|
289
|
-
agentFiles: sa.agentFiles
|
|
290
|
-
};
|
|
291
|
-
if (records.length > 0) {
|
|
292
|
-
const last = records[records.length - 1];
|
|
293
|
-
last.subagents = saBlock;
|
|
294
|
-
mergeUnknownModels(last, saBreakdown.unknownModels);
|
|
295
|
-
} else {
|
|
296
|
-
const rec = {
|
|
297
|
-
schemaVersion: 1,
|
|
298
|
-
ts: maxCursorTs(sa.newCursors) ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
299
|
-
sessionId: "",
|
|
300
|
-
project: "",
|
|
301
|
-
gitBranch: null,
|
|
302
|
-
models: collectModels(sa.perModel, {}),
|
|
303
|
-
tokens: emptyBuckets(),
|
|
304
|
-
sidechainTokens: null,
|
|
305
|
-
apiCalls: 0,
|
|
306
|
-
costUSD: 0,
|
|
307
|
-
costByModel: {},
|
|
308
|
-
costJPY: 0,
|
|
309
|
-
fxRate: fx.rate,
|
|
310
|
-
fxSource: fx.source,
|
|
311
|
-
prompt: "",
|
|
312
|
-
subagents: saBlock,
|
|
313
|
-
ingest: "sweep"
|
|
314
|
-
};
|
|
315
|
-
mergeUnknownModels(rec, saBreakdown.unknownModels);
|
|
316
|
-
records.push(rec);
|
|
317
|
-
}
|
|
318
|
-
summary.agentFiles += sa.agentFiles;
|
|
319
|
-
}
|
|
320
|
-
for (const rec of records) {
|
|
321
|
-
summary.newRecords += 1;
|
|
322
|
-
summary.totalUSD += rec.costUSD;
|
|
323
|
-
if (rec.costByModel) {
|
|
324
|
-
for (const [m, c] of Object.entries(rec.costByModel)) {
|
|
325
|
-
summary.byModel[m] = (summary.byModel[m] ?? 0) + c;
|
|
275
|
+
const cursor = sanitizeCursor(loadCursor(mainPath));
|
|
276
|
+
const { drafts, newCursor } = await splitIntoTurnDrafts(mainPath, cursor);
|
|
277
|
+
const records = [];
|
|
278
|
+
for (const draft of drafts) {
|
|
279
|
+
const ts = draft.lastTs ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
280
|
+
if (daysCutoff !== null) {
|
|
281
|
+
const tsMs = Date.parse(ts);
|
|
282
|
+
if (!Number.isFinite(tsMs) || tsMs < daysCutoff) continue;
|
|
326
283
|
}
|
|
284
|
+
records.push(draftToRecord(draft, ts, table, fx));
|
|
327
285
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
286
|
+
let sa = null;
|
|
287
|
+
try {
|
|
288
|
+
sa = await collectSubagentUsage(mainPath);
|
|
289
|
+
} catch (err) {
|
|
290
|
+
logError("sweep:subagents", err);
|
|
291
|
+
sa = null;
|
|
292
|
+
}
|
|
293
|
+
const saHasUsage = sa !== null && sa.apiCalls > 0;
|
|
332
294
|
if (saHasUsage) {
|
|
333
|
-
|
|
295
|
+
const saBreakdown = computeCost(sa.perModel, {}, table);
|
|
296
|
+
summary.subagentsUSD += saBreakdown.usd;
|
|
297
|
+
const saBlock = {
|
|
298
|
+
costUSD: saBreakdown.usd,
|
|
299
|
+
costByModel: saBreakdown.byModel,
|
|
300
|
+
tokens: sumBuckets(sa.perModel),
|
|
301
|
+
apiCalls: sa.apiCalls,
|
|
302
|
+
agentFiles: sa.agentFiles
|
|
303
|
+
};
|
|
304
|
+
if (records.length > 0) {
|
|
305
|
+
const last = records[records.length - 1];
|
|
306
|
+
last.subagents = saBlock;
|
|
307
|
+
mergeUnknownModels(last, saBreakdown.unknownModels);
|
|
308
|
+
} else {
|
|
309
|
+
const rec = {
|
|
310
|
+
schemaVersion: 1,
|
|
311
|
+
ts: maxCursorTs(sa.newCursors) ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
312
|
+
sessionId: "",
|
|
313
|
+
project: "",
|
|
314
|
+
gitBranch: null,
|
|
315
|
+
models: collectModels(sa.perModel, {}),
|
|
316
|
+
tokens: emptyBuckets(),
|
|
317
|
+
sidechainTokens: null,
|
|
318
|
+
apiCalls: 0,
|
|
319
|
+
costUSD: 0,
|
|
320
|
+
costByModel: {},
|
|
321
|
+
costJPY: 0,
|
|
322
|
+
fxRate: fx.rate,
|
|
323
|
+
fxSource: fx.source,
|
|
324
|
+
prompt: "",
|
|
325
|
+
subagents: saBlock,
|
|
326
|
+
ingest: "sweep"
|
|
327
|
+
};
|
|
328
|
+
mergeUnknownModels(rec, saBreakdown.unknownModels);
|
|
329
|
+
records.push(rec);
|
|
330
|
+
}
|
|
331
|
+
summary.agentFiles += sa.agentFiles;
|
|
334
332
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (entries === null) return;
|
|
343
|
-
for (const e of entries) {
|
|
344
|
-
const full = join(dir, e.name);
|
|
345
|
-
if (e.isFile()) {
|
|
346
|
-
if (e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")) found.push(full);
|
|
347
|
-
} else if (e.isDirectory() && depth < CODEX_MAX_DEPTH) {
|
|
348
|
-
await walk(full, depth + 1);
|
|
333
|
+
for (const rec of records) {
|
|
334
|
+
summary.newRecords += 1;
|
|
335
|
+
summary.totalUSD += rec.costUSD;
|
|
336
|
+
if (rec.costByModel) {
|
|
337
|
+
for (const [m, c] of Object.entries(rec.costByModel)) {
|
|
338
|
+
summary.byModel[m] = (summary.byModel[m] ?? 0) + c;
|
|
339
|
+
}
|
|
349
340
|
}
|
|
350
341
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
342
|
+
if (!dryRun) {
|
|
343
|
+
for (const rec of records) appendTurn(rec);
|
|
344
|
+
if (drafts.length > 0) saveCursor(mainPath, newCursor);
|
|
345
|
+
if (saHasUsage) {
|
|
346
|
+
for (const nc of sa.newCursors) saveCursor(nc.path, nc.cursor);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} finally {
|
|
350
|
+
lock?.release();
|
|
351
|
+
}
|
|
354
352
|
}
|
|
355
353
|
function codexDraftToRecord(draft, ts, table, fx) {
|
|
356
354
|
const main = draft.agg.main;
|
|
@@ -379,33 +377,43 @@ function codexDraftToRecord(draft, ts, table, fx) {
|
|
|
379
377
|
if (breakdown.unknownModels.length > 0) rec.unknownModels = breakdown.unknownModels;
|
|
380
378
|
return rec;
|
|
381
379
|
}
|
|
382
|
-
async function processCodexRollout(rolloutPath, table, fx, daysCutoff, dryRun, summary) {
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const ts = draft.endTs ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
389
|
-
if (daysCutoff !== null) {
|
|
390
|
-
const tsMs = Date.parse(ts);
|
|
391
|
-
if (!Number.isFinite(tsMs) || tsMs < daysCutoff) continue;
|
|
392
|
-
}
|
|
393
|
-
records.push(codexDraftToRecord(draft, ts, table, fx));
|
|
380
|
+
async function processCodexRollout(rolloutPath, table, fx, daysCutoff, dryRun, summary, lockProvider) {
|
|
381
|
+
const lock = dryRun ? null : await lockProvider();
|
|
382
|
+
if (!dryRun && lock === null) {
|
|
383
|
+
logError("sweep:data-lock", new Error(`data lock timeout: ${rolloutPath}`));
|
|
384
|
+
summary.lockTimeouts += 1;
|
|
385
|
+
return;
|
|
394
386
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
387
|
+
try {
|
|
388
|
+
const cursor = sanitizeCursor(loadCursor(rolloutPath));
|
|
389
|
+
const drafts = await splitIntoCodexTurnDrafts(rolloutPath, cursor);
|
|
390
|
+
if (drafts === null || drafts.length === 0) return;
|
|
391
|
+
const records = [];
|
|
392
|
+
for (const draft of drafts) {
|
|
393
|
+
const ts = draft.endTs ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
394
|
+
if (daysCutoff !== null) {
|
|
395
|
+
const tsMs = Date.parse(ts);
|
|
396
|
+
if (!Number.isFinite(tsMs) || tsMs < daysCutoff) continue;
|
|
403
397
|
}
|
|
398
|
+
records.push(codexDraftToRecord(draft, ts, table, fx));
|
|
404
399
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
400
|
+
for (const rec of records) {
|
|
401
|
+
summary.newRecords += 1;
|
|
402
|
+
summary.totalUSD += rec.costUSD;
|
|
403
|
+
summary.codexRecords += 1;
|
|
404
|
+
summary.codexUSD += rec.costUSD;
|
|
405
|
+
if (rec.costByModel) {
|
|
406
|
+
for (const [m, c] of Object.entries(rec.costByModel)) {
|
|
407
|
+
summary.byModel[m] = (summary.byModel[m] ?? 0) + c;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (!dryRun) {
|
|
412
|
+
for (const rec of records) appendTurn(rec);
|
|
413
|
+
saveCursor(rolloutPath, drafts[drafts.length - 1].agg.newCursor);
|
|
414
|
+
}
|
|
415
|
+
} finally {
|
|
416
|
+
lock?.release();
|
|
409
417
|
}
|
|
410
418
|
}
|
|
411
419
|
async function codexSessionsRoot() {
|
|
@@ -414,17 +422,17 @@ async function codexSessionsRoot() {
|
|
|
414
422
|
const isDir = await fsp.stat(sessionsRoot).then((st) => st.isDirectory()).catch(() => false);
|
|
415
423
|
return isDir ? sessionsRoot : null;
|
|
416
424
|
}
|
|
417
|
-
async function sweepCodex(summary, table, fx, daysCutoff, flags) {
|
|
425
|
+
async function sweepCodex(summary, table, fx, daysCutoff, flags, lockProvider) {
|
|
418
426
|
const sessionsRoot = await codexSessionsRoot();
|
|
419
427
|
if (sessionsRoot === null) return;
|
|
420
|
-
const
|
|
421
|
-
for (const rolloutPath of rollouts) {
|
|
428
|
+
const discovery = await listCodexRollouts(sessionsRoot);
|
|
429
|
+
for (const rolloutPath of discovery.rollouts) {
|
|
422
430
|
if (!flags.includeActive && await isRecentlyModified(rolloutPath)) {
|
|
423
431
|
summary.skippedActive += 1;
|
|
424
432
|
continue;
|
|
425
433
|
}
|
|
426
434
|
try {
|
|
427
|
-
await processCodexRollout(rolloutPath, table, fx, daysCutoff, flags.dryRun, summary);
|
|
435
|
+
await processCodexRollout(rolloutPath, table, fx, daysCutoff, flags.dryRun, summary, lockProvider);
|
|
428
436
|
} catch (err) {
|
|
429
437
|
logError("sweep:codex", err);
|
|
430
438
|
}
|
|
@@ -489,7 +497,12 @@ function printSweepSummary(summary, fx) {
|
|
|
489
497
|
`\u30B9\u30AD\u30C3\u30D7: ${summary.skippedActive} transcript(\u76F4\u8FD1${ACTIVE_GUARD_MIN}\u5206\u4EE5\u5185\u306B\u66F4\u65B0 = \u9032\u884C\u4E2D\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u53EF\u80FD\u6027)\u3002\u30BB\u30C3\u30B7\u30E7\u30F3\u5B8C\u4E86\u5F8C\u306B\u518D\u5B9F\u884C\u3059\u308B\u304B\u3001\u5B8C\u4E86\u6E08\u307F\u3068\u5206\u304B\u3063\u3066\u3044\u308B\u5834\u5408\u306F --include-active \u3067\u53D6\u308A\u8FBC\u3081\u307E\u3059`
|
|
490
498
|
);
|
|
491
499
|
}
|
|
492
|
-
if (summary.
|
|
500
|
+
if (summary.lockTimeouts > 0) {
|
|
501
|
+
console.log(
|
|
502
|
+
`\u672A\u5B8C\u4E86: ${summary.lockTimeouts} \u4EF6\u306Fdata lock\u3092\u53D6\u5F97\u3067\u304D\u305A\u672A\u51E6\u7406\u3067\u3059\u3002\u30AB\u30FC\u30BD\u30EB\u306F\u9032\u3081\u3066\u3044\u306A\u3044\u305F\u3081\u518D\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044`
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
if (summary.newRecords === 0 && summary.lockTimeouts === 0) {
|
|
493
506
|
console.log("\u65B0\u898F\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F");
|
|
494
507
|
} else {
|
|
495
508
|
console.log(
|
|
@@ -513,10 +526,12 @@ function printSweepSummary(summary, fx) {
|
|
|
513
526
|
}
|
|
514
527
|
}
|
|
515
528
|
}
|
|
516
|
-
|
|
529
|
+
if (summary.lockTimeouts === 0) {
|
|
530
|
+
console.log("\u65E2\u306B\u8A08\u4E0A\u6E08\u307F\u306E\u5206\u306F\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u307E\u3057\u305F(\u4E8C\u91CD\u8A08\u4E0A\u306A\u3057)");
|
|
531
|
+
}
|
|
517
532
|
console.log(`\u5186\u63DB\u7B97\u30EC\u30FC\u30C8: 1USD = ${fx.rate}JPY(source=${fx.source})`);
|
|
518
533
|
}
|
|
519
|
-
async function runSweep(argv) {
|
|
534
|
+
async function runSweep(argv, deps = {}) {
|
|
520
535
|
const flags = parseSweepFlags(argv);
|
|
521
536
|
const root = projectsRoot(flags.projects);
|
|
522
537
|
const projectDirs = await listProjectDirs(root);
|
|
@@ -543,9 +558,11 @@ async function runSweep(argv) {
|
|
|
543
558
|
skippedActive: 0,
|
|
544
559
|
codexRecords: 0,
|
|
545
560
|
codexUSD: 0,
|
|
546
|
-
dryRun: flags.dryRun
|
|
561
|
+
dryRun: flags.dryRun,
|
|
562
|
+
lockTimeouts: 0
|
|
547
563
|
};
|
|
548
564
|
const daysCutoff = flags.days !== null ? Date.now() - flags.days * DAY_MS : null;
|
|
565
|
+
const lockProvider = deps.lockProvider ?? (() => waitForDataLock());
|
|
549
566
|
for (const projectDir of projectDirs ?? []) {
|
|
550
567
|
const transcripts = await listTranscripts(projectDir);
|
|
551
568
|
for (const mainPath of transcripts) {
|
|
@@ -555,16 +572,16 @@ async function runSweep(argv) {
|
|
|
555
572
|
continue;
|
|
556
573
|
}
|
|
557
574
|
try {
|
|
558
|
-
await processTranscript(mainPath, table, fx, daysCutoff, flags.dryRun, summary);
|
|
575
|
+
await processTranscript(mainPath, table, fx, daysCutoff, flags.dryRun, summary, lockProvider);
|
|
559
576
|
} catch (err) {
|
|
560
577
|
logError("sweep:transcript", err);
|
|
561
578
|
}
|
|
562
579
|
}
|
|
563
580
|
}
|
|
564
|
-
await sweepCodex(summary, table, fx, daysCutoff, flags);
|
|
581
|
+
await sweepCodex(summary, table, fx, daysCutoff, flags, lockProvider);
|
|
565
582
|
summary.totalJPY = summary.totalUSD * fx.rate;
|
|
566
583
|
printSweepSummary(summary, fx);
|
|
567
|
-
return 0;
|
|
584
|
+
return summary.lockTimeouts > 0 ? 1 : 0;
|
|
568
585
|
}
|
|
569
586
|
export {
|
|
570
587
|
runSweep,
|