pi-harness-runtime 0.9.7 → 0.9.11

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 (134) hide show
  1. package/CHANGELOG.md +24 -12
  2. package/README.md +42 -5
  3. package/footer-status.ts +108 -11
  4. package/harness/e2e/minimax-quota-scraper.ts +46 -4
  5. package/index.ts +224 -16
  6. package/mirror.ts +194 -18
  7. package/package.json +1 -1
  8. package/packages/a2a-adapter/package.json +1 -1
  9. package/packages/auth/package.json +1 -1
  10. package/packages/autonomous-refactor/package.json +1 -1
  11. package/packages/cache-strategy/package.json +1 -1
  12. package/packages/capability-registry/package.json +1 -1
  13. package/packages/checkpoint/package.json +1 -1
  14. package/packages/cli-plugin-sdk/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/context-compiler/package.json +1 -1
  19. package/packages/context-manager/package.json +1 -1
  20. package/packages/cookie-sanitizer/README.md +141 -0
  21. package/packages/cookie-sanitizer/dist/atomic-write.d.ts +38 -0
  22. package/packages/cookie-sanitizer/dist/atomic-write.d.ts.map +1 -0
  23. package/packages/cookie-sanitizer/dist/atomic-write.js +67 -0
  24. package/packages/cookie-sanitizer/dist/atomic-write.js.map +1 -0
  25. package/packages/cookie-sanitizer/dist/detect-format.d.ts +36 -0
  26. package/packages/cookie-sanitizer/dist/detect-format.d.ts.map +1 -0
  27. package/packages/cookie-sanitizer/dist/detect-format.js +88 -0
  28. package/packages/cookie-sanitizer/dist/detect-format.js.map +1 -0
  29. package/packages/cookie-sanitizer/dist/index.d.ts +41 -0
  30. package/packages/cookie-sanitizer/dist/index.d.ts.map +1 -0
  31. package/packages/cookie-sanitizer/dist/index.js +39 -0
  32. package/packages/cookie-sanitizer/dist/index.js.map +1 -0
  33. package/packages/cookie-sanitizer/dist/infer-provider.d.ts +29 -0
  34. package/packages/cookie-sanitizer/dist/infer-provider.d.ts.map +1 -0
  35. package/packages/cookie-sanitizer/dist/infer-provider.js +96 -0
  36. package/packages/cookie-sanitizer/dist/infer-provider.js.map +1 -0
  37. package/packages/cookie-sanitizer/dist/normalize.d.ts +26 -0
  38. package/packages/cookie-sanitizer/dist/normalize.d.ts.map +1 -0
  39. package/packages/cookie-sanitizer/dist/normalize.js +75 -0
  40. package/packages/cookie-sanitizer/dist/normalize.js.map +1 -0
  41. package/packages/cookie-sanitizer/dist/parse-json.d.ts +45 -0
  42. package/packages/cookie-sanitizer/dist/parse-json.d.ts.map +1 -0
  43. package/packages/cookie-sanitizer/dist/parse-json.js +108 -0
  44. package/packages/cookie-sanitizer/dist/parse-json.js.map +1 -0
  45. package/packages/cookie-sanitizer/dist/parse-netscape.d.ts +43 -0
  46. package/packages/cookie-sanitizer/dist/parse-netscape.d.ts.map +1 -0
  47. package/packages/cookie-sanitizer/dist/parse-netscape.js +99 -0
  48. package/packages/cookie-sanitizer/dist/parse-netscape.js.map +1 -0
  49. package/packages/cookie-sanitizer/dist/sync.d.ts +62 -0
  50. package/packages/cookie-sanitizer/dist/sync.d.ts.map +1 -0
  51. package/packages/cookie-sanitizer/dist/sync.js +266 -0
  52. package/packages/cookie-sanitizer/dist/sync.js.map +1 -0
  53. package/packages/cookie-sanitizer/dist/types.d.ts +100 -0
  54. package/packages/cookie-sanitizer/dist/types.d.ts.map +1 -0
  55. package/packages/cookie-sanitizer/dist/types.js +13 -0
  56. package/packages/cookie-sanitizer/dist/types.js.map +1 -0
  57. package/packages/cookie-sanitizer/dist/watcher.d.ts +51 -0
  58. package/packages/cookie-sanitizer/dist/watcher.d.ts.map +1 -0
  59. package/packages/cookie-sanitizer/dist/watcher.js +148 -0
  60. package/packages/cookie-sanitizer/dist/watcher.js.map +1 -0
  61. package/packages/cookie-sanitizer/package.json +27 -0
  62. package/packages/cookie-sanitizer/src/atomic-write.ts +79 -0
  63. package/packages/cookie-sanitizer/src/detect-format.ts +102 -0
  64. package/packages/cookie-sanitizer/src/index.ts +89 -0
  65. package/packages/cookie-sanitizer/src/infer-provider.ts +103 -0
  66. package/packages/cookie-sanitizer/src/normalize.ts +92 -0
  67. package/packages/cookie-sanitizer/src/parse-json.ts +122 -0
  68. package/packages/cookie-sanitizer/src/parse-netscape.ts +118 -0
  69. package/packages/cookie-sanitizer/src/sync.ts +304 -0
  70. package/packages/cookie-sanitizer/src/types.ts +101 -0
  71. package/packages/cookie-sanitizer/src/watcher.ts +180 -0
  72. package/packages/cookie-sanitizer/test/infer.test.ts +110 -0
  73. package/packages/cookie-sanitizer/test/normalize.test.ts +113 -0
  74. package/packages/cookie-sanitizer/test/parse.test.ts +204 -0
  75. package/packages/cookie-sanitizer/test/sync.test.ts +231 -0
  76. package/packages/cookie-sanitizer/test/watcher.test.ts +27 -0
  77. package/packages/cookie-sanitizer/tsconfig.json +8 -0
  78. package/packages/cost-optimizer/package.json +1 -1
  79. package/packages/django-plugin/package.json +1 -1
  80. package/packages/doc-generator/package.json +1 -1
  81. package/packages/evaluation-engine/package.json +1 -1
  82. package/packages/evaluation-runner/package.json +1 -1
  83. package/packages/experience-replay/package.json +1 -1
  84. package/packages/feedback-collector/package.json +1 -1
  85. package/packages/framework-detector/package.json +1 -1
  86. package/packages/framework-plugin-sdk/package.json +1 -1
  87. package/packages/frappe-plugin/package.json +1 -1
  88. package/packages/generic-web-plugin/package.json +1 -1
  89. package/packages/health-monitor/package.json +1 -1
  90. package/packages/laravel-plugin/package.json +1 -1
  91. package/packages/learning-engine/package.json +1 -1
  92. package/packages/mcp-adapter/package.json +1 -1
  93. package/packages/memory-engine/package.json +1 -1
  94. package/packages/model-registry/package.json +1 -1
  95. package/packages/nextjs-plugin/package.json +1 -1
  96. package/packages/notification/package.json +1 -1
  97. package/packages/observability/package.json +1 -1
  98. package/packages/performance-optimizer/package.json +1 -1
  99. package/packages/project-analyzer/package.json +1 -1
  100. package/packages/prompt-compiler/package.json +1 -1
  101. package/packages/prompt-versioning/package.json +1 -1
  102. package/packages/provider-adapter-sdk/package.json +1 -1
  103. package/packages/provider-router/package.json +1 -1
  104. package/packages/provider-selector/package.json +1 -1
  105. package/packages/providers/dist/index.d.ts +1 -0
  106. package/packages/providers/dist/index.d.ts.map +1 -1
  107. package/packages/providers/dist/index.js +1 -0
  108. package/packages/providers/dist/index.js.map +1 -1
  109. package/packages/providers/dist/provider-id.d.ts +39 -0
  110. package/packages/providers/dist/provider-id.d.ts.map +1 -0
  111. package/packages/providers/dist/provider-id.js +96 -0
  112. package/packages/providers/dist/provider-id.js.map +1 -0
  113. package/packages/providers/package.json +1 -1
  114. package/packages/providers/src/index.ts +1 -0
  115. package/packages/providers/src/provider-id.ts +110 -0
  116. package/packages/quota-manager/package.json +1 -1
  117. package/packages/rate-limiter/package.json +1 -1
  118. package/packages/react-vite-plugin/package.json +1 -1
  119. package/packages/release-manager/package.json +1 -1
  120. package/packages/requirement-compiler/package.json +1 -1
  121. package/packages/runtime/package.json +1 -1
  122. package/packages/scheduler/package.json +1 -1
  123. package/packages/session/package.json +1 -1
  124. package/packages/shared-context/package.json +1 -1
  125. package/packages/skill-registry/package.json +1 -1
  126. package/packages/sprint-planner/package.json +1 -1
  127. package/packages/task-compiler/package.json +1 -1
  128. package/packages/test-data-generator/package.json +1 -1
  129. package/packages/test-generator/package.json +1 -1
  130. package/packages/token-estimation/package.json +1 -1
  131. package/packages/token-optimizer/package.json +1 -1
  132. package/packages/tui/package.json +1 -1
  133. package/packages/types/package.json +1 -1
  134. package/packages/worktree/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/codex-adapter",
