@zigrivers/mmr 1.3.0 → 1.4.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 (130) hide show
  1. package/README.md +444 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +4 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/ack.d.ts +11 -0
  6. package/dist/commands/ack.d.ts.map +1 -0
  7. package/dist/commands/ack.js +123 -0
  8. package/dist/commands/ack.js.map +1 -0
  9. package/dist/commands/config.d.ts +5 -0
  10. package/dist/commands/config.d.ts.map +1 -1
  11. package/dist/commands/config.js +248 -14
  12. package/dist/commands/config.js.map +1 -1
  13. package/dist/commands/jobs.d.ts.map +1 -1
  14. package/dist/commands/jobs.js +3 -4
  15. package/dist/commands/jobs.js.map +1 -1
  16. package/dist/commands/reconcile.d.ts.map +1 -1
  17. package/dist/commands/reconcile.js +12 -5
  18. package/dist/commands/reconcile.js.map +1 -1
  19. package/dist/commands/results.d.ts.map +1 -1
  20. package/dist/commands/results.js +13 -5
  21. package/dist/commands/results.js.map +1 -1
  22. package/dist/commands/review.d.ts +25 -0
  23. package/dist/commands/review.d.ts.map +1 -1
  24. package/dist/commands/review.js +459 -44
  25. package/dist/commands/review.js.map +1 -1
  26. package/dist/commands/sessions.d.ts +58 -0
  27. package/dist/commands/sessions.d.ts.map +1 -0
  28. package/dist/commands/sessions.js +266 -0
  29. package/dist/commands/sessions.js.map +1 -0
  30. package/dist/commands/status.d.ts.map +1 -1
  31. package/dist/commands/status.js +2 -3
  32. package/dist/commands/status.js.map +1 -1
  33. package/dist/config/defaults.d.ts +2 -2
  34. package/dist/config/defaults.d.ts.map +1 -1
  35. package/dist/config/defaults.js +76 -0
  36. package/dist/config/defaults.js.map +1 -1
  37. package/dist/config/loader.d.ts +22 -0
  38. package/dist/config/loader.d.ts.map +1 -1
  39. package/dist/config/loader.js +279 -36
  40. package/dist/config/loader.js.map +1 -1
  41. package/dist/config/schema.d.ts +897 -53
  42. package/dist/config/schema.d.ts.map +1 -1
  43. package/dist/config/schema.js +155 -4
  44. package/dist/config/schema.js.map +1 -1
  45. package/dist/core/ack-store.d.ts +109 -0
  46. package/dist/core/ack-store.d.ts.map +1 -0
  47. package/dist/core/ack-store.js +363 -0
  48. package/dist/core/ack-store.js.map +1 -0
  49. package/dist/core/auth.d.ts +10 -1
  50. package/dist/core/auth.d.ts.map +1 -1
  51. package/dist/core/auth.js +106 -35
  52. package/dist/core/auth.js.map +1 -1
  53. package/dist/core/compensator.d.ts +33 -4
  54. package/dist/core/compensator.d.ts.map +1 -1
  55. package/dist/core/compensator.js +120 -15
  56. package/dist/core/compensator.js.map +1 -1
  57. package/dist/core/diff-introspect.d.ts +21 -0
  58. package/dist/core/diff-introspect.d.ts.map +1 -0
  59. package/dist/core/diff-introspect.js +42 -0
  60. package/dist/core/diff-introspect.js.map +1 -0
  61. package/dist/core/dispatcher.d.ts +10 -0
  62. package/dist/core/dispatcher.d.ts.map +1 -1
  63. package/dist/core/dispatcher.js +91 -20
  64. package/dist/core/dispatcher.js.map +1 -1
  65. package/dist/core/git-show.d.ts +31 -0
  66. package/dist/core/git-show.d.ts.map +1 -0
  67. package/dist/core/git-show.js +72 -0
  68. package/dist/core/git-show.js.map +1 -0
  69. package/dist/core/host-isolation.d.ts +24 -0
  70. package/dist/core/host-isolation.d.ts.map +1 -0
  71. package/dist/core/host-isolation.js +107 -0
  72. package/dist/core/host-isolation.js.map +1 -0
  73. package/dist/core/http-dispatcher.d.ts +20 -0
  74. package/dist/core/http-dispatcher.d.ts.map +1 -0
  75. package/dist/core/http-dispatcher.js +125 -0
  76. package/dist/core/http-dispatcher.js.map +1 -0
  77. package/dist/core/job-store.d.ts +7 -1
  78. package/dist/core/job-store.d.ts.map +1 -1
  79. package/dist/core/job-store.js +21 -1
  80. package/dist/core/job-store.js.map +1 -1
  81. package/dist/core/jsonpath.d.ts +15 -0
  82. package/dist/core/jsonpath.d.ts.map +1 -0
  83. package/dist/core/jsonpath.js +63 -0
  84. package/dist/core/jsonpath.js.map +1 -0
  85. package/dist/core/oss-examples.d.ts +18 -0
  86. package/dist/core/oss-examples.d.ts.map +1 -0
  87. package/dist/core/oss-examples.js +66 -0
  88. package/dist/core/oss-examples.js.map +1 -0
  89. package/dist/core/parser.d.ts +8 -3
  90. package/dist/core/parser.d.ts.map +1 -1
  91. package/dist/core/parser.js +157 -6
  92. package/dist/core/parser.js.map +1 -1
  93. package/dist/core/project-root.d.ts +10 -0
  94. package/dist/core/project-root.d.ts.map +1 -0
  95. package/dist/core/project-root.js +23 -0
  96. package/dist/core/project-root.js.map +1 -0
  97. package/dist/core/reconciler.d.ts +1 -1
  98. package/dist/core/reconciler.d.ts.map +1 -1
  99. package/dist/core/reconciler.js +100 -18
  100. package/dist/core/reconciler.js.map +1 -1
  101. package/dist/core/redact.d.ts +17 -0
  102. package/dist/core/redact.d.ts.map +1 -0
  103. package/dist/core/redact.js +140 -0
  104. package/dist/core/redact.js.map +1 -0
  105. package/dist/core/results-pipeline.d.ts +8 -2
  106. package/dist/core/results-pipeline.d.ts.map +1 -1
  107. package/dist/core/results-pipeline.js +50 -3
  108. package/dist/core/results-pipeline.js.map +1 -1
  109. package/dist/core/runtime-probe.d.ts +14 -0
  110. package/dist/core/runtime-probe.d.ts.map +1 -0
  111. package/dist/core/runtime-probe.js +57 -0
  112. package/dist/core/runtime-probe.js.map +1 -0
  113. package/dist/core/stable-id.d.ts +19 -0
  114. package/dist/core/stable-id.d.ts.map +1 -0
  115. package/dist/core/stable-id.js +148 -0
  116. package/dist/core/stable-id.js.map +1 -0
  117. package/dist/core/trust-mode.d.ts +29 -0
  118. package/dist/core/trust-mode.d.ts.map +1 -0
  119. package/dist/core/trust-mode.js +103 -0
  120. package/dist/core/trust-mode.js.map +1 -0
  121. package/dist/formatters/markdown.d.ts.map +1 -1
  122. package/dist/formatters/markdown.js +9 -0
  123. package/dist/formatters/markdown.js.map +1 -1
  124. package/dist/formatters/text.d.ts.map +1 -1
  125. package/dist/formatters/text.js +9 -0
  126. package/dist/formatters/text.js.map +1 -1
  127. package/dist/types.d.ts +44 -1
  128. package/dist/types.d.ts.map +1 -1
  129. package/dist/types.js.map +1 -1
  130. package/package.json +2 -2
