ccc-notifier 0.3.0 → 0.4.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.
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ paths
4
+ } from "./chunk-26CISNOE.js";
5
+
6
+ // src/data-lock.ts
7
+ import { randomUUID } from "crypto";
8
+ import { hostname } from "os";
9
+ import {
10
+ existsSync,
11
+ mkdirSync,
12
+ readFileSync,
13
+ renameSync,
14
+ rmSync,
15
+ writeFileSync
16
+ } from "fs";
17
+ import { join } from "path";
18
+ var DATA_LOCK_LEASE_MS = 3e4;
19
+ var HEARTBEAT_MS = 5e3;
20
+ function ownerFile(dir) {
21
+ return join(dir, "owner.json");
22
+ }
23
+ function readOwner(dir) {
24
+ try {
25
+ const v = JSON.parse(readFileSync(ownerFile(dir), "utf8"));
26
+ if (typeof v.token !== "string" || typeof v.pid !== "number" || typeof v.hostname !== "string" || typeof v.acquiredAt !== "string" || typeof v.heartbeatAt !== "string") return null;
27
+ return v;
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ function writeOwner(dir, owner) {
33
+ const tmp = join(dir, `owner.${owner.token}.${randomUUID()}.tmp`);
34
+ try {
35
+ writeFileSync(tmp, `${JSON.stringify(owner)}
36
+ `, "utf8");
37
+ renameSync(tmp, ownerFile(dir));
38
+ } finally {
39
+ rmSync(tmp, { force: true });
40
+ }
41
+ }
42
+ function quarantineOwned(dir, token, label) {
43
+ const before = readOwner(dir);
44
+ if (before?.token !== token) return false;
45
+ const quarantine = `${dir}.${label}-${token}-${randomUUID()}`;
46
+ try {
47
+ renameSync(dir, quarantine);
48
+ } catch {
49
+ return false;
50
+ }
51
+ const moved = readOwner(quarantine);
52
+ if (moved?.token !== token) {
53
+ return false;
54
+ }
55
+ rmSync(quarantine, { recursive: true, force: true });
56
+ return true;
57
+ }
58
+ function claimDir(fixed, label, now, metadataWriter = writeOwner) {
59
+ const token = randomUUID();
60
+ const staging = `${fixed}.${label}-${token}`;
61
+ const iso = now.toISOString();
62
+ const owner = { token, pid: process.pid, hostname: hostname(), acquiredAt: iso, heartbeatAt: iso };
63
+ try {
64
+ mkdirSync(staging);
65
+ metadataWriter(staging, owner);
66
+ renameSync(staging, fixed);
67
+ return { token, owner };
68
+ } catch {
69
+ rmSync(staging, { recursive: true, force: true });
70
+ return null;
71
+ }
72
+ }
73
+ function processDefinitelyDead(pid) {
74
+ if (!Number.isInteger(pid) || pid <= 0) return false;
75
+ try {
76
+ process.kill(pid, 0);
77
+ return false;
78
+ } catch (err) {
79
+ return err.code === "ESRCH";
80
+ }
81
+ }
82
+ function reclaimerGuardBlocks(now, leaseMs) {
83
+ const owner = readOwner(paths().dataReclaimDir);
84
+ if (owner === null) return true;
85
+ if (owner.hostname !== hostname()) return true;
86
+ const heartbeat = Date.parse(owner.heartbeatAt);
87
+ if (!Number.isFinite(heartbeat) || now.getTime() - heartbeat <= leaseMs) return true;
88
+ return !processDefinitelyDead(owner.pid);
89
+ }
90
+ function tryReclaim(now, leaseMs) {
91
+ const p = paths();
92
+ const guard = claimDir(p.dataReclaimDir, "claim", now);
93
+ if (guard === null) return false;
94
+ try {
95
+ const first = readOwner(p.dataLockDir);
96
+ if (first === null || first.hostname !== hostname()) return false;
97
+ const heartbeatMs = Date.parse(first.heartbeatAt);
98
+ if (!Number.isFinite(heartbeatMs) || now.getTime() - heartbeatMs <= leaseMs) return false;
99
+ if (!processDefinitelyDead(first.pid)) return false;
100
+ const second = readOwner(p.dataLockDir);
101
+ if (second === null || second.token !== first.token || second.heartbeatAt !== first.heartbeatAt) return false;
102
+ const orphan = `${p.dataLockDir}.orphan-${first.token}-${randomUUID()}`;
103
+ try {
104
+ renameSync(p.dataLockDir, orphan);
105
+ } catch {
106
+ return false;
107
+ }
108
+ const moved = readOwner(orphan);
109
+ if (moved?.token !== first.token || moved.heartbeatAt !== first.heartbeatAt) return false;
110
+ rmSync(orphan, { recursive: true, force: true });
111
+ return true;
112
+ } finally {
113
+ quarantineOwned(p.dataReclaimDir, guard.token, "released");
114
+ }
115
+ }
116
+ function acquireDataLock(opts = {}) {
117
+ const now = opts.now ?? /* @__PURE__ */ new Date();
118
+ const leaseMs = opts.leaseMs ?? DATA_LOCK_LEASE_MS;
119
+ const p = paths();
120
+ if (existsSync(p.dataReclaimDir) && reclaimerGuardBlocks(now, leaseMs)) return null;
121
+ let claim = claimDir(p.dataLockDir, "acquire", now, opts.metadataWriter);
122
+ if (claim === null) {
123
+ if (!tryReclaim(now, leaseMs)) return null;
124
+ if (existsSync(p.dataReclaimDir) && reclaimerGuardBlocks(now, leaseMs)) return null;
125
+ claim = claimDir(p.dataLockDir, "acquire", now, opts.metadataWriter);
126
+ if (claim === null) return null;
127
+ }
128
+ if (existsSync(p.dataReclaimDir) && reclaimerGuardBlocks(now, leaseMs)) {
129
+ quarantineOwned(p.dataLockDir, claim.token, "yielded");
130
+ return null;
131
+ }
132
+ let released = false;
133
+ const heartbeat = () => {
134
+ if (released) return false;
135
+ const current = readOwner(p.dataLockDir);
136
+ if (current?.token !== claim.token) return false;
137
+ writeOwner(p.dataLockDir, { ...current, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString() });
138
+ return readOwner(p.dataLockDir)?.token === claim.token;
139
+ };
140
+ const timer = setInterval(() => {
141
+ try {
142
+ heartbeat();
143
+ } catch {
144
+ }
145
+ }, HEARTBEAT_MS);
146
+ timer.unref();
147
+ return {
148
+ token: claim.token,
149
+ heartbeat,
150
+ release() {
151
+ if (released) return;
152
+ released = true;
153
+ clearInterval(timer);
154
+ quarantineOwned(p.dataLockDir, claim.token, "released");
155
+ }
156
+ };
157
+ }
158
+ async function waitForDataLock(timeoutMs, pollMs = 25) {
159
+ const envTimeout = Number.parseInt(process.env.CCCN_LOCK_TIMEOUT_MS ?? "", 10);
160
+ timeoutMs = timeoutMs ?? (Number.isFinite(envTimeout) && envTimeout >= 0 ? envTimeout : 5e3);
161
+ const deadline = Date.now() + timeoutMs;
162
+ do {
163
+ const lock = acquireDataLock();
164
+ if (lock !== null) return lock;
165
+ if (Date.now() >= deadline) return null;
166
+ await new Promise((resolve) => setTimeout(resolve, pollMs));
167
+ } while (true);
168
+ }
169
+
170
+ export {
171
+ waitForDataLock
172
+ };
@@ -4,7 +4,7 @@ import {
4
4
  isMuted,
5
5
  readMuteState,
6
6
  writeMuteState
7
- } from "./chunk-ECADO26T.js";
7
+ } from "./chunk-26CISNOE.js";
8
8
 
9
9
  // src/mute.ts
10
10
  function parseDuration(arg) {
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ paths
4
+ } from "./chunk-26CISNOE.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/cli.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  fmtMuteUntil
4
- } from "./chunk-4RZ6OGTD.js";
4
+ } from "./chunk-TUZVISLD.js";
5
5
  import {
6
6
  codexHooksFile,
7
7
  matchesMarker
8
- } from "./chunk-7KVOQ4UZ.js";
8
+ } from "./chunk-5LHZOZZO.js";
9
9
  import {
10
10
  notifyOS,
11
11
  notifySlack,
@@ -35,7 +35,7 @@ import {
35
35
  readConfig,
36
36
  readMuteState,
37
37
  readTurns
38
- } from "./chunk-ECADO26T.js";
38
+ } from "./chunk-26CISNOE.js";
39
39
 
