ccc-notifier 0.5.0 → 0.6.1
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 +129 -124
- package/dist/{budget-TBWVHUKA.js → budget-V7CCMJHF.js} +1 -1
- package/dist/{chunk-O34L3NSI.js → chunk-27SJELD2.js} +107 -22
- package/dist/chunk-6HTETN26.js +226 -0
- package/dist/{chunk-QWBQN4XY.js → chunk-6UZXXO5J.js} +41 -18
- package/dist/{chunk-S3XKO7MY.js → chunk-CH34OJ7Q.js} +7 -3
- package/dist/{chunk-LVMKY6JB.js → chunk-D4D76RGQ.js} +27 -217
- package/dist/{chunk-5PH7PPD6.js → chunk-OOAC5ULQ.js} +25 -1
- package/dist/chunk-OYD6H3ZZ.js +124 -0
- package/dist/{chunk-DDUK4EWQ.js → chunk-T6Z2ZAN4.js} +1 -1
- package/dist/cli.js +26 -24
- package/dist/{dashboard-6F2QNUJT.js → dashboard-VEKQ4TI3.js} +3 -4
- package/dist/{history-7KN26LJH.js → history-DL4SG3PG.js} +3 -5
- package/dist/{mute-MPX3K7QN.js → mute-UIR5P5N5.js} +2 -2
- package/dist/{setup-IXPMVWOE.js → setup-3SRNQ5PG.js} +4 -3
- package/dist/{subagent-store-A6MIN22X.js → subagent-store-US4TJJQR.js} +1 -1
- package/dist/sweep-65D57PXU.js +795 -0
- package/dist/{track-KEZON6KI.js → track-WCP7446C.js} +40 -28
- package/package.json +1 -1
- package/dist/chunk-ENGUOLTD.js +0 -92
- package/dist/chunk-O2AR4PXU.js +0 -82
- package/dist/sweep-UIONM4UA.js +0 -589
|
@@ -3,29 +3,29 @@ import {
|
|
|
3
3
|
notifyOS,
|
|
4
4
|
notifySlack
|
|
5
5
|
} from "./chunk-NV5UOHJA.js";
|
|
6
|
-
import {
|
|
7
|
-
writeDashboardHtml
|
|
8
|
-
} from "./chunk-QWBQN4XY.js";
|
|
9
|
-
import "./chunk-DGXUSPS4.js";
|
|
10
6
|
import {
|
|
11
7
|
collectSubagentUsage
|
|
12
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-OYD6H3ZZ.js";
|
|
13
9
|
import {
|
|
14
10
|
aggregateCodexTurn,
|
|
15
11
|
aggregateNewTurn,
|
|
12
|
+
getUsdJpy
|
|
13
|
+
} from "./chunk-D4D76RGQ.js";
|
|
14
|
+
import {
|
|
16
15
|
computeCost,
|
|
17
|
-
getUsdJpy,
|
|
18
16
|
loadPriceTable
|
|
19
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-6HTETN26.js";
|
|
18
|
+
import {
|
|
19
|
+
writeDashboardHtml
|
|
20
|
+
} from "./chunk-6UZXXO5J.js";
|
|
21
|
+
import "./chunk-DGXUSPS4.js";
|
|
20
22
|
import "./chunk-J5QAYTFE.js";
|
|
21
23
|
import {
|
|
22
24
|
isFullDashboardDue,
|
|
23
25
|
makeFullDashboardState,
|
|
26
|
+
waitForDataLock,
|
|
24
27
|
writeFullDashboardStateAtomic
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import {
|
|
27
|
-
waitForDataLock
|
|
28
|
-
} from "./chunk-O34L3NSI.js";
|
|
28
|
+
} from "./chunk-27SJELD2.js";
|
|
29
29
|
import {
|
|
30
30
|
appendTurn,
|
|
31
31
|
closeCodexRootContext,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
sanitizeCursor,
|
|
39
39
|
saveCursor,
|
|
40
40
|
todayTotalUSD
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-OOAC5ULQ.js";
|
|
42
42
|
|
|
43
43
|
// src/track.ts
|
|
44
44
|
function isRecord(v) {
|
|
@@ -100,6 +100,7 @@ async function runTrack(stdinText, opts) {
|
|
|
100
100
|
const table = await loadPriceTable(cacheDir, { offline: true });
|
|
101
101
|
const fx = await getUsdJpy(cfg, cacheDir);
|
|
102
102
|
let record;
|
|
103
|
+
let hasMainUsage = false;
|
|
103
104
|
const commitLock = await waitForDataLock(1e3);
|
|
104
105
|
if (commitLock === null) {
|
|
105
106
|
logError("track:data-lock", new Error("data lock timeout; turn was not consumed"));
|
|
@@ -108,33 +109,44 @@ async function runTrack(stdinText, opts) {
|
|
|
108
109
|
try {
|
|
109
110
|
const cursor = sanitizeCursor(loadCursor(transcriptPath));
|
|
110
111
|
let agg = isCodex ? await aggregateCodexTurn(transcriptPath, cursor) : await aggregateNewTurn(transcriptPath, cursor);
|
|
111
|
-
if (agg === null) return;
|
|
112
|
-
|
|
112
|
+
if (isCodex && agg === null) return;
|
|
113
|
+
hasMainUsage = agg !== null;
|
|
114
|
+
if (isCodex && agg !== null) {
|
|
113
115
|
agg = withCodexModel(agg, input.model);
|
|
114
116
|
}
|
|
115
117
|
let sa = null;
|
|
116
118
|
if (!isCodex) {
|
|
117
119
|
try {
|
|
118
|
-
|
|
120
|
+
const excluded = new Set(cursor?.seenMessageKeys ?? []);
|
|
121
|
+
if (agg !== null && "messageKeys" in agg) {
|
|
122
|
+
for (const key of agg.messageKeys) excluded.add(key);
|
|
123
|
+
}
|
|
124
|
+
sa = await collectSubagentUsage(transcriptPath, { excludeMessageKeys: excluded });
|
|
119
125
|
} catch (err) {
|
|
120
126
|
logError("track:subagents", err);
|
|
121
127
|
sa = null;
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
if (agg === null && (sa === null || sa.apiCalls === 0)) {
|
|
131
|
+
for (const nc of sa?.newCursors ?? []) saveCursor(nc.path, nc.cursor);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const main = agg?.main ?? {};
|
|
135
|
+
const sidechain = agg?.sidechain ?? {};
|
|
136
|
+
const breakdown = computeCost(main, sidechain, table);
|
|
137
|
+
const sessionId = agg?.sessionId || sa?.sessionId || (typeof input.session_id === "string" ? input.session_id : "") || "";
|
|
138
|
+
const project = agg?.cwd ?? sa?.cwd ?? (typeof input.cwd === "string" ? input.cwd : void 0) ?? "";
|
|
139
|
+
const sidechainHasModels = Object.keys(sidechain).length > 0;
|
|
128
140
|
record = {
|
|
129
141
|
schemaVersion: 1,
|
|
130
|
-
ts: agg
|
|
142
|
+
ts: agg?.lastTs ?? sa?.lastTs ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
131
143
|
sessionId,
|
|
132
144
|
project,
|
|
133
|
-
gitBranch: agg
|
|
134
|
-
models: collectModels(
|
|
135
|
-
tokens: sumBuckets(
|
|
136
|
-
sidechainTokens: sidechainHasModels ? sumBuckets(
|
|
137
|
-
apiCalls: agg
|
|
145
|
+
gitBranch: agg?.gitBranch ?? sa?.gitBranch ?? null,
|
|
146
|
+
models: hasMainUsage ? collectModels(main, sidechain) : collectModels(sa?.perModel ?? {}, {}),
|
|
147
|
+
tokens: sumBuckets(main),
|
|
148
|
+
sidechainTokens: sidechainHasModels ? sumBuckets(sidechain) : null,
|
|
149
|
+
apiCalls: agg?.apiCalls ?? 0,
|
|
138
150
|
costUSD: breakdown.usd,
|
|
139
151
|
costByModel: breakdown.byModel,
|
|
140
152
|
// モデル別 USD(main+sidechain 合算、丸めない)
|
|
@@ -142,7 +154,7 @@ async function runTrack(stdinText, opts) {
|
|
|
142
154
|
// 丸めない(表示時に丸める)
|
|
143
155
|
fxRate: fx.rate,
|
|
144
156
|
fxSource: fx.source,
|
|
145
|
-
prompt: agg
|
|
157
|
+
prompt: agg?.prompt ?? ""
|
|
146
158
|
};
|
|
147
159
|
if (isCodex) {
|
|
148
160
|
record.source = "codex";
|
|
@@ -169,7 +181,7 @@ async function runTrack(stdinText, opts) {
|
|
|
169
181
|
}
|
|
170
182
|
}
|
|
171
183
|
appendTurn(record);
|
|
172
|
-
saveCursor(transcriptPath, agg.newCursor);
|
|
184
|
+
if (agg !== null) saveCursor(transcriptPath, agg.newCursor);
|
|
173
185
|
if (sa !== null) {
|
|
174
186
|
for (const nc of sa.newCursors) {
|
|
175
187
|
saveCursor(nc.path, nc.cursor);
|
|
@@ -179,7 +191,7 @@ async function runTrack(stdinText, opts) {
|
|
|
179
191
|
commitLock.release();
|
|
180
192
|
}
|
|
181
193
|
const tasks = [];
|
|
182
|
-
if ((cfg.notify.os || cfg.notify.slack !== null) && record.costUSD >= cfg.minNotifyUSD && !isMuted()) {
|
|
194
|
+
if (hasMainUsage && (cfg.notify.os || cfg.notify.slack !== null) && record.costUSD >= cfg.minNotifyUSD && !isMuted()) {
|
|
183
195
|
const todayUSD = cfg.includeDailyTotal ? todayTotalUSD() : void 0;
|
|
184
196
|
tasks.push(notifyOS(record, cfg, todayUSD));
|
|
185
197
|
tasks.push(notifySlack(record, cfg, todayUSD));
|
package/package.json
CHANGED
package/dist/chunk-ENGUOLTD.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
paths
|
|
4
|
-
} from "./chunk-5PH7PPD6.js";
|
|
5
|
-
|
|
6
|
-
// src/dashboard-state.ts
|
|
7
|
-
import {
|
|
8
|
-
closeSync,
|
|
9
|
-
existsSync,
|
|
10
|
-
openSync,
|
|
11
|
-
readFileSync,
|
|
12
|
-
readSync,
|
|
13
|
-
renameSync,
|
|
14
|
-
rmSync,
|
|
15
|
-
writeFileSync
|
|
16
|
-
} from "fs";
|
|
17
|
-
import { randomUUID } from "crypto";
|
|
18
|
-
function localDate(now) {
|
|
19
|
-
const y = now.getFullYear();
|
|
20
|
-
const m = String(now.getMonth() + 1).padStart(2, "0");
|
|
21
|
-
const d = String(now.getDate()).padStart(2, "0");
|
|
22
|
-
return `${y}-${m}-${d}`;
|
|
23
|
-
}
|
|
24
|
-
function timeZone() {
|
|
25
|
-
return Intl.DateTimeFormat().resolvedOptions().timeZone || "unknown";
|
|
26
|
-
}
|
|
27
|
-
function makeFullDashboardState(now = /* @__PURE__ */ new Date()) {
|
|
28
|
-
return { localDate: localDate(now), timeZone: timeZone(), generatedAt: now.toISOString() };
|
|
29
|
-
}
|
|
30
|
-
function readState() {
|
|
31
|
-
const file = paths().dashboardFullStateFile;
|
|
32
|
-
if (!existsSync(file)) return null;
|
|
33
|
-
try {
|
|
34
|
-
const value = JSON.parse(readFileSync(file, "utf8"));
|
|
35
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
36
|
-
const v = value;
|
|
37
|
-
if (typeof v.localDate !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(v.localDate) || typeof v.timeZone !== "string" || v.timeZone.length === 0 || typeof v.generatedAt !== "string" || !Number.isFinite(Date.parse(v.generatedAt))) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
return v;
|
|
41
|
-
} catch {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function isFullDashboardDue(now = /* @__PURE__ */ new Date()) {
|
|
46
|
-
const p = paths();
|
|
47
|
-
if (!existsSync(p.fullDashboardFile)) return true;
|
|
48
|
-
try {
|
|
49
|
-
const fd = openSync(p.fullDashboardFile, "r");
|
|
50
|
-
try {
|
|
51
|
-
const head = Buffer.alloc(512);
|
|
52
|
-
const n = readSync(fd, head, 0, head.length, 0);
|
|
53
|
-
if (head.toString("utf8", 0, n).includes('name="cccn-placeholder"')) return true;
|
|
54
|
-
} finally {
|
|
55
|
-
closeSync(fd);
|
|
56
|
-
}
|
|
57
|
-
} catch {
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
const state = readState();
|
|
61
|
-
if (state === null) return true;
|
|
62
|
-
const expected = makeFullDashboardState(now);
|
|
63
|
-
if (state.timeZone !== expected.timeZone) return true;
|
|
64
|
-
if (state.localDate !== expected.localDate) return true;
|
|
65
|
-
if (state.localDate > expected.localDate) return true;
|
|
66
|
-
if (Date.parse(state.generatedAt) > now.getTime()) return true;
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
function writeFullDashboardStateAtomic(state) {
|
|
70
|
-
const file = paths().dashboardFullStateFile;
|
|
71
|
-
const tmp = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
72
|
-
try {
|
|
73
|
-
writeFileSync(tmp, `${JSON.stringify(state)}
|
|
74
|
-
`, "utf8");
|
|
75
|
-
renameSync(tmp, file);
|
|
76
|
-
} finally {
|
|
77
|
-
rmSync(tmp, { force: true });
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function invalidateCanonicalDashboards() {
|
|
81
|
-
const p = paths();
|
|
82
|
-
for (const file of [p.recentDashboardFile, p.fullDashboardFile, p.dashboardFullStateFile]) {
|
|
83
|
-
rmSync(file, { force: true });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export {
|
|
88
|
-
makeFullDashboardState,
|
|
89
|
-
isFullDashboardDue,
|
|
90
|
-
writeFullDashboardStateAtomic,
|
|
91
|
-
invalidateCanonicalDashboards
|
|
92
|
-
};
|
package/dist/chunk-O2AR4PXU.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
aggregateNewTurn
|
|
4
|
-
} from "./chunk-LVMKY6JB.js";
|
|
5
|
-
import {
|
|
6
|
-
loadCursor,
|
|
7
|
-
logError,
|
|
8
|
-
sanitizeCursor
|
|
9
|
-
} from "./chunk-5PH7PPD6.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 subagentsDirOf(mainTranscriptPath) {
|
|
31
|
-
const base = mainTranscriptPath.endsWith(".jsonl") ? mainTranscriptPath.slice(0, -".jsonl".length) : mainTranscriptPath;
|
|
32
|
-
return join(base, "subagents");
|
|
33
|
-
}
|
|
34
|
-
async function listAgentFiles(dir, entries) {
|
|
35
|
-
const files = entries.filter((e) => e.isFile() && e.name.startsWith("agent-") && e.name.endsWith(".jsonl")).map((e) => join(dir, e.name));
|
|
36
|
-
if (files.length <= MAX_AGENT_FILES) return files;
|
|
37
|
-
const withMtime = [];
|
|
38
|
-
for (const p of files) {
|
|
39
|
-
let mtime = 0;
|
|
40
|
-
try {
|
|
41
|
-
mtime = (await fs.stat(p)).mtimeMs;
|
|
42
|
-
} catch {
|
|
43
|
-
mtime = 0;
|
|
44
|
-
}
|
|
45
|
-
withMtime.push({ path: p, mtime });
|
|
46
|
-
}
|
|
47
|
-
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
48
|
-
return withMtime.slice(0, MAX_AGENT_FILES).map((x) => x.path);
|
|
49
|
-
}
|
|
50
|
-
async function collectSubagentUsage(mainTranscriptPath) {
|
|
51
|
-
const dir = subagentsDirOf(mainTranscriptPath);
|
|
52
|
-
let entries;
|
|
53
|
-
try {
|
|
54
|
-
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
55
|
-
} catch {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
const files = await listAgentFiles(dir, entries);
|
|
59
|
-
const perModel = {};
|
|
60
|
-
let apiCalls = 0;
|
|
61
|
-
let agentFiles = 0;
|
|
62
|
-
const newCursors = [];
|
|
63
|
-
for (const filePath of files) {
|
|
64
|
-
try {
|
|
65
|
-
const cursor = sanitizeCursor(loadCursor(filePath));
|
|
66
|
-
const agg = await aggregateNewTurn(filePath, cursor);
|
|
67
|
-
if (agg === null) continue;
|
|
68
|
-
mergeUsage(perModel, agg.main);
|
|
69
|
-
mergeUsage(perModel, agg.sidechain);
|
|
70
|
-
apiCalls += agg.apiCalls;
|
|
71
|
-
agentFiles += 1;
|
|
72
|
-
newCursors.push({ path: filePath, cursor: agg.newCursor });
|
|
73
|
-
} catch (err) {
|
|
74
|
-
logError("subagents:file", err);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return { perModel, apiCalls, agentFiles, newCursors };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export {
|
|
81
|
-
collectSubagentUsage
|
|
82
|
-
};
|