@@ -0,0 +1,363 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import crypto from 'node:crypto';
4
+ import { jaccardSimilarity } from './stable-id.js';
5
+ import { findProjectRoot } from './project-root.js';
6
+ import { readFileAtRef, listFilesAtRef } from './git-show.js';
7
+ const FUZZY_THRESHOLD = 0.7;
8
+ /** Canonical finding_key format: sha1 hex. Exported so the CLI can validate
9
+ * the same way before any path construction. */
10
+ export const FINDING_KEY_RE = /^[a-f0-9]{40}$/;
11
+ // Acks are tiny (a key, a location, a few dozen shingles, a short reason).
12
+ // Cap reads well above any realistic record so a planted oversized file in an
13
+ // untrusted project tree can't OOM the review.
14
+ const MAX_ACK_BYTES = 256 * 1024;
15
+ /** Repo-relative location of project-scoped acks (under <projectRoot>/). */
16
+ const PROJECT_ACKS_REL = '.mmr/acks';
17
+ /**
18
+ * Build an AckStore for a review run. User-scope acks (`<userRoot>/acks`,
19
+ * userRoot = resolveSessionRoot(), MMR_HOME-aware) are always loaded — they
20
+ * live on the operator's own machine. Project-scope acks live in the reviewed
21
+ * tree, which may be untrusted (a PR checkout in CI), so they are loaded only
22
+ * when explicitly trusted — otherwise an attacker could commit
23
+ * `.mmr/acks/<sha>.json` to self-suppress their own findings. The full trusted
24
+ * path (loading project acks from a git base ref) is added by the trust-mode
25
+ * thread; until then this gates project acks behind trust_project_acks.
26
+ *
27
+ * The project root is discovered by walking up from `cwd` (default
28
+ * process.cwd()) to the repository root, so acks resolve correctly even when
29
+ * the command runs from a subdirectory. userRoot is supplied by the caller
30
+ * (resolveSessionRoot()); cwd is injectable for tests.
31
+ */
32
+ export function buildReviewAckStore(opts) {
33
+ // Precedence mirrors loadProjectYaml: an explicit trust opt-in reads project
34
+ // acks from the working tree; otherwise a base ref (when present) is the
35
+ // trusted source; otherwise project acks are disabled (untrusted tree).
36
+ const useBaseRef = !opts.trustProjectAcks && opts.configBaseRef !== undefined;
37
+ const useProject = opts.trustProjectAcks || useBaseRef;
38
+ return new AckStore({
39
+ projectRoot: useProject ? findProjectRoot(opts.cwd) : undefined,
40
+ userRoot: opts.userRoot,
41
+ configBaseRef: useBaseRef ? opts.configBaseRef : undefined,
42
+ });
43
+ }
44
+ /**
45
+ * Validates a parsed ack record's shape AND that its embedded finding_key
46
+ * matches the filename it was loaded from. The filename==key invariant guards
47
+ * against desync (manual edit, merge conflict, tampering) silently
48
+ * misattributing or shadowing acks — which matters because acks suppress
49
+ * review findings.
50
+ */
51
+ function isValidAckRecord(value, expectedKey) {
52
+ if (value === null || typeof value !== 'object')
53
+ return false;
54
+ const r = value;
55
+ return (r.finding_key === expectedKey &&
56
+ typeof r.normalized_location === 'string' &&
57
+ Array.isArray(r.description_shingle) &&
58
+ r.description_shingle.every((s) => typeof s === 'string') &&
59
+ typeof r.created_at === 'string' &&
60
+ (r.reason === undefined || typeof r.reason === 'string'));
61
+ }
62
+ export class AckStore {
63
+ projectDir;
64
+ userDir;
65
+ projectRootResolved;
66
+ userRootResolved;
67
+ configBaseRef;
68
+ // Per-instance lazy cache so a review that calls lookup() once per finding
69
+ // reads each acks dir from disk at most once (avoids O(N*M) FS operations).
70
+ loaded = {};
71
+ constructor(opts) {
72
+ this.projectRootResolved = opts.projectRoot === undefined ? undefined : path.resolve(opts.projectRoot);
73
+ this.userRootResolved = path.resolve(opts.userRoot);
74
+ // Project acks are repo-committed under <projectRoot>/.mmr/acks. User acks
75
+ // live under the MMR state root at <userRoot>/acks (beside jobs/sessions),
76
+ // so they honor MMR_HOME — no extra `.mmr` segment for the user scope.
77
+ this.projectDir = this.projectRootResolved === undefined
78
+ ? undefined
79
+ : path.join(this.projectRootResolved, PROJECT_ACKS_REL);
80
+ this.userDir = path.join(this.userRootResolved, 'acks');
81
+ this.configBaseRef = opts.configBaseRef;
82
+ }
83
+ validateKey(key) {
84
+ if (!FINDING_KEY_RE.test(key)) {
85
+ throw new Error(`Invalid finding_key: ${key} — must match ^[a-f0-9]{40}$`);
86
+ }
87
+ }
88
+ /**
89
+ * Returns the acks dir for a scope after verifying it does not escape its
90
+ * root via a symlinked ancestor. A leaf-only symlink check is not enough:
91
+ * project acks live in the untrusted reviewed tree, where `.mmr` or
92
+ * `.mmr/acks` could itself be a symlink redirecting every mkdir/read/write/
93
+ * unlink out of the sandbox. We realpath the deepest existing ancestor of
94
+ * the acks dir and require it to stay within the (realpath'd) root.
95
+ */
96
+ dirForScope(scope) {
97
+ const dir = scope === 'project' ? this.projectDir : this.userDir;
98
+ const root = scope === 'project' ? this.projectRootResolved : this.userRootResolved;
99
+ if (dir === undefined || root === undefined) {
100
+ throw new Error('project-scope acks are disabled (no project root configured)');
101
+ }
102
+ // Resolve BOTH the root and the acks dir via their deepest existing
103
+ // ancestor, so the comparison is symlink-consistent even when the root
104
+ // itself does not exist yet (e.g. a fresh ~/.mmr). Comparing a realpath'd
105
+ // probe against an unresolved root would false-positive on platforms where
106
+ // a parent like /tmp is itself a symlink (/private/tmp on macOS).
107
+ const realRoot = this.realDeepestAncestor(root);
108
+ const realProbe = this.realDeepestAncestor(dir);
109
+ if (realRoot === undefined || realProbe === undefined)
110
+ return dir;
111
+ const rel = path.relative(realRoot, realProbe);
112
+ if (rel !== '' && (rel.startsWith('..') || path.isAbsolute(rel))) {
113
+ throw new Error(`ack ${scope} directory escapes its root via a symlinked ancestor: ${dir}`);
114
+ }
115
+ return dir;
116
+ }
117
+ /** realpath of the deepest existing ancestor of `p` (p itself if it exists). */
118
+ realDeepestAncestor(p) {
119
+ let probe = path.resolve(p);
120
+ while (probe !== path.dirname(probe) && !fs.existsSync(probe))
121
+ probe = path.dirname(probe);
122
+ try {
123
+ return fs.realpathSync(probe);
124
+ }
125
+ catch {
126
+ return undefined;
127
+ }
128
+ }
129
+ filePath(key, scope) {
130
+ this.validateKey(key);
131
+ const dir = this.dirForScope(scope);
132
+ const resolved = path.resolve(dir, `${key}.json`);
133
+ // Defense in depth: the resolved path must stay inside the scope dir.
134
+ const rel = path.relative(dir, resolved);
135
+ if (rel.startsWith('..') || path.isAbsolute(rel)) {
136
+ throw new Error(`Resolved ack path escapes its scope directory: ${resolved}`);
137
+ }
138
+ return resolved;
139
+ }
140
+ add(record, scope) {
141
+ const key = record.finding_key;
142
+ this.validateKey(key);
143
+ // Enforce the full shape on write so every persisted record is guaranteed
144
+ // loadable; otherwise an invalid record could be written then silently
145
+ // dropped by the load-side integrity check (write/read asymmetry).
146
+ if (!isValidAckRecord(record, key)) {
147
+ throw new Error(`Invalid ack record for ${key}: missing or mistyped fields`);
148
+ }
149
+ const dir = this.dirForScope(scope);
150
+ fs.mkdirSync(dir, { recursive: true });
151
+ const fp = this.filePath(record.finding_key, scope);
152
+ // Refuse to write through a symlink: acks gate finding suppression, so a
153
+ // symlinked ack file must not be able to clobber an arbitrary target.
154
+ try {
155
+ if (fs.lstatSync(fp).isSymbolicLink()) {
156
+ throw new Error(`Refusing to write ack through a symlink: ${fp}`);
157
+ }
158
+ }
159
+ catch (err) {
160
+ if (err.code !== 'ENOENT')
161
+ throw err;
162
+ }
163
+ // Atomic write: write to a fresh temp then rename. The temp name uses a
164
+ // random suffix and the 'wx' (O_CREAT|O_EXCL) flag, which fails if the path
165
+ // already exists — including a pre-planted symlink — closing the TOCTOU on
166
+ // the temp path. rename() replaces fp's directory entry without following a
167
+ // symlink target, so readers never observe a partial record.
168
+ const tmp = `${fp}.${crypto.randomBytes(6).toString('hex')}.tmp`;
169
+ try {
170
+ fs.writeFileSync(tmp, JSON.stringify(record, null, 2), { flag: 'wx' });
171
+ fs.renameSync(tmp, fp);
172
+ }
173
+ catch (err) {
174
+ // Clean up a partial/leftover temp on any failure (write or rename).
175
+ try {
176
+ fs.rmSync(tmp, { force: true });
177
+ }
178
+ catch {
179
+ // ignore cleanup failure; surface the original error
180
+ }
181
+ throw err;
182
+ }
183
+ this.loaded[scope] = undefined;
184
+ }
185
+ remove(key, scope) {
186
+ this.validateKey(key);
187
+ const fp = this.filePath(key, scope);
188
+ // Unlink directly and tolerate ENOENT rather than existsSync-then-unlink
189
+ // (which has a TOCTOU window if the file is removed concurrently).
190
+ try {
191
+ fs.unlinkSync(fp);
192
+ }
193
+ catch (err) {
194
+ if (err.code !== 'ENOENT')
195
+ throw err;
196
+ }
197
+ this.loaded[scope] = undefined;
198
+ }
199
+ readDir(dir) {
200
+ let entries;
201
+ try {
202
+ if (!fs.existsSync(dir))
203
+ return [];
204
+ entries = fs.readdirSync(dir);
205
+ }
206
+ catch {
207
+ return []; // transient FS error (ENOENT/ENOTDIR/race) → treat as no acks
208
+ }
209
+ const out = [];
210
+ for (const entry of entries) {
211
+ if (!entry.endsWith('.json'))
212
+ continue;
213
+ const keyOnly = entry.replace(/\.json$/, '');
214
+ if (!FINDING_KEY_RE.test(keyOnly))
215
+ continue;
216
+ const fp = path.join(dir, entry);
217
+ let st;
218
+ try {
219
+ st = fs.lstatSync(fp);
220
+ }
221
+ catch {
222
+ continue;
223
+ }
224
+ // Skip symlinks and oversized/non-regular files before reading. Project
225
+ // acks live in the untrusted reviewed tree, so a planted symlink could
226
+ // point readFileSync at a huge or sensitive target (DoS / disclosure) —
227
+ // mirror the write-side symlink hardening on the read path.
228
+ if (st.isSymbolicLink() || !st.isFile() || st.size > MAX_ACK_BYTES)
229
+ continue;
230
+ let parsed;
231
+ try {
232
+ parsed = JSON.parse(fs.readFileSync(fp, 'utf-8'));
233
+ }
234
+ catch {
235
+ continue; // best-effort: skip unreadable/malformed JSON
236
+ }
237
+ // Skip records whose shape is invalid or whose embedded key disagrees
238
+ // with the filename (desynced/tampered/corrupt) — see isValidAckRecord.
239
+ if (!isValidAckRecord(parsed, keyOnly))
240
+ continue;
241
+ out.push(parsed);
242
+ }
243
+ return out;
244
+ }
245
+ /**
246
+ * Read project-scope ack records from the configured base ref (committed
247
+ * blobs via git), not the working tree. Each file's embedded key must match
248
+ * its filename and the record must be shape-valid (isValidAckRecord), same as
249
+ * the working-tree path. git show returns blob content, so the FS symlink/
250
+ * traversal guards are unnecessary here; readFileAtRef caps the read size.
251
+ */
252
+ readProjectRecordsFromRef(ref) {
253
+ if (this.projectRootResolved === undefined)
254
+ return [];
255
+ const cwd = this.projectRootResolved;
256
+ const out = [];
257
+ for (const file of listFilesAtRef({ cwd, ref, dirPath: PROJECT_ACKS_REL })) {
258
+ const base = path.posix.basename(file);
259
+ if (!base.endsWith('.json'))
260
+ continue;
261
+ const keyOnly = base.replace(/\.json$/, '');
262
+ if (!FINDING_KEY_RE.test(keyOnly))
263
+ continue;
264
+ const raw = readFileAtRef({ cwd, ref, filePath: `./${PROJECT_ACKS_REL}/${base}` });
265
+ // Bound the parse, mirroring the working-tree path's MAX_ACK_BYTES guard.
266
+ if (raw === undefined || raw.length > MAX_ACK_BYTES)
267
+ continue;
268
+ let parsed;
269
+ try {
270
+ parsed = JSON.parse(raw);
271
+ }
272
+ catch {
273
+ continue;
274
+ }
275
+ if (!isValidAckRecord(parsed, keyOnly))
276
+ continue;
277
+ out.push(parsed);
278
+ }
279
+ return out;
280
+ }
281
+ // NOTE: in base-ref mode the project cache is a snapshot of the ref. add()/
282
+ // remove() invalidate the cache and write to the working tree, but a
283
+ // subsequent project lookup re-reads the (unchanged) ref, not the just-
284
+ // written file. That's correct for review use (writes are operator actions
285
+ // via the ack CLI, which runs in working-tree mode), just worth noting.
286
+ records(scope) {
287
+ let cached = this.loaded[scope];
288
+ if (cached === undefined) {
289
+ let records;
290
+ if (scope === 'project' && this.configBaseRef !== undefined) {
291
+ // Trust boundary: read project acks from the base ref, not the tree.
292
+ records = this.readProjectRecordsFromRef(this.configBaseRef);
293
+ }
294
+ else {
295
+ // A disabled project scope (no project root) contributes no records.
296
+ const dir = scope === 'project' ? this.projectDir : this.userDir;
297
+ records = dir === undefined ? [] : this.readDir(this.dirForScope(scope));
298
+ }
299
+ const byKey = new Map();
300
+ const byLocation = new Map();
301
+ for (const r of records) {
302
+ byKey.set(r.finding_key, r);
303
+ const bucket = byLocation.get(r.normalized_location);
304
+ if (bucket)
305
+ bucket.push(r);
306
+ else
307
+ byLocation.set(r.normalized_location, [r]);
308
+ }
309
+ cached = { records, byKey, byLocation };
310
+ this.loaded[scope] = cached;
311
+ }
312
+ return cached;
313
+ }
314
+ /** Merge project and user acks; project shadows user on finding_key conflict. */
315
+ listAll() {
316
+ const byKey = new Map();
317
+ for (const u of this.records('user').records)
318
+ byKey.set(u.finding_key, u);
319
+ for (const p of this.records('project').records)
320
+ byKey.set(p.finding_key, p); // project wins
321
+ return [...byKey.values()];
322
+ }
323
+ /**
324
+ * Lookup an ack matching the given finding identity, applying the two-step
325
+ * rule from T2-D: (1) exact `finding_key` match (O(1) via the per-scope
326
+ * index); (2) fuzzy fallback only when normalized_location matches AND
327
+ * shingle Jaccard ≥ 0.7. Project scope shadows user scope.
328
+ */
329
+ lookup(finding) {
330
+ const project = this.records('project');
331
+ const user = this.records('user');
332
+ // Exact match — project then user so project shadows user.
333
+ const exactProject = project.byKey.get(finding.finding_key);
334
+ if (exactProject)
335
+ return { record: exactProject, match: 'exact', scope: 'project' };
336
+ const exactUser = user.byKey.get(finding.finding_key);
337
+ if (exactUser)
338
+ return { record: exactUser, match: 'exact', scope: 'user' };
339
+ // Fuzzy fallback — location must match exactly, shingle Jaccard ≥ 0.7.
340
+ // Use the per-location index so only same-location acks (usually 0–1) are
341
+ // scored, not every record. Project scope wins over user.
342
+ if (finding.shingle.length === 0)
343
+ return undefined;
344
+ const projectFuzzy = this.fuzzyScan(project, finding);
345
+ if (projectFuzzy)
346
+ return { record: projectFuzzy, match: 'fuzzy', scope: 'project' };
347
+ const userFuzzy = this.fuzzyScan(user, finding);
348
+ if (userFuzzy)
349
+ return { record: userFuzzy, match: 'fuzzy', scope: 'user' };
350
+ return undefined;
351
+ }
352
+ fuzzyScan(scope, finding) {
353
+ const candidates = scope.byLocation.get(finding.normalized_location);
354
+ if (!candidates)
355
+ return undefined;
356
+ for (const r of candidates) {
357
+ if (jaccardSimilarity(r.description_shingle, finding.shingle) >= FUZZY_THRESHOLD)
358
+ return r;
359
+ }
360
+ return undefined;
361
+ }
362
+ }
363
+ //# sourceMappingURL=ack-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ack-store.js","sourceRoot":"","sources":["../../src/core/ack-store.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
- import type { ChannelConfigParsed } from '../config/schema.js';
1
+ import type { ChannelConfigParsed, HttpChannelParsed } from '../config/schema.js';
2
+ import { deriveProbeUrl } from '../config/schema.js';
3
+ export { deriveProbeUrl };
2
4
  export interface AuthResult {
3
5
  status: 'ok' | 'failed' | 'timeout';
4
6
  recovery?: string;
@@ -13,4 +15,11 @@ export declare function checkInstalled(command: string): Promise<boolean>;
13
15
  * to handle transient network issues.
14
16
  */
15
17
  export declare function checkAuth(config: ChannelConfigParsed): Promise<AuthResult>;
18
+ /**
19
+ * Auth-probe an HTTP (openai-chat) channel: GET the configured (or derived)
20
+ * probe URL with the channel's full request context and map the status to
21
+ * ok/failed/timeout. The API key value is read only to build the request
22
+ * header — it is NEVER logged or returned in the result.
23
+ */
24
+ export declare function checkHttpAuth(channel: HttpChannelParsed): Promise<AuthResult>;
16
25
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAE9D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAStE;AAoDD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAOhF"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAA2B,MAAM,qBAAqB,CAAA;AAC1G,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAIpD,OAAO,EAAE,cAAc,EAAE,CAAA;AAMzB,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAStE;AAyDD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAehF;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CA0CnF"}
package/dist/core/auth.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import { spawn } from 'node:child_process';
2
+ import { deriveProbeUrl } from '../config/schema.js';
3
+ import { withNeutralPosture } from './host-isolation.js';
4
+ // Re-exported so callers (and tests) can derive an http probe URL from auth.ts.
5
+ export { deriveProbeUrl };
2
6
  /**
3
7
  * Check whether a CLI command is installed (available on PATH).
4
8
  * Uses `command -v` via shell.
@@ -21,52 +25,119 @@ export async function checkInstalled(command) {
21
25
  */
