@lannguyensi/harness 0.51.0 → 0.53.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/CHANGELOG.md +182 -0
- package/dist/cli/approve/understanding.d.ts +46 -0
- package/dist/cli/approve/understanding.js +50 -22
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/audit.d.ts +29 -0
- package/dist/cli/audit.js +130 -17
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/delegate/index.d.ts +63 -0
- package/dist/cli/delegate/index.js +313 -0
- package/dist/cli/delegate/index.js.map +1 -0
- package/dist/cli/doctor/auto-approve-mode.d.ts +37 -0
- package/dist/cli/doctor/auto-approve-mode.js +80 -0
- package/dist/cli/doctor/auto-approve-mode.js.map +1 -0
- package/dist/cli/doctor/bypass-without-auto-approve.d.ts +38 -0
- package/dist/cli/doctor/bypass-without-auto-approve.js +89 -0
- package/dist/cli/doctor/bypass-without-auto-approve.js.map +1 -0
- package/dist/cli/doctor/codex-config-drift.d.ts +28 -0
- package/dist/cli/doctor/codex-config-drift.js +240 -0
- package/dist/cli/doctor/codex-config-drift.js.map +1 -0
- package/dist/cli/doctor/format.js +39 -1
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.js +68 -7
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/settings-drift.d.ts +6 -0
- package/dist/cli/doctor/settings-drift.js +20 -6
- package/dist/cli/doctor/settings-drift.js.map +1 -1
- package/dist/cli/doctor/types.d.ts +54 -0
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/doctor/ug-auto-approvals.js +18 -43
- package/dist/cli/doctor/ug-auto-approvals.js.map +1 -1
- package/dist/cli/doctor/ug-delegations.d.ts +26 -0
- package/dist/cli/doctor/ug-delegations.js +112 -0
- package/dist/cli/doctor/ug-delegations.js.map +1 -0
- package/dist/cli/doctor/understanding-mode-env.d.ts +2 -0
- package/dist/cli/doctor/understanding-mode-env.js +5 -2
- package/dist/cli/doctor/understanding-mode-env.js.map +1 -1
- package/dist/cli/gc/index.d.ts +13 -2
- package/dist/cli/gc/index.js +203 -1
- package/dist/cli/gc/index.js.map +1 -1
- package/dist/cli/index.js +133 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/composer.js +9 -0
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +2 -2
- package/dist/cli/init/profiles.js +11 -0
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +11 -0
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/approve-escape.js +16 -7
- package/dist/cli/pack/approve-escape.js.map +1 -1
- package/dist/cli/pack/auto-approve-path.d.ts +17 -0
- package/dist/cli/pack/auto-approve-path.js +56 -3
- package/dist/cli/pack/auto-approve-path.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.d.ts +25 -0
- package/dist/cli/pack/hook-pre-tool-use.js +335 -3
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/index.d.ts +1 -0
- package/dist/cli/pack/index.js +1 -0
- package/dist/cli/pack/index.js.map +1 -1
- package/dist/cli/pack/transcript-report-scan.d.ts +71 -0
- package/dist/cli/pack/transcript-report-scan.js +359 -0
- package/dist/cli/pack/transcript-report-scan.js.map +1 -0
- package/dist/cli/pack/upgrade.d.ts +32 -0
- package/dist/cli/pack/upgrade.js +273 -0
- package/dist/cli/pack/upgrade.js.map +1 -0
- package/dist/cli/smoke/index.d.ts +18 -1
- package/dist/cli/smoke/index.js +54 -0
- package/dist/cli/smoke/index.js.map +1 -1
- package/dist/io/read-json-dir-entries.d.ts +36 -0
- package/dist/io/read-json-dir-entries.js +38 -0
- package/dist/io/read-json-dir-entries.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.d.ts +35 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js +86 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.d.ts +76 -2
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js +139 -2
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +237 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +647 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +4 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +26 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.d.ts +71 -0
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js +169 -0
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution.d.ts +19 -0
- package/dist/policy-packs/builtin/understanding-before-execution.js +29 -0
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/runtime/approval-signing.js +28 -0
- package/dist/runtime/approval-signing.js.map +1 -1
- package/dist/runtime/git-context.js +7 -1
- package/dist/runtime/git-context.js.map +1 -1
- package/package.json +1 -1
|
@@ -15,10 +15,8 @@
|
|
|
15
15
|
// marker's HMAC signature (that is `checkApprovalMarker`'s job at gate
|
|
16
16
|
// time; a forged marker still gets rejected there regardless of what
|
|
17
17
|
// this listing shows) and it never blocks or writes anything.
|
|
18
|
-
import * as fs from "node:fs";
|
|
19
18
|
import * as path from "node:path";
|
|
20
|
-
import {
|
|
21
|
-
import { safeJsonParse } from "../../io/safe-json-parse.js";
|
|
19
|
+
import { readJsonDirEntriesRejectingSymlinks } from "../../io/read-json-dir-entries.js";
|
|
22
20
|
import { BRANCH_PROTECTION_MARKER_PREFIX } from "../../policy-packs/builtin/branch-protection-runtime.js";
|
|
23
21
|
import { APPROVAL_MARKER_DIRNAME, APPROVAL_MARKER_TASK_PREFIX, parseAutoApprovedBy, } from "../../policy-packs/builtin/understanding-before-execution/index.js";
|
|
24
22
|
/** Default doctor window when `--recent-sessions` is not passed. */
|
|
@@ -44,47 +42,24 @@ function emptySection(windowSize) {
|
|
|
44
42
|
export function buildUgAutoApprovals(generatedDir, opts) {
|
|
45
43
|
const windowSize = opts.recentSessions;
|
|
46
44
|
const dir = path.join(generatedDir, APPROVAL_MARKER_DIRNAME);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
// A symlink / non-regular / raced-away entry is not a session marker this
|
|
46
|
+
// listing owns an opinion about; the shared reader skips it silently
|
|
47
|
+
// rather than counting it as "unreadable" (that count is for a FILE we
|
|
48
|
+
// expected to be a marker but could not parse).
|
|
49
|
+
const { dirPresent, entries: readable, unreadableCount } = readJsonDirEntriesRejectingSymlinks(dir, {
|
|
50
|
+
skip: (name) => name.startsWith(APPROVAL_MARKER_TASK_PREFIX) || name.startsWith(BRANCH_PROTECTION_MARKER_PREFIX),
|
|
51
|
+
parse: (raw, name) => {
|
|
52
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
53
|
+
return null;
|
|
54
|
+
const obj = raw;
|
|
55
|
+
const approvedAt = obj["approvedAt"];
|
|
56
|
+
if (typeof approvedAt !== "string" || Number.isNaN(Date.parse(approvedAt)))
|
|
57
|
+
return null;
|
|
58
|
+
return { sessionId: name, approvedAt, approvedBy: obj["approvedBy"] };
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
if (!dirPresent)
|
|
52
62
|
return emptySection(windowSize);
|
|
53
|
-
}
|
|
54
|
-
const readable = [];
|
|
55
|
-
let unreadableCount = 0;
|
|
56
|
-
for (const d of dirents) {
|
|
57
|
-
const name = d.name;
|
|
58
|
-
if (name.startsWith(APPROVAL_MARKER_TASK_PREFIX))
|
|
59
|
-
continue;
|
|
60
|
-
if (name.startsWith(BRANCH_PROTECTION_MARKER_PREFIX))
|
|
61
|
-
continue;
|
|
62
|
-
const full = path.join(dir, name);
|
|
63
|
-
const read = readRegularFileRejectingSymlink(full);
|
|
64
|
-
// A symlink / non-regular / raced-away entry is not a session marker
|
|
65
|
-
// this listing owns an opinion about; skip silently rather than
|
|
66
|
-
// counting it as "unreadable" (that count is for a FILE we expected
|
|
67
|
-
// to be a marker but could not parse).
|
|
68
|
-
if (read.kind === "symlink" || read.kind === "not-regular" || read.kind === "missing") {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (read.kind === "unreadable") {
|
|
72
|
-
unreadableCount++;
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const parsed = safeJsonParse(read.content);
|
|
76
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
77
|
-
unreadableCount++;
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const obj = parsed;
|
|
81
|
-
const approvedAt = obj["approvedAt"];
|
|
82
|
-
if (typeof approvedAt !== "string" || Number.isNaN(Date.parse(approvedAt))) {
|
|
83
|
-
unreadableCount++;
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
readable.push({ sessionId: name, approvedAt, approvedBy: obj["approvedBy"] });
|
|
87
|
-
}
|
|
88
63
|
readable.sort((a, b) => Date.parse(b.approvedAt) - Date.parse(a.approvedAt));
|
|
89
64
|
const windowed = readable.slice(0, windowSize);
|
|
90
65
|
const byMode = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ug-auto-approvals.js","sourceRoot":"","sources":["../../../src/cli/doctor/ug-auto-approvals.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,oEAAoE;AACpE,sEAAsE;AACtE,kCAAkC;AAClC,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,iEAAiE;AACjE,mEAAmE;AACnE,qEAAqE;AACrE,iEAAiE;AACjE,8DAA8D;AAC9D,EAAE;AACF,sEAAsE;AACtE,uEAAuE;AACvE,qEAAqE;AACrE,8DAA8D;AAE9D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ug-auto-approvals.js","sourceRoot":"","sources":["../../../src/cli/doctor/ug-auto-approvals.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface UgDelegationsSection {
|
|
2
|
+
/** Whether `<generatedDir>/.delegations/` exists at all. */
|
|
3
|
+
delegationsDirPresent: boolean;
|
|
4
|
+
/** Delegation files found in `.delegations/`, readable or not. */
|
|
5
|
+
total: number;
|
|
6
|
+
/** Of `total`, how many parsed with an `expires` segment in the past. */
|
|
7
|
+
expired: number;
|
|
8
|
+
/** Of `total`, how many were not valid JSON, or failed `parseDelegationApprovedBy`. */
|
|
9
|
+
unreadable: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build the delegations-on-disk doctor metric. Pure filesystem read;
|
|
13
|
+
* never throws for "nothing to see" states (missing `.delegations/`,
|
|
14
|
+
* empty directory, every file unreadable), those all resolve to a
|
|
15
|
+
* zero-ish section, mirroring `buildUgAutoApprovals`. `total`/`expired`/
|
|
16
|
+
* `unreadable` read `0` for both an EMPTY and a MISSING directory; only
|
|
17
|
+
* `delegationsDirPresent` tells them apart, the same way
|
|
18
|
+
* `approvalsDirPresent` does for `buildUgAutoApprovals`, doctor's
|
|
19
|
+
* render layer uses it to stay silent when there is truly nothing on
|
|
20
|
+
* disk (`format.ts`'s own "no line for a check that found nothing"
|
|
21
|
+
* convention) while still rendering the zero-count line for a
|
|
22
|
+
* `harness delegate`-touched, now-empty directory.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildUgDelegations(generatedDir: string, opts?: {
|
|
25
|
+
now?: Date;
|
|
26
|
+
}): UgDelegationsSection;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Doctor visibility for slice 3 delegations (ADR
|
|
2
|
+
// docs/decisions/2026-08-27-ug-auto-mode-approval.md, "Audit and
|
|
3
|
+
// doctor", agent-tasks 37ad0b05): "delegations on disk: <count>
|
|
4
|
+
// (<count> expired)" from `<generatedDir>/.delegations/`, distinct from
|
|
5
|
+
// the `.approvals/` auto-approval listing in `ug-auto-approvals.ts`.
|
|
6
|
+
//
|
|
7
|
+
// SEPARATE DIRECTORY, DELIBERATELY: a delegation never lands in
|
|
8
|
+
// `.approvals/` (delegation-markers.ts's module header), so this scan
|
|
9
|
+
// reads `DELEGATION_MARKER_DIRNAME` (".delegations") only and never
|
|
10
|
+
// touches the approvals directory `ug-auto-approvals.ts` owns. Mirrors
|
|
11
|
+
// that module's read-only-audit shape (no signature verification here
|
|
12
|
+
// either; a forged delegation still gets rejected by the dedicated
|
|
13
|
+
// verifier at gate time, `delegation-markers.ts`'s `verifyDelegation`)
|
|
14
|
+
// but does not share its code: the two scans read different
|
|
15
|
+
// directories, filter differently (no task-marker / branch-protection
|
|
16
|
+
// prefix skip here), and count differently (an approval count vs. an
|
|
17
|
+
// expiry count), so a shared helper would mostly be parameters threading
|
|
18
|
+
// through an otherwise-empty shell. `npm run check:duplication` is the
|
|
19
|
+
// backstop if that stops being true.
|
|
20
|
+
import * as fs from "node:fs";
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
import { readRegularFileRejectingSymlink } from "../../io/read-regular-file.js";
|
|
23
|
+
import { safeJsonParse } from "../../io/safe-json-parse.js";
|
|
24
|
+
import { DELEGATION_MARKER_DIRNAME, parseDelegationApprovedBy, } from "../../policy-packs/builtin/understanding-before-execution/index.js";
|
|
25
|
+
function emptySection() {
|
|
26
|
+
return { delegationsDirPresent: false, total: 0, expired: 0, unreadable: 0 };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A delegation filename is always a session id: a UUID handed out by
|
|
30
|
+
* Claude Code or Codex, or (in this repo's own test fixtures) a short
|
|
31
|
+
* alnum/hyphen placeholder. Neither ever contains a literal `.` or
|
|
32
|
+
* starts with one. Filtering on that BEFORE the read is what keeps
|
|
33
|
+
* filesystem debris that happens to sit next to `.delegations/` files
|
|
34
|
+
* (macOS's `.DS_Store`, a stray `foo.txt`) from counting toward `total`
|
|
35
|
+
* at all: without it, such a file reads as valid-but-not-JSON and rolls
|
|
36
|
+
* into `unreadable`, which flips this metric's doctor line from
|
|
37
|
+
* informational (ℹ) straight to a WARNING (⚠) for debris that was never
|
|
38
|
+
* a delegation to begin with. Mirrors the same non-empty/no-traversal
|
|
39
|
+
* shape family `rejectMalformedSessionId` pins for a delegation's own
|
|
40
|
+
* filename on the write side (`delegationMarkerPathFor`), tightened to
|
|
41
|
+
* additionally exclude a dot, which is the part that actually
|
|
42
|
+
* discriminates a stray dotfile from a real session id.
|
|
43
|
+
*/
|
|
44
|
+
const SESSION_ID_BASENAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
45
|
+
/**
|
|
46
|
+
* Build the delegations-on-disk doctor metric. Pure filesystem read;
|
|
47
|
+
* never throws for "nothing to see" states (missing `.delegations/`,
|
|
48
|
+
* empty directory, every file unreadable), those all resolve to a
|
|
49
|
+
* zero-ish section, mirroring `buildUgAutoApprovals`. `total`/`expired`/
|
|
50
|
+
* `unreadable` read `0` for both an EMPTY and a MISSING directory; only
|
|
51
|
+
* `delegationsDirPresent` tells them apart, the same way
|
|
52
|
+
* `approvalsDirPresent` does for `buildUgAutoApprovals`, doctor's
|
|
53
|
+
* render layer uses it to stay silent when there is truly nothing on
|
|
54
|
+
* disk (`format.ts`'s own "no line for a check that found nothing"
|
|
55
|
+
* convention) while still rendering the zero-count line for a
|
|
56
|
+
* `harness delegate`-touched, now-empty directory.
|
|
57
|
+
*/
|
|
58
|
+
export function buildUgDelegations(generatedDir, opts = {}) {
|
|
59
|
+
const now = opts.now ?? new Date();
|
|
60
|
+
const dir = path.join(generatedDir, DELEGATION_MARKER_DIRNAME);
|
|
61
|
+
let dirents;
|
|
62
|
+
try {
|
|
63
|
+
dirents = fs.readdirSync(dir, { withFileTypes: true });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return emptySection();
|
|
67
|
+
}
|
|
68
|
+
let total = 0;
|
|
69
|
+
let expired = 0;
|
|
70
|
+
let unreadable = 0;
|
|
71
|
+
for (const d of dirents) {
|
|
72
|
+
// Not a session-id-shaped basename: never a delegation file (a
|
|
73
|
+
// stray dotfile, an extensioned file, a directory, ...), skip
|
|
74
|
+
// before even reading it. The once-per-session adoption ledger is
|
|
75
|
+
// never nested under here: `.delegation-adoptions/` is a SIBLING of
|
|
76
|
+
// `.delegations/` (see delegation-markers.ts's module header), so
|
|
77
|
+
// this filter has no ledger-shaped entry to reason about; an
|
|
78
|
+
// ordinary session-id-named directory would still fall through to
|
|
79
|
+
// the `not-regular` skip below, same as any other directory would.
|
|
80
|
+
if (!SESSION_ID_BASENAME_RE.test(d.name))
|
|
81
|
+
continue;
|
|
82
|
+
const full = path.join(dir, d.name);
|
|
83
|
+
const read = readRegularFileRejectingSymlink(full);
|
|
84
|
+
// A symlink / non-regular / raced-away entry is not a delegation
|
|
85
|
+
// file this metric owns an opinion about; skip silently, same
|
|
86
|
+
// reasoning as `buildUgAutoApprovals`.
|
|
87
|
+
if (read.kind === "symlink" || read.kind === "not-regular" || read.kind === "missing") {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
total++;
|
|
91
|
+
if (read.kind === "unreadable") {
|
|
92
|
+
unreadable++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const parsed = safeJsonParse(read.content);
|
|
96
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
97
|
+
unreadable++;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const obj = parsed;
|
|
101
|
+
const segments = parseDelegationApprovedBy(obj["approvedBy"]);
|
|
102
|
+
if (!segments.ok) {
|
|
103
|
+
unreadable++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (Date.parse(segments.value.expiresAt) <= now.getTime()) {
|
|
107
|
+
expired++;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { delegationsDirPresent: true, total, expired, unreadable };
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=ug-delegations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ug-delegations.js","sourceRoot":"","sources":["../../../src/cli/doctor/ug-delegations.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Mode } from "../../policy-packs/builtin/understanding-before-execution.js";
|
|
2
2
|
import type { Manifest } from "../../schema/index.js";
|
|
3
|
+
import type { PolicyPack } from "../../schema/policy-packs.js";
|
|
3
4
|
export interface UnderstandingModeEnvDivergence {
|
|
4
5
|
/** `UNDERSTANDING_GATE_MODE`, normalised (trim + lowercase, mirrors `resolveMode`'s own normalisation). */
|
|
5
6
|
envMode: Mode;
|
|
@@ -45,5 +46,6 @@ export interface UnderstandingModeEnvDivergence {
|
|
|
45
46
|
* `p.enabled !== false` gate `checkUnderstandingModeEnvDivergence` uses
|
|
46
47
|
* inline below.
|
|
47
48
|
*/
|
|
49
|
+
export declare function findEnabledUnderstandingPack(manifest: Manifest): PolicyPack | undefined;
|
|
48
50
|
export declare function isUnderstandingPackEnabled(manifest: Manifest): boolean;
|
|
49
51
|
export declare function checkUnderstandingModeEnvDivergence(manifest: Manifest, env: NodeJS.ProcessEnv): UnderstandingModeEnvDivergence | undefined;
|
|
@@ -57,11 +57,14 @@ import { isMode, MODE_ENV, PACK_NAME as UNDERSTANDING_PACK_NAME, resolveModeFrom
|
|
|
57
57
|
* `p.enabled !== false` gate `checkUnderstandingModeEnvDivergence` uses
|
|
58
58
|
* inline below.
|
|
59
59
|
*/
|
|
60
|
+
export function findEnabledUnderstandingPack(manifest) {
|
|
61
|
+
return manifest.policy_packs.find((p) => p.name === UNDERSTANDING_PACK_NAME && p.enabled !== false);
|
|
62
|
+
}
|
|
60
63
|
export function isUnderstandingPackEnabled(manifest) {
|
|
61
|
-
return manifest
|
|
64
|
+
return findEnabledUnderstandingPack(manifest) !== undefined;
|
|
62
65
|
}
|
|
63
66
|
export function checkUnderstandingModeEnvDivergence(manifest, env) {
|
|
64
|
-
const pack = manifest
|
|
67
|
+
const pack = findEnabledUnderstandingPack(manifest);
|
|
65
68
|
if (!pack)
|
|
66
69
|
return undefined;
|
|
67
70
|
const envRaw = env[MODE_ENV];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"understanding-mode-env.js","sourceRoot":"","sources":["../../../src/cli/doctor/understanding-mode-env.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,wEAAwE;AACxE,mEAAmE;AACnE,EAAE;AACF,iEAAiE;AACjE,0EAA0E;AAC1E,kEAAkE;AAClE,wEAAwE;AACxE,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,sEAAsE;AACtE,yEAAyE;AACzE,wEAAwE;AACxE,yEAAyE;AACzE,uDAAuD;AACvD,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,wEAAwE;AACxE,sEAAsE;AACtE,4DAA4D;AAC5D,wEAAwE;AACxE,yEAAyE;AACzE,wEAAwE;AACxE,qEAAqE;AACrE,yEAAyE;AACzE,+DAA+D;AAC/D,EAAE;AACF,+DAA+D;AAC/D,0EAA0E;AAC1E,kEAAkE;AAClE,2BAA2B;AAE3B,OAAO,EACL,MAAM,EACN,QAAQ,EACR,SAAS,IAAI,uBAAuB,EACpC,qBAAqB,GAEtB,MAAM,8DAA8D,CAAC;
|
|
1
|
+
{"version":3,"file":"understanding-mode-env.js","sourceRoot":"","sources":["../../../src/cli/doctor/understanding-mode-env.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,wEAAwE;AACxE,mEAAmE;AACnE,EAAE;AACF,iEAAiE;AACjE,0EAA0E;AAC1E,kEAAkE;AAClE,wEAAwE;AACxE,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,sEAAsE;AACtE,yEAAyE;AACzE,wEAAwE;AACxE,yEAAyE;AACzE,uDAAuD;AACvD,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,wEAAwE;AACxE,sEAAsE;AACtE,4DAA4D;AAC5D,wEAAwE;AACxE,yEAAyE;AACzE,wEAAwE;AACxE,qEAAqE;AACrE,yEAAyE;AACzE,+DAA+D;AAC/D,EAAE;AACF,+DAA+D;AAC/D,0EAA0E;AAC1E,kEAAkE;AAClE,2BAA2B;AAE3B,OAAO,EACL,MAAM,EACN,QAAQ,EACR,SAAS,IAAI,uBAAuB,EACpC,qBAAqB,GAEtB,MAAM,8DAA8D,CAAC;AAyBtE;;;;;;;;;;;;;;;GAeG;AACH;;;;;;;;GAQG;AACH,MAAM,UAAU,4BAA4B,CAAC,QAAkB;IAC7D,OAAO,QAAQ,CAAC,YAAY,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,uBAAuB,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,CACjE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,QAAkB;IAC3D,OAAO,4BAA4B,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,mCAAmC,CACjD,QAAkB,EAClB,GAAsB;IAEtB,MAAM,IAAI,GAAG,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IACpD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAE5B,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC5D,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAClD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;QAAE,OAAO,SAAS,CAAC;IAE7C,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACjF,IAAI,aAAa,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IAEnD,yEAAyE;IACzE,yEAAyE;IACzE,yEAAyE;IACzE,uEAAuE;IACvE,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,sBAAsB,CAAC;IAEhE,OAAO;QACL,OAAO,EAAE,aAAa;QACtB,UAAU;QACV,OAAO,EAAE,GAAG,QAAQ,IAAI,aAAa,8BAA8B,UAAU,EAAE;QAC/E,MAAM,EAAE;YACN,uFAAuF;gBACrF,iFAAiF,aAAa,GAAG;YACnG,yBAAyB,UAAU,GAAG,WAAW,EAAE;YACnD,uDAAuD,QAAQ,wBAAwB;SACxF;KACF,CAAC;AACJ,CAAC"}
|
package/dist/cli/gc/index.d.ts
CHANGED
|
@@ -12,13 +12,19 @@ export interface GcOptions extends LoaderOptions {
|
|
|
12
12
|
/** Test seam: clock anchor. */
|
|
13
13
|
now?: Date;
|
|
14
14
|
}
|
|
15
|
-
export type GcCategory = "report" | "parse-error" | "approval-marker";
|
|
15
|
+
export type GcCategory = "report" | "parse-error" | "approval-marker" | "delegation" | "permission-mode-observation";
|
|
16
16
|
export interface GcCandidate {
|
|
17
17
|
filePath: string;
|
|
18
18
|
category: GcCategory;
|
|
19
19
|
/** Why this file aged out (status + age), for the listing UI. */
|
|
20
20
|
reason: string;
|
|
21
21
|
}
|
|
22
|
+
/** A delegation-sweep file gc could not parse: reported, never a deletion candidate. */
|
|
23
|
+
export interface GcUnparseable {
|
|
24
|
+
filePath: string;
|
|
25
|
+
category: GcCategory;
|
|
26
|
+
reason: string;
|
|
27
|
+
}
|
|
22
28
|
export interface GcResult {
|
|
23
29
|
retentionDays: number;
|
|
24
30
|
cutoffIso: string;
|
|
@@ -32,7 +38,12 @@ export interface GcResult {
|
|
|
32
38
|
*/
|
|
33
39
|
parseErrorsDir: string | null;
|
|
34
40
|
approvalsDir: string;
|
|
41
|
+
delegationsDir: string;
|
|
42
|
+
adoptionLedgerDir: string;
|
|
43
|
+
permissionModeObservationsDir: string;
|
|
35
44
|
candidates: GcCandidate[];
|
|
45
|
+
/** Delegation-sweep files inspected but left in place because they could not be parsed. */
|
|
46
|
+
unparseable: GcUnparseable[];
|
|
36
47
|
/** Files actually deleted (apply mode only). */
|
|
37
48
|
removed: string[];
|
|
38
49
|
/** Per-file deletion failures (apply mode only); never silent. */
|
|
@@ -40,7 +51,7 @@ export interface GcResult {
|
|
|
40
51
|
filePath: string;
|
|
41
52
|
reason: string;
|
|
42
53
|
}>;
|
|
43
|
-
/** Count of artifacts inspected but kept (fresh
|
|
54
|
+
/** Count of artifacts inspected but kept (fresh, non-terminal, or unparseable). */
|
|
44
55
|
keptCount: number;
|
|
45
56
|
applied: boolean;
|
|
46
57
|
}
|
package/dist/cli/gc/index.js
CHANGED
|
@@ -14,12 +14,47 @@
|
|
|
14
14
|
// <reportsDir> terminal-status reports (approved / expired)
|
|
15
15
|
// <reportsDir>/../parse-errors parse-error logs
|
|
16
16
|
// <generatedDir>/.approvals session / task / branch-protection markers
|
|
17
|
+
// <generatedDir>/.delegations signed delegation markers (slice 3)
|
|
18
|
+
// <generatedDir>/.delegation-adoptions once-per-session adoption ledgers
|
|
19
|
+
// <generatedDir>/.permission-mode-observations per-session PreToolUse
|
|
20
|
+
// permission_mode observations (task 8f637efd)
|
|
17
21
|
// The evidence ledger (grounding-mcp) and solution-acceptance
|
|
18
22
|
// verdict dirs (producer-owned) are out of scope by design.
|
|
19
23
|
// - Deletion failures are surfaced loudly per file, never swallowed.
|
|
24
|
+
//
|
|
25
|
+
// DELEGATIONS SWEEP (task 3ece079d, follow-up from UG auto-mode slice 3,
|
|
26
|
+
// agent-tasks 37ad0b05): a delegation marker is refused once expired
|
|
27
|
+
// (`verifyDelegation`'s "expired" reason), but nothing ever removed the
|
|
28
|
+
// FILE, so `.delegations/` grows the same way `.approvals/` used to. The
|
|
29
|
+
// adoption ledger at `.delegation-adoptions/<sid>` is worse: it is
|
|
30
|
+
// appended to once per captured transcript entry and never cleared at
|
|
31
|
+
// all, delegation expiry or not. Both sweep under one `"delegation"`
|
|
32
|
+
// category, on the SAME retention/grace window as everything else in this
|
|
33
|
+
// file (`retentionDays` / `cutoffMs`): a delegation marker is a candidate
|
|
34
|
+
// once its own `expires` segment (read via `parseDelegationApprovedBy`,
|
|
35
|
+
// no signature check needed for a retention decision) is older than
|
|
36
|
+
// `cutoffMs`, and its ledger sibling is a candidate once the marker for
|
|
37
|
+
// that session id is gone or itself a candidate. A marker whose content
|
|
38
|
+
// cannot be parsed is never a deletion candidate (fail closed, same as a
|
|
39
|
+
// pending report): it is surfaced separately in `GcResult.unparseable` so
|
|
40
|
+
// an operator can see it, but `--apply` never touches it, and its ledger
|
|
41
|
+
// sibling (if any) stays too, since "this delegation is dead" cannot be
|
|
42
|
+
// established for one gc cannot read.
|
|
43
|
+
//
|
|
44
|
+
// PERMISSION-MODE OBSERVATIONS SWEEP (task 8f637efd review round 2 F5):
|
|
45
|
+
// `.permission-mode-observations/` (one small per-session file, see
|
|
46
|
+
// permission-mode-observations.ts) grows the same way `.approvals/` and
|
|
47
|
+
// `.delegations/` do (nothing else ever removes an entry), and gc had
|
|
48
|
+
// not been taught about it. Swept under its own `"permission-mode-
|
|
49
|
+
// observation"` category by mtime, same `staleFilesByMtime` helper and
|
|
50
|
+
// `retentionDays` window every mtime-aged category here already uses (no
|
|
51
|
+
// signed `expires` to key off, unlike the delegation sweep: this is a
|
|
52
|
+
// plain observation record, not an approval artifact).
|
|
20
53
|
import * as fs from "node:fs";
|
|
21
54
|
import * as path from "node:path";
|
|
22
|
-
import {
|
|
55
|
+
import { readRegularFileRejectingSymlink } from "../../io/read-regular-file.js";
|
|
56
|
+
import { safeJsonParse } from "../../io/safe-json-parse.js";
|
|
57
|
+
import { ADOPTION_LEDGER_DIRNAME, APPROVAL_MARKER_DIRNAME, DELEGATION_MARKER_DIRNAME, PERMISSION_MODE_OBSERVATION_DIRNAME, defaultReportsDir, listPersistedReports, parseDelegationApprovedBy, } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
|
|
23
58
|
import { resolveGeneratedDir } from "../../runtime/pending-approval.js";
|
|
24
59
|
import { resolvePaths } from "../loader.js";
|
|
25
60
|
export const DEFAULT_RETENTION_DAYS = 30;
|
|
@@ -66,6 +101,158 @@ function staleFilesByMtime(dir, cutoffMs, nowMs, category) {
|
|
|
66
101
|
}
|
|
67
102
|
return { candidates, kept };
|
|
68
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* A delegation filename is always a session id (mirrors
|
|
106
|
+
* `doctor/ug-delegations.ts`'s own filter and rationale): filtering on
|
|
107
|
+
* this shape before reading keeps filesystem debris that happens to sit
|
|
108
|
+
* next to `.delegations/` or `.delegation-adoptions/` (macOS's
|
|
109
|
+
* `.DS_Store`, a stray dotfile) from ever becoming an "unparseable"
|
|
110
|
+
* candidate gc reports on. Not shared code with that module (see its own
|
|
111
|
+
* header for why); `check:duplication` is the backstop if that stops
|
|
112
|
+
* being a coincidence.
|
|
113
|
+
*/
|
|
114
|
+
const SESSION_ID_BASENAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
115
|
+
/**
|
|
116
|
+
* Read a `.delegations/<sid>` marker and classify it against `cutoffMs`,
|
|
117
|
+
* WITHOUT verifying its signature: gc is deciding whether to delete a
|
|
118
|
+
* file, not whether to trust it as an approval, and `parseDelegationApprovedBy`
|
|
119
|
+
* is the same reader `harness doctor`'s delegations metric uses read-only.
|
|
120
|
+
* Any failure to read the file, parse it as JSON, or parse its
|
|
121
|
+
* `approvedBy`/`expires` segment is `unparseable`, never `expired`: a
|
|
122
|
+
* marker gc cannot understand is never deleted (mutation probe M2).
|
|
123
|
+
*/
|
|
124
|
+
function readDelegationStatus(filePath, cutoffMs, nowMs) {
|
|
125
|
+
const read = readRegularFileRejectingSymlink(filePath);
|
|
126
|
+
if (read.kind !== "ok") {
|
|
127
|
+
return { kind: "unparseable", reason: `could not read ${filePath} (${read.kind})` };
|
|
128
|
+
}
|
|
129
|
+
const parsed = safeJsonParse(read.content);
|
|
130
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
131
|
+
return { kind: "unparseable", reason: "not a JSON object" };
|
|
132
|
+
}
|
|
133
|
+
const segments = parseDelegationApprovedBy(parsed["approvedBy"]);
|
|
134
|
+
if (!segments.ok) {
|
|
135
|
+
return { kind: "unparseable", reason: segments.reason };
|
|
136
|
+
}
|
|
137
|
+
const expiresMs = Date.parse(segments.value.expiresAt);
|
|
138
|
+
if (!Number.isFinite(expiresMs)) {
|
|
139
|
+
return {
|
|
140
|
+
kind: "unparseable",
|
|
141
|
+
reason: `expires segment is not a valid instant: ${JSON.stringify(segments.value.expiresAt)}`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// The expiry comparison itself: mutation probe M1 skips this and treats
|
|
145
|
+
// every parseable marker as expired, which flips a "valid delegation is
|
|
146
|
+
// kept" test red.
|
|
147
|
+
if (expiresMs < cutoffMs) {
|
|
148
|
+
return {
|
|
149
|
+
kind: "expired",
|
|
150
|
+
reason: `expired ${ageDays(nowMs, expiresMs)}d ago (past the ${ageDays(nowMs, cutoffMs)}d grace)`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return { kind: "valid" };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Sweep `.delegations/` (expired markers) and `.delegation-adoptions/`
|
|
157
|
+
* (ledgers orphaned once their marker is gone or expired). One shared
|
|
158
|
+
* category (`"delegation"`), mirroring the approval-marker sweep's
|
|
159
|
+
* dry-run/candidate/kept shape but keyed off the marker's OWN `expires`
|
|
160
|
+
* segment instead of file mtime, since a delegation's age on disk and its
|
|
161
|
+
* validity window are two different things.
|
|
162
|
+
*/
|
|
163
|
+
function sweepDelegations(generatedDir, cutoffMs, nowMs) {
|
|
164
|
+
const delegationsDir = path.join(generatedDir, DELEGATION_MARKER_DIRNAME);
|
|
165
|
+
const ledgerDir = path.join(generatedDir, ADOPTION_LEDGER_DIRNAME);
|
|
166
|
+
const candidates = [];
|
|
167
|
+
const unparseable = [];
|
|
168
|
+
let kept = 0;
|
|
169
|
+
// Session id -> status, for every delegation marker actually present
|
|
170
|
+
// and session-id-shaped, so the ledger pass below can tell "expired"
|
|
171
|
+
// from "absent" from "still valid" without re-reading the file.
|
|
172
|
+
const statusBySessionId = new Map();
|
|
173
|
+
let delegationNames;
|
|
174
|
+
try {
|
|
175
|
+
delegationNames = fs.readdirSync(delegationsDir);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
delegationNames = [];
|
|
179
|
+
}
|
|
180
|
+
for (const name of delegationNames) {
|
|
181
|
+
if (!SESSION_ID_BASENAME_RE.test(name))
|
|
182
|
+
continue;
|
|
183
|
+
const full = path.join(delegationsDir, name);
|
|
184
|
+
const status = readDelegationStatus(full, cutoffMs, nowMs);
|
|
185
|
+
statusBySessionId.set(name, status);
|
|
186
|
+
if (status.kind === "expired") {
|
|
187
|
+
candidates.push({ filePath: full, category: "delegation", reason: status.reason });
|
|
188
|
+
}
|
|
189
|
+
else if (status.kind === "unparseable") {
|
|
190
|
+
unparseable.push({ filePath: full, category: "delegation", reason: status.reason });
|
|
191
|
+
kept += 1;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
kept += 1;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
let ledgerNames;
|
|
198
|
+
try {
|
|
199
|
+
ledgerNames = fs.readdirSync(ledgerDir);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
ledgerNames = [];
|
|
203
|
+
}
|
|
204
|
+
for (const name of ledgerNames) {
|
|
205
|
+
if (!SESSION_ID_BASENAME_RE.test(name))
|
|
206
|
+
continue;
|
|
207
|
+
const full = path.join(ledgerDir, name);
|
|
208
|
+
let stat;
|
|
209
|
+
try {
|
|
210
|
+
stat = fs.statSync(full);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (!stat.isFile()) {
|
|
216
|
+
kept += 1;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const status = statusBySessionId.get(name);
|
|
220
|
+
if (status === undefined) {
|
|
221
|
+
// No marker for this session id at all: only a candidate once the
|
|
222
|
+
// LEDGER's own age clears the cutoff. Without this check a ledger
|
|
223
|
+
// written seconds ago (its marker legitimately not written yet, or
|
|
224
|
+
// already cleaned up by something else) would be swept regardless
|
|
225
|
+
// of age, contradicting the "older than the retention window"
|
|
226
|
+
// posture documented on the command and the listing header; it
|
|
227
|
+
// also closes the TOCTOU window between the two readdir calls
|
|
228
|
+
// above, since a ledger written between them is newer than cutoff.
|
|
229
|
+
if (stat.mtimeMs < cutoffMs) {
|
|
230
|
+
candidates.push({
|
|
231
|
+
filePath: full,
|
|
232
|
+
category: "delegation",
|
|
233
|
+
reason: "orphaned adoption ledger (no delegation marker for this session)",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
kept += 1;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else if (status.kind === "expired") {
|
|
241
|
+
candidates.push({
|
|
242
|
+
filePath: full,
|
|
243
|
+
category: "delegation",
|
|
244
|
+
reason: `orphaned adoption ledger (delegation ${status.reason})`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Marker still valid, or unparseable (gc cannot prove it is dead):
|
|
249
|
+
// keep the ledger either way, same fail-closed posture as the
|
|
250
|
+
// marker itself.
|
|
251
|
+
kept += 1;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return { candidates, unparseable, kept };
|
|
255
|
+
}
|
|
69
256
|
export function gc(opts = {}) {
|
|
70
257
|
const retentionDays = opts.retentionDays ?? DEFAULT_RETENTION_DAYS;
|
|
71
258
|
if (!Number.isFinite(retentionDays) || retentionDays < 1) {
|
|
@@ -92,7 +279,11 @@ export function gc(opts = {}) {
|
|
|
92
279
|
manifestPath: manifestBase(),
|
|
93
280
|
});
|
|
94
281
|
const approvalsDir = path.join(generatedDir, APPROVAL_MARKER_DIRNAME);
|
|
282
|
+
const delegationsDir = path.join(generatedDir, DELEGATION_MARKER_DIRNAME);
|
|
283
|
+
const adoptionLedgerDir = path.join(generatedDir, ADOPTION_LEDGER_DIRNAME);
|
|
284
|
+
const permissionModeObservationsDir = path.join(generatedDir, PERMISSION_MODE_OBSERVATION_DIRNAME);
|
|
95
285
|
const candidates = [];
|
|
286
|
+
const unparseable = [];
|
|
96
287
|
let keptCount = 0;
|
|
97
288
|
// Reports: only terminal statuses age out. A pending report is never
|
|
98
289
|
// deleted regardless of age; since the C1 fix, stale pending leftovers
|
|
@@ -119,6 +310,13 @@ export function gc(opts = {}) {
|
|
|
119
310
|
const markers = staleFilesByMtime(approvalsDir, cutoffMs, nowMs, "approval-marker");
|
|
120
311
|
candidates.push(...markers.candidates);
|
|
121
312
|
keptCount += markers.kept;
|
|
313
|
+
const delegations = sweepDelegations(generatedDir, cutoffMs, nowMs);
|
|
314
|
+
candidates.push(...delegations.candidates);
|
|
315
|
+
unparseable.push(...delegations.unparseable);
|
|
316
|
+
keptCount += delegations.kept;
|
|
317
|
+
const permissionModeObservations = staleFilesByMtime(permissionModeObservationsDir, cutoffMs, nowMs, "permission-mode-observation");
|
|
318
|
+
candidates.push(...permissionModeObservations.candidates);
|
|
319
|
+
keptCount += permissionModeObservations.kept;
|
|
122
320
|
const removed = [];
|
|
123
321
|
const failures = [];
|
|
124
322
|
if (opts.apply === true) {
|
|
@@ -138,7 +336,11 @@ export function gc(opts = {}) {
|
|
|
138
336
|
reportsDir,
|
|
139
337
|
parseErrorsDir,
|
|
140
338
|
approvalsDir,
|
|
339
|
+
delegationsDir,
|
|
340
|
+
adoptionLedgerDir,
|
|
341
|
+
permissionModeObservationsDir,
|
|
141
342
|
candidates,
|
|
343
|
+
unparseable,
|
|
142
344
|
removed,
|
|
143
345
|
failures,
|
|
144
346
|
keptCount,
|
package/dist/cli/gc/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/gc/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,iEAAiE;AACjE,sEAAsE;AACtE,oEAAoE;AACpE,kEAAkE;AAClE,sEAAsE;AACtE,gEAAgE;AAChE,0CAA0C;AAC1C,EAAE;AACF,yDAAyD;AACzD,6CAA6C;AAC7C,+DAA+D;AAC/D,6EAA6E;AAC7E,wDAAwD;AACxD,kFAAkF;AAClF,kEAAkE;AAClE,gEAAgE;AAChE,uEAAuE;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/gc/index.ts"],"names":[],"mappings":"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"}
|