40
40
  // src/cli.ts
41
41
  import { realpathSync } from "fs";
@@ -586,7 +586,7 @@ var COMMANDS = [
586
586
  en: "Print an aggregated cost report"
587
587
  },
588
588
  {
589
- cmd: "dashboard [--days N] [--no-open] [--out <path>] [--refresh <sec>|--no-refresh]",
589
+ cmd: "dashboard [--all|--days N] [--no-open] [--out <path>] [--refresh <sec>|--no-refresh]",
590
590
  ja: "HTML\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3092\u751F\u6210\u3057\u3066\u30D6\u30E9\u30A6\u30B6\u3067\u958B\u304F",
591
591
  en: "Generate and open the HTML dashboard"
592
592
  },
@@ -683,18 +683,18 @@ async function main(argv) {
683
683
  const text = await readStdin();
684
684
  const codex = rest.includes("--codex");
685
685
  try {
686
- const trackMod = await import("./track-PXFSGMGL.js");
686
+ const trackMod = await import("./track-QT2U3E2R.js");
687
687
  await trackMod.runTrack(text, { codex });
688
688
  } catch {
689
689
  }
690
690
  return 0;
691
691
  }
692
692
  case "init": {
693
- const { runInit } = await import("./setup-PTO5N46B.js");
693
+ const { runInit } = await import("./setup-W3CSTHJC.js");
694
694
  return await runInit(rest);
695
695
  }
