@indigoai-us/hq-cli 5.345.163 → 5.345.165

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 (41) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/command-catalog.generated.d.ts +66 -0
  3. package/dist/command-catalog.generated.js +82 -0
  4. package/dist/commands/billing-sponsor.d.ts +20 -0
  5. package/dist/commands/billing-sponsor.js +116 -0
  6. package/dist/commands/billing-usage.d.ts +16 -2
  7. package/dist/commands/billing-usage.js +63 -9
  8. package/dist/commands/billing.d.ts +34 -0
  9. package/dist/commands/billing.js +108 -1
  10. package/dist/commands/deploy-domains.d.ts +2 -0
  11. package/dist/commands/deploy-domains.js +18 -3
  12. package/dist/commands/scan.d.ts +6 -0
  13. package/dist/commands/scan.js +11 -1
  14. package/dist/lib/billing/billing-status-lines.d.ts +14 -0
  15. package/dist/lib/billing/billing-status-lines.js +43 -0
  16. package/dist/lib/billing/usage-only.d.ts +18 -0
  17. package/dist/lib/billing/usage-only.js +18 -1
  18. package/dist/lib/doctor/checks/sync-runtime.d.ts +34 -0
  19. package/dist/lib/doctor/checks/sync-runtime.js +217 -8
  20. package/dist/lib/doctor/fix/apply.js +1 -0
  21. package/dist/lib/doctor/fix/refresh-sync-outcome.d.ts +4 -1
  22. package/dist/lib/doctor/fix/refresh-sync-outcome.js +15 -0
  23. package/dist/lib/doctor/fix/refresh-sync-progress.d.ts +42 -0
  24. package/dist/lib/doctor/fix/refresh-sync-progress.js +100 -0
  25. package/dist/lib/doctor/json-output.d.ts +7 -0
  26. package/dist/lib/doctor/json-output.js +8 -1
  27. package/dist/lib/doctor/types.d.ts +1 -1
  28. package/dist/lib/doctor/types.js +1 -0
  29. package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +1 -1
  30. package/dist/lib/mesh/live/daemon/work-subscribe.js +121 -38
  31. package/dist/lib/mesh/live/paths.d.ts +1 -0
  32. package/dist/lib/mesh/live/paths.js +1 -0
  33. package/dist/lib/secret-scan/detect.js +2 -0
  34. package/dist/lib/secret-scan/probes.d.ts +13 -0
  35. package/dist/lib/secret-scan/probes.js +69 -0
  36. package/dist/lib/secret-scan/repo-config.d.ts +24 -0
  37. package/dist/lib/secret-scan/repo-config.js +159 -29
  38. package/dist/lib/secret-scan/rules.d.ts +2 -0
  39. package/dist/lib/secret-scan/staged.d.ts +15 -1
  40. package/dist/lib/secret-scan/staged.js +46 -4
  41. package/package.json +7 -7
@@ -4,46 +4,67 @@
4
4
  */
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
7
+ import { randomUUID } from "node:crypto";
7
8
  import { createPersonalRealtimeFetcher, } from "./credentials.js";
8
9
  import { presignIotWssUrl } from "./presign.js";
9
10
  import { applyProjectViewPush, parseProjectViewPush } from "./live-cache.js";
10
11
  import { backoffDelayMs, RECONNECT_MIN_DELAY_MS } from "./presence.js";
11
- import { AGENT_INBOX_PUSH_STATE_BASENAME } from "../paths.js";
12
- export { AGENT_INBOX_PUSH_STATE_BASENAME } from "../paths.js";
12
+ import { AGENT_INBOX_PUSH_STATE_BASENAME, SYNC_WAKE_STATE_BASENAME } from "../paths.js";
13
+ export { AGENT_INBOX_PUSH_STATE_BASENAME, SYNC_WAKE_STATE_BASENAME } from "../paths.js";
13
14
  const INBOX_PUSH_HEARTBEAT_MS = 5_000;