3
- "version": "0.9.6",
3
+ "version": "0.9.11",
4
4
  "type": "module",
5
5
  "description": "OpenAI Codex provider adapter for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/context-compiler",
3
- "version": "0.9.6",
3
+ "version": "0.9.11",
4
4
  "description": "Bounded context selection engine for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/context-manager",
3
- "version": "0.9.6",
3
+ "version": "0.9.11",
4
4
  "description": "Context Window Manager — prevents model context overflow via compaction and resume prompts (RFC-0010)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,141 @@
1
+ # @pi-harness/cookie-sanitizer
2
+
3
+ > Forgiving input → strict canonical cache for provider-quota mirroring.
4
+
5
+ A normalization boundary that lets users drop **any** cookie file (any name, any format) into a known folder, and turns it into a single standard Netscape file the rest of the runtime can rely on.
6
+
7
+ ```
8
+ ~/.pi-harness-runtime/cookies/ ← user drops anything here
9
+ │
10
+ ▼
11
+ Cookie Sanitizer ← THIS PACKAGE
12
+ │
13
+ ▼
14
+ ~/.config/<provider>-cookies.txt ← canonical, runtime-owned
15
+ │
16
+ ▼
17
+ MiniMaxQuotaScraper (unchanged) ← reads only the canonical
18
+ ```
19
+
20
+ ## Why
21
+
22
+ The previous design expected users to know the exact path (`~/.config/minimax-cookies.txt`) and the exact format (Netscape). On a fresh machine, no-one ever set this up — the scraper silently no-op'd and the footer lost its `5h:` / `week:` lines. This package is the discoverable, forgiving adapter between messy human input and the clean contract downstream code already understands.
23
+
24
+ ## What it accepts
25
+
26
+ **Input formats** (auto-detected per file):
27
+
28
+ 1. **Netscape HTTP Cookie File** — the canonical format
29
+ 2. **EditThisCookie / Chrome devtools JSON export** — array of records in the Chrome/Edge cookie store shape
30
+
31
+ **File names** — any name in the drop folder. Inference prefers:
32
+
33
+ 1. Filename hint (`minimax-*.txt`, `*claude*`, etc.) → provider
34
+ 2. Dominant cookie domain (≥ 80% match) → provider
35
+ 3. `providerHint` option overrides all of the above
36
+
37
+ **Provider scope** — currently shipping with: `minimax`, `anthropic`, `openai`, `glm`, `openrouter`. The registry is open and inference by domain works for any provider whose cookies are in the drop folder.
38
+
39
+ ## Usage
40
+
41
+ ### One-shot sync
42
+
43
+ ```typescript
44
+ import { sync } from "@pi-harness/cookie-sanitizer";
45
+
46
+ const result = sync();
47
+
48
+ if (result.wrote) {
49
+ console.log(`Synced ${result.totalCookies} cookies for ${result.provider} → ${result.cachePath}`);
50
+ } else {
51
+ console.log(`No-op: ${result.errors[0]?.message ?? "nothing to sync"}`);
52
+ }
53
+ ```
54
+
55
+ ### Live watcher
56
+
57
+ ```typescript
58
+ import { CookieWatcher } from "@pi-harness/cookie-sanitizer";
59
+
60
+ const watcher = new CookieWatcher({
61
+ dropDir: "~/.pi-harness-runtime/cookies",
62
+ onEvent: (e) => {
63
+ if (e.kind === "sync-ok") console.log(`Synced ${e.cookies} cookies → ${e.cachePath}`);
64
+ if (e.kind === "sync-error") console.error(`Sync error: ${e.message}`);
65
+ },
66
+ });
67
+
68
+ watcher.start();
69
+ // ... later
70
+ await watcher.stop();
71
+ ```
72
+
73
+ ### Custom drop folder / cache path
74
+
75
+ ```typescript
76
+ import { sync } from "@pi-harness/cookie-sanitizer";
77
+
78
+ const result = sync({
79
+ dropDir: "/var/spool/cookies",
80
+ cachePath: "/var/spool/canonical/minimax-cookies.txt",
81
+ providerHint: "minimax", // skip inference
82
+ });
83
+ ```
84
+
85
+ ## Storage contract
86
+
87
+ | Path | Owned by | Mode | Format |
88
+ |---|---|---|---|
89
+ | `~/.pi-harness-runtime/cookies/` | user (writes freely) | `0o644` | anything |
90
+ | `~/.config/<provider>-cookies.txt` | sanitizer (single writer) | `0o600` | standard Netscape |
91
+
92
+ The sanitizer is the **only** writer of the canonical cache. Manual edits are overwritten on next sync — by design. If you want a manual override, set `QUOTA_COOKIE_FILE` env var to a different path; the sanitizer will then skip its work and the scraper reads the override directly.
93
+
94
+ ## What is NOT logged
95
+
96
+ - Cookie **values** — never. Diagnostics use file basenames and 64-char previews with values redacted.
97
+ - Cookie **paths** (except the canonical cache path, which is a configuration location).
98
+ - Cookie **domains** are OK to print (public).
99
+
100
+ ## Tests
101
+
102
+ ```bash
103
+ bun test # all package tests
104
+ bun test test/parse.test.ts
105
+ bun test test/sync.test.ts
106
+ ```
107
+
108
+ ## Security
109
+
110
+ - The canonical cache is `0o600` (owner R/W only) — atomic write with `0o600` parent, `fsync` before `rename`, `chmod` to `0o600` after rename.
111
+ - The drop folder is gitignored by convention (`.gitleaks.toml` allowlists `cookies.txt` files in both locations).
112
+ - `chokidar` v5 watches the drop folder; sandboxed to `depth: 2` so we don't follow symlinks into the rest of the home directory.
113
+
114
+ ## Files
115
+
116
+ ```
117
+ src/
118
+ ├── index.ts # public API surface
119
+ ├── types.ts # canonical types
120
+ ├── detect-format.ts # Netscape vs JSON detection
121
+ ├── parse-netscape.ts # Netscape parser + serializer
122
+ ├── parse-json.ts # EditThisCookie JSON parser
123
+ ├── normalize.ts # dedupe, expire, domain-match
124
+ ├── infer-provider.ts # filename + domain inference
125
+ ├── atomic-write.ts # POSIX atomic write
126
+ ├── sync.ts # orchestrator
127
+ └── watcher.ts # chokidar v5 wrapper
128
+
129
+ test/
130
+ ├── parse.test.ts # 14 cases — Netscape + JSON
131
+ ├── normalize.test.ts # 12 cases — dedupe/expire/domain
132
+ ├── infer.test.ts # 13 cases — filename + domain + combined
133
+ ├── sync.test.ts # 12 cases — end-to-end
134
+ └── watcher.test.ts # 2 cases — config sanity (chokidar is exercised live)
135
+ ```
136
+
137
+ ## Limitations
138
+
139
+ - Watcher tests are light-touch (chokidar needs a real OS-level fs watcher). Real behavior is exercised by the live runtime.
140
+ - Multi-format inputs (e.g. raw `Cookie:` headers, single-line `k=v`) are deferred. Add on demand.
141
+ - Provider scope is currently hard-coded to a small set. Adding a new provider = adding an entry in `infer-provider.ts`.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Atomic file write — write to temp, fsync, then rename.
3
+ *
4
+ * POSIX `rename(2)` is atomic — on partial-write crash, the prior target
5
+ * file remains valid. We pair this with `fsync(tempfile)` so the temp
6
+ * file's data is durable on disk before the rename.
7
+ *
8
+ * Permissions: `0o600` (owner R/W only) to keep the canonical cache
9
+ * owner-readable.
10
+ *
11
+ * Sec-001: Cookie values go into the file content. The caller is
12
+ * responsible for value-handling hygiene; this module just writes
13
+ * bytes. We do not log the content.
14
+ */
15
+ import type { ProviderId } from "./types.js";
16
+ /**
17
+ * Atomically write `content` to `finalPath`.
18
+ *
19
+ * Steps:
20
+ * 1. mkdir -p the parent directory
21
+ * 2. write to <finalPath>.tmp.<rand> with mode 0o600
22
+ * 3. fsync the temp file
23
+ * 4. rename temp → final (atomic on POSIX)
24
+ * 5. chmod final to 0o600 (in case rename preserved looser perms)
25
+ *
26
+ * Throws if any step fails. The original `finalPath` is unchanged
27
+ * until step 4 succeeds.
28
+ */
29
+ export declare function atomicWriteFileSync(finalPath: string, content: string): void;
30
+ /**
31
+ * Compute the default cache path for a given provider, e.g.:
32
+ * defaultCachePathFor("minimax") → "~/.config/minimax-cookies.txt"
33
+ *
34
+ * The home directory is read via `process.env.HOME` so tests can
35
+ * override it.
36
+ */
37
+ export declare function defaultCachePathFor(provider: ProviderId): string;
38
+ //# sourceMappingURL=atomic-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-write.d.ts","sourceRoot":"","sources":["../src/atomic-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAaH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAyB5E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAGhE"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Atomic file write — write to temp, fsync, then rename.
3
+ *
4
+ * POSIX `rename(2)` is atomic — on partial-write crash, the prior target
5
+ * file remains valid. We pair this with `fsync(tempfile)` so the temp
6
+ * file's data is durable on disk before the rename.
7
+ *
8
+ * Permissions: `0o600` (owner R/W only) to keep the canonical cache
9
+ * owner-readable.
10
+ *
11
+ * Sec-001: Cookie values go into the file content. The caller is
12
+ * responsible for value-handling hygiene; this module just writes
13
+ * bytes. We do not log the content.
14
+ */
15
+ import { mkdirSync, openSync, writeSync, fsyncSync, closeSync, renameSync, chmodSync, } from "node:fs";
16
+ import { dirname, join } from "node:path";
17
+ import { randomBytes } from "node:crypto";
18
+ /**
19
+ * Atomically write `content` to `finalPath`.
20
+ *
21
+ * Steps:
22
+ * 1. mkdir -p the parent directory
23
+ * 2. write to <finalPath>.tmp.<rand> with mode 0o600
24
+ * 3. fsync the temp file
25
+ * 4. rename temp → final (atomic on POSIX)
26
+ * 5. chmod final to 0o600 (in case rename preserved looser perms)
27
+ *
28
+ * Throws if any step fails. The original `finalPath` is unchanged
29
+ * until step 4 succeeds.
30
+ */
31
+ export function atomicWriteFileSync(finalPath, content) {
32
+ mkdirSync(dirname(finalPath), { recursive: true });
33
+ const rand = randomBytes(6).toString("hex");
34
+ const tempPath = `${finalPath}.tmp.${rand}`;
35
+ let fd;
36
+ try {
37
+ fd = openSync(tempPath, "w", 0o600);
38
+ writeSync(fd, Buffer.from(content, "utf8"));
39
+ fsyncSync(fd);
40
+ }
41
+ finally {
42
+ if (fd !== undefined)
43
+ closeSync(fd);
44
+ }
45
+ renameSync(tempPath, finalPath);
46
+ // Belt-and-braces in case rename preserved a looser mode from an
47
+ // existing file on some systems.
48
+ try {
49
+ chmodSync(finalPath, 0o600);
50
+ }
51
+ catch {
52
+ // Filesystems that don't support chmod (e.g. FAT32) are not a
53
+ // target platform here — drop the error silently.
54
+ }
55
+ }
56
+ /**
57
+ * Compute the default cache path for a given provider, e.g.:
58
+ * defaultCachePathFor("minimax") → "~/.config/minimax-cookies.txt"
59
+ *
60
+ * The home directory is read via `process.env.HOME` so tests can
61
+ * override it.
62
+ */
63
+ export function defaultCachePathFor(provider) {
64
+ const home = process.env.HOME ?? "/tmp";
65
+ return join(home, ".config", `${provider}-cookies.txt`);
66
+ }
67
+ //# sourceMappingURL=atomic-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../src/atomic-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACN,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB,EAAE,OAAe;IACrE,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEnD,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,GAAG,SAAS,QAAQ,IAAI,EAAE,CAAC;IAE5C,IAAI,EAAsB,CAAC;IAC3B,IAAI,CAAC;QACJ,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACpC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QAC5C,SAAS,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;YAAS,CAAC;QACV,IAAI,EAAE,KAAK,SAAS;YAAE,SAAS,CAAC,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAEhC,iEAAiE;IACjE,iCAAiC;IACjC,IAAI,CAAC;QACJ,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACR,8DAA8D;QAC9D,kDAAkD;IACnD,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAoB;IACvD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC;IACxC,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,QAAQ,cAAc,CAAC,CAAC;AACzD,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Format detection — does the input look like Netscape or EditThisCookie JSON?
3
+ *
4
+ * Detection rule:
5
+ * - File extension `.json` AND first non-blank byte is `[` or `{` → JSON
6
+ * - File content has `# Netscape HTTP Cookie File` header OR
7
+ * ≥ 1 tab-separated 7-column row → Netscape
8
+ * - Otherwise → unknown
9
+ *
10
+ * Cookie VALUES are never copied into return values that get logged.
11
+ * Only the basename and a 64-char prefix of the first non-blank line.
12
+ */
13
+ import type { CookieFileFormat, DetectedFile } from "./types.js";
14
+ /**
15
+ * Returns the basename only, never a path that could leak directory
16
+ * structure. Safe to log.
17
+ */
18
+ export declare function safeBasename(filePath: string): string;
19
+ /**
20
+ * Safely truncated preview of a file's content (for diagnostics).
21
+ * Strips cookie values to avoid leaking secrets.
22
+ *
23
+ * Heuristic: any line that has a tab-separated name=value pair,
24
+ * replace the value column with `<redacted>`.
25
+ */
26
+ export declare function safePreview(text: string, maxChars?: number): string;
27
+ /**
28
+ * Detect the format of cookie file text.
29
+ */
30
+ export declare function detectFormat(text: string): CookieFileFormat | null;
31
+ /**
32
+ * Build a DetectedFile from a path and its text contents.
33
+ * Used by sync.ts after reading the file from disk.
34
+ */
35
+ export declare function buildDetectedFile(filePath: string, text: string): DetectedFile;
36
+ //# sourceMappingURL=detect-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-format.d.ts","sourceRoot":"","sources":["../src/detect-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAKjE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,MAAM,CAa/D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CA+BlE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACV,YAAY,CASd"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Format detection — does the input look like Netscape or EditThisCookie JSON?
3
+ *
4
+ * Detection rule:
5
+ * - File extension `.json` AND first non-blank byte is `[` or `{` → JSON
6
+ * - File content has `# Netscape HTTP Cookie File` header OR
7
+ * ≥ 1 tab-separated 7-column row → Netscape
8
+ * - Otherwise → unknown
9
+ *
10
+ * Cookie VALUES are never copied into return values that get logged.
11
+ * Only the basename and a 64-char prefix of the first non-blank line.
12
+ */
13
+ import { basename } from "node:path";
14
+ const HEADER_RE = /^# Netscape HTTP Cookie File/im;
15
+ const MIN_TAB_COLS = 7;
16
+ /**
17
+ * Returns the basename only, never a path that could leak directory
18
+ * structure. Safe to log.
19
+ */
20
+ export function safeBasename(filePath) {
21
+ return basename(filePath);
22
+ }
23
+ /**
24
+ * Safely truncated preview of a file's content (for diagnostics).
25
+ * Strips cookie values to avoid leaking secrets.
26
+ *
27
+ * Heuristic: any line that has a tab-separated name=value pair,
28
+ * replace the value column with `<redacted>`.
29
+ */
30
+ export function safePreview(text, maxChars = 64) {
31
+ const firstLine = text
32
+ .split("\n")
33
+ .map((l) => l.trim())
34
+ .find((l) => l.length > 0) ?? "";
35
+ const redacted = firstLine
36
+ .split("\t")
37
+ .map((col, i) => (i >= 5 ? "<redacted>" : col))
38
+ .join("\t");
39
+ return redacted.length > maxChars
40
+ ? `${redacted.slice(0, maxChars)}…`
41
+ : redacted;
42
+ }
43
+ /**
44
+ * Detect the format of cookie file text.
45
+ */
46
+ export function detectFormat(text) {
47
+ if (typeof text !== "string" || text.length === 0)
48
+ return null;
49
+ const trimmed = text.trimStart();
50
+ const firstByte = trimmed[0];
51
+ // JSON heuristic — array or object at the top.
52
+ if (firstByte === "[" || firstByte === "{") {
53
+ return "edit-this-cookie-json";
54
+ }
55
+ // Netscape heuristic — header OR a tab-separated 7-column row.
56
+ if (HEADER_RE.test(text)) {
57
+ return "netscape";
58
+ }
59
+ const lines = text.split(/\r?\n/);
60
+ for (const line of lines) {
61
+ if (!line || (line.startsWith("#") && !line.startsWith("#HttpOnly_"))) {
62
+ continue;
63
+ }
64
+ const trimmedLine = line.startsWith("#HttpOnly_")
65
+ ? line.slice("#HttpOnly_".length)
66
+ : line;
67
+ const cols = trimmedLine.split("\t");
68
+ if (cols.length >= MIN_TAB_COLS) {
69
+ return "netscape";
70
+ }
71
+ }
72
+ return null;
73
+ }
74
+ /**
75
+ * Build a DetectedFile from a path and its text contents.
76
+ * Used by sync.ts after reading the file from disk.
77
+ */
78
+ export function buildDetectedFile(filePath, text) {
79
+ const format = detectFormat(text);
80
+ return {
81
+ path: filePath,
82
+ basename: safeBasename(filePath),
83
+ format,
84
+ text,
85
+ // No error if format is null — unknown format is a normal case.
86
+ };
87
+ }
88
+ //# sourceMappingURL=detect-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-format.js","sourceRoot":"","sources":["../src/detect-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC,MAAM,SAAS,GAAG,gCAAgC,CAAC;AACnD,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC5C,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAAQ,GAAG,EAAE;IACtD,MAAM,SAAS,GACd,IAAI;SACF,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,SAAS;SACxB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;SAC9C,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,OAAO,QAAQ,CAAC,MAAM,GAAG,QAAQ;QAChC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG;QACnC,CAAC,CAAC,QAAQ,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACxC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/D,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IACjC,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAE7B,+CAA+C;IAC/C,IAAI,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;QAC5C,OAAO,uBAAuB,CAAC;IAChC,CAAC;IAED,+DAA+D;IAC/D,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;YACvE,SAAS;QACV,CAAC;QACD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;YAChD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC;YACjC,CAAC,CAAC,IAAI,CAAC;QACR,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,IAAI,CAAC,MAAM,IAAI,YAAY,EAAE,CAAC;YACjC,OAAO,UAAU,CAAC;QACnB,CAAC;IACF,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAChC,QAAgB,EAChB,IAAY;IAEZ,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO;QACN,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,YAAY,CAAC,QAAQ,CAAC;QAChC,MAAM;QACN,IAAI;QACJ,gEAAgE;KAChE,CAAC;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Cookie Sanitizer — public API.
3
+ *
4
+ * Forgiving input → strict canonical cache for downstream consumers
5
+ * (e.g. MiniMaxQuotaScraper reads only `~/.config/minimax-cookies.txt`).
6
+ *
7
+ * The package is provider-agnostic but currently shipping with:
8
+ * - filename hints: minimax, anthropic, openai, glm, openrouter
9
+ * - domain hints : the matching provider domains
10
+ *
11
+ * Inputs accepted (auto-detected per file):
12
+ * - Netscape HTTP Cookie File
13
+ * - EditThisCookie / Chrome devtools JSON export
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { sync, hasAnyCookieSource, CookieWatcher } from "@pi-harness/cookie-sanitizer";
18
+ *
19
+ * // One-shot sync.
20
+ * const result = sync();
21
+ * console.log(result.provider, result.cachePath, result.totalCookies);
22
+ *
23
+ * // Live updates.
24
+ * const watcher = new CookieWatcher({
25
+ * dropDir: "~/.pi-harness-runtime/cookies",
26
+ * onEvent: (e) => console.log(e),
27
+ * });
28
+ * watcher.start();
29
+ * ```
30
+ */
31
+ export { sync, hasAnyCookieSource, inferCachePath, enumerateDropFiles, readInputFile, DEFAULT_DROP_DIR, } from "./sync.js";
32
+ export { parseNetscape, serializeNetscape, } from "./parse-netscape.js";
33
+ export { parseEditThisCookieJson } from "./parse-json.js";
34
+ export { inferProvider, inferProviderFromFilename, inferProviderFromCookies, knownProviders, } from "./infer-provider.js";
35
+ export { normalize, domainMatchesProvider, isExpired, } from "./normalize.js";
36
+ export { detectFormat, buildDetectedFile, safeBasename, safePreview, } from "./detect-format.js";
37
+ export { atomicWriteFileSync, defaultCachePathFor, } from "./atomic-write.js";
38
+ export { CookieWatcher, createWatcher, defaultDropDir, } from "./watcher.js";
39
+ export type { CanonicalCookie, CookieFileFormat, DetectedFile, ProviderId, SyncOptions, SyncResult, WatcherEvent, } from "./types.js";
40
+ export type { CookieWatcherConfig } from "./watcher.js";
41
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EACN,IAAI,EACJ,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACN,aAAa,EACb,iBAAiB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,EACN,aAAa,EACb,yBAAyB,EACzB,wBAAwB,EACxB,cAAc,GACd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACN,SAAS,EACT,qBAAqB,EACrB,SAAS,GACT,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACN,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,WAAW,GACX,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,aAAa,EACb,aAAa,EACb,cAAc,GACd,MAAM,cAAc,CAAC;AAEtB,YAAY,EACX,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,GACZ,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Cookie Sanitizer — public API.
3
+ *
4
+ * Forgiving input → strict canonical cache for downstream consumers
5
+ * (e.g. MiniMaxQuotaScraper reads only `~/.config/minimax-cookies.txt`).
6
+ *
7
+ * The package is provider-agnostic but currently shipping with:
8
+ * - filename hints: minimax, anthropic, openai, glm, openrouter
9
+ * - domain hints : the matching provider domains
10
+ *
11
+ * Inputs accepted (auto-detected per file):
12
+ * - Netscape HTTP Cookie File
13
+ * - EditThisCookie / Chrome devtools JSON export
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { sync, hasAnyCookieSource, CookieWatcher } from "@pi-harness/cookie-sanitizer";
18
+ *
19
+ * // One-shot sync.
20
+ * const result = sync();
21
+ * console.log(result.provider, result.cachePath, result.totalCookies);
22
+ *
23
+ * // Live updates.
24
+ * const watcher = new CookieWatcher({
25
+ * dropDir: "~/.pi-harness-runtime/cookies",
26
+ * onEvent: (e) => console.log(e),
27
+ * });
28
+ * watcher.start();
29
+ * ```
30
+ */
31
+ export { sync, hasAnyCookieSource, inferCachePath, enumerateDropFiles, readInputFile, DEFAULT_DROP_DIR, } from "./sync.js";
32
+ export { parseNetscape, serializeNetscape, } from "./parse-netscape.js";
33
+ export { parseEditThisCookieJson } from "./parse-json.js";
34
+ export { inferProvider, inferProviderFromFilename, inferProviderFromCookies, knownProviders, } from "./infer-provider.js";
35
+ export { normalize, domainMatchesProvider, isExpired, } from "./normalize.js";
36
+ export { detectFormat, buildDetectedFile, safeBasename, safePreview, } from "./detect-format.js";
37
+ export { atomicWriteFileSync, defaultCachePathFor, } from "./atomic-write.js";
38
+ export { CookieWatcher, createWatcher, defaultDropDir, } from "./watcher.js";
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EACN,IAAI,EACJ,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACN,aAAa,EACb,iBAAiB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,EACN,aAAa,EACb,yBAAyB,EACzB,wBAAwB,EACxB,cAAc,GACd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACN,SAAS,EACT,qBAAqB,EACrB,SAAS,GACT,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACN,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,WAAW,GACX,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,aAAa,EACb,aAAa,EACb,cAAc,GACd,MAAM,cAAc,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Provider inference — figure out which provider a cookie dump belongs to
3
+ * without the user having to organize by subfolder.
4
+ *
5
+ * Three inference passes, first hit wins:
6
+ * 1. Filename hint (e.g. `minimax-*.txt`, `*minimax.json` → "minimax")
7
+ * 2. Dominant domain (≥ 80% of cookies on `*.minimax.io` → "minimax")
8
+ * 3. Nothing → null (caller will surface a friendly error)
9
+ *
10
+ * Provider hint from SyncOptions overrides all of the above.
11
+ */
12
+ import type { CanonicalCookie, ProviderId } from "./types.js";
13
+ /** Returns the first provider whose filename pattern matches, or null. */
14
+ export declare function inferProviderFromFilename(filename: string): ProviderId | null;
15
+ /** Returns the provider whose domain is the dominant (≥ 80%) match. */
16
+ export declare function inferProviderFromCookies(cookies: CanonicalCookie[]): ProviderId | null;
17
+ /**
18
+ * Combined inference: hint → filename → dominant domain → null.
19
+ *
20
+ * If `filename` is provided it takes priority over domain inference
21
+ * (filename hints are explicit). Domain inference is the fallback.
22
+ */
23
+ export declare function inferProvider(filename: string | undefined, cookies: CanonicalCookie[], hint?: ProviderId): ProviderId | null;
24
+ /**
25
+ * Build a list of provider rules used by other parts of the package.
26
+ * Exposed for UI display (e.g. "which providers does this sanitizer know?").
27
+ */
28
+ export declare function knownProviders(): ProviderId[];
29
+ //# sourceMappingURL=infer-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infer-provider.d.ts","sourceRoot":"","sources":["../src/infer-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAwB9D,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAM7E;AAED,uEAAuE;AACvE,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,eAAe,EAAE,GACxB,UAAU,GAAG,IAAI,CAyBnB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,eAAe,EAAE,EAC1B,IAAI,CAAC,EAAE,UAAU,GACf,UAAU,GAAG,IAAI,CAOnB;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAI7C"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Provider inference — figure out which provider a cookie dump belongs to
3
+ * without the user having to organize by subfolder.
4
+ *
5
+ * Three inference passes, first hit wins:
6
+ * 1. Filename hint (e.g. `minimax-*.txt`, `*minimax.json` → "minimax")
7
+ * 2. Dominant domain (≥ 80% of cookies on `*.minimax.io` → "minimax")
8
+ * 3. Nothing → null (caller will surface a friendly error)
9
+ *
10
+ * Provider hint from SyncOptions overrides all of the above.
11
+ */
12
+ const FILENAME_HINTS = [
13
+ { pattern: /minimax/i, provider: "minimax" },
14
+ { pattern: /anthropic|claude/i, provider: "anthropic" },
15
+ { pattern: /openai|gpt|chatgpt/i, provider: "openai" },
16
+ { pattern: /glm|z\.ai|zhipuai/i, provider: "glm" },
17
+ { pattern: /openrouter/i, provider: "openrouter" },
18
+ ];
19
+ const DOMAIN_HINTS = [
20
+ { pattern: /\.minimax\.io$|^minimax\.io$/i, provider: "minimax" },
21
+ { pattern: /\.anthropic\.com$|^anthropic\.com$/i, provider: "anthropic" },
22
+ {
23
+ pattern: /\.openai\.com$|^openai\.com$|\.chatgpt\.com$|^chatgpt\.com$/i,
24
+ provider: "openai",
25
+ },
26
+ {
27
+ pattern: /\.z\.ai$|^z\.ai$|\.zhipuai\.cn$|^zhipuai\.cn$/i,
28
+ provider: "glm",
29
+ },
30
+ { pattern: /\.openrouter\.ai$|^openrouter\.ai$/i, provider: "openrouter" },
31
+ ];
32
+ /** Returns the first provider whose filename pattern matches, or null. */
33
+ export function inferProviderFromFilename(filename) {
34
+ if (!filename)
35
+ return null;
36
+ for (const { pattern, provider } of FILENAME_HINTS) {
37
+ if (pattern.test(filename))
38
+ return provider;
39
+ }
40
+ return null;
41
+ }
42
+ /** Returns the provider whose domain is the dominant (≥ 80%) match. */
43
+ export function inferProviderFromCookies(cookies) {
44
+ if (!Array.isArray(cookies) || cookies.length === 0)
45
+ return null;
46
+ const counts = new Map();
47
+ let total = 0;
48
+ for (const c of cookies) {
49
+ if (!c.domain)
50
+ continue;
51
+ total += 1;
52
+ for (const { pattern, provider } of DOMAIN_HINTS) {
53
+ if (pattern.test(c.domain.toLowerCase())) {
54
+ counts.set(provider, (counts.get(provider) ?? 0) + 1);
55
+ break;
56
+ }
57
+ }
58
+ }
59
+ if (total === 0)
60
+ return null;
61
+ const threshold = Math.ceil(total * 0.8); // ≥ 80 %
62
+ let best = null;
63
+ for (const [provider, count] of counts) {
64
+ if (count >= threshold && (!best || count > best.count)) {
65
+ best = { provider, count };
66
+ }
67
+ }
68
+ return best?.provider ?? null;
69
+ }
70
+ /**
71
+ * Combined inference: hint → filename → dominant domain → null.
72
+ *
73
+ * If `filename` is provided it takes priority over domain inference
74
+ * (filename hints are explicit). Domain inference is the fallback.
75
+ */
76
+ export function inferProvider(filename, cookies, hint) {
77
+ if (hint)
78
+ return hint;
79
+ if (filename) {
80
+ const fromName = inferProviderFromFilename(filename);
81
+ if (fromName)
82
+ return fromName;
83
+ }
84
+ return inferProviderFromCookies(cookies);
85
+ }
86
+ /**
87
+ * Build a list of provider rules used by other parts of the package.
88
+ * Exposed for UI display (e.g. "which providers does this sanitizer know?").
89
+ */
90
+ export function knownProviders() {
91
+ const set = new Set();
92
+ for (const { provider } of FILENAME_HINTS)
93
+ set.add(provider);
94
+ return Array.from(set);
95
+ }
96
+ //# sourceMappingURL=infer-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infer-provider.js","sourceRoot":"","sources":["../src/infer-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,cAAc,GAAqD;IACxE,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC5C,EAAE,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,WAAW,EAAE;IACvD,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,QAAQ,EAAE;IACtD,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,KAAK,EAAE;IAClD,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE;CAClD,CAAC;AAEF,MAAM,YAAY,GAAqD;IACtE,EAAE,OAAO,EAAE,+BAA+B,EAAE,QAAQ,EAAE,SAAS,EAAE;IACjE,EAAE,OAAO,EAAE,qCAAqC,EAAE,QAAQ,EAAE,WAAW,EAAE;IACzE;QACC,OAAO,EAAE,8DAA8D;QACvE,QAAQ,EAAE,QAAQ;KAClB;IACD;QACC,OAAO,EAAE,gDAAgD;QACzD,QAAQ,EAAE,KAAK;KACf;IACD,EAAE,OAAO,EAAE,qCAAqC,EAAE,QAAQ,EAAE,YAAY,EAAE;CAC1E,CAAC;AAEF,0EAA0E;AAC1E,MAAM,UAAU,yBAAyB,CAAC,QAAgB;IACzD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,KAAK,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,cAAc,EAAE,CAAC;QACpD,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAQ,CAAC;IAC7C,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,wBAAwB,CACvC,OAA0B;IAE1B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEjE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,CAAC,MAAM;YAAE,SAAS;QACxB,KAAK,IAAI,CAAC,CAAC;QACX,KAAK,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,YAAY,EAAE,CAAC;YAClD,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACtD,MAAM;YACP,CAAC;QACF,CAAC;IACF,CAAC;IAED,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS;IACnD,IAAI,IAAI,GAAmD,IAAI,CAAC;IAChE,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QACxC,IAAI,KAAK,IAAI,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACzD,IAAI,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC5B,CAAC;IACF,CAAC;IACD,OAAO,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC5B,QAA4B,EAC5B,OAA0B,EAC1B,IAAiB;IAEjB,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,QAAQ,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;IAC/B,CAAC;IACD,OAAO,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC7B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAc,CAAC;IAClC,KAAK,MAAM,EAAE,QAAQ,EAAE,IAAI,cAAc;QAAE,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC"}