696
696
  case "uninstall": {
697
- const { runUninstall } = await import("./setup-PTO5N46B.js");
697
+ const { runUninstall } = await import("./setup-W3CSTHJC.js");
698
698
  return await runUninstall(rest);
699
699
  }
700
700
  case "doctor":
@@ -702,27 +702,27 @@ async function main(argv) {
702
702
  case "report":
703
703
  return await runReport(rest);
704
704
  case "dashboard": {
705
- const { runDashboard } = await import("./dashboard-TH3NOVT3.js");
705
+ const { runDashboard } = await import("./dashboard-GYKABTTN.js");
706
706
  return await runDashboard(rest);
707
707
  }
708
708
  case "sweep": {
709
- const { runSweep } = await import("./sweep-25EZAOGC.js");
709
+ const { runSweep } = await import("./sweep-IBSV4LRD.js");
710
710
  return await runSweep(rest);
711
711
  }
712
712
  case "history": {
713
- const { runHistory } = await import("./history-DLFYQJFM.js");
713
+ const { runHistory } = await import("./history-6OUJLXJT.js");
714
714
  return await runHistory(rest);
715
715
  }
716
716
  case "budget": {
717
- const { runBudget } = await import("./budget-KKTFYAXH.js");
717
+ const { runBudget } = await import("./budget-EHWPEYXY.js");
718
718
  return runBudget(rest);
719
719
  }
720
720
  case "mute": {
721
- const { runMute } = await import("./mute-EPLYH67P.js");
721
+ const { runMute } = await import("./mute-GBDNN5PB.js");
722
722
  return runMute(rest);
723
723
  }
724
724
  case "unmute": {
725
- const { runUnmute } = await import("./mute-EPLYH67P.js");
725
+ const { runUnmute } = await import("./mute-GBDNN5PB.js");
726
726
  return runUnmute();
727
727
  }
728
728
  case "--version":
@@ -3,10 +3,12 @@ import {
3
3
  browserOpenPlan,
4
4
  runDashboard,
5
5
  writeDashboardHtml
6
- } from "./chunk-QX5KIRSU.js";
6
+ } from "./chunk-2VPBBIDW.js";
7
7
  import "./chunk-DGXUSPS4.js";
8
+ import "./chunk-ZHIZZ6V5.js";
9
+ import "./chunk-J6Y3RMMC.js";
8
10
  import "./chunk-J5QAYTFE.js";
9
- import "./chunk-ECADO26T.js";
11
+ import "./chunk-26CISNOE.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-ZHIZZ6V5.js";
5
+ import {
6
+ waitForDataLock
7
+ } from "./chunk-J6Y3RMMC.js";
8
+ import {
9
+ paths
10
+ } from "./chunk-26CISNOE.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,8 +3,8 @@ import {
3
3
  fmtMuteUntil,
4
4
  runMute,
5
5
  runUnmute
6
- } from "./chunk-4RZ6OGTD.js";
7
- import "./chunk-ECADO26T.js";
6
+ } from "./chunk-TUZVISLD.js";
7
+ import "./chunk-26CISNOE.js";
8
8
  export {
9
9
  fmtMuteUntil,
10
10
  runMute,
@@ -3,12 +3,12 @@ import {
3
3
  matchesMarker,
4
4
  runInit,
5
5
  runUninstall
6
- } from "./chunk-7KVOQ4UZ.js";
6
+ } from "./chunk-5LHZOZZO.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-ECADO26T.js";
11
+ import "./chunk-26CISNOE.js";
12
12
  export {
13
13
  matchesMarker,
14
14
  runInit,