failproofai 1.0.4-beta.2 → 1.0.4-beta.6
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +5 -5
- package/.next/standalone/.next/server/app/audit/page.js +3 -3
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +4 -4
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +4 -4
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +3 -3
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +2 -2
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1lh7m5d._.js → [externals]__0v1hz15._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1rqkg_y._.js → [externals]__1nb206a._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_08w6xzm._.js → _0otft92._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +4 -4
- package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0p-5p8u._.js → [root-of-the-server]__0-4ja6q._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1p2otjt._.js → [root-of-the-server]__00cr3xu._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__013jr2b._.js → [root-of-the-server]__04px77e._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__190won8._.js → [root-of-the-server]__0dxf928._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0s740oi._.js → [root-of-the-server]__0egr5s1._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01wy8d-._.js → [root-of-the-server]__0i0l1di._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0n_lxhg._.js → [root-of-the-server]__0kokc8z._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ctpynv._.js → [root-of-the-server]__0n2txhk._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__02npjtd._.js → [root-of-the-server]__0r3c1_0._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ftmoxc._.js → [root-of-the-server]__17xnxlx._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0da85px._.js → [root-of-the-server]__1ckpmkk._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00-s7h8._.js → [root-of-the-server]__1fvfw59._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ui5ur8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_02b3i98._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0oq1dh6._.js → _04ng_6x._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1u8-lu2._.js → _07t3jol._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0h_douw._.js → _0s-77v5._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +4 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +22 -22
- package/.next/standalone/.next/static/chunks/{1pb1oztsbwcss.js → 07c6l1lq5_69b.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0o-hh5_turzlz.css → 0jpd8dv2vk930.css} +1 -1
- package/.next/standalone/.next/static/chunks/{32spub4wqjem-.js → 12bsgz8czvcnn.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3m4upvybtrexd.js → 1gn08w2pgqh09.js} +1 -1
- package/.next/standalone/.next/static/chunks/{010bv1w6j171t.js → 1xgq22tri86an.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2aquitk72k2op.js → 20xkiqzvfkuvm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2bi_1y0a_smt7.js → 2a84ug32tzik2.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2ej3b8gk5ittu.js → 32m2ryfankf_2.js} +1 -1
- package/.next/standalone/.next/static/chunks/3ioub_lpen-sq.js +1 -0
- package/.next/standalone/.next/static/chunks/{0wz8yftk18ts2.js → 3jxd133cw2vm3.js} +1 -1
- package/.next/standalone/.next/static/chunks/44ncu88eq6z71.js +1 -0
- package/.next/standalone/app/actions/get-leaks.ts +5 -1
- package/.next/standalone/app/actions/update-scheduled-audit.ts +4 -56
- package/.next/standalone/app/api/auth/status/route.ts +3 -2
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +29 -2
- package/.next/standalone/app/audit/_components/leak-section.tsx +15 -2
- package/.next/standalone/app/audit/audit-styles.css +18 -0
- package/.next/standalone/app/settings/page.tsx +1 -1
- package/.next/standalone/app/settings/settings-client.tsx +13 -39
- package/.next/standalone/lib/auth/api-server-client.ts +6 -6
- package/.next/standalone/lib/codex-sessions.ts +20 -4
- package/.next/standalone/lib/copilot-sessions.ts +32 -1
- package/.next/standalone/lib/cursor-sessions.ts +62 -5
- package/.next/standalone/lib/sqlite-reader.ts +78 -1
- package/.next/standalone/package.json +10 -10
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +1 -1
- package/dist/cli.mjs +1191 -1130
- package/dist/worker.mjs +198 -359
- package/lib/auth/api-server-client.ts +6 -6
- package/lib/codex-sessions.ts +20 -4
- package/lib/copilot-sessions.ts +32 -1
- package/lib/cursor-sessions.ts +62 -5
- package/lib/sqlite-reader.ts +78 -1
- package/package.json +10 -10
- package/pi-extension/index.ts +11 -4
- package/src/audit/cache.ts +6 -0
- package/src/audit/cli-adapters/claude.ts +13 -0
- package/src/audit/cli-login.ts +4 -1
- package/src/audit/cli.ts +67 -51
- package/src/audit/harm-report.ts +12 -6
- package/src/audit/index.ts +151 -8
- package/src/audit/leak-fingerprint.ts +2 -4
- package/src/audit/leak-record.ts +13 -9
- package/src/audit/leak-scan.ts +20 -44
- package/src/audit/leak-store.ts +5 -2
- package/src/audit/machine-store.ts +8 -9
- package/src/audit/macos-notifier.ts +2 -2
- package/src/audit/redact-example.ts +62 -5
- package/src/audit/report-harm.ts +31 -46
- package/src/audit/schedule-cli.ts +17 -33
- package/src/audit/types.ts +11 -0
- package/src/hooks/configure-wizard.ts +28 -0
- package/src/hooks/fp-config.ts +29 -54
- package/src/hooks/handler.ts +1 -59
- package/src/hooks/integrations.ts +4 -1
- package/src/hooks/notice.ts +23 -154
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rgu2r3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0-oij9d._.js +0 -23
- package/.next/standalone/.next/static/chunks/04r6ch8uf_n8m.js +0 -1
- package/.next/standalone/.next/static/chunks/0zebh1n9jkfbt.js +0 -1
- package/src/audit/leak-notice.ts +0 -161
- /package/.next/standalone/.next/static/{aKNv4Kmv98Xpwns0Xa-C- → aeM365dNTtUZOZvNkX8ba}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{aKNv4Kmv98Xpwns0Xa-C- → aeM365dNTtUZOZvNkX8ba}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{aKNv4Kmv98Xpwns0Xa-C- → aeM365dNTtUZOZvNkX8ba}/_ssgManifest.js +0 -0
package/src/audit/leak-notice.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deciding whether a finding still owes the user a notice — exactly once.
|
|
3
|
-
*
|
|
4
|
-
* ## Why this is not a field on the record
|
|
5
|
-
*
|
|
6
|
-
* The obvious design is `notifiedAt` on each finding, written back after the
|
|
7
|
-
* notice goes out. Measured against this repo's real `writeJsonAtomically`
|
|
8
|
-
* semantics, that fails 100% of the time under the conditions it actually runs
|
|
9
|
-
* in: two concurrent sessions with the same finding produced two notices, four
|
|
10
|
-
* produced four, eight produced eight. Worse, two concurrent sessions with
|
|
11
|
-
* DIFFERENT findings produced two notices and a surviving watermark containing
|
|
12
|
-
* only one of them — the loser's mark permanently gone, so it re-notified
|
|
13
|
-
* forever afterwards. `lib/atomic-write.ts` says so in its own header: atomic
|
|
14
|
-
* per FILE, last-writer-wins per CONTENT, i.e. a lost update on a map.
|
|
15
|
-
*
|
|
16
|
-
* Running several agents at once in one project is not an edge case here; it is
|
|
17
|
-
* how the tool is used.
|
|
18
|
-
*
|
|
19
|
-
* So the claim is one marker FILE per finding, created with `wx` — O_EXCL,
|
|
20
|
-
* which the kernel makes atomic across processes. Eight concurrent claimants
|
|
21
|
-
* produce exactly one winner, verified 3/3 trials. Concurrent claims on
|
|
22
|
-
* different findings all succeed and all survive, because they touch different
|
|
23
|
-
* files.
|
|
24
|
-
*
|
|
25
|
-
* ## Claim ordering, and which way to fail
|
|
26
|
-
*
|
|
27
|
-
* The marker is written BEFORE the notice reaches the stream, and the caller
|
|
28
|
-
* only keeps it if the notice was actually shaped. The alternative orderings
|
|
29
|
-
* were both measured: emit-then-mark nags forever if the process dies between
|
|
30
|
-
* them (5 sessions → 5 notices), and mark-then-never-emit swallows the alert
|
|
31
|
-
* permanently with nothing on disk saying so (total delivered: 0).
|
|
32
|
-
*
|
|
33
|
-
* Between "told twice" and "never told", a security tool picks told twice.
|
|
34
|
-
*/
|
|
35
|
-
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync, rmSync } from "node:fs";
|
|
36
|
-
import { resolve } from "node:path";
|
|
37
|
-
|
|
38
|
-
import { auditDir } from "../hooks/fp-home";
|
|
39
|
-
import { readLeakRecord, activeFindings } from "./leak-store";
|
|
40
|
-
import { isFindingId } from "./leak-fingerprint";
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Which way the user was told. Each channel claims independently.
|
|
44
|
-
*
|
|
45
|
-
* They are NOT one guarantee. The desktop banner and the in-CLI notice reach a
|
|
46
|
-
* person at different moments and fail in unrelated ways — and the banner has a
|
|
47
|
-
* failure it cannot detect at all: on a locked screen the shell accepts the
|
|
48
|
-
* call and returns an id while nobody sees anything. Letting a successful
|
|
49
|
-
* `Notify` claim the finding would then silence the in-session notice too, and
|
|
50
|
-
* the only channel that actually reached a human is the one we suppressed.
|
|
51
|
-
*
|
|
52
|
-
* So each channel gets its own marker directory and its own once-per-finding
|
|
53
|
-
* guarantee. A user at a desktop sees the banner AND the next session's notice;
|
|
54
|
-
* that is a bounded, deliberate duplicate, and it is the right side of the
|
|
55
|
-
* trade this file already made once (told twice beats never told).
|
|
56
|
-
*/
|
|
57
|
-
export type NoticeChannel = "cli" | "desktop";
|
|
58
|
-
|
|
59
|
-
/** One empty file per claimed finding, per channel. Cheap: a few hundred
|
|
60
|
-
* zero-byte files at the very worst, and the record itself is capped at 500
|
|
61
|
-
* findings. "cli" keeps the original directory name so an existing machine's
|
|
62
|
-
* claims survive the upgrade — renaming it would re-notify for every finding
|
|
63
|
-
* already delivered. */
|
|
64
|
-
const NOTICE_DIR = (home?: string, channel: NoticeChannel = "cli") =>
|
|
65
|
-
resolve(auditDir(home), channel === "cli" ? "notified" : `notified-${channel}`);
|
|
66
|
-
|
|
67
|
-
/** Markers older than this are swept, so the directory cannot grow forever on a
|
|
68
|
-
* machine that has been running for years. Comfortably longer than the
|
|
69
|
-
* record's own 90-day TTL, so a marker never outlives its finding. */
|
|
70
|
-
const MARKER_TTL_MS = 120 * 86_400_000;
|
|
71
|
-
|
|
72
|
-
export interface PendingNotice {
|
|
73
|
-
/** Findings that have never been notified about and are not dismissed. */
|
|
74
|
-
ids: string[];
|
|
75
|
-
count: number;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Findings that still owe the user a notice.
|
|
80
|
-
*
|
|
81
|
-
* Reads the record and subtracts what has already been claimed. Never throws:
|
|
82
|
-
* on any failure it returns "nothing pending", because a hook that cannot read
|
|
83
|
-
* its bookkeeping must stay silent rather than guess.
|
|
84
|
-
*/
|
|
85
|
-
export function pendingLeakNotice(home?: string, channel: NoticeChannel = "cli"): PendingNotice {
|
|
86
|
-
try {
|
|
87
|
-
const dir = NOTICE_DIR(home, channel);
|
|
88
|
-
const claimed = new Set(existsSync(dir) ? readdirSync(dir) : []);
|
|
89
|
-
const ids = activeFindings(readLeakRecord(home), home)
|
|
90
|
-
.map((f) => f.id)
|
|
91
|
-
.filter((id) => !claimed.has(id));
|
|
92
|
-
return { ids, count: ids.length };
|
|
93
|
-
} catch {
|
|
94
|
-
return { ids: [], count: 0 };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Claim these findings, atomically and per-finding.
|
|
100
|
-
*
|
|
101
|
-
* Returns the ids this process actually won. A caller that loses a race simply
|
|
102
|
-
* gets a shorter list — it never blocks, and it never overwrites a claim made
|
|
103
|
-
* by a concurrent session.
|
|
104
|
-
*/
|
|
105
|
-
export function markLeakNoticeDelivered(
|
|
106
|
-
ids: string[],
|
|
107
|
-
home?: string,
|
|
108
|
-
channel: NoticeChannel = "cli",
|
|
109
|
-
): string[] {
|
|
110
|
-
const won: string[] = [];
|
|
111
|
-
try {
|
|
112
|
-
const dir = NOTICE_DIR(home, channel);
|
|
113
|
-
mkdirSync(dir, { recursive: true });
|
|
114
|
-
for (const id of ids) {
|
|
115
|
-
// An id is about to become a filename. Anything that is not the exact
|
|
116
|
-
// shape `fingerprintId` mints is refused rather than resolved — see
|
|
117
|
-
// `isFindingId`. Refusing also means NOT reporting it as won, so a caller
|
|
118
|
-
// treats it as unclaimed and never records it as delivered.
|
|
119
|
-
if (!isFindingId(id)) continue;
|
|
120
|
-
try {
|
|
121
|
-
// `wx` is O_EXCL: it throws EEXIST if another process got here first.
|
|
122
|
-
// That throw IS the lock — there is no read-then-write window for two
|
|
123
|
-
// processes to both pass through.
|
|
124
|
-
writeFileSync(resolve(dir, id), "", { flag: "wx", mode: 0o600 });
|
|
125
|
-
won.push(id);
|
|
126
|
-
} catch {
|
|
127
|
-
// Already claimed by a concurrent session. Correct outcome: the user
|
|
128
|
-
// is told once, by whichever session got there first.
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
} catch {
|
|
132
|
-
// Cannot claim at all — the caller has already decided to emit, and being
|
|
133
|
-
// told twice beats never being told.
|
|
134
|
-
}
|
|
135
|
-
return won;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Drop markers for findings that no longer exist, and very old ones. */
|
|
139
|
-
export function pruneNoticeMarkers(home?: string, nowMs = Date.now()): void {
|
|
140
|
-
try {
|
|
141
|
-
const live = new Set(readLeakRecord(home).findings.map((f) => f.id));
|
|
142
|
-
for (const channel of ["cli", "desktop"] as const) {
|
|
143
|
-
const dir = NOTICE_DIR(home, channel);
|
|
144
|
-
if (!existsSync(dir)) continue;
|
|
145
|
-
for (const name of readdirSync(dir)) {
|
|
146
|
-
const path = resolve(dir, name);
|
|
147
|
-
let stale = !live.has(name);
|
|
148
|
-
if (!stale) {
|
|
149
|
-
try {
|
|
150
|
-
stale = nowMs - statSync(path).mtimeMs > MARKER_TTL_MS;
|
|
151
|
-
} catch {
|
|
152
|
-
stale = false;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (stale) rmSync(path, { force: true });
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
} catch {
|
|
159
|
-
// Housekeeping only.
|
|
160
|
-
}
|
|
161
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|