vouchington-tooling 0.44.0 → 0.46.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 +10 -52
- package/dist/agent-blackboard/client.d.mts +6 -0
- package/dist/agent-blackboard/feedback-audit-source.d.mts +4 -10
- package/dist/agent-blackboard/feedback-audit-source.mjs +7 -18
- package/dist/agent-blackboard/feedback-compose-audit.mjs +6 -14
- package/dist/agent-blackboard/feedback-compose.d.mts +1 -4
- package/dist/agent-blackboard/feedback-compose.mjs +2 -5
- package/dist/agent-blackboard/feedback-fields.mjs +1 -1
- package/dist/agent-blackboard/feedback-identity.mjs +1 -1
- package/dist/agent-blackboard/feedback-journal-audit.d.mts +6 -8
- package/dist/agent-blackboard/feedback-journal-audit.mjs +9 -10
- package/dist/agent-blackboard/feedback-journal-sandbox.d.mts +2 -2
- package/dist/agent-blackboard/feedback-journal-sandbox.mjs +2 -2
- package/dist/agent-blackboard/feedback-outbox-rejected.mjs +1 -1
- package/dist/agent-blackboard/feedback-outbox.mjs +2 -2
- package/dist/agent-blackboard/index.d.mts +1 -1
- package/dist/audit-journal/ci-failures.d.mts +6 -0
- package/dist/{session-friction → audit-journal}/ci-failures.mjs +3 -9
- package/dist/{session-friction → audit-journal}/journal.d.mts +0 -1
- package/dist/{session-friction → audit-journal}/journal.mjs +1 -1
- package/dist/audit-journal/session-id.d.mts +1 -0
- package/dist/{session-friction → audit-journal}/session-id.mjs +0 -5
- package/dist/{session-friction → audit-journal}/text.d.mts +1 -4
- package/dist/{session-friction → audit-journal}/text.mjs +4 -22
- package/dist/audit-journal/types.d.mts +17 -0
- package/dist/cli/commands/gha-runtime-audit.mjs +2 -0
- package/dist/cli/usage.d.mts +1 -1
- package/dist/cli/usage.mjs +1 -1
- package/dist/gh-cli/env.d.mts +9 -0
- package/dist/gh-cli/env.mjs +12 -0
- package/dist/gh-cli/exec.d.mts +7 -4
- package/dist/gh-cli/exec.mjs +7 -5
- package/dist/gh-cli/index.d.mts +1 -0
- package/dist/gh-cli/index.mjs +1 -0
- package/dist/gha-post-review/github.mjs +2 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/mcp-server/context.d.mts +5 -0
- package/dist/mcp-server/dispatch.d.mts +5 -0
- package/dist/mcp-server/dispatch.mjs +8 -1
- package/dist/mcp-server/fake-blackboard.test-helpers.d.mts +2 -0
- package/dist/mcp-server/fake-blackboard.test-helpers.mjs +3 -0
- package/dist/mcp-server/handlers-pr.d.mts +1 -1
- package/dist/mcp-server/handlers-pr.mjs +6 -97
- package/dist/mcp-server/handlers-retrospective-save.d.mts +10 -0
- package/dist/mcp-server/handlers-retrospective-save.mjs +138 -0
- package/dist/mcp-server/handlers-retrospective.d.mts +1 -12
- package/dist/mcp-server/handlers-retrospective.mjs +1 -154
- package/dist/mcp-server/handlers-session.mjs +25 -6
- package/dist/mcp-server/handlers-snapshot-cleanup.d.mts +7 -0
- package/dist/mcp-server/handlers-snapshot-cleanup.mjs +36 -0
- package/dist/mcp-server/harness.test-helpers.d.mts +3 -0
- package/dist/mcp-server/harness.test-helpers.mjs +2 -0
- package/dist/mcp-server/policy-run.d.mts +1 -0
- package/dist/mcp-server/policy-run.mjs +1 -0
- package/dist/mcp-server/pr-agent.d.mts +12 -0
- package/dist/mcp-server/pr-agent.mjs +34 -0
- package/dist/mcp-server/pr-body.d.mts +6 -2
- package/dist/mcp-server/pr-body.mjs +16 -5
- package/dist/mcp-server/pr-defaults.d.mts +3 -0
- package/dist/mcp-server/pr-defaults.mjs +7 -2
- package/dist/mcp-server/pr-gh.mjs +2 -1
- package/dist/mcp-server/pr-input.d.mts +14 -6
- package/dist/mcp-server/pr-input.mjs +22 -5
- package/dist/mcp-server/pr-live.d.mts +4 -1
- package/dist/mcp-server/pr-live.mjs +8 -3
- package/dist/mcp-server/pr-mode.d.mts +7 -0
- package/dist/mcp-server/pr-mode.mjs +34 -0
- package/dist/mcp-server/pr-no-source.d.mts +9 -0
- package/dist/mcp-server/pr-no-source.mjs +22 -0
- package/dist/mcp-server/pr-prepare.d.mts +25 -0
- package/dist/mcp-server/pr-prepare.mjs +124 -0
- package/dist/mcp-server/preview-registry.d.mts +25 -0
- package/dist/mcp-server/preview-registry.mjs +38 -0
- package/dist/mcp-server/retrospective-check.d.mts +3 -0
- package/dist/mcp-server/retrospective-check.mjs +38 -0
- package/dist/mcp-server/retrospective-input.mjs +2 -2
- package/dist/mcp-server/retrospective-preview.d.mts +17 -0
- package/dist/mcp-server/retrospective-preview.mjs +60 -0
- package/dist/mcp-server/server.mjs +1 -0
- package/dist/mcp-server/snapshot-registry.d.mts +25 -0
- package/dist/mcp-server/snapshot-registry.mjs +32 -0
- package/dist/mcp-server/tools-pr.mjs +28 -16
- package/dist/mcp-server/tools-retrospective.mjs +25 -5
- package/dist/mcp-server/tools.mjs +6 -0
- package/dist/{session-friction → private-files}/lock.mjs +3 -2
- package/dist/private-files/log-file.d.mts +3 -0
- package/dist/private-files/log-file.mjs +53 -0
- package/dist/{session-friction → private-files}/utf8.mjs +1 -1
- package/dist/retrospective-facts/exec.mjs +2 -1
- package/docs/mcp-server.md +112 -26
- package/package.json +3 -8
- package/skills/retrospective/SKILL.md +3 -1
- package/skills/retrospective-distill/SKILL.md +5 -0
- package/dist/session-friction/ci-failures.d.mts +0 -13
- package/dist/session-friction/classify.d.mts +0 -2
- package/dist/session-friction/classify.mjs +0 -98
- package/dist/session-friction/command-segments.d.mts +0 -3
- package/dist/session-friction/command-segments.mjs +0 -94
- package/dist/session-friction/coverage-ledger.d.mts +0 -10
- package/dist/session-friction/coverage-ledger.mjs +0 -75
- package/dist/session-friction/directory.d.mts +0 -1
- package/dist/session-friction/directory.mjs +0 -71
- package/dist/session-friction/index.d.mts +0 -6
- package/dist/session-friction/index.mjs +0 -5
- package/dist/session-friction/log-file.d.mts +0 -7
- package/dist/session-friction/log-file.mjs +0 -95
- package/dist/session-friction/log.d.mts +0 -8
- package/dist/session-friction/log.mjs +0 -163
- package/dist/session-friction/normalize.d.mts +0 -1
- package/dist/session-friction/normalize.mjs +0 -81
- package/dist/session-friction/report.d.mts +0 -16
- package/dist/session-friction/report.mjs +0 -91
- package/dist/session-friction/sandbox.d.mts +0 -3
- package/dist/session-friction/sandbox.mjs +0 -43
- package/dist/session-friction/session-id.d.mts +0 -2
- package/dist/session-friction/types.d.mts +0 -80
- /package/dist/{session-friction → audit-journal}/audit-block.d.mts +0 -0
- /package/dist/{session-friction → audit-journal}/audit-block.mjs +0 -0
- /package/dist/{session-friction → audit-journal}/audit-fit.d.mts +0 -0
- /package/dist/{session-friction → audit-journal}/audit-fit.mjs +0 -0
- /package/dist/{session-friction → audit-journal}/types.mjs +0 -0
- /package/dist/{session-friction → private-files}/lock.d.mts +0 -0
- /package/dist/{session-friction → private-files}/utf8.d.mts +0 -0
package/README.md
CHANGED
|
@@ -315,7 +315,6 @@ import {
|
|
|
315
315
|
} from 'vouchington-tooling/vitest-diagnostics'
|
|
316
316
|
import { runRetrospectiveTranscript } from 'vouchington-tooling/retrospective-transcript'
|
|
317
317
|
import { appendJournal, probeBlackboard } from 'vouchington-tooling/agent-blackboard'
|
|
318
|
-
import { buildSessionFrictionReport, recordFriction } from 'vouchington-tooling/session-friction'
|
|
319
318
|
import { GITHUB_BODY_MAX_CHARACTERS, validateGitHubBodyLength } from 'vouchington-tooling'
|
|
320
319
|
import { createPullRequest, processDiffCommand, runGh, runGit } from 'vouchington-tooling/gh-cli'
|
|
321
320
|
import {
|
|
@@ -475,41 +474,6 @@ audit finishes, when the process receives SIGINT, SIGTERM, or SIGHUP, and on the
|
|
|
475
474
|
previous process died first, including SIGKILL. Each workspace records its owner's PID so a later
|
|
476
475
|
audit can delete directories whose owner is gone.
|
|
477
476
|
|
|
478
|
-
`session-friction` is an opt-in capture and reporting library. Callers supply the session id,
|
|
479
|
-
absolute log directory, host-independent observation, and journal loader; it does not inspect host
|
|
480
|
-
environment variables, install hooks, or connect to a journal service by itself. Invoking
|
|
481
|
-
`recordFriction` touches the session log even when no event is classified, preserving the
|
|
482
|
-
difference between an observed clean session and missing evidence. Report markdown keeps backend
|
|
483
|
-
diagnostics separate from its paste-safe output. Capture stores at most 500 events per session and counts rejected captures in a fixed-size atomic
|
|
484
|
-
sidecar under the same session lock for either the event or byte limit, with visible `truncated`/`droppedCount` metadata. Saturated
|
|
485
|
-
capture reads only the bounded counter; reports also preserve historical overflow markers.
|
|
486
|
-
Interrupted or inconsistent counter updates make coverage unavailable. Malformed records also make the scan partial. It
|
|
487
|
-
truncates event detail to 1,000 characters, and consumes up to 500 entries from the journal loader
|
|
488
|
-
when building a report, stopping earlier when its aggregate 1 MB inspected-byte budget is reached.
|
|
489
|
-
Bounded journal scans that stop before exhaustion are reported as incomplete rather than clean.
|
|
490
|
-
Report liveness inherits the caller-supplied journal loader, which must bound its own I/O and yields.
|
|
491
|
-
Log reads are capped at 2 MB, journal Markdown at 10,000 bytes per entry,
|
|
492
|
-
and rendered audit fields are kept whole: composition loads the journal and friction log once, redacts each field from its raw text before Markdown escaping (a caller-supplied redactor runs first, then the built-in one), then renders the CI and sandbox sections, measures the real envelope against its 12,000-byte markdown and 16,384-byte envelope limits, and shrinks them in a few bounded attempts. A block shrinks Root diagnostic first and Evidence last, cutting only at token boundaries (never inside a URL or SHA) with a trailing `…`; groups that still cannot fit are omitted and counted in an explicit conforming group that points at the journal. The final attempt keeps only that omission note (about 300 bytes), so an input within roughly 300 bytes of the limit can still fail to compose. A caller-supplied `budgets` cap limits rendered groups only; the omission note is exempt from it. Permission requests record requested outcomes; explicit result observations may record approved
|
|
493
|
-
or denied outcomes. A decision and simultaneous tool failure are both retained and counted in
|
|
494
|
-
the report. Escalation detail without a decision remains unknown. Localhost connection
|
|
495
|
-
refusal is an ambiguous failure and does not establish a sandbox cause. The supplied log directory must be dedicated
|
|
496
|
-
to session-friction; existing directories must already be owner-only, while newly created
|
|
497
|
-
directories and log files are enforced as owner-only when recording. Reads use a fixed bounded
|
|
498
|
-
buffer that can detect growth one byte beyond the documented 2 MB cap.
|
|
499
|
-
Ownership checks require POSIX effective-user IDs (Linux and macOS); session-friction throws on
|
|
500
|
-
Windows and other platforms where those IDs are unavailable.
|
|
501
|
-
Root-owned system symlink ancestors are supported for paths such as macOS `/var`; callers must not
|
|
502
|
-
allow the directory chain to be mutated while it is being validated.
|
|
503
|
-
Command-prefix normalization recognizes simple shell
|
|
504
|
-
segments with single or double quotes; it does not evaluate substitutions or implement a full shell
|
|
505
|
-
grammar. Normalization attempts limited redaction of obvious credential patterns but is not a secret
|
|
506
|
-
scrubber; callers must ensure credentials are never included in captured commands.
|
|
507
|
-
Failure classification inspects at most 100,000 structured-stderr characters, split evenly between
|
|
508
|
-
the beginning and end when input exceeds that bound.
|
|
509
|
-
Cooperating log readers and writers are serialized, including the initial clean-session
|
|
510
|
-
touch. Recording and report log reads are synchronous: on contention they block the caller's
|
|
511
|
-
event loop for up to one second before failing explicitly. Avoid these APIs on hot request paths.
|
|
512
|
-
|
|
513
477
|
The artifact, review-payload, HTTP body, and pagination APIs validate untrusted inputs at their
|
|
514
478
|
boundaries. Neutral review posting lives in `gha-post-review` and requires a caller-supplied GitHub
|
|
515
479
|
token. Claude App OIDC is an explicitly selected `gha-claude-post-review` adapter; the legacy
|
|
@@ -669,31 +633,25 @@ replay the identical envelope and source ID to verify its actual persistence.
|
|
|
669
633
|
generates required sections and markers. Tool and architectural assessments preserve observation,
|
|
670
634
|
evidence, disposition and tracking reference. None-observed and unassessed states need a scoped
|
|
671
635
|
reason. Complete coverage is rejected when required sources are unavailable or capture is partial.
|
|
672
|
-
Typed collector reports expose availability independently of rendered text.
|
|
673
|
-
aggregate dropped counts below observed friction drops and retains larger caller aggregates without
|
|
674
|
-
double counting. Generated frontmatter carries validated `work_outcome` and `feedback_coverage`
|
|
636
|
+
Typed collector reports expose availability independently of rendered text. Generated frontmatter carries validated `work_outcome` and `feedback_coverage`
|
|
675
637
|
for consumers to preserve or reject conflicting explicit overrides; trusted mode stays external.
|
|
676
638
|
Human top-five summaries do not authorize dropping additional durable findings.
|
|
677
639
|
|
|
678
640
|
The `## CI Failures` and `## Sandbox & Permission Audit` sections come from one optional audit
|
|
679
|
-
source
|
|
680
|
-
|
|
681
|
-
journal
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
no log was observed. `feedbackCoverage.status: 'complete'` accepts either source. It is rejected
|
|
685
|
-
when neither is supplied (the error names the missing source), and a journal-only audit does not
|
|
686
|
-
count as assessed when the loader reports `not-found` (there is no log to establish that the session
|
|
687
|
-
was observed), throws, or is truncated by the scan bounds.
|
|
641
|
+
source, `journal: {journalLoader}`: both sections are assessed from the session's journal entries
|
|
642
|
+
alone, with the text stating `journal entries only`. `feedbackCoverage.status: 'complete'` is
|
|
643
|
+
rejected when no journal source is supplied (the error names the missing source), and a journal
|
|
644
|
+
audit does not count as assessed when the loader reports `not-found` (nothing else establishes that
|
|
645
|
+
the session was observed), throws, or is truncated by the scan bounds.
|
|
688
646
|
|
|
689
647
|
```ts
|
|
690
648
|
await composeRetrospective({ ...input, journal: { journalLoader } })
|
|
691
649
|
```
|
|
692
650
|
|
|
693
|
-
`journalLoader` is
|
|
651
|
+
`journalLoader` is a `JournalLoader`; `JournalLoader`, `JournalLoadResult`,
|
|
694
652
|
`JournalEntry`, and `JournalAuditOptions` are exported as types from
|
|
695
|
-
`vouchington-tooling/agent-blackboard`. The `## CI Failures` block grammar
|
|
696
|
-
lines (`failures observed`, `none observed`, `unavailable (<reason>)`)
|
|
653
|
+
`vouchington-tooling/agent-blackboard`. The `## CI Failures` block grammar has three status
|
|
654
|
+
lines (`failures observed`, `none observed`, `unavailable (<reason>)`).
|
|
697
655
|
Journal-only sandbox and permission entries are journal Markdown entries that hold exactly one
|
|
698
656
|
block, escaped and length-bounded like CI failures, and other entries are ignored:
|
|
699
657
|
|
|
@@ -709,7 +667,7 @@ block, escaped and length-bounded like CI failures, and other entries are ignore
|
|
|
709
667
|
|
|
710
668
|
`ambiguous-failure` takes the same shape as `sandbox-failure`. The section reports
|
|
711
669
|
`Status: none observed` or `Status: events observed`, each followed by
|
|
712
|
-
`(journal entries only
|
|
670
|
+
`(journal entries only)`; a truncated scan reports
|
|
713
671
|
`unavailable (journal scan incomplete)` with the entries read so far.
|
|
714
672
|
|
|
715
673
|
## Basic Auth exemption facts
|
|
@@ -32,6 +32,12 @@ export type BlackboardClientModule = {
|
|
|
32
32
|
Snapshots?: new (connection: BlackboardConnection) => {
|
|
33
33
|
export(options?: unknown): Promise<unknown>;
|
|
34
34
|
};
|
|
35
|
+
/** Newer clients own the ownership markers of their exports and clean them with a capability. */
|
|
36
|
+
cleanupSnapshotPartitions?: (options: {
|
|
37
|
+
path?: string;
|
|
38
|
+
directory?: string;
|
|
39
|
+
cleanupToken?: string;
|
|
40
|
+
}) => Promise<void>;
|
|
35
41
|
};
|
|
36
42
|
type BlackboardClientLoader = () => Promise<BlackboardClientModule>;
|
|
37
43
|
export type BlackboardClientDependencies = {
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { JournalScan } from '../session-friction/journal.mts';
|
|
1
|
+
import type { JournalScan } from '../audit-journal/journal.mts';
|
|
3
2
|
import { type JournalAuditOptions, type JournalAuditReport } from './feedback-journal-audit.mts';
|
|
4
3
|
export type AuditSourceInput = {
|
|
5
|
-
friction?: SessionFrictionReportOptions;
|
|
6
4
|
journal?: JournalAuditOptions;
|
|
7
5
|
};
|
|
8
|
-
export type AuditReport =
|
|
9
|
-
export declare function assertSingleAuditSource(input: AuditSourceInput): void;
|
|
6
|
+
export type AuditReport = JournalAuditReport;
|
|
10
7
|
/** What the audit sections read from outside, loaded once and rendered as often as needed. */
|
|
11
8
|
export type AuditInputs = {
|
|
12
|
-
kind: 'friction';
|
|
13
|
-
loaded: FrictionReportInputs;
|
|
14
|
-
} | {
|
|
15
9
|
kind: 'journal';
|
|
16
10
|
scanned: JournalScan;
|
|
17
11
|
} | {
|
|
@@ -19,7 +13,7 @@ export type AuditInputs = {
|
|
|
19
13
|
};
|
|
20
14
|
export declare function loadAuditInputs(sessionId: string, input: AuditSourceInput): Promise<AuditInputs>;
|
|
21
15
|
/** Builds the CI-failure and sandbox sections from loaded inputs, with no further I/O. */
|
|
22
|
-
export declare function renderAuditReport(
|
|
23
|
-
/** Only a
|
|
16
|
+
export declare function renderAuditReport(input: AuditSourceInput, inputs: AuditInputs): AuditReport;
|
|
17
|
+
/** Only a complete, untruncated journal scan counts as assessed. */
|
|
24
18
|
export declare function auditAssessed({ coverage }: AuditReport): boolean;
|
|
25
19
|
export declare function completeCoverageError(input: AuditSourceInput): Error;
|
|
@@ -1,37 +1,26 @@
|
|
|
1
|
-
import { loadFrictionReportInputs, renderFrictionReport, } from "../session-friction/index.mjs";
|
|
2
1
|
import { loadJournalAuditInput, renderJournalAuditReport, } from "./feedback-journal-audit.mjs";
|
|
3
2
|
const ASSESSED_SOURCE_ERROR = 'complete feedback coverage requires assessed available factual and finding sources';
|
|
4
3
|
const NOT_ASSESSED_MARKDOWN = '## CI Failures\nStatus: unavailable (not assessed)\n\n## Sandbox & Permission Audit\nStatus: unavailable (not assessed)';
|
|
5
|
-
export function assertSingleAuditSource(input) {
|
|
6
|
-
if (input.friction && input.journal)
|
|
7
|
-
throw new Error('composition accepts either a friction or a journal audit source, not both');
|
|
8
|
-
}
|
|
9
4
|
export async function loadAuditInputs(sessionId, input) {
|
|
10
|
-
if (input.friction)
|
|
11
|
-
return { kind: 'friction', loaded: await loadFrictionReportInputs(sessionId, input.friction) };
|
|
12
5
|
if (input.journal)
|
|
13
6
|
return { kind: 'journal', scanned: await loadJournalAuditInput(sessionId, input.journal) };
|
|
14
7
|
return { kind: 'none' };
|
|
15
8
|
}
|
|
16
9
|
/** Builds the CI-failure and sandbox sections from loaded inputs, with no further I/O. */
|
|
17
|
-
export function renderAuditReport(
|
|
18
|
-
if (inputs.kind === 'friction')
|
|
19
|
-
return renderFrictionReport(sessionId, input.friction, inputs.loaded);
|
|
10
|
+
export function renderAuditReport(input, inputs) {
|
|
20
11
|
if (inputs.kind === 'journal')
|
|
21
|
-
return renderJournalAuditReport(
|
|
12
|
+
return renderJournalAuditReport(input.journal, inputs.scanned);
|
|
22
13
|
return {
|
|
23
|
-
coverage: { journalStatus: 'unavailable',
|
|
14
|
+
coverage: { journalStatus: 'unavailable', truncated: false },
|
|
24
15
|
markdown: NOT_ASSESSED_MARKDOWN,
|
|
25
16
|
};
|
|
26
17
|
}
|
|
27
|
-
/** Only a
|
|
18
|
+
/** Only a complete, untruncated journal scan counts as assessed. */
|
|
28
19
|
export function auditAssessed({ coverage }) {
|
|
29
|
-
return
|
|
30
|
-
['empty', 'events', 'journal-only'].includes(coverage.frictionStatus) &&
|
|
31
|
-
!coverage.truncated);
|
|
20
|
+
return coverage.journalStatus === 'complete' && !coverage.truncated;
|
|
32
21
|
}
|
|
33
22
|
export function completeCoverageError(input) {
|
|
34
|
-
return new Error(input.
|
|
23
|
+
return new Error(input.journal
|
|
35
24
|
? ASSESSED_SOURCE_ERROR
|
|
36
|
-
: `${ASSESSED_SOURCE_ERROR} (no
|
|
25
|
+
: `${ASSESSED_SOURCE_ERROR} (no journal source supplied for CI failures and sandbox audit)`);
|
|
37
26
|
}
|
|
@@ -25,21 +25,13 @@ export function boundedAuditSource(input, caps) {
|
|
|
25
25
|
ciBytes: Math.min(caller?.ciBytes ?? Infinity, caps.ciBytes),
|
|
26
26
|
sandboxBytes: Math.min(caller?.sandboxBytes ?? Infinity, caps.sandboxBytes),
|
|
27
27
|
});
|
|
28
|
-
return input.
|
|
28
|
+
return input.journal
|
|
29
29
|
? {
|
|
30
|
-
|
|
31
|
-
...input.
|
|
32
|
-
redact: redactionFor(input.
|
|
33
|
-
budgets: clamp(input.
|
|
30
|
+
journal: {
|
|
31
|
+
...input.journal,
|
|
32
|
+
redact: redactionFor(input.journal.redact),
|
|
33
|
+
budgets: clamp(input.journal.budgets),
|
|
34
34
|
},
|
|
35
35
|
}
|
|
36
|
-
:
|
|
37
|
-
? {
|
|
38
|
-
journal: {
|
|
39
|
-
...input.journal,
|
|
40
|
-
redact: redactionFor(input.journal.redact),
|
|
41
|
-
budgets: clamp(input.journal.budgets),
|
|
42
|
-
},
|
|
43
|
-
}
|
|
44
|
-
: {};
|
|
36
|
+
: {};
|
|
45
37
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type RetrospectiveFactsOptions } from '../retrospective-facts/index.mts';
|
|
2
2
|
import { type ResolveOptions } from '../retrospective-transcript/index.mts';
|
|
3
|
-
import type { SessionFrictionReportOptions } from '../session-friction/index.mts';
|
|
4
3
|
import type { JournalAuditOptions } from './feedback-journal-audit.mts';
|
|
5
4
|
import type { FeedbackCoverage, FeedbackReference, WorkOutcome } from './feedback-types.mts';
|
|
6
5
|
export type FeedbackAssessment = {
|
|
@@ -29,9 +28,7 @@ export type RetrospectiveCompositionInput = {
|
|
|
29
28
|
narrative: string;
|
|
30
29
|
facts: Omit<RetrospectiveFactsOptions, 'raw'> | Unassessed;
|
|
31
30
|
transcript: ResolveOptions | Unassessed;
|
|
32
|
-
/** CI failures and sandbox audit from
|
|
33
|
-
friction?: SessionFrictionReportOptions;
|
|
34
|
-
/** CI failures and sandbox audit from journal entries alone (`frictionStatus: 'journal-only'`). */
|
|
31
|
+
/** CI failures and sandbox audit from the session journal's entries. */
|
|
35
32
|
journal?: JournalAuditOptions;
|
|
36
33
|
tools: FeedbackAssessment;
|
|
37
34
|
architecture: FeedbackAssessment;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { runRetrospectiveFactsReport, } from "../retrospective-facts/index.mjs";
|
|
2
2
|
import { runRetrospectiveTranscriptReport, } from "../retrospective-transcript/index.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { auditAssessed, loadAuditInputs, renderAuditReport, completeCoverageError, } from "./feedback-audit-source.mjs";
|
|
4
4
|
import { AUDIT_ATTEMPTS, boundedAuditSource, shrinkCaps, UNBOUNDED_CAPS, } from "./feedback-compose-audit.mjs";
|
|
5
5
|
import { createFeedbackEnvelope, feedbackOverflowBytes, redactFeedbackText, } from "./feedback-codec.mjs";
|
|
6
6
|
import { validateFeedbackText } from "./feedback-fields.mjs";
|
|
@@ -33,7 +33,6 @@ function unavailable(marker, input) {
|
|
|
33
33
|
return `${marker}\nStatus: ${input.status.replaceAll('-', ' ')} (${input.reason})`;
|
|
34
34
|
}
|
|
35
35
|
export async function composeRetrospective(input) {
|
|
36
|
-
assertSingleAuditSource(input);
|
|
37
36
|
const unavailableFacts = 'status' in input.facts
|
|
38
37
|
? {
|
|
39
38
|
markdown: unavailable('=== Retrospective Facts ===', input.facts),
|
|
@@ -93,7 +92,7 @@ export async function composeRetrospective(input) {
|
|
|
93
92
|
let audit;
|
|
94
93
|
let markdown;
|
|
95
94
|
for (let attempt = 1;; attempt++) {
|
|
96
|
-
audit = renderAuditReport(
|
|
95
|
+
audit = renderAuditReport(boundedAuditSource(input, caps), loaded);
|
|
97
96
|
markdown = assemble(audit.markdown);
|
|
98
97
|
const overflow = feedbackOverflowBytes(envelopeFor(markdown));
|
|
99
98
|
if (!overflow || attempt === AUDIT_ATTEMPTS)
|
|
@@ -107,7 +106,5 @@ export async function composeRetrospective(input) {
|
|
|
107
106
|
['not-assessed', 'unavailable'].includes(input.architecture.status) ||
|
|
108
107
|
!auditAssessed(audit)))
|
|
109
108
|
throw completeCoverageError(input);
|
|
110
|
-
if ((audit.coverage.droppedCount ?? 0) > input.feedbackCoverage.droppedCount)
|
|
111
|
-
throw new Error('feedback coverage dropped count must include observed friction drops');
|
|
112
109
|
return createFeedbackEnvelope(envelopeFor(markdown)).markdown;
|
|
113
110
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isWellFormedUnicode } from "../
|
|
1
|
+
import { isWellFormedUnicode } from "../audit-journal/text.mjs";
|
|
2
2
|
import { assertSessionId } from "./session-id.mjs";
|
|
3
3
|
import { isObject } from "./snapshot-partition-guards.mjs";
|
|
4
4
|
export function validateFeedbackIdentity(identity) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type JournalScan } from '../
|
|
2
|
-
import type {
|
|
1
|
+
import { type JournalScan } from '../audit-journal/journal.mts';
|
|
2
|
+
import type { AuditCoverage, JournalLoader } from '../audit-journal/types.mts';
|
|
3
3
|
export type JournalAuditOptions = {
|
|
4
4
|
journalLoader: JournalLoader;
|
|
5
5
|
/** Redacts raw field text before Markdown escaping, which would otherwise break matching. */
|
|
@@ -13,19 +13,17 @@ export type JournalAuditOptions = {
|
|
|
13
13
|
/** `journal-only` says both sections were assessed from journal entries, with no log observed. */
|
|
14
14
|
export type JournalAuditReport = {
|
|
15
15
|
coverage: {
|
|
16
|
-
journalStatus:
|
|
17
|
-
frictionStatus: 'journal-only';
|
|
16
|
+
journalStatus: AuditCoverage['journalStatus'];
|
|
18
17
|
truncated: false;
|
|
19
|
-
droppedCount?: never;
|
|
20
18
|
};
|
|
21
19
|
markdown: string;
|
|
22
20
|
};
|
|
23
21
|
/**
|
|
24
22
|
* Builds `## CI Failures` and `## Sandbox & Permission Audit` from the session's journal entries
|
|
25
|
-
* alone.
|
|
26
|
-
*
|
|
23
|
+
* alone. A session with no journal is unavailable rather than empty: nothing else establishes
|
|
24
|
+
* that the session was observed.
|
|
27
25
|
*/
|
|
28
26
|
export declare function loadJournalAuditInput(sessionId: string, options: JournalAuditOptions): Promise<JournalScan>;
|
|
29
27
|
/** Pure render of a loaded journal scan, so it can be repeated without reloading. */
|
|
30
|
-
export declare function renderJournalAuditReport(
|
|
28
|
+
export declare function renderJournalAuditReport(options: JournalAuditOptions, scanned: JournalScan): JournalAuditReport;
|
|
31
29
|
export declare function buildJournalAuditReport(sessionId: string, options: JournalAuditOptions): Promise<JournalAuditReport>;
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { buildCiFailuresSection, getConformingGroups, incompleteCiSection, } from "../
|
|
2
|
-
import { scanJournal } from "../
|
|
3
|
-
import { validateSessionId } from "../
|
|
1
|
+
import { buildCiFailuresSection, getConformingGroups, incompleteCiSection, } from "../audit-journal/ci-failures.mjs";
|
|
2
|
+
import { scanJournal } from "../audit-journal/journal.mjs";
|
|
3
|
+
import { validateSessionId } from "../audit-journal/session-id.mjs";
|
|
4
4
|
import { getConformingSandboxBlocks, journalSandboxSection, unavailableSandboxSection, } from "./feedback-journal-sandbox.mjs";
|
|
5
5
|
function report(journalStatus, markdown) {
|
|
6
|
-
return { coverage: { journalStatus,
|
|
6
|
+
return { coverage: { journalStatus, truncated: false }, markdown };
|
|
7
7
|
}
|
|
8
8
|
function unavailable(reason) {
|
|
9
9
|
return report('unavailable', `## CI Failures\nStatus: unavailable (${reason})\n\n${unavailableSandboxSection(reason)}`);
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* Builds `## CI Failures` and `## Sandbox & Permission Audit` from the session's journal entries
|
|
13
|
-
* alone.
|
|
14
|
-
*
|
|
13
|
+
* alone. A session with no journal is unavailable rather than empty: nothing else establishes
|
|
14
|
+
* that the session was observed.
|
|
15
15
|
*/
|
|
16
16
|
export async function loadJournalAuditInput(sessionId, options) {
|
|
17
17
|
validateSessionId(sessionId);
|
|
18
18
|
return scanJournal(sessionId, options.journalLoader);
|
|
19
19
|
}
|
|
20
20
|
/** Pure render of a loaded journal scan, so it can be repeated without reloading. */
|
|
21
|
-
export function renderJournalAuditReport(
|
|
21
|
+
export function renderJournalAuditReport(options, scanned) {
|
|
22
22
|
if (scanned.status === 'unreachable')
|
|
23
23
|
return unavailable('blackboard unreachable');
|
|
24
24
|
if (scanned.status === 'not-found')
|
|
@@ -27,9 +27,8 @@ export function renderJournalAuditReport(sessionId, options, scanned) {
|
|
|
27
27
|
const sandbox = getConformingSandboxBlocks(scanned.entries, options.redact, options.budgets?.sandboxBytes);
|
|
28
28
|
if (scanned.truncated)
|
|
29
29
|
return report('partial', `${incompleteCiSection(ci)}\n\n${unavailableSandboxSection('journal scan incomplete', sandbox)}`);
|
|
30
|
-
|
|
31
|
-
return report('complete', `${buildCiFailuresSection(sessionId, journal, 'journal-only')}\n\n${journalSandboxSection(sandbox)}`);
|
|
30
|
+
return report('complete', `${buildCiFailuresSection(ci)}\n\n${journalSandboxSection(sandbox)}`);
|
|
32
31
|
}
|
|
33
32
|
export async function buildJournalAuditReport(sessionId, options) {
|
|
34
|
-
return renderJournalAuditReport(
|
|
33
|
+
return renderJournalAuditReport(options, await loadJournalAuditInput(sessionId, options));
|
|
35
34
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AuditRedactor } from '../
|
|
2
|
-
import type { JournalEntry } from '../
|
|
1
|
+
import type { AuditRedactor } from '../audit-journal/text.mts';
|
|
2
|
+
import type { JournalEntry } from '../audit-journal/types.mts';
|
|
3
3
|
/** Journal entries that hold exactly one sandbox or permission block, made paste-safe. */
|
|
4
4
|
export declare function getConformingSandboxBlocks(entries: Iterable<JournalEntry>, redact?: AuditRedactor, maxBytes?: number): string[];
|
|
5
5
|
export declare function unavailableSandboxSection(reason: string, blocks?: string[]): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { conformingBlocks, matchAuditBlock } from "../
|
|
1
|
+
import { conformingBlocks, matchAuditBlock } from "../audit-journal/audit-block.mjs";
|
|
2
2
|
const SANDBOX_SECTION_HEADER = '## Sandbox & Permission Audit';
|
|
3
|
-
const SOURCE = 'journal entries only
|
|
3
|
+
const SOURCE = 'journal entries only';
|
|
4
4
|
const EVIDENCE = /^ {2}- Evidence: .*[^\s]$/;
|
|
5
5
|
const DISPOSITION = /^ {2}- Disposition: .*[^\s]$/;
|
|
6
6
|
const ESCALATION = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readdirSync, renameSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { withFileLock } from "../
|
|
3
|
+
import { withFileLock } from "../private-files/lock.mjs";
|
|
4
4
|
import { canonicalFeedback } from "./feedback-canonical.mjs";
|
|
5
5
|
import { ensureOutboxDirectory } from "./feedback-outbox-directory.mjs";
|
|
6
6
|
import { filename, outboxDirectory, readRecord, records, syncDirectory, } from "./feedback-outbox.mjs";
|
|
@@ -3,8 +3,8 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
3
3
|
import { closeSync, constants, fsyncSync, fstatSync, openSync, readdirSync, renameSync, unlinkSync, } from 'node:fs';
|
|
4
4
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
5
5
|
import { ensureOutboxDirectory } from "./feedback-outbox-directory.mjs";
|
|
6
|
-
import { withFileLock } from "../
|
|
7
|
-
import { openLogFile, readLogContent, writeAll } from "../
|
|
6
|
+
import { withFileLock } from "../private-files/lock.mjs";
|
|
7
|
+
import { openLogFile, readLogContent, writeAll } from "../private-files/log-file.mjs";
|
|
8
8
|
import { validateFeedbackEnvelope } from "./feedback-codec.mjs";
|
|
9
9
|
import { validateFeedbackIdentity } from "./feedback-identity.mjs";
|
|
10
10
|
import { isObject } from "./snapshot-partition-guards.mjs";
|
|
@@ -23,5 +23,5 @@ export { feedbackOutboxStatus, readFeedbackOutbox } from './feedback-outbox.mts'
|
|
|
23
23
|
export { composeRetrospective } from './feedback-compose.mts';
|
|
24
24
|
export type { RetrospectiveCompositionInput, FeedbackAssessment } from './feedback-compose.mts';
|
|
25
25
|
export type { JournalAuditOptions } from './feedback-journal-audit.mts';
|
|
26
|
-
export type { JournalEntry, JournalLoader, JournalLoadResult } from '../
|
|
26
|
+
export type { JournalEntry, JournalLoader, JournalLoadResult } from '../audit-journal/types.mts';
|
|
27
27
|
export { feedbackOutboxCounts } from './feedback-outbox-counts.mts';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { JournalEntry } from './types.mts';
|
|
2
|
+
import type { AuditRedactor } from './text.mts';
|
|
3
|
+
export declare function isConformingCiFailureBlock(markdown: string): boolean;
|
|
4
|
+
export declare function getConformingGroups(entries: Iterable<JournalEntry>, redact?: AuditRedactor, maxBytes?: number): string[];
|
|
5
|
+
export declare function incompleteCiSection(markdownBlocks: string[]): string;
|
|
6
|
+
export declare function buildCiFailuresSection(markdownBlocks: string[]): string;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { conformingBlocks, matchAuditBlock } from "./audit-block.mjs";
|
|
2
|
-
import { markdownAuditText } from "./text.mjs";
|
|
3
2
|
const GROUP_HEADER = /^- `(recurring|one-off)` — `GitHub Actions` — .*[^\s]$/;
|
|
4
3
|
const EVIDENCE = /^ {2}- Evidence: .*[^\s]$/;
|
|
5
4
|
const ROOT_DIAGNOSTIC = /^ {2}- Root diagnostic: .*[^\s]$/;
|
|
@@ -17,7 +16,6 @@ const CI_SECTION_MAX_BYTES = 4_000;
|
|
|
17
16
|
// Field order: header, Evidence, Root diagnostic, Disposition. Cut Root diagnostic first and
|
|
18
17
|
// Evidence last, so the run URL and commit SHA survive longest.
|
|
19
18
|
const SHRINK_ORDER = [2, 3, 0, 1];
|
|
20
|
-
const SESSION_ID_MAX_BYTES = 120;
|
|
21
19
|
function matchBlock(markdown, render = {}) {
|
|
22
20
|
return matchAuditBlock(markdown, [GROUP_HEADER, EVIDENCE, ROOT_DIAGNOSTIC, DISPOSITION], FIELD_PREFIXES, { ...render, shrinkOrder: SHRINK_ORDER });
|
|
23
21
|
}
|
|
@@ -45,12 +43,8 @@ export function incompleteCiSection(markdownBlocks) {
|
|
|
45
43
|
...markdownBlocks,
|
|
46
44
|
].join('\n\n');
|
|
47
45
|
}
|
|
48
|
-
export function buildCiFailuresSection(
|
|
49
|
-
if (
|
|
50
|
-
return `${CI_FAILURES_HEADER}\nStatus: unavailable (blackboard unreachable)`;
|
|
51
|
-
if (journal.markdownBlocks.length === 0 && frictionStatus === 'absent')
|
|
52
|
-
return `${CI_FAILURES_HEADER}\nStatus: unavailable (no friction log for session ${markdownAuditText(sessionId, undefined, SESSION_ID_MAX_BYTES)})`;
|
|
53
|
-
if (journal.markdownBlocks.length === 0)
|
|
46
|
+
export function buildCiFailuresSection(markdownBlocks) {
|
|
47
|
+
if (markdownBlocks.length === 0)
|
|
54
48
|
return `${CI_FAILURES_HEADER}\nStatus: none observed`;
|
|
55
|
-
return `${CI_FAILURES_HEADER}\nStatus: failures observed\n\n${
|
|
49
|
+
return `${CI_FAILURES_HEADER}\nStatus: failures observed\n\n${markdownBlocks.join('\n\n')}`;
|
|
56
50
|
}
|
|
@@ -9,6 +9,5 @@ export type JournalScan = {
|
|
|
9
9
|
status: 'unreachable';
|
|
10
10
|
diagnostic: string;
|
|
11
11
|
};
|
|
12
|
-
export declare function errorMessage(error: unknown): string;
|
|
13
12
|
/** Loads the session journal through the caller's loader within the shared bounded scan. */
|
|
14
13
|
export declare function scanJournal(sessionId: string, loader: JournalLoader): Promise<JournalScan>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function validateSessionId(sessionId: string): void;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
1
|
import { isWellFormedUnicode, normalizeAuditText } from "./text.mjs";
|
|
3
2
|
const SESSION_ID_MAX_LENGTH = 4096;
|
|
4
3
|
export function validateSessionId(sessionId) {
|
|
@@ -11,7 +10,3 @@ export function validateSessionId(sessionId) {
|
|
|
11
10
|
if (normalizeAuditText(sessionId) === '')
|
|
12
11
|
throw new Error('sessionId must be non-empty');
|
|
13
12
|
}
|
|
14
|
-
export function sanitizeSessionId(sessionId) {
|
|
15
|
-
validateSessionId(sessionId);
|
|
16
|
-
return createHash('sha256').update(sessionId).digest('hex');
|
|
17
|
-
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export declare function normalizeAuditText(value: string): string;
|
|
2
|
-
export declare function boundedText(value: string, maximum: number): string;
|
|
3
2
|
export declare function isWellFormedUnicode(value: string): boolean;
|
|
4
|
-
export declare function isSafeAuditText(value: unknown): value is string;
|
|
5
3
|
export type AuditRedactor = (value: string) => string;
|
|
6
4
|
/**
|
|
7
5
|
* Splits a field into atomic Markdown-escaped units (one code point, plus its backslash when
|
|
@@ -14,5 +12,4 @@ export declare function markdownAuditUnits(value: string, redact?: AuditRedactor
|
|
|
14
12
|
* Joins escaped units whole, or, when over `maxBytes`, keeps only complete whitespace-separated
|
|
15
13
|
* tokens (so URLs and SHAs are never split) and marks the cut. Never ends in whitespace.
|
|
16
14
|
*/
|
|
17
|
-
export declare function fitUnits(units: string[], maxBytes: number
|
|
18
|
-
export declare function markdownAuditText(value: string, redact?: AuditRedactor, maxBytes?: number): string;
|
|
15
|
+
export declare function fitUnits(units: string[], maxBytes: number): string;
|
|
@@ -3,14 +3,6 @@ const MARKDOWN_CHARACTER = /\\|`|\*|_|~|\[|\]|<|>|#|-|\+|!|\||&/;
|
|
|
3
3
|
export function normalizeAuditText(value) {
|
|
4
4
|
return value.replace(CONTROL_CHARACTERS, ' ').trim();
|
|
5
5
|
}
|
|
6
|
-
export function boundedText(value, maximum) {
|
|
7
|
-
let end = Math.min(value.length, maximum);
|
|
8
|
-
if (end < value.length &&
|
|
9
|
-
/[\uD800-\uDBFF]/.test(value[end - 1]) &&
|
|
10
|
-
/[\uDC00-\uDFFF]/.test(value[end]))
|
|
11
|
-
end--;
|
|
12
|
-
return value.slice(0, end);
|
|
13
|
-
}
|
|
14
6
|
export function isWellFormedUnicode(value) {
|
|
15
7
|
for (let index = 0; index < value.length; index++) {
|
|
16
8
|
const code = value.charCodeAt(index);
|
|
@@ -25,12 +17,6 @@ export function isWellFormedUnicode(value) {
|
|
|
25
17
|
}
|
|
26
18
|
return true;
|
|
27
19
|
}
|
|
28
|
-
export function isSafeAuditText(value) {
|
|
29
|
-
return (typeof value === 'string' &&
|
|
30
|
-
value !== '' &&
|
|
31
|
-
isWellFormedUnicode(value) &&
|
|
32
|
-
value === normalizeAuditText(value));
|
|
33
|
-
}
|
|
34
20
|
/**
|
|
35
21
|
* Splits a field into atomic Markdown-escaped units (one code point, plus its backslash when
|
|
36
22
|
* escaped), so a later cut can never land inside an escape sequence. Redaction runs on the raw
|
|
@@ -47,7 +33,7 @@ const WHITESPACE = /^\s$/u;
|
|
|
47
33
|
* Joins escaped units whole, or, when over `maxBytes`, keeps only complete whitespace-separated
|
|
48
34
|
* tokens (so URLs and SHAs are never split) and marks the cut. Never ends in whitespace.
|
|
49
35
|
*/
|
|
50
|
-
export function fitUnits(units, maxBytes
|
|
36
|
+
export function fitUnits(units, maxBytes) {
|
|
51
37
|
const whole = units.join('');
|
|
52
38
|
if (Buffer.byteLength(whole) <= maxBytes)
|
|
53
39
|
return whole;
|
|
@@ -58,13 +44,9 @@ export function fitUnits(units, maxBytes, atTokens = true) {
|
|
|
58
44
|
used += Buffer.byteLength(unit);
|
|
59
45
|
if (used > room)
|
|
60
46
|
break;
|
|
61
|
-
if (
|
|
62
|
-
boundary =
|
|
47
|
+
if (WHITESPACE.test(unit))
|
|
48
|
+
boundary = index;
|
|
63
49
|
}
|
|
64
50
|
const kept = units.slice(0, boundary).join('').trimEnd();
|
|
65
|
-
return kept ? `${kept}
|
|
66
|
-
}
|
|
67
|
-
export function markdownAuditText(value, redact, maxBytes = Infinity) {
|
|
68
|
-
// Used for identifiers, which have no token boundaries to cut at.
|
|
69
|
-
return fitUnits(markdownAuditUnits(value, redact), maxBytes, false);
|
|
51
|
+
return kept ? `${kept} ${CUT_MARKER}` : CUT_MARKER;
|
|
70
52
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type JournalEntry = {
|
|
2
|
+
data?: {
|
|
3
|
+
type?: unknown;
|
|
4
|
+
markdown?: unknown;
|
|
5
|
+
};
|
|
6
|
+
};
|
|
7
|
+
export type JournalLoadResult = {
|
|
8
|
+
status: 'ok';
|
|
9
|
+
entries: Iterable<JournalEntry> | AsyncIterable<JournalEntry>;
|
|
10
|
+
} | {
|
|
11
|
+
status: 'not-found';
|
|
12
|
+
};
|
|
13
|
+
export type JournalLoader = (sessionId: string) => JournalLoadResult | Promise<JournalLoadResult>;
|
|
14
|
+
export type AuditCoverage = {
|
|
15
|
+
journalStatus: 'complete' | 'partial' | 'unavailable';
|
|
16
|
+
truncated: boolean;
|
|
17
|
+
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { ghSpawnEnv } from "../../gh-cli/env.mjs";
|
|
2
3
|
import { auditCiJobRuntime } from "../../gha-runtime-audit/index.mjs";
|
|
3
4
|
/* v8 ignore next 10 -- live `gh api` adapter */
|
|
4
5
|
function defaultGhApiExecutor() {
|
|
5
6
|
return async (request) => JSON.parse(execFileSync('gh', ['api', request.endpoint, '--jq', request.jq], {
|
|
6
7
|
encoding: 'utf8',
|
|
8
|
+
env: ghSpawnEnv(),
|
|
7
9
|
maxBuffer: 16 * 1024 * 1024,
|
|
8
10
|
}));
|
|
9
11
|
}
|