@zigrivers/mmr 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +422 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/ack.d.ts +11 -0
- package/dist/commands/ack.d.ts.map +1 -0
- package/dist/commands/ack.js +123 -0
- package/dist/commands/ack.js.map +1 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +248 -14
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/jobs.d.ts.map +1 -1
- package/dist/commands/jobs.js +3 -4
- package/dist/commands/jobs.js.map +1 -1
- package/dist/commands/reconcile.d.ts.map +1 -1
- package/dist/commands/reconcile.js +12 -5
- package/dist/commands/reconcile.js.map +1 -1
- package/dist/commands/results.d.ts.map +1 -1
- package/dist/commands/results.js +13 -5
- package/dist/commands/results.js.map +1 -1
- package/dist/commands/review.d.ts +25 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +457 -44
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/sessions.d.ts +58 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +266 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +2 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/config/defaults.d.ts +2 -2
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +66 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts +22 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +279 -36
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +869 -53
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +151 -4
- package/dist/config/schema.js.map +1 -1
- package/dist/core/ack-store.d.ts +109 -0
- package/dist/core/ack-store.d.ts.map +1 -0
- package/dist/core/ack-store.js +363 -0
- package/dist/core/ack-store.js.map +1 -0
- package/dist/core/auth.d.ts +10 -1
- package/dist/core/auth.d.ts.map +1 -1
- package/dist/core/auth.js +65 -2
- package/dist/core/auth.js.map +1 -1
- package/dist/core/compensator.d.ts +32 -4
- package/dist/core/compensator.d.ts.map +1 -1
- package/dist/core/compensator.js +118 -15
- package/dist/core/compensator.js.map +1 -1
- package/dist/core/diff-introspect.d.ts +21 -0
- package/dist/core/diff-introspect.d.ts.map +1 -0
- package/dist/core/diff-introspect.js +42 -0
- package/dist/core/diff-introspect.js.map +1 -0
- package/dist/core/dispatcher.d.ts +7 -0
- package/dist/core/dispatcher.d.ts.map +1 -1
- package/dist/core/dispatcher.js +21 -3
- package/dist/core/dispatcher.js.map +1 -1
- package/dist/core/git-show.d.ts +31 -0
- package/dist/core/git-show.d.ts.map +1 -0
- package/dist/core/git-show.js +72 -0
- package/dist/core/git-show.js.map +1 -0
- package/dist/core/http-dispatcher.d.ts +20 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -0
- package/dist/core/http-dispatcher.js +125 -0
- package/dist/core/http-dispatcher.js.map +1 -0
- package/dist/core/job-store.d.ts +7 -1
- package/dist/core/job-store.d.ts.map +1 -1
- package/dist/core/job-store.js +21 -1
- package/dist/core/job-store.js.map +1 -1
- package/dist/core/jsonpath.d.ts +15 -0
- package/dist/core/jsonpath.d.ts.map +1 -0
- package/dist/core/jsonpath.js +63 -0
- package/dist/core/jsonpath.js.map +1 -0
- package/dist/core/oss-examples.d.ts +18 -0
- package/dist/core/oss-examples.d.ts.map +1 -0
- package/dist/core/oss-examples.js +66 -0
- package/dist/core/oss-examples.js.map +1 -0
- package/dist/core/parser.d.ts +8 -3
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +157 -6
- package/dist/core/parser.js.map +1 -1
- package/dist/core/project-root.d.ts +10 -0
- package/dist/core/project-root.d.ts.map +1 -0
- package/dist/core/project-root.js +23 -0
- package/dist/core/project-root.js.map +1 -0
- package/dist/core/reconciler.d.ts +1 -1
- package/dist/core/reconciler.d.ts.map +1 -1
- package/dist/core/reconciler.js +100 -18
- package/dist/core/reconciler.js.map +1 -1
- package/dist/core/redact.d.ts +17 -0
- package/dist/core/redact.d.ts.map +1 -0
- package/dist/core/redact.js +140 -0
- package/dist/core/redact.js.map +1 -0
- package/dist/core/results-pipeline.d.ts +8 -2
- package/dist/core/results-pipeline.d.ts.map +1 -1
- package/dist/core/results-pipeline.js +50 -3
- package/dist/core/results-pipeline.js.map +1 -1
- package/dist/core/runtime-probe.d.ts +14 -0
- package/dist/core/runtime-probe.d.ts.map +1 -0
- package/dist/core/runtime-probe.js +57 -0
- package/dist/core/runtime-probe.js.map +1 -0
- package/dist/core/stable-id.d.ts +19 -0
- package/dist/core/stable-id.d.ts.map +1 -0
- package/dist/core/stable-id.js +148 -0
- package/dist/core/stable-id.js.map +1 -0
- package/dist/core/trust-mode.d.ts +29 -0
- package/dist/core/trust-mode.d.ts.map +1 -0
- package/dist/core/trust-mode.js +103 -0
- package/dist/core/trust-mode.js.map +1 -0
- package/dist/formatters/markdown.d.ts.map +1 -1
- package/dist/formatters/markdown.js +9 -0
- package/dist/formatters/markdown.js.map +1 -1
- package/dist/formatters/text.d.ts.map +1 -1
- package/dist/formatters/text.js +9 -0
- package/dist/formatters/text.js.map +1 -1
- package/dist/types.d.ts +44 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- 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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"}
|
package/dist/core/auth.d.ts
CHANGED
|
@@ -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
|
package/dist/core/auth.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AAGpD,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;AAoDD;;;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,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { deriveProbeUrl } from '../config/schema.js';
|
|
3
|
+
// Re-exported so callers (and tests) can derive an http probe URL from auth.ts.
|
|
4
|
+
export { deriveProbeUrl };
|
|
2
5
|
/**
|
|
3
6
|
* Check whether a CLI command is installed (available on PATH).
|
|
4
7
|
* Uses `command -v` via shell.
|
|
@@ -62,11 +65,71 @@ async function runAuthCheck(config) {
|
|
|
62
65
|
* to handle transient network issues.
|
|
63
66
|
*/
|
|
64
67
|
export async function checkAuth(config) {
|
|
65
|
-
|
|
68
|
+
// Polymorphic over channel kind: HTTP channels are auth-probed over the wire.
|
|
69
|
+
if (config.kind === 'http') {
|
|
70
|
+
return checkHttpAuth(config);
|
|
71
|
+
}
|
|
72
|
+
if (!config.auth) {
|
|
73
|
+
return { status: 'ok' };
|
|
74
|
+
}
|
|
75
|
+
const authConfig = { ...config, auth: config.auth };
|
|
76
|
+
const result = await runAuthCheck(authConfig);
|
|
66
77
|
if (result.status === 'timeout') {
|
|
67
78
|
// Retry once on timeout (transient network issue)
|
|
68
|
-
return runAuthCheck(
|
|
79
|
+
return runAuthCheck(authConfig);
|
|
69
80
|
}
|
|
70
81
|
return result;
|
|
71
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Auth-probe an HTTP (openai-chat) channel: GET the configured (or derived)
|
|
85
|
+
* probe URL with the channel's full request context and map the status to
|
|
86
|
+
* ok/failed/timeout. The API key value is read only to build the request
|
|
87
|
+
* header — it is NEVER logged or returned in the result.
|
|
88
|
+
*/
|
|
89
|
+
export async function checkHttpAuth(channel) {
|
|
90
|
+
const probeUrl = channel.auth.check_endpoint ?? deriveProbeUrl(channel.endpoint);
|
|
91
|
+
if (!probeUrl) {
|
|
92
|
+
return { status: 'failed', recovery: channel.auth.recovery };
|
|
93
|
+
}
|
|
94
|
+
const headers = { ...(channel.headers ?? {}) };
|
|
95
|
+
if (channel.api_key_env) {
|
|
96
|
+
const value = process.env[channel.api_key_env];
|
|
97
|
+
if (!value) {
|
|
98
|
+
return { status: 'failed', recovery: channel.auth.recovery };
|
|
99
|
+
}
|
|
100
|
+
// Drop any case-variant of the target header so the api-key header wins.
|
|
101
|
+
const target = channel.api_key_header.toLowerCase();
|
|
102
|
+
for (const k of Object.keys(headers)) {
|
|
103
|
+
if (k.toLowerCase() === target)
|
|
104
|
+
delete headers[k];
|
|
105
|
+
}
|
|
106
|
+
headers[channel.api_key_header] = `${channel.api_key_prefix}${value}`;
|
|
107
|
+
}
|
|
108
|
+
const controller = new AbortController();
|
|
109
|
+
const timer = setTimeout(() => controller.abort(), channel.auth.timeout * 1000);
|
|
110
|
+
try {
|
|
111
|
+
const res = await fetch(probeUrl, {
|
|
112
|
+
method: channel.auth.check_method,
|
|
113
|
+
headers,
|
|
114
|
+
signal: controller.signal,
|
|
115
|
+
});
|
|
116
|
+
if (channel.auth.check_status_ok.includes(res.status)) {
|
|
117
|
+
return { status: 'ok' };
|
|
118
|
+
}
|
|
119
|
+
// A non-ok HTTP response is plausibly an auth problem (401/403/…) → surface
|
|
120
|
+
// the user's auth-setup recovery guidance.
|
|
121
|
+
return { status: 'failed', recovery: channel.auth.recovery };
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
if (err.name === 'AbortError') {
|
|
125
|
+
return { status: 'timeout' };
|
|
126
|
+
}
|
|
127
|
+
// Transport-level failure (DNS, connection refused, TLS) is NOT an auth
|
|
128
|
+
// problem — do not attach auth recovery text, which would mislead.
|
|
129
|
+
return { status: 'failed' };
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
72
135
|
//# sourceMappingURL=auth.js.map
|
package/dist/core/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,gFAAgF;AAChF,OAAO,EAAE,cAAc,EAAE,CAAA;AAWzB;;;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,MAAkC;IAC5D,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,8EAA8E;IAC9E,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,OAAO,aAAa,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IACzB,CAAC;IACD,MAAM,UAAU,GAAG,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAA;IACnD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,CAAA;IAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAChC,kDAAkD;QAClD,OAAO,YAAY,CAAC,UAAU,CAAC,CAAA;IACjC,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA0B;IAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAChF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAA;IAC9D,CAAC;IACD,MAAM,OAAO,GAA2B,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAA;IACtE,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAA;QAC9D,CAAC;QACD,yEAAyE;QACzE,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,WAAW,EAAE,CAAA;QACnD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM;gBAAE,OAAO,OAAO,CAAC,CAAC,CAAC,CAAA;QACnD,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,GAAG,OAAO,CAAC,cAAc,GAAG,KAAK,EAAE,CAAA;IACvE,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAC/E,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;YAChC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY;YACjC,OAAO;YACP,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAA;QACF,IAAI,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACtD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QACzB,CAAC;QACD,4EAA4E;QAC5E,2CAA2C;QAC3C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAA;IAC9D,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAAyB,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACrD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;QAC9B,CAAC;QACD,wEAAwE;QACxE,mEAAmE;QACnE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAA;IAC7B,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAA;IACrB,CAAC;AACH,CAAC"}
|
|
@@ -1,21 +1,49 @@
|
|
|
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
|
+
}
|
|
20
|
+
export declare function resolveCompensatorDispatch(config: MmrConfigParsed): CompensatorDispatch;
|
|
21
|
+
export declare function resolveCompensatorChannelName(config: MmrConfigParsed): string;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the configured compensator channel config (any kind), or undefined
|
|
24
|
+
* when none is configured (the default `claude -p` subprocess fallback applies).
|
|
25
|
+
* Throws only for a missing / abstract reference. Unlike
|
|
26
|
+
* getDispatchableCompensatorChannel this does NOT require a `command`, so it is
|
|
27
|
+
* safe for http compensators.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getCompensatorChannel(config: MmrConfigParsed): ChannelConfigParsed | undefined;
|
|
30
|
+
/** Output parser for the configured compensator channel (any kind); 'default' when none. */
|
|
31
|
+
export declare function resolveCompensatorOutputParser(config: MmrConfigParsed): string | OutputParserConfig;
|
|
32
|
+
export declare function getDispatchableCompensatorChannel(config: MmrConfigParsed, channelName: string): ChannelConfigParsed & {
|
|
33
|
+
command: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the focus-area prompt prefix for a compensating pass.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveCompensatorFocus(config: MmrConfigParsed, originalChannel: string): string;
|
|
11
39
|
/**
|
|
12
40
|
* Determine which channels need compensating passes.
|
|
13
41
|
* Returns a list of compensating channel descriptors.
|
|
14
42
|
*/
|
|
15
|
-
export declare function getCompensatingChannels(channelStatuses: Record<string, ChannelStatus
|
|
43
|
+
export declare function getCompensatingChannels(channelStatuses: Record<string, ChannelStatus>, compensatorChannel: string): CompensatingChannel[];
|
|
16
44
|
/**
|
|
17
45
|
* Dispatch compensating passes via claude CLI for unavailable channels.
|
|
18
46
|
* Each compensating pass uses the same prompt but with a focused preamble.
|
|
19
47
|
*/
|
|
20
|
-
export declare function dispatchCompensatingPasses(store: JobStore, jobId: string, prompt: string, compensatingChannels: CompensatingChannel[],
|
|
48
|
+
export declare function dispatchCompensatingPasses(store: JobStore, jobId: string, prompt: string, compensatingChannels: CompensatingChannel[], config: MmrConfigParsed): Promise<void>;
|
|
21
49
|
//# sourceMappingURL=compensator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compensator.d.ts","sourceRoot":"","sources":["../../src/core/compensator.ts"],"names":[],"mappings":"
|
|
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;CAC1C;AAcD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,eAAe,GAAG,mBAAmB,CAmBvF;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,CAuCf"}
|