14
- function readInboxWakeSequence(meshRoot, onInfo) {
15
- const target = path.join(meshRoot, AGENT_INBOX_PUSH_STATE_BASENAME);
15
+ function readWakeSequence(meshRoot, basename, label, onInfo) {
16
+ const target = path.join(meshRoot, basename);
16
17
  try {
17
18
  const info = fs.lstatSync(target);
18
19
  if (!info.isFile() || info.isSymbolicLink()) {
19
- onInfo?.("agent inbox push state read ignored (not a regular file)");
20
- return 0;
20
+ onInfo?.(`${label} state read ignored (not a regular file)`);
21
+ return { sequence: 0, writable: false };
21
22
  }
22
23
  const parsed = JSON.parse(fs.readFileSync(target, "utf8"));
23
24
  if (Number.isSafeInteger(parsed.wakeSequence) && Number(parsed.wakeSequence) >= 0) {
24
- return Number(parsed.wakeSequence);
25
+ return { sequence: Number(parsed.wakeSequence), writable: true };
25
26
  }
26
- onInfo?.("agent inbox push state read ignored (invalid wake sequence)");
27
- return 0;
27
+ onInfo?.(`${label} state read ignored (invalid wake sequence)`);
28
+ return { sequence: 0, writable: false };
28
29
  }
29
30
  catch (error) {
30
31
  if (error.code !== "ENOENT") {
31
32
  const name = error instanceof Error ? error.name : "UnknownError";
32
- onInfo?.(`agent inbox push state read failed (${name})`);
33
+ onInfo?.(`${label} state read failed (${name})`);
33
34
  }
34
- return 0;
35
+ return { sequence: 0, writable: error.code === "ENOENT" };
35
36
  }
36
37
  }
37
- function writeInboxPushState(meshRoot, state) {
38
+ function writeWakeState(meshRoot, basename, state, durable) {
38
39
  fs.mkdirSync(meshRoot, { recursive: true, mode: 0o700 });
39
- const target = path.join(meshRoot, AGENT_INBOX_PUSH_STATE_BASENAME);
40
- const temporary = `${target}.${process.pid}.tmp`;
40
+ const rootInfo = fs.lstatSync(meshRoot);
41
+ if (!rootInfo.isDirectory() || rootInfo.isSymbolicLink()) {
42
+ throw new Error("wake state root is not a regular directory");
43
+ }
44
+ const target = path.join(meshRoot, basename);
45
+ const temporary = path.join(meshRoot, `.${basename}.${process.pid}.${randomUUID()}.tmp`);
46
+ let file;
41
47
  try {
42
- fs.writeFileSync(temporary, `${JSON.stringify(state)}\n`, { mode: 0o600 });
43
- fs.chmodSync(temporary, 0o600);
48
+ file = fs.openSync(temporary, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_NOFOLLOW, 0o600);
49
+ fs.writeFileSync(file, `${JSON.stringify(state)}\n`);
50
+ if (durable)
51
+ fs.fsyncSync(file);
52
+ fs.closeSync(file);
53
+ file = undefined;
44
54
  fs.renameSync(temporary, target);
55
+ if (durable && process.platform !== "win32") {
56
+ const directory = fs.openSync(meshRoot, fs.constants.O_RDONLY);
57
+ try {
58
+ fs.fsyncSync(directory);
59
+ }
60
+ finally {
61
+ fs.closeSync(directory);
62
+ }
63
+ }
45
64
  }
46
65
  catch (error) {
66
+ if (file !== undefined)
67
+ fs.closeSync(file);
47
68
  try {
48
69
  fs.unlinkSync(temporary);
49
70
  }
@@ -79,37 +100,46 @@ export async function startWorkCacheSubscriber(opts) {
79
100
  let heartbeatTimer = null;
80
101
  let generation = 0;
81
102
  let inboxSubscribed = false;
82
- let inboxAgentUid;
83
- // Carry the sequence forward from the last state marker so a daemon restart
84
- // cannot reuse a value the watcher has already consumed.
85
- let wakeSequence = readInboxWakeSequence(opts.meshRoot, opts.onInfo);
103
+ let syncSubscribed = false;
104
+ let actorUid;
105
+ let inboxWakeSequence = readWakeSequence(opts.meshRoot, AGENT_INBOX_PUSH_STATE_BASENAME, "agent inbox push", opts.onInfo).sequence;
106
+ const syncWakeState = readWakeSequence(opts.meshRoot, SYNC_WAKE_STATE_BASENAME, "sync wake", opts.onInfo);
107
+ let syncWakeSequence = syncWakeState.sequence;
108
+ const syncStateWritable = syncWakeState.writable;
86
109
  const clearInboxHeartbeat = () => {
87
110
  if (heartbeatTimer !== null)
88
111
  timers.clearTimeout(heartbeatTimer);
89
112
  heartbeatTimer = null;
90
113
  };
91
- const persistInboxState = (connected) => {
114
+ const persistState = (basename, label, connected, wakeSequence, identity, durable = true) => {
92
115
  try {
93
- writeInboxPushState(opts.meshRoot, {
116
+ writeWakeState(opts.meshRoot, basename, {
94
117
  connected,
95
118
  heartbeatAtMs: now().getTime(),
96
119
  wakeSequence,
97
- ...(connected && inboxAgentUid ? { agentUid: inboxAgentUid } : {}),
98
- });
120
+ ...(connected && actorUid && identity ? { [identity]: actorUid } : {}),
121
+ }, durable);
99
122
  }
100
123
  catch (error) {
101
124
  const name = error instanceof Error ? error.name : "UnknownError";
102
- opts.onInfo?.(`agent inbox push state write failed (${name})`);
125
+ opts.onInfo?.(`${label} state write failed (${name})`);
103
126
  }
104
127
  };
128
+ const persistInboxState = (connected, durable = true) => persistState(AGENT_INBOX_PUSH_STATE_BASENAME, "agent inbox push", connected, inboxWakeSequence, "agentUid", durable);
129
+ const persistSyncState = (connected, durable = true) => syncStateWritable
130
+ ? persistState(SYNC_WAKE_STATE_BASENAME, "sync wake", connected, syncWakeSequence, undefined, durable)
131
+ : undefined;
105
132
  const scheduleInboxHeartbeat = () => {
106
- if (stopped || !inboxSubscribed)
133
+ if (stopped || (!inboxSubscribed && !syncSubscribed))
107
134
  return;
108
135
  clearInboxHeartbeat();
109
136
  heartbeatTimer = timers.setTimeout(() => {
110
137
  heartbeatTimer = null;
111
- if (!stopped && inboxSubscribed) {
112
- persistInboxState(true);
138
+ if (!stopped && (inboxSubscribed || syncSubscribed)) {
139
+ if (inboxSubscribed)
140
+ persistInboxState(true, false);
141
+ if (syncSubscribed)
142
+ persistSyncState(true, false);
113
143
  scheduleInboxHeartbeat();
114
144
  }
115
145
  }, INBOX_PUSH_HEARTBEAT_MS);
@@ -118,9 +148,11 @@ export async function startWorkCacheSubscriber(opts) {
118
148
  if (stopped)
119
149
  return;
120
150
  inboxSubscribed = false;
121
- inboxAgentUid = undefined;
151
+ syncSubscribed = false;
152
+ actorUid = undefined;
122
153
  clearInboxHeartbeat();
123
154
  persistInboxState(false);
155
+ persistSyncState(false);
124
156
  const gen = ++generation;
125
157
  let bundle;
126
158
  try {
@@ -137,14 +169,15 @@ export async function startWorkCacheSubscriber(opts) {
137
169
  bundle.topics.inbox === `hq/${bundle.actorUid}/inbox`
138
170
  ? bundle.topics.inbox
139
171
  : undefined;
140
- inboxAgentUid = inboxTopic ? bundle.actorUid : undefined;
141
- if (!topic && !inboxTopic) {
172
+ const syncTopic = syncStateWritable ? `hq/${bundle.actorUid}/sync` : undefined;
173
+ actorUid = inboxTopic || syncTopic ? bundle.actorUid : undefined;
174
+ if (!topic && !inboxTopic && !syncTopic) {
142
175
  opts.onInfo?.("work cache subscribe skipped: credentials have no supported topic");
143
176
  return;
144
177
  }
145
178
  const url = presignIotWssUrl(bundle.credentials, bundle.iotEndpoint, bundle.region, now());
146
179
  const next = opts.mqttConnect(url, {
147
- clientId: `${bundle.clientId}-work`,
180
+ clientId: bundle.clientId,
148
181
  clean: true,
149
182
  reconnectPeriod: 0,
150
183
  protocolVersion: 4,
@@ -154,7 +187,8 @@ export async function startWorkCacheSubscriber(opts) {
154
187
  if (stopped || gen !== generation)
155
188
  return;
156
189
  reconnectLogged = false;
157
- let pendingSubscriptions = Number(Boolean(topic)) + Number(Boolean(inboxTopic));
190
+ let pendingSubscriptions = Number(Boolean(topic)) + Number(Boolean(inboxTopic)) +
191
+ Number(Boolean(syncTopic));
158
192
  let subscriptionFailed = false;
159
193
  const finishSubscription = (err) => {
160
194
  if (err) {
@@ -171,6 +205,13 @@ export async function startWorkCacheSubscriber(opts) {
171
205
  if (pendingSubscriptions === 0)
172
206
  attempt = 0;
173
207
  };
208
+ const finishOptionalSubscription = () => {
209
+ if (subscriptionFailed)
210
+ return;
211
+ pendingSubscriptions -= 1;
212
+ if (pendingSubscriptions === 0)
213
+ attempt = 0;
214
+ };
174
215
  if (topic) {
175
216
  next.subscribe(topic, { qos: 1 }, (err) => finishSubscription(err));
176
217
  }
@@ -180,7 +221,6 @@ export async function startWorkCacheSubscriber(opts) {
180
221
  return;
181
222
  if (err) {
182
223
  inboxSubscribed = false;
183
- inboxAgentUid = undefined;
184
224
  clearInboxHeartbeat();
185
225
  persistInboxState(false);
186
226
  opts.onInfo?.("agent inbox push subscribe failed");
@@ -193,17 +233,37 @@ export async function startWorkCacheSubscriber(opts) {
193
233
  finishSubscription();
194
234
  });
195
235
  }
236
+ if (syncTopic) {
237
+ next.subscribe(syncTopic, { qos: 1 }, (err) => {
238
+ if (stopped || gen !== generation)
239
+ return;
240
+ if (err) {
241
+ syncSubscribed = false;
242
+ persistSyncState(false);
243
+ opts.onInfo?.("sync wake subscribe failed");
244
+ finishOptionalSubscription();
245
+ return;
246
+ }
247
+ syncSubscribed = true;
248
+ persistSyncState(true);
249
+ scheduleInboxHeartbeat();
250
+ finishSubscription();
251
+ });
252
+ }
196
253
  });
197
254
  next.on("message", (_topic, payload) => {
198
255
  if (stopped || gen !== generation)
199
256
  return;
200
257
  if (inboxTopic && _topic === inboxTopic && inboxSubscribed) {
201
- // A topic message is a doorbell only. Never parse, persist, or log its
202
- // content; the authenticated inbox GET remains the source of truth.
203
- wakeSequence += 1;
258
+ inboxWakeSequence += 1;
204
259
  persistInboxState(true);
205
260
  return;
206
261
  }
262
+ if (syncTopic && _topic === syncTopic && syncSubscribed) {
263
+ syncWakeSequence += 1;
264
+ persistSyncState(true);
265
+ return;
266
+ }
207
267
  if (_topic !== topic)
208
268
  return;
209
269
  const push = parseProjectViewPush(payload);
@@ -214,13 +274,33 @@ export async function startWorkCacheSubscriber(opts) {
214
274
  opts.onInfo?.(`project-view cache cursor=${result.cursor} ${push.companyUid}/${push.projectId}`);
215
275
  }
216
276
  });
277
+ next.on("error", (_error) => {
278
+ if (stopped || gen !== generation)
279
+ return;
280
+ inboxSubscribed = false;
281
+ syncSubscribed = false;
282
+ actorUid = undefined;
283
+ clearInboxHeartbeat();
284
+ persistInboxState(false);
285
+ persistSyncState(false);
286
+ opts.onInfo?.("work cache mqtt error");
287
+ schedule();
288
+ try {
289
+ next.end(true);
290
+ }
291
+ catch {
292
+ return;
293
+ }
294
+ });
217
295
  next.on("close", () => {
218
296
  if (stopped || gen !== generation)
219
297
  return;
220
298
  inboxSubscribed = false;
221
- inboxAgentUid = undefined;
299
+ syncSubscribed = false;
300
+ actorUid = undefined;
222
301
  clearInboxHeartbeat();
223
302
  persistInboxState(false);
303
+ persistSyncState(false);
224
304
  schedule();
225
305
  });
226
306
  };
@@ -250,7 +330,10 @@ export async function startWorkCacheSubscriber(opts) {
250
330
  timers.clearTimeout(timer);
251
331
  clearInboxHeartbeat();
252
332
  inboxSubscribed = false;
333
+ syncSubscribed = false;
334
+ actorUid = undefined;
253
335
  persistInboxState(false);
336
+ persistSyncState(false);
254
337
  const current = client;
255
338
  client = null;
256
339
  if (!current)
@@ -4,6 +4,7 @@
4
4
  */
5
5
  /** Content-free sequence file written by the mesh MQTT inbox subscriber. */
6
6
  export declare const AGENT_INBOX_PUSH_STATE_BASENAME = "agent-inbox-push.json";
7
+ export declare const SYNC_WAKE_STATE_BASENAME = "sync-wake.json";
7
8
  export declare function workMeshRoot(home?: string, env?: NodeJS.ProcessEnv): string;
8
9
  export declare function workMeshSpoolPath(root?: string): string;
9
10
  export declare function workMeshHeldPath(root?: string): string;
@@ -7,6 +7,7 @@ import * as path from "node:path";
7
7
  const WORK_MESH_DIRNAME = "work-mesh";
8
8
  /** Content-free sequence file written by the mesh MQTT inbox subscriber. */
9
9
  export const AGENT_INBOX_PUSH_STATE_BASENAME = "agent-inbox-push.json";
10
+ export const SYNC_WAKE_STATE_BASENAME = "sync-wake.json";
10
11
  export function workMeshRoot(home = os.homedir(), env = process.env) {
11
12
  const override = env.HQ_WORK_MESH_ROOT?.trim();
12
13
  if (override)
@@ -38,6 +38,8 @@ function allowed(al, c) {
38
38
  }
39
39
  if (al.commits.length)
40
40
  checks.push(c.commit !== undefined && al.commits.includes(c.commit.toLowerCase()));
41
+ if (al.exact?.length)
42
+ checks.push(al.exact.includes(c.secret));
41
43
  if (!checks.length)
42
44
  return false;
43
45
  return al.condition === "AND" ? checks.every(Boolean) : checks.some(Boolean);
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A fixed corpus of realistic credential lines, used to judge a repo
3
+ * allowlist by what it does: each line is placed in a file at the allowlist's
4
+ * paths and run through the real detector. Values are generated from a fixed
5
+ * seed at runtime, so no secret-shaped literal sits in this file and the
6
+ * corpus is the same on every run.
7
+ */
8
+ export interface Probe {
9
+ kind: string;
10
+ line: string;
11
+ }
12
+ export declare function credentialProbes(): Probe[];
13
+ //# sourceMappingURL=probes.d.ts.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * A fixed corpus of realistic credential lines, used to judge a repo
3
+ * allowlist by what it does: each line is placed in a file at the allowlist's
4
+ * paths and run through the real detector. Values are generated from a fixed
5
+ * seed at runtime, so no secret-shaped literal sits in this file and the
6
+ * corpus is the same on every run.
7
+ */
8
+ const UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
9
+ const LOWER = "abcdefghijklmnopqrstuvwxyz";
10
+ const DIGITS = "0123456789";
11
+ const ALNUM = UPPER + LOWER + DIGITS;
12
+ const HEX = "0123456789abcdef";
13
+ const B64URL = ALNUM + "-_";
14
+ /** Deterministic xorshift32 generator. */
15
+ function generator(seed) {
16
+ let s = seed >>> 0 || 1;
17
+ return (alphabet, n) => {
18
+ let out = "";
19
+ for (let i = 0; i < n; i++) {
20
+ s ^= s << 13;
21
+ s >>>= 0;
22
+ s ^= s >>> 17;
23
+ s ^= s << 5;
24
+ s >>>= 0;
25
+ out += alphabet[s % alphabet.length];
26
+ }
27
+ return out;
28
+ };
29
+ }
30
+ const b64url = (s) => Buffer.from(s).toString("base64url");
31
+ const j = (...parts) => parts.join("");
32
+ let cached;
33
+ export function credentialProbes() {
34
+ if (cached)
35
+ return cached;
36
+ const r = generator(0x9e3779b9);
37
+ const probes = [];
38
+ const add = (kind, line) => probes.push({ kind, line });
39
+ // GitHub tokens: classic PAT, OAuth, app and fine-grained PAT.
40
+ add("GitHub PAT", `token = "${j("gh", "p_", r(ALNUM, 36))}"`);
41
+ add("GitHub PAT", `export GH_TOKEN=${j("gh", "o_", r(ALNUM, 36))}`);
42
+ add("GitHub PAT", ` "github": "${j("gh", "s_", r(ALNUM, 36))}",`);
43
+ add("GitHub PAT", j("gith", "ub_pat_", r(ALNUM, 22), "_", r(ALNUM, 59)));
44
+ // AWS access key ids.
45
+ add("AWS key", `aws_access_key_id = ${j("AK", "IA", r(UPPER + "234567", 16))}`);
46
+ add("AWS key", `AWS_ACCESS_KEY_ID="${j("AS", "IA", r(UPPER + "234567", 16))}"`);
47
+ add("AWS key", j("AK", "IA", r(UPPER + "234567", 16)));
48
+ // Slack bot and user tokens.
49
+ add("Slack token", `SLACK_BOT_TOKEN=${j("xo", "xb-", r(DIGITS, 12), "-", r(DIGITS, 13), "-", r(ALNUM, 24))}`);
50
+ add("Slack token", `slack: "${j("xo", "xb-", r(DIGITS, 11), "-", r(DIGITS, 12), "-", r(ALNUM, 24))}"`);
51
+ add("Slack token", `token = "${j("xo", "xp-", r(DIGITS, 12), "-", r(DIGITS, 12), "-", r(DIGITS, 13), "-", r(HEX, 32))}"`);
52
+ // 64-character hex secrets under credential-looking keys.
53
+ add("hex-64", `api_secret = "${r(HEX, 64)}"`);
54
+ add("hex-64", ` "auth_token": "${r(HEX, 64)}",`);
55
+ add("hex-64", `SECRET_KEY=${r(HEX, 64)}`);
56
+ // JWTs of different lengths.
57
+ for (const n of [24, 48, 96]) {
58
+ const header = b64url('{"alg":"HS256","typ":"JWT"}');
59
+ const payload = b64url(JSON.stringify({ sub: r(DIGITS, 10), name: r(LOWER, n / 4), iat: 1700000000 }));
60
+ add("JWT", `const jwt = "${header}.${payload}.${r(B64URL, 43)}";`);
61
+ }
62
+ // Generic key = value credentials of different lengths.
63
+ add("generic api_key", `api_key = "${r(ALNUM, 20)}"`);
64
+ add("generic api_key", `client_secret: '${r(ALNUM, 32)}'`);
65
+ add("generic api_key", `ACCESS_TOKEN=${r(ALNUM, 48)}`);
66
+ cached = probes;
67
+ return probes;
68
+ }
69
+ //# sourceMappingURL=probes.js.map
@@ -18,13 +18,28 @@
18
18
  * literal prefix (`^golden/...`); `regexes` and `stopwords` need
19
19
  * `condition = "AND"`, so they never reach files outside those paths.
20
20
  *
21
+ * File scans (`hq scan secrets <files> --repo`, used by pr-check) honor only
22
+ * exact-value exemptions: each allowlist names its paths and lists, in
23
+ * `stopwords`, the exact secret values it allows (e.g. known digests). A
24
+ * detected secret is allowed only when it equals one of them. Regexes,
25
+ * targetRules, commits, regexTarget and path-only allowlists are rejected
26
+ * there, because a pattern can always be widened, alone or combined with other
27
+ * entries, to hide a real credential.
28
+ *
21
29
  * Fails closed: anything not acceptable is a ScanConfigError, and the scan
22
30
  * exits 2 rather than running with a guess.
23
31
  */
32
+ import { type Probe } from "./probes.js";
24
33
  import { type CompiledAllowlist } from "./rules.js";
25
34
  export declare class ScanConfigError extends Error {
26
35
  }
27
36
  export declare const REPO_CONFIG_FILE = ".gitleaks.toml";
37
+ /**
38
+ * The first probe credential the allowlist would hide, judged as the detector
39
+ * judges it (every regex ORed, every check combined by its condition, its
40
+ * targetRules applied), for a file at one of its paths. undefined: none.
41
+ */
42
+ export declare function suppressedProbe(al: CompiledAllowlist): Probe | undefined;
28
43
  /**
29
44
  * Length of the literal text right after a leading `^`: letters, digits,
30
45
  * `/ _ - @ ~` and backslash-escaped punctuation. A quantifier on the last
@@ -33,9 +48,18 @@ export declare const REPO_CONFIG_FILE = ".gitleaks.toml";
33
48
  export declare function literalPathPrefix(src: string): number;
34
49
  /** Validated allowlists from the text of a .gitleaks.toml. */
35
50
  export declare function parseRepoAllowlists(text: string, file?: string): CompiledAllowlist[];
51
+ /** Validated exact-value allowlists for a file scan, from the text of a .gitleaks.toml. */
52
+ export declare function parseExactAllowlists(text: string, file?: string): CompiledAllowlist[];
36
53
  /**
37
54
  * Compiled allowlists for a staged scan of the repo at `repoRoot`, read from
38
55
  * git as described at the top of this file. [] when the repo has none.
39
56
  */
40
57
  export declare function loadRepoAllowlists(repoRoot: string): CompiledAllowlist[];
58
+ /**
59
+ * Exact-value allowlists from the copy committed at `rev` (default HEAD), for a
60
+ * file scan (`hq scan secrets --repo [--allowlist-rev]`). The index and working
61
+ * tree are ignored. pr-check passes the base branch, so a branch cannot exempt
62
+ * its own commits by adding an allowlist. An unknown rev is an error.
63
+ */
64
+ export declare function loadCommittedAllowlists(repoRoot: string, rev?: string): CompiledAllowlist[];
41
65
  //# sourceMappingURL=repo-config.d.ts.map