22
26
  async function runAuthCheck(config) {
23
27
  const { auth, env } = config;
24
- return new Promise((resolve) => {
25
- let settled = false;
26
- let timedOut = false;
27
- const child = spawn('sh', ['-c', auth.check], {
28
- env: { ...process.env, ...env },
29
- stdio: 'ignore',
30
- });
31
- const timer = setTimeout(() => {
32
- timedOut = true;
33
- child.kill('SIGKILL');
34
- }, auth.timeout * 1000);
35
- child.on('close', (code) => {
36
- if (settled)
37
- return;
38
- settled = true;
39
- clearTimeout(timer);
40
- if (timedOut) {
41
- resolve({ status: 'timeout' });
42
- return;
43
- }
44
- if (code !== null && auth.failure_exit_codes.includes(code)) {
28
+ const posture = withNeutralPosture(env, config.cwd);
29
+ try {
30
+ return await new Promise((resolve) => {
31
+ let settled = false;
32
+ let timedOut = false;
33
+ const child = spawn('sh', ['-c', auth.check], {
34
+ env: { ...process.env, ...posture.env },
35
+ cwd: posture.cwd,
36
+ stdio: 'ignore',
37
+ });
38
+ const timer = setTimeout(() => {
39
+ timedOut = true;
40
+ child.kill('SIGKILL');
41
+ }, auth.timeout * 1000);
42
+ child.on('close', (code) => {
43
+ if (settled)
44
+ return;
45
+ settled = true;
46
+ clearTimeout(timer);
47
+ if (timedOut) {
48
+ resolve({ status: 'timeout' });
49
+ return;
50
+ }
51
+ if (code !== null && auth.failure_exit_codes.includes(code)) {
52
+ resolve({ status: 'failed', recovery: auth.recovery });
53
+ return;
54
+ }
55
+ // Exit code 0 or any code not in failure_exit_codes -> ok (transient)
56
+ resolve({ status: 'ok' });
57
+ });
58
+ child.on('error', () => {
59
+ if (settled)
60
+ return;
61
+ settled = true;
62
+ clearTimeout(timer);
45
63
  resolve({ status: 'failed', recovery: auth.recovery });
46
- return;
47
- }
48
- // Exit code 0 or any code not in failure_exit_codes -> ok (transient)
49
- resolve({ status: 'ok' });
50
- });
51
- child.on('error', () => {
52
- if (settled)
53
- return;
54
- settled = true;
55
- clearTimeout(timer);
56
- resolve({ status: 'failed', recovery: auth.recovery });
64
+ });
57
65
  });
58
- });
66
+ }
67
+ finally {
68
+ posture.cleanup();
69
+ }
59
70
  }
60
71
  /**
61
72
  * Run the auth check defined in a channel config, retrying once on timeout
62
73
  * to handle transient network issues.
63
74
  */
64
75
  export async function checkAuth(config) {
65
- const result = await runAuthCheck(config);
76
+ // Polymorphic over channel kind: HTTP channels are auth-probed over the wire.
77
+ if (config.kind === 'http') {
78
+ return checkHttpAuth(config);
79
+ }
80
+ if (!config.auth) {
81
+ return { status: 'ok' };
82
+ }
83
+ const authConfig = { ...config, auth: config.auth };
84
+ const result = await runAuthCheck(authConfig);
66
85
  if (result.status === 'timeout') {
67
86
  // Retry once on timeout (transient network issue)
68
- return runAuthCheck(config);
87
+ return runAuthCheck(authConfig);
69
88
  }
70
89
  return result;
71
90
  }
91
+ /**
92
+ * Auth-probe an HTTP (openai-chat) channel: GET the configured (or derived)
93
+ * probe URL with the channel's full request context and map the status to
94
+ * ok/failed/timeout. The API key value is read only to build the request
95
+ * header — it is NEVER logged or returned in the result.
96
+ */
97
+ export async function checkHttpAuth(channel) {
98
+ const probeUrl = channel.auth.check_endpoint ?? deriveProbeUrl(channel.endpoint);
99
+ if (!probeUrl) {
100
+ return { status: 'failed', recovery: channel.auth.recovery };
101
+ }
102
+ const headers = { ...(channel.headers ?? {}) };
103
+ if (channel.api_key_env) {
104
+ const value = process.env[channel.api_key_env];
105
+ if (!value) {
106
+ return { status: 'failed', recovery: channel.auth.recovery };
107
+ }
108
+ // Drop any case-variant of the target header so the api-key header wins.
109
+ const target = channel.api_key_header.toLowerCase();
110
+ for (const k of Object.keys(headers)) {
111
+ if (k.toLowerCase() === target)
112
+ delete headers[k];
113
+ }
114
+ headers[channel.api_key_header] = `${channel.api_key_prefix}${value}`;
115
+ }
116
+ const controller = new AbortController();
117
+ const timer = setTimeout(() => controller.abort(), channel.auth.timeout * 1000);
118
+ try {
119
+ const res = await fetch(probeUrl, {
120
+ method: channel.auth.check_method,
121
+ headers,
122
+ signal: controller.signal,
123
+ });
124
+ if (channel.auth.check_status_ok.includes(res.status)) {
125
+ return { status: 'ok' };
126
+ }
127
+ // A non-ok HTTP response is plausibly an auth problem (401/403/…) → surface
128
+ // the user's auth-setup recovery guidance.
129
+ return { status: 'failed', recovery: channel.auth.recovery };
130
+ }
131
+ catch (err) {
132
+ if (err.name === 'AbortError') {
133
+ return { status: 'timeout' };
134
+ }
135
+ // Transport-level failure (DNS, connection refused, TLS) is NOT an auth
136
+ // problem — do not attach auth recovery text, which would mislead.
137
+ return { status: 'failed' };
138
+ }
139
+ finally {
140
+ clearTimeout(timer);
141
+ }
142
+ }
72
143
  //# sourceMappingURL=auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAQ1C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,sDAAsD;IACtD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IACpD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,mFAAmF;QACnF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;QACxF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAA;QAChD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,YAAY,CAAC,MAA2B;IACrD,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,CAAA;IAE5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,QAAQ,GAAG,KAAK,CAAA;QAEpB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE;YAC5C,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE;YAC/B,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAA;QAEF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,QAAQ,GAAG,IAAI,CAAA;YACf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACvB,CAAC,EAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;QAEvB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,YAAY,CAAC,KAAK,CAAC,CAAA;YAEnB,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;gBAC9B,OAAM;YACR,CAAC;YAED,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5D,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;gBACtD,OAAM;YACR,CAAC;YAED,sEAAsE;YACtE,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;QAC3B,CAAC,CAAC,CAAA;QAEF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACrB,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QACxD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAA2B;IACzD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;IACzC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAChC,kDAAkD;QAClD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"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"}
@@ -1,21 +1,50 @@
1
1
  import type { JobStore } from './job-store.js';
2
+ import type { ChannelConfigParsed, MmrConfigParsed, OutputParserConfig } from '../config/schema.js';
2
3
  import type { ChannelStatus } from '../types.js';
3
4
  export interface CompensatingChannel {
4
5
  /** Name of the original channel being compensated */
5
6
  originalChannel: string;
6
7
  /** Name used for the compensating dispatch (e.g., "compensating-codex") */
7
8
  compensatingName: string;
8
- /** Focus prompt to prepend */
9
- focusPrompt: string;
10
9
  }
10
+ export interface CompensatorDispatch {
11
+ command: string;
12
+ flags: string[];
13
+ env: Record<string, string>;
14
+ timeout: number;
15
+ prompt_wrapper: string;
16
+ stderr: 'capture' | 'suppress' | 'passthrough';
17
+ output_parser: string | OutputParserConfig;
18
+ prompt_delivery?: 'stdin' | 'prompt-file';
19
+ cwd?: string;
20
+ }
21
+ export declare function resolveCompensatorDispatch(config: MmrConfigParsed): CompensatorDispatch;
22
+ export declare function resolveCompensatorChannelName(config: MmrConfigParsed): string;
23
+ /**
24
+ * Resolve the configured compensator channel config (any kind), or undefined
25
+ * when none is configured (the default `claude -p` subprocess fallback applies).
26
+ * Throws only for a missing / abstract reference. Unlike
27
+ * getDispatchableCompensatorChannel this does NOT require a `command`, so it is
28
+ * safe for http compensators.
29
+ */
30
+ export declare function getCompensatorChannel(config: MmrConfigParsed): ChannelConfigParsed | undefined;
31
+ /** Output parser for the configured compensator channel (any kind); 'default' when none. */
32
+ export declare function resolveCompensatorOutputParser(config: MmrConfigParsed): string | OutputParserConfig;
33
+ export declare function getDispatchableCompensatorChannel(config: MmrConfigParsed, channelName: string): ChannelConfigParsed & {
34
+ command: string;
35
+ };
36
+ /**
37
+ * Resolve the focus-area prompt prefix for a compensating pass.
38
+ */
39
+ export declare function resolveCompensatorFocus(config: MmrConfigParsed, originalChannel: string): string;
11
40
  /**
12
41
  * Determine which channels need compensating passes.
13
42
  * Returns a list of compensating channel descriptors.
14
43
  */
15
- export declare function getCompensatingChannels(channelStatuses: Record<string, ChannelStatus>): CompensatingChannel[];
44
+ export declare function getCompensatingChannels(channelStatuses: Record<string, ChannelStatus>, compensatorChannel: string): CompensatingChannel[];
16
45
  /**
17
46
  * Dispatch compensating passes via claude CLI for unavailable channels.
18
47
  * Each compensating pass uses the same prompt but with a focused preamble.
19
48
  */
20
- export declare function dispatchCompensatingPasses(store: JobStore, jobId: string, prompt: string, compensatingChannels: CompensatingChannel[], timeout: number): Promise<void>;
49
+ export declare function dispatchCompensatingPasses(store: JobStore, jobId: string, prompt: string, compensatingChannels: CompensatingChannel[], config: MmrConfigParsed): Promise<void>;
21
50
  //# sourceMappingURL=compensator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compensator.d.ts","sourceRoot":"","sources":["../../src/core/compensator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAiBhD,MAAM,WAAW,mBAAmB;IAClC,qDAAqD;IACrD,eAAe,EAAE,MAAM,CAAA;IACvB,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAA;IACxB,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAC7C,mBAAmB,EAAE,CAwBvB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,oBAAoB,EAAE,mBAAmB,EAAE,EAC3C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAcf"}
1
+ {"version":3,"file":"compensator.d.ts","sourceRoot":"","sources":["../../src/core/compensator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACnG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAkBhD,MAAM,WAAW,mBAAmB;IAClC,qDAAqD;IACrD,eAAe,EAAE,MAAM,CAAA;IACvB,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;IAC9C,aAAa,EAAE,MAAM,GAAG,kBAAkB,CAAA;IAC1C,eAAe,CAAC,EAAE,OAAO,GAAG,aAAa,CAAA;IACzC,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAcD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,eAAe,GAAG,mBAAmB,CAoBvF;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAE7E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,mBAAmB,GAAG,SAAS,CAW9F;AAED,4FAA4F;AAC5F,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,kBAAkB,CAEnG;AAED,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,eAAe,EACvB,WAAW,EAAE,MAAM,GAClB,mBAAmB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAY3C;AAQD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,eAAe,EACvB,eAAe,EAAE,MAAM,GACtB,MAAM,CAOR;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EAC9C,kBAAkB,EAAE,MAAM,GACzB,mBAAmB,EAAE,CAoBvB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,oBAAoB,EAAE,mBAAmB,EAAE,EAC3C,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CAwCf"}