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
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
rmSync,
|
|
29
29
|
statSync
|
|
30
30
|
} from "fs";
|
|
31
|
+
import { randomUUID } from "crypto";
|
|
31
32
|
import { join } from "path";
|
|
32
33
|
import { homedir } from "os";
|
|
33
34
|
|
|
@@ -216,9 +217,21 @@ function loadCursor(transcriptPath) {
|
|
|
216
217
|
const cursor = parsed[transcriptPath];
|
|
217
218
|
return cursor ?? null;
|
|
218
219
|
}
|
|
220
|
+
function cursorPaths() {
|
|
221
|
+
const p = paths();
|
|
222
|
+
if (!existsSync(p.cursorsFile)) return /* @__PURE__ */ new Set();
|
|
223
|
+
try {
|
|
224
|
+
const parsed = JSON.parse(readFileSync(p.cursorsFile, "utf8"));
|
|
225
|
+
if (!isPlainObject(parsed)) return /* @__PURE__ */ new Set();
|
|
226
|
+
return new Set(Object.keys(parsed));
|
|
227
|
+
} catch (err) {
|
|
228
|
+
logError("cursorPaths", err);
|
|
229
|
+
return /* @__PURE__ */ new Set();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
219
232
|
function sanitizeCursor(raw) {
|
|
220
233
|
if (!isPlainObject(raw)) return null;
|
|
221
|
-
const { offset, lastUuid, lastTs, seenMessageKeys, codexTotals } = raw;
|
|
234
|
+
const { offset, lastUuid, lastTs, seenMessageKeys, codexTotals, codexOriginator, codexModel } = raw;
|
|
222
235
|
if (typeof offset !== "number" || !Number.isFinite(offset)) return null;
|
|
223
236
|
if (lastUuid !== null && typeof lastUuid !== "string") return null;
|
|
224
237
|
if (lastTs !== null && typeof lastTs !== "string") return null;
|
|
@@ -235,8 +248,44 @@ function sanitizeCursor(raw) {
|
|
|
235
248
|
cursor.codexTotals = { input, cached, output };
|
|
236
249
|
}
|
|
237
250
|
}
|
|
251
|
+
if (Object.hasOwn(raw, "codexOriginator")) {
|
|
252
|
+
if (codexOriginator === null || typeof codexOriginator === "string") {
|
|
253
|
+
cursor.codexOriginator = codexOriginator;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (Object.hasOwn(raw, "codexModel")) {
|
|
257
|
+
if (codexModel === null || typeof codexModel === "string") {
|
|
258
|
+
cursor.codexModel = codexModel;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
238
261
|
return cursor;
|
|
239
262
|
}
|
|
263
|
+
function loadAllCursors() {
|
|
264
|
+
const p = paths();
|
|
265
|
+
if (!existsSync(p.cursorsFile)) return {};
|
|
266
|
+
try {
|
|
267
|
+
const raw = readFileSync(p.cursorsFile, "utf8");
|
|
268
|
+
const parsed = JSON.parse(raw);
|
|
269
|
+
if (!isPlainObject(parsed)) {
|
|
270
|
+
logError("loadAllCursors", new Error("cursors.json root is not an object"));
|
|
271
|
+
return {};
|
|
272
|
+
}
|
|
273
|
+
return parsed;
|
|
274
|
+
} catch (err) {
|
|
275
|
+
logError("loadAllCursors", err);
|
|
276
|
+
return {};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function saveAllCursors(dict) {
|
|
280
|
+
const p = paths();
|
|
281
|
+
const tmpFile = `${p.cursorsFile}.${randomUUID()}.tmp`;
|
|
282
|
+
try {
|
|
283
|
+
writeFileSync(tmpFile, JSON.stringify(dict), "utf8");
|
|
284
|
+
renameSync(tmpFile, p.cursorsFile);
|
|
285
|
+
} finally {
|
|
286
|
+
rmSync(tmpFile, { force: true });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
240
289
|
function saveCursor(transcriptPath, c) {
|
|
241
290
|
const p = paths();
|
|
242
291
|
let dict = {};
|
|
@@ -252,9 +301,83 @@ function saveCursor(transcriptPath, c) {
|
|
|
252
301
|
}
|
|
253
302
|
}
|
|
254
303
|
dict[transcriptPath] = c;
|
|
255
|
-
const tmpFile = `${p.cursorsFile}.tmp`;
|
|
256
|
-
|
|
257
|
-
|
|
304
|
+
const tmpFile = `${p.cursorsFile}.${randomUUID()}.tmp`;
|
|
305
|
+
try {
|
|
306
|
+
writeFileSync(tmpFile, JSON.stringify(dict), "utf8");
|
|
307
|
+
renameSync(tmpFile, p.cursorsFile);
|
|
308
|
+
} finally {
|
|
309
|
+
rmSync(tmpFile, { force: true });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function pendingAppendPath() {
|
|
313
|
+
return join(paths().cacheDir, "pending-append.json");
|
|
314
|
+
}
|
|
315
|
+
function readPendingAppends() {
|
|
316
|
+
let raw;
|
|
317
|
+
try {
|
|
318
|
+
raw = readFileSync(pendingAppendPath(), "utf8");
|
|
319
|
+
} catch (err) {
|
|
320
|
+
if (err?.code === "ENOENT") return { dict: {}, trusted: true };
|
|
321
|
+
logError("readPendingAppends", err);
|
|
322
|
+
return { dict: {}, trusted: false };
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
const parsed = JSON.parse(raw);
|
|
326
|
+
if (!isPlainObject(parsed)) {
|
|
327
|
+
logError("readPendingAppends", new Error("pending-append.json root is not an object"));
|
|
328
|
+
return { dict: {}, trusted: false };
|
|
329
|
+
}
|
|
330
|
+
return { dict: parsed, trusted: true };
|
|
331
|
+
} catch (err) {
|
|
332
|
+
logError("readPendingAppends", err);
|
|
333
|
+
return { dict: {}, trusted: false };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function writePendingAppends(dict) {
|
|
337
|
+
const file = pendingAppendPath();
|
|
338
|
+
const tmp = `${file}.${randomUUID()}.tmp`;
|
|
339
|
+
try {
|
|
340
|
+
writeFileSync(tmp, JSON.stringify(dict), "utf8");
|
|
341
|
+
renameSync(tmp, file);
|
|
342
|
+
} finally {
|
|
343
|
+
rmSync(tmp, { force: true });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
var PENDING_ALL = "*";
|
|
347
|
+
function hasPendingAppend(transcriptPath) {
|
|
348
|
+
try {
|
|
349
|
+
const { dict, trusted } = readPendingAppends();
|
|
350
|
+
if (!trusted) return true;
|
|
351
|
+
return Object.hasOwn(dict, PENDING_ALL) || Object.hasOwn(dict, transcriptPath);
|
|
352
|
+
} catch (err) {
|
|
353
|
+
logError("hasPendingAppend", err);
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function markPendingAppend(transcriptPath, ingestKey) {
|
|
358
|
+
const { dict, trusted } = readPendingAppends();
|
|
359
|
+
if (!trusted) dict[PENDING_ALL] = "unreadable-marker";
|
|
360
|
+
dict[transcriptPath] = ingestKey;
|
|
361
|
+
writePendingAppends(dict);
|
|
362
|
+
}
|
|
363
|
+
function clearPendingAppend(transcriptPath) {
|
|
364
|
+
try {
|
|
365
|
+
const { dict, trusted } = readPendingAppends();
|
|
366
|
+
if (!trusted) return;
|
|
367
|
+
if (!Object.hasOwn(dict, transcriptPath)) return;
|
|
368
|
+
delete dict[transcriptPath];
|
|
369
|
+
writePendingAppends(dict);
|
|
370
|
+
} catch (err) {
|
|
371
|
+
logError("clearPendingAppend", err);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function hasUnresolvedPendingMarker() {
|
|
375
|
+
try {
|
|
376
|
+
const { dict, trusted } = readPendingAppends();
|
|
377
|
+
return !trusted || Object.hasOwn(dict, PENDING_ALL);
|
|
378
|
+
} catch {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
258
381
|
}
|
|
259
382
|
function appendTurn(record) {
|
|
260
383
|
const p = paths();
|
|
@@ -309,6 +432,53 @@ function readTurns(days) {
|
|
|
309
432
|
}
|
|
310
433
|
return result;
|
|
311
434
|
}
|
|
435
|
+
function floorKey(scope, sessionId) {
|
|
436
|
+
return `${scope}\0${sessionId}`;
|
|
437
|
+
}
|
|
438
|
+
function loadHistoryIndex() {
|
|
439
|
+
const index = { countedCalls: /* @__PURE__ */ new Set(), ingestKeys: /* @__PURE__ */ new Set(), legacyFloors: /* @__PURE__ */ new Map() };
|
|
440
|
+
let raw;
|
|
441
|
+
try {
|
|
442
|
+
raw = readFileSync(paths().historyFile, "utf8");
|
|
443
|
+
} catch {
|
|
444
|
+
return index;
|
|
445
|
+
}
|
|
446
|
+
for (const line of raw.split("\n")) {
|
|
447
|
+
if (line.length === 0) continue;
|
|
448
|
+
let rec;
|
|
449
|
+
try {
|
|
450
|
+
rec = JSON.parse(line);
|
|
451
|
+
} catch {
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
let hasFingerprints = false;
|
|
455
|
+
if (Array.isArray(rec.countedCalls)) {
|
|
456
|
+
for (const fp of rec.countedCalls) {
|
|
457
|
+
if (typeof fp === "string" && fp.length > 0) {
|
|
458
|
+
index.countedCalls.add(fp);
|
|
459
|
+
hasFingerprints = true;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (typeof rec.ingestKey === "string" && rec.ingestKey.length > 0) index.ingestKeys.add(rec.ingestKey);
|
|
464
|
+
if (hasFingerprints) continue;
|
|
465
|
+
const { sessionId, ts } = rec;
|
|
466
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) continue;
|
|
467
|
+
if (typeof ts !== "string") continue;
|
|
468
|
+
const ms = Date.parse(ts);
|
|
469
|
+
if (!Number.isFinite(ms)) continue;
|
|
470
|
+
const iso = new Date(ms).toISOString();
|
|
471
|
+
const isCodex = rec.source === "codex";
|
|
472
|
+
const scopes = isCodex ? ["codex"] : ["claude"];
|
|
473
|
+
if (!isCodex && rec.subagents !== void 0 && rec.subagents !== null) scopes.push("claude-sa");
|
|
474
|
+
for (const scope of scopes) {
|
|
475
|
+
const key = floorKey(scope, sessionId);
|
|
476
|
+
const cur = index.legacyFloors.get(key);
|
|
477
|
+
if (cur === void 0 || cur < iso) index.legacyFloors.set(key, iso);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return index;
|
|
481
|
+
}
|
|
312
482
|
function todayTotalUSD() {
|
|
313
483
|
const now = /* @__PURE__ */ new Date();
|
|
314
484
|
const y = now.getFullYear();
|
|
@@ -1090,11 +1260,21 @@ export {
|
|
|
1090
1260
|
writeMuteState,
|
|
1091
1261
|
clearMuteState,
|
|
1092
1262
|
loadCursor,
|
|
1263
|
+
cursorPaths,
|
|
1093
1264
|
sanitizeCursor,
|
|
1265
|
+
loadAllCursors,
|
|
1266
|
+
saveAllCursors,
|
|
1094
1267
|
saveCursor,
|
|
1268
|
+
pendingAppendPath,
|
|
1269
|
+
hasPendingAppend,
|
|
1270
|
+
markPendingAppend,
|
|
1271
|
+
clearPendingAppend,
|
|
1272
|
+
hasUnresolvedPendingMarker,
|
|
1095
1273
|
appendTurn,
|
|
1096
1274
|
resetHistoryAndCursors,
|
|
1097
1275
|
readTurns,
|
|
1276
|
+
floorKey,
|
|
1277
|
+
loadHistoryIndex,
|
|
1098
1278
|
todayTotalUSD,
|
|
1099
1279
|
currentMonthTotals,
|
|
1100
1280
|
logError
|
|
@@ -2,19 +2,17 @@
|
|
|
2
2
|
import {
|
|
3
3
|
notifyOS,
|
|
4
4
|
notifySlack
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-JLDI4D5E.js";
|
|
6
6
|
import {
|
|
7
7
|
codexHome,
|
|
8
|
-
detectCodex
|
|
9
|
-
} from "./chunk-HTYUYKFW.js";
|
|
10
|
-
import {
|
|
8
|
+
detectCodex,
|
|
11
9
|
loadPriceTable
|
|
12
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-Q45AAPJY.js";
|
|
13
11
|
import {
|
|
14
12
|
configFilePath,
|
|
15
13
|
paths,
|
|
16
14
|
readConfig
|
|
17
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WONMZ466.js";
|
|
18
16
|
|
|
19
17
|
// src/setup.ts
|
|
20
18
|
import {
|
|
@@ -80,11 +80,20 @@ function formatSummary(record, cfg, todayUSD) {
|
|
|
80
80
|
return { title, body: `${line1}
|
|
81
81
|
${line2}` };
|
|
82
82
|
}
|
|
83
|
+
function formatIngestSummary(input, cfg) {
|
|
84
|
+
const label = cfg.costLabel === "api_equivalent" ? "API\u63DB\u7B97 " : "";
|
|
85
|
+
const title = `\u{1F4B0} ${label}${formatUSD(input.totalUSD)}(${formatJPY(input.totalJPY)})| hook\u975E\u4F9D\u5B58\u306E\u53D6\u308A\u8FBC\u307F ${input.recordCount}\u4EF6`;
|
|
86
|
+
const bySurfaceLine = Object.entries(input.bySurface).sort((a, b) => b[1].usd - a[1].usd).map(([surface, v]) => `${surface}: ${v.turns}\u4EF6 ${formatUSD(v.usd)}`).join(" / ");
|
|
87
|
+
const body = `${bySurfaceLine}
|
|
88
|
+
hook \u975E\u4F9D\u5B58\u306E\u5897\u5206\u53D6\u308A\u8FBC\u307F(scan)\u306B\u3088\u308B\u65B0\u898F\u5206\u3067\u3059`;
|
|
89
|
+
return { title, body };
|
|
90
|
+
}
|
|
83
91
|
|
|
84
92
|
export {
|
|
85
93
|
formatUSD,
|
|
86
94
|
formatJPY,
|
|
87
95
|
formatTokens,
|
|
88
96
|
modelDisplayName,
|
|
89
|
-
formatSummary
|
|
97
|
+
formatSummary,
|
|
98
|
+
formatIngestSummary
|
|
90
99
|
};
|