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.
Files changed (30) hide show
  1. package/README.md +5 -0
  2. package/dist/{budget-V7CCMJHF.js → budget-O4VAP6RB.js} +2 -2
  3. package/dist/chunk-3M5UQK76.js +92 -0
  4. package/dist/{sweep-65D57PXU.js → chunk-6BXD6HU5.js} +275 -113
  5. package/dist/chunk-6IVSZ3S4.js +22 -0
  6. package/dist/{chunk-NV5UOHJA.js → chunk-JLDI4D5E.js} +5 -5
  7. package/dist/{chunk-T6Z2ZAN4.js → chunk-ML5H43NH.js} +1 -1
  8. package/dist/{chunk-6UZXXO5J.js → chunk-O2E4VTDR.js} +36 -9
  9. package/dist/{chunk-D4D76RGQ.js → chunk-OCKZZVOG.js} +353 -20
  10. package/dist/{chunk-27SJELD2.js → chunk-PXMXEFM7.js} +22 -107
  11. package/dist/{chunk-6HTETN26.js → chunk-Q45AAPJY.js} +18 -1
  12. package/dist/chunk-V4IS4FXH.js +454 -0
  13. package/dist/{chunk-OOAC5ULQ.js → chunk-WONMZ466.js} +184 -4
  14. package/dist/{chunk-OXXDZZPJ.js → chunk-X7U63BZG.js} +4 -6
  15. package/dist/{chunk-J5QAYTFE.js → chunk-XG73C6CC.js} +10 -1
  16. package/dist/cli.js +286 -29
  17. package/dist/{dashboard-VEKQ4TI3.js → dashboard-64XHDUVT.js} +6 -4
  18. package/dist/{history-DL4SG3PG.js → history-W34EJOVV.js} +5 -3
  19. package/dist/{mute-UIR5P5N5.js → mute-23BWQXS3.js} +2 -2
  20. package/dist/reset-cursors-5CCM6MQL.js +41 -0
  21. package/dist/scan-UB6ORRZK.js +87 -0
  22. package/dist/{setup-I3JNGWZG.js → setup-VLMW433R.js} +5 -6
  23. package/dist/{subagent-store-US4TJJQR.js → subagent-store-USG3WJEB.js} +1 -1
  24. package/dist/sweep-DVOV4XIZ.js +20 -0
  25. package/dist/track-P6A5WDJZ.js +368 -0
  26. package/package.json +1 -1
  27. package/dist/chunk-HLJAJS2W.js +0 -55
  28. package/dist/chunk-HTYUYKFW.js +0 -21
  29. package/dist/chunk-OYD6H3ZZ.js +0 -124
  30. 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
- writeFileSync(tmpFile, JSON.stringify(dict), "utf8");
257
- renameSync(tmpFile, p.cursorsFile);
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-NV5UOHJA.js";
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-6HTETN26.js";
10
+ } from "./chunk-Q45AAPJY.js";
13
11
  import {
14
12
  configFilePath,
15
13
  paths,
16
14
  readConfig
17
- } from "./chunk-OOAC5ULQ.js";
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
  };