whatileaked 0.2.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +8 -1
  2. package/dist/cli.js +88 -25
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # whatileaked
2
2
 
3
+ [![ci](https://github.com/selan-ai/whatileaked/actions/workflows/ci.yml/badge.svg)](https://github.com/selan-ai/whatileaked/actions/workflows/ci.yml)
4
+
3
5
  Your coding agent saves every conversation to your disk. If you ever pasted an
4
6
  API key into a chat, or asked the agent to read a `.env` file, that key is now
5
7
  sitting in a log file on your laptop — and it was uploaded to Anthropic or
@@ -193,7 +195,12 @@ npx whatileaked scan || echo "found something"
193
195
  | Agent | Folder |
194
196
  | --- | --- |
195
197
  | Claude Code | `~/.claude/projects/` · `~/.claude/CLAUDE.md` |
196
- | Codex | `~/.codex/sessions/` · `~/.codex/AGENTS.md` |
198
+ | Codex | `~/.codex/sessions/` · `~/.codex/archived_sessions/` · `~/.codex/AGENTS.md` |
199
+ | Cursor | `~/.cursor/projects/*/agent-transcripts/` |
200
+
201
+ Cursor keeps its older Ask-mode chats in SQLite rather than jsonl, and those are
202
+ not scanned. Reading them needs `node:sqlite`, which arrived in Node 22.5, and
203
+ this runs on Node 20. Agent transcripts are covered; Ask history is not.
197
204
 
198
205
  Transcripts record credentials that were already sent. Instruction and memory
199
206
  files are worse: they are read again at the start of the next session, so a
package/dist/cli.js CHANGED
@@ -2524,7 +2524,8 @@ async function walkDirectories(root) {
2524
2524
  // src/transcript/entry.ts
2525
2525
  var SourceName = {
2526
2526
  "claude-code": "claude-code",
2527
- codex: "codex"
2527
+ codex: "codex",
2528
+ cursor: "cursor"
2528
2529
  };
2529
2530
 
2530
2531
  // src/transcript/shape.ts
@@ -2556,16 +2557,18 @@ var ClaudeCodeSource = class {
2556
2557
  for (const slug of await listDirectories(root)) {
2557
2558
  const dir = join2(root, slug);
2558
2559
  let project = slug;
2559
- for (const file of await listFiles(dir, FileExtension.jsonl)) {
2560
- const path = join2(dir, file);
2561
- const cwd = await firstCwd(path);
2562
- if (cwd !== null) project = basename(cwd);
2563
- yield {
2564
- source: this.name,
2565
- kind: FileKind.transcript,
2566
- path,
2567
- project: cwd === null ? slug : basename(cwd)
2568
- };
2560
+ for (const nested of await walkDirectories(dir)) {
2561
+ for (const file of await listFiles(nested, FileExtension.jsonl)) {
2562
+ const path = join2(nested, file);
2563
+ const cwd = await firstCwd(path);
2564
+ if (cwd !== null) project = basename(cwd);
2565
+ yield {
2566
+ source: this.name,
2567
+ kind: FileKind.transcript,
2568
+ path,
2569
+ project: cwd === null ? slug : basename(cwd)
2570
+ };
2571
+ }
2569
2572
  }
2570
2573
  for (const nested of await walkDirectories(join2(dir, "memory"))) {
2571
2574
  for (const file of await listFiles(nested, FileExtension.markdown)) {
@@ -2603,17 +2606,18 @@ var CodexSource = class {
2603
2606
  if (instructions !== null) {
2604
2607
  yield { source: this.name, kind: FileKind.memory, path: instructions, project: this.name };
2605
2608
  }
2606
- const root = join3(this.#home, ".codex", "sessions");
2607
- for (const dir of await walkDirectories(root)) {
2608
- for (const file of await listFiles(dir, FileExtension.jsonl)) {
2609
- const path = join3(dir, file);
2610
- const cwd = await firstCwd2(path);
2611
- yield {
2612
- source: this.name,
2613
- kind: FileKind.transcript,
2614
- path,
2615
- project: cwd === null ? basename2(dir) : basename2(cwd)
2616
- };
2609
+ for (const root of ["sessions", "archived_sessions"]) {
2610
+ for (const dir of await walkDirectories(join3(this.#home, ".codex", root))) {
2611
+ for (const file of await listFiles(dir, FileExtension.jsonl)) {
2612
+ const path = join3(dir, file);
2613
+ const cwd = await firstCwd2(path);
2614
+ yield {
2615
+ source: this.name,
2616
+ kind: FileKind.transcript,
2617
+ path,
2618
+ project: cwd === null ? basename2(dir) : basename2(cwd)
2619
+ };
2620
+ }
2617
2621
  }
2618
2622
  }
2619
2623
  }
@@ -2630,6 +2634,61 @@ async function firstCwd2(path) {
2630
2634
  return null;
2631
2635
  }
2632
2636
 
2637
+ // src/sources/cursor-source.ts
2638
+ import { stat as stat2 } from "fs/promises";
2639
+ import { basename as basename3, join as join4 } from "path";
2640
+ var CursorSource = class {
2641
+ name = SourceName.cursor;
2642
+ #home;
2643
+ constructor(home2) {
2644
+ this.#home = home2;
2645
+ }
2646
+ async *discover() {
2647
+ const root = join4(this.#home, ".cursor", "projects");
2648
+ for (const slug of await listDirectories(root)) {
2649
+ const project = await projectName(slug);
2650
+ for (const dir of await walkDirectories(join4(root, slug, "agent-transcripts"))) {
2651
+ for (const file of await listFiles(dir, FileExtension.jsonl)) {
2652
+ yield {
2653
+ source: this.name,
2654
+ kind: FileKind.transcript,
2655
+ path: join4(dir, file),
2656
+ project
2657
+ };
2658
+ }
2659
+ }
2660
+ }
2661
+ }
2662
+ };
2663
+ async function projectName(slug) {
2664
+ const path = await resolveSlug(slug);
2665
+ return path === null ? slug : basename3(path);
2666
+ }
2667
+ async function resolveSlug(slug) {
2668
+ const parts = slug.split("-");
2669
+ let path = "";
2670
+ let index = 0;
2671
+ while (index < parts.length) {
2672
+ let end = index;
2673
+ let candidate = `${path}/${parts[index]}`;
2674
+ while (!await isDirectory(candidate) && end + 1 < parts.length) {
2675
+ end++;
2676
+ candidate = `${candidate}-${parts[end]}`;
2677
+ }
2678
+ if (!await isDirectory(candidate)) return null;
2679
+ path = candidate;
2680
+ index = end + 1;
2681
+ }
2682
+ return path === "" ? null : path;
2683
+ }
2684
+ async function isDirectory(path) {
2685
+ try {
2686
+ return (await stat2(path)).isDirectory();
2687
+ } catch {
2688
+ return false;
2689
+ }
2690
+ }
2691
+
2633
2692
  // src/wipe/confirm.ts
2634
2693
  import { createInterface as createInterface3 } from "readline/promises";
2635
2694
  var CONFIRMATION = "wipe";
@@ -2646,7 +2705,7 @@ var TerminalPrompt = class {
2646
2705
  };
2647
2706
 
2648
2707
  // src/wipe/wipe-run.ts
2649
- import { readFile, rename, stat as stat2, writeFile } from "fs/promises";
2708
+ import { readFile, rename, stat as stat3, writeFile } from "fs/promises";
2650
2709
 
2651
2710
  // src/wipe/redact.ts
2652
2711
  function placeholder(rule) {
@@ -2734,7 +2793,7 @@ var WipeRun = class {
2734
2793
  redacted += secrets.size;
2735
2794
  return redactLine(line, secrets);
2736
2795
  });
2737
- const { mode } = await stat2(file);
2796
+ const { mode } = await stat3(file);
2738
2797
  const temporary = `${file}.whatileaked-tmp`;
2739
2798
  await writeFile(temporary, rewritten.join("\n"), { encoding: "utf8", mode });
2740
2799
  await rename(temporary, file);
@@ -2774,7 +2833,11 @@ async function main(argv) {
2774
2833
  for (const line of home()) write(line);
2775
2834
  return 0;
2776
2835
  }
2777
- const sources = [new ClaudeCodeSource(homedir()), new CodexSource(homedir())];
2836
+ const sources = [
2837
+ new ClaudeCodeSource(homedir()),
2838
+ new CodexSource(homedir()),
2839
+ new CursorSource(homedir())
2840
+ ];
2778
2841
  const scanner = new Scanner(new KeywordIndex(compileRules(SECRET_RULE_SOURCES)));
2779
2842
  return command === Command.wipe ? await wipe(sources, scanner, write) : await scan(sources, scanner, write);
2780
2843
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "whatileaked",
3
- "version": "0.2.0",
4
- "description": "Scan your local Claude Code and Codex transcripts for credentials you already sent to a model provider.",
3
+ "version": "0.3.1",
4
+ "description": "Scan your local Claude Code, Codex and Cursor transcripts for credentials you already sent to a model provider.",
5
5
  "type": "module",
6
6
  "imports": {
7
7
  "#scripts/*": "./scripts/*.ts",