auditor-lambda 0.3.40 → 0.5.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/audit-code-wrapper-lib.mjs +20 -2
- package/dist/cli/args.d.ts +59 -0
- package/dist/cli/args.js +244 -0
- package/dist/cli/dispatch.d.ts +80 -0
- package/dist/cli/dispatch.js +532 -0
- package/dist/cli/prompts.d.ts +37 -0
- package/dist/cli/prompts.js +225 -0
- package/dist/cli/steps.d.ts +29 -0
- package/dist/cli/steps.js +30 -0
- package/dist/cli/waveManifest.d.ts +40 -0
- package/dist/cli/waveManifest.js +41 -0
- package/dist/cli/workerResult.d.ts +18 -0
- package/dist/cli/workerResult.js +42 -0
- package/dist/cli.d.ts +2 -22
- package/dist/cli.js +442 -975
- package/dist/extractors/analyzers/css.d.ts +2 -0
- package/dist/extractors/analyzers/css.js +101 -0
- package/dist/extractors/analyzers/html.d.ts +2 -0
- package/dist/extractors/analyzers/html.js +92 -0
- package/dist/extractors/analyzers/merge.d.ts +14 -0
- package/dist/extractors/analyzers/merge.js +85 -0
- package/dist/extractors/analyzers/python.d.ts +2 -0
- package/dist/extractors/analyzers/python.js +104 -0
- package/dist/extractors/analyzers/registry.d.ts +33 -0
- package/dist/extractors/analyzers/registry.js +100 -0
- package/dist/extractors/analyzers/resourceUrl.d.ts +7 -0
- package/dist/extractors/analyzers/resourceUrl.js +25 -0
- package/dist/extractors/analyzers/sql.d.ts +2 -0
- package/dist/extractors/analyzers/sql.js +19 -0
- package/dist/extractors/analyzers/treeSitter.d.ts +34 -0
- package/dist/extractors/analyzers/treeSitter.js +111 -0
- package/dist/extractors/analyzers/types.d.ts +53 -0
- package/dist/extractors/analyzers/typescript.d.ts +2 -0
- package/dist/extractors/analyzers/typescript.js +257 -0
- package/dist/extractors/browserExtension.d.ts +1 -3
- package/dist/extractors/browserExtension.js +2 -2
- package/dist/extractors/designAssessment.d.ts +1 -3
- package/dist/extractors/disposition.d.ts +2 -1
- package/dist/extractors/disposition.js +11 -1
- package/dist/extractors/flows.d.ts +1 -3
- package/dist/extractors/flows.js +2 -2
- package/dist/extractors/graph.d.ts +2 -2
- package/dist/extractors/graph.js +171 -327
- package/dist/extractors/graphManifestEdges.d.ts +1 -1
- package/dist/extractors/graphPathUtils.d.ts +1 -1
- package/dist/extractors/graphPythonImports.d.ts +18 -0
- package/dist/extractors/graphPythonImports.js +362 -0
- package/dist/extractors/pathPatterns.d.ts +6 -0
- package/dist/extractors/pathPatterns.js +8 -0
- package/dist/extractors/risk.d.ts +1 -2
- package/dist/extractors/surfaces.d.ts +1 -3
- package/dist/extractors/surfaces.js +2 -2
- package/dist/io/artifacts.d.ts +12 -5
- package/dist/io/artifacts.js +13 -1
- package/dist/io/runArtifacts.js +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/orchestrator/advance.d.ts +21 -0
- package/dist/orchestrator/advance.js +69 -7
- package/dist/orchestrator/auditTaskUtils.d.ts +4 -0
- package/dist/orchestrator/auditTaskUtils.js +27 -0
- package/dist/orchestrator/dependencyMap.js +27 -0
- package/dist/orchestrator/edgeReasoning.d.ts +39 -0
- package/dist/orchestrator/edgeReasoning.js +125 -0
- package/dist/orchestrator/executors.js +11 -1
- package/dist/orchestrator/fileAnchors.d.ts +1 -1
- package/dist/orchestrator/fileIntegrity.d.ts +7 -0
- package/dist/orchestrator/fileIntegrity.js +41 -0
- package/dist/orchestrator/flowCoverage.d.ts +1 -1
- package/dist/orchestrator/flowPlanning.d.ts +1 -1
- package/dist/orchestrator/flowRequeue.d.ts +1 -1
- package/dist/orchestrator/graphEnrichmentExecutor.d.ts +29 -0
- package/dist/orchestrator/graphEnrichmentExecutor.js +196 -0
- package/dist/orchestrator/internalExecutors.d.ts +13 -2
- package/dist/orchestrator/internalExecutors.js +112 -16
- package/dist/orchestrator/localCommands.js +6 -25
- package/dist/orchestrator/nextStep.d.ts +2 -1
- package/dist/orchestrator/nextStep.js +3 -1
- package/dist/orchestrator/planning.d.ts +1 -1
- package/dist/orchestrator/requeueCommand.d.ts +1 -1
- package/dist/orchestrator/reviewPackets.d.ts +37 -4
- package/dist/orchestrator/reviewPackets.js +113 -158
- package/dist/orchestrator/runtimeValidation.d.ts +1 -1
- package/dist/orchestrator/runtimeValidation.js +4 -31
- package/dist/orchestrator/scope.d.ts +62 -0
- package/dist/orchestrator/scope.js +227 -0
- package/dist/orchestrator/state.js +2 -0
- package/dist/orchestrator/taskBuilder.d.ts +1 -1
- package/dist/orchestrator/taskBuilder.js +1 -12
- package/dist/orchestrator/unionFind.d.ts +7 -0
- package/dist/orchestrator/unionFind.js +32 -0
- package/dist/orchestrator/unitBuilder.d.ts +2 -2
- package/dist/orchestrator/unitBuilder.js +4 -18
- package/dist/prompts/renderWorkerPrompt.js +18 -1
- package/dist/providers/claudeCodeProvider.d.ts +4 -4
- package/dist/providers/claudeCodeProvider.js +9 -3
- package/dist/providers/constants.d.ts +1 -1
- package/dist/providers/constants.js +1 -1
- package/dist/providers/index.d.ts +1 -2
- package/dist/providers/index.js +5 -4
- package/dist/providers/localSubprocessProvider.d.ts +2 -2
- package/dist/providers/localSubprocessProvider.js +1 -1
- package/dist/providers/opencodeProvider.d.ts +4 -4
- package/dist/providers/opencodeProvider.js +7 -2
- package/dist/providers/spawnLoggedCommand.d.ts +3 -1
- package/dist/providers/spawnLoggedCommand.js +21 -0
- package/dist/providers/subprocessTemplateProvider.d.ts +4 -4
- package/dist/providers/subprocessTemplateProvider.js +8 -3
- package/dist/providers/vscodeTaskProvider.d.ts +3 -4
- package/dist/providers/vscodeTaskProvider.js +2 -2
- package/dist/quota/discoveredLimits.js +1 -1
- package/dist/quota/hostLimits.d.ts +1 -2
- package/dist/quota/hostLimits.js +4 -46
- package/dist/quota/index.d.ts +18 -15
- package/dist/quota/index.js +4 -9
- package/dist/quota/scheduler.d.ts +1 -3
- package/dist/quota/scheduler.js +1 -2
- package/dist/reporting/synthesis.d.ts +37 -3
- package/dist/reporting/synthesis.js +97 -16
- package/dist/reporting/synthesisNarrativePrompt.d.ts +7 -0
- package/dist/reporting/synthesisNarrativePrompt.js +60 -0
- package/dist/reporting/workBlocks.d.ts +2 -11
- package/dist/supervisor/operatorHandoff.js +1 -1
- package/dist/supervisor/runLedger.d.ts +1 -1
- package/dist/supervisor/runLedger.js +2 -2
- package/dist/supervisor/sessionConfig.d.ts +8 -1
- package/dist/supervisor/sessionConfig.js +22 -3
- package/dist/types/analyzerCapability.d.ts +16 -0
- package/dist/types/auditScope.d.ts +43 -0
- package/dist/types/auditScope.js +14 -0
- package/dist/types/reviewPlanning.d.ts +1 -1
- package/dist/types/synthesisNarrative.d.ts +7 -0
- package/dist/types/synthesisNarrative.js +5 -0
- package/dist/types/workerSession.d.ts +6 -0
- package/dist/types.d.ts +2 -19
- package/dist/validation/artifacts.d.ts +1 -1
- package/dist/validation/artifacts.js +10 -1
- package/dist/validation/auditResults.d.ts +1 -1
- package/dist/validation/auditResults.js +1 -1
- package/dist/validation/sessionConfig.d.ts +2 -3
- package/dist/validation/sessionConfig.js +25 -3
- package/package.json +7 -3
- package/schemas/analyzer_capability.schema.json +47 -0
- package/schemas/audit_findings.schema.json +141 -0
- package/schemas/finding.schema.json +2 -1
- package/schemas/graph_bundle.schema.json +5 -0
- package/schemas/scope.schema.json +46 -0
- package/scripts/postinstall.mjs +0 -1
- package/dist/io/json.d.ts +0 -10
- package/dist/io/json.js +0 -142
- package/dist/providers/types.d.ts +0 -33
- package/dist/quota/compositeQuotaSource.d.ts +0 -7
- package/dist/quota/compositeQuotaSource.js +0 -20
- package/dist/quota/errorParsers/claudeCodeErrorParser.d.ts +0 -6
- package/dist/quota/errorParsers/claudeCodeErrorParser.js +0 -39
- package/dist/quota/errorParsers/genericErrorParser.d.ts +0 -9
- package/dist/quota/errorParsers/genericErrorParser.js +0 -7
- package/dist/quota/errorParsers/index.d.ts +0 -5
- package/dist/quota/errorParsers/index.js +0 -12
- package/dist/quota/errorParsing.d.ts +0 -7
- package/dist/quota/errorParsing.js +0 -69
- package/dist/quota/fileLock.d.ts +0 -6
- package/dist/quota/fileLock.js +0 -64
- package/dist/quota/learnedQuotaSource.d.ts +0 -7
- package/dist/quota/learnedQuotaSource.js +0 -25
- package/dist/quota/limits.d.ts +0 -16
- package/dist/quota/limits.js +0 -77
- package/dist/quota/quotaSource.d.ts +0 -12
- package/dist/quota/slidingWindow.d.ts +0 -4
- package/dist/quota/slidingWindow.js +0 -28
- package/dist/quota/state.d.ts +0 -15
- package/dist/quota/state.js +0 -148
- package/dist/quota/types.d.ts +0 -67
- package/dist/quota/types.js +0 -1
- package/dist/reporting/rootCause.d.ts +0 -10
- package/dist/reporting/rootCause.js +0 -146
- package/dist/types/disposition.d.ts +0 -9
- package/dist/types/disposition.js +0 -1
- package/dist/types/flows.d.ts +0 -17
- package/dist/types/flows.js +0 -1
- package/dist/types/graph.d.ts +0 -22
- package/dist/types/graph.js +0 -1
- package/dist/types/risk.d.ts +0 -9
- package/dist/types/risk.js +0 -1
- package/dist/types/runLedger.d.ts +0 -17
- package/dist/types/runLedger.js +0 -6
- package/dist/types/sessionConfig.d.ts +0 -79
- package/dist/types/sessionConfig.js +0 -15
- package/dist/types/surfaces.d.ts +0 -15
- package/dist/types/surfaces.js +0 -1
- package/dist/validation/basic.d.ts +0 -13
- package/dist/validation/basic.js +0 -46
- /package/dist/{providers → extractors/analyzers}/types.js +0 -0
- /package/dist/{quota/quotaSource.js → types/analyzerCapability.js} +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { changedFiles, gitRefExists, isGitRepo } from "@audit-tools/shared";
|
|
2
|
+
import { buildDispositionMap } from "../extractors/disposition.js";
|
|
3
|
+
import { buildPathLookup } from "../extractors/graph.js";
|
|
4
|
+
import { HIGH_FAN_DEGREE_THRESHOLD, buildGraphDegreeIndex, collectGraphEdges, graphEdgeConfidence, normalizeGraphPath, } from "./reviewPackets.js";
|
|
5
|
+
/** Default cap on in-scope files (seeds + expanded) before expansion stops. */
|
|
6
|
+
export const DEFAULT_SCOPE_MAX_FILES = 200;
|
|
7
|
+
/** Graph edges below this confidence are never traversed during expansion. */
|
|
8
|
+
export const SCOPE_EDGE_CONFIDENCE_FLOOR = 0.5;
|
|
9
|
+
/**
|
|
10
|
+
* Expansion stops along a path once the accumulated path-confidence (the product
|
|
11
|
+
* of the traversed edge confidences) drops below this floor. With no fixed hop
|
|
12
|
+
* count, this — together with hub-skipping and the file budget — bounds the
|
|
13
|
+
* frontier deterministically.
|
|
14
|
+
*/
|
|
15
|
+
export const SCOPE_MIN_FRONTIER_CONFIDENCE = 0.5;
|
|
16
|
+
/**
|
|
17
|
+
* Deterministic priority-frontier expansion (Phase 3). Starting from the changed
|
|
18
|
+
* files (seeds), walk the dependency graph outward, always visiting the neighbour
|
|
19
|
+
* with the highest accumulated path-confidence first (tie-broken by path). High
|
|
20
|
+
* fan-in/out hubs are skipped so a single change near a hub does not drag the
|
|
21
|
+
* whole repo into scope, low-confidence edges are dropped, and expansion halts at
|
|
22
|
+
* the file budget or when the best remaining frontier confidence falls below the
|
|
23
|
+
* floor. Same inputs → identical scope.
|
|
24
|
+
*/
|
|
25
|
+
export function computeAuditScope(input) {
|
|
26
|
+
const maxFiles = input.budget?.max_files ?? DEFAULT_SCOPE_MAX_FILES;
|
|
27
|
+
// normalized graph key -> canonical (repo-manifest) path for auditable files.
|
|
28
|
+
const canonicalByNorm = new Map();
|
|
29
|
+
for (const file of input.includedFiles) {
|
|
30
|
+
const key = normalizeGraphPath(file);
|
|
31
|
+
if (!canonicalByNorm.has(key)) {
|
|
32
|
+
canonicalByNorm.set(key, file);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Seeds = changed files that are auditable (present in the manifest). Changed
|
|
36
|
+
// files that are excluded, deleted, or otherwise absent simply drop out.
|
|
37
|
+
const seedKeys = [];
|
|
38
|
+
const seedSeen = new Set();
|
|
39
|
+
for (const path of input.changed) {
|
|
40
|
+
const key = normalizeGraphPath(path);
|
|
41
|
+
if (canonicalByNorm.has(key) && !seedSeen.has(key)) {
|
|
42
|
+
seedSeen.add(key);
|
|
43
|
+
seedKeys.push(key);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const edges = collectGraphEdges(input.graphBundle);
|
|
47
|
+
const degree = buildGraphDegreeIndex(edges);
|
|
48
|
+
const isHub = (key) => (degree.fanIn.get(key) ?? 0) > HIGH_FAN_DEGREE_THRESHOLD ||
|
|
49
|
+
(degree.fanOut.get(key) ?? 0) > HIGH_FAN_DEGREE_THRESHOLD;
|
|
50
|
+
// Bidirectional adjacency: a change to a file is relevant to what it depends
|
|
51
|
+
// on AND to what depends on it. Edges below the confidence floor are dropped.
|
|
52
|
+
const adjacency = new Map();
|
|
53
|
+
const addEdge = (from, to, confidence) => {
|
|
54
|
+
const list = adjacency.get(from) ?? [];
|
|
55
|
+
list.push({ to, confidence });
|
|
56
|
+
adjacency.set(from, list);
|
|
57
|
+
};
|
|
58
|
+
for (const edge of edges) {
|
|
59
|
+
const confidence = graphEdgeConfidence(edge);
|
|
60
|
+
if (confidence < SCOPE_EDGE_CONFIDENCE_FLOOR) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const from = normalizeGraphPath(edge.from);
|
|
64
|
+
const to = normalizeGraphPath(edge.to);
|
|
65
|
+
addEdge(from, to, confidence);
|
|
66
|
+
addEdge(to, from, confidence);
|
|
67
|
+
}
|
|
68
|
+
// Max-product shortest-path frontier. `best` holds the highest accumulated
|
|
69
|
+
// confidence discovered for each node; seeds start at 1.
|
|
70
|
+
const best = new Map();
|
|
71
|
+
for (const key of seedKeys) {
|
|
72
|
+
best.set(key, 1);
|
|
73
|
+
}
|
|
74
|
+
const visited = new Set();
|
|
75
|
+
const inScope = new Set(seedKeys);
|
|
76
|
+
const expandedKeys = [];
|
|
77
|
+
let budgetHit = false;
|
|
78
|
+
for (;;) {
|
|
79
|
+
let pick;
|
|
80
|
+
let pickConfidence = -1;
|
|
81
|
+
for (const [key, confidence] of best) {
|
|
82
|
+
if (visited.has(key))
|
|
83
|
+
continue;
|
|
84
|
+
if (confidence > pickConfidence ||
|
|
85
|
+
(confidence === pickConfidence && (pick === undefined || key < pick))) {
|
|
86
|
+
pick = key;
|
|
87
|
+
pickConfidence = confidence;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (pick === undefined || pickConfidence < SCOPE_MIN_FRONTIER_CONFIDENCE) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
visited.add(pick);
|
|
94
|
+
// Record newly-reached auditable files (seeds are already in scope).
|
|
95
|
+
if (canonicalByNorm.has(pick) && !inScope.has(pick)) {
|
|
96
|
+
if (inScope.size >= maxFiles) {
|
|
97
|
+
budgetHit = true;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
inScope.add(pick);
|
|
101
|
+
expandedKeys.push(pick);
|
|
102
|
+
}
|
|
103
|
+
// Relax neighbours, skipping hubs (never traverse through or into a hub) and
|
|
104
|
+
// non-auditable nodes.
|
|
105
|
+
for (const neighbour of adjacency.get(pick) ?? []) {
|
|
106
|
+
if (isHub(neighbour.to) || !canonicalByNorm.has(neighbour.to)) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const candidate = pickConfidence * neighbour.confidence;
|
|
110
|
+
if (candidate < SCOPE_MIN_FRONTIER_CONFIDENCE) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (candidate > (best.get(neighbour.to) ?? 0)) {
|
|
114
|
+
best.set(neighbour.to, candidate);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const seedFiles = seedKeys
|
|
119
|
+
.map((key) => canonicalByNorm.get(key))
|
|
120
|
+
.sort((a, b) => a.localeCompare(b));
|
|
121
|
+
const expandedFiles = expandedKeys
|
|
122
|
+
.map((key) => canonicalByNorm.get(key))
|
|
123
|
+
.sort((a, b) => a.localeCompare(b));
|
|
124
|
+
const notes = [];
|
|
125
|
+
if (seedFiles.length === 0) {
|
|
126
|
+
notes.push(`No auditable files changed since ${input.since}.`);
|
|
127
|
+
}
|
|
128
|
+
if (budgetHit) {
|
|
129
|
+
notes.push(`Expansion stopped at the ${maxFiles}-file budget; some graph neighbours were left out of scope.`);
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
mode: "delta",
|
|
133
|
+
since: input.since,
|
|
134
|
+
seed_files: seedFiles,
|
|
135
|
+
expanded_files: expandedFiles,
|
|
136
|
+
budget: { max_files: maxFiles },
|
|
137
|
+
...(notes.length > 0 ? { dropped_note: notes.join(" ") } : {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/** A full-audit scope (the default, and every fallback). */
|
|
141
|
+
export function fullAuditScope(budget, droppedNote) {
|
|
142
|
+
return {
|
|
143
|
+
mode: "full",
|
|
144
|
+
since: null,
|
|
145
|
+
seed_files: [],
|
|
146
|
+
expanded_files: [],
|
|
147
|
+
budget: { max_files: budget?.max_files ?? DEFAULT_SCOPE_MAX_FILES },
|
|
148
|
+
...(droppedNote ? { dropped_note: droppedNote } : {}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Resolve the scope for a planning run. Returns a full-audit scope unless a
|
|
153
|
+
* `--since` ref was supplied against a real git repository; an unusable ref or
|
|
154
|
+
* missing root degrades to a full audit with an honest note. Reads the auditable
|
|
155
|
+
* file set from the repo manifest + disposition (the same lookup the graph
|
|
156
|
+
* extractor uses) and the dependency graph from the bundle.
|
|
157
|
+
*/
|
|
158
|
+
export function resolveAuditScope(input) {
|
|
159
|
+
const since = input.since?.trim();
|
|
160
|
+
if (!since) {
|
|
161
|
+
return fullAuditScope(input.budget);
|
|
162
|
+
}
|
|
163
|
+
if (!input.root) {
|
|
164
|
+
return fullAuditScope(input.budget, `--since '${since}' was ignored: no repository root was available, so a full audit ran.`);
|
|
165
|
+
}
|
|
166
|
+
if (!isGitRepo(input.root)) {
|
|
167
|
+
return fullAuditScope(input.budget, `--since '${since}' was ignored: '${input.root}' is not a git repository, so a full audit ran.`);
|
|
168
|
+
}
|
|
169
|
+
if (!gitRefExists(input.root, since)) {
|
|
170
|
+
return fullAuditScope(input.budget, `--since '${since}' could not be resolved to a commit, so a full audit ran.`);
|
|
171
|
+
}
|
|
172
|
+
const dispositionMap = buildDispositionMap(input.bundle.file_disposition);
|
|
173
|
+
const includedFiles = input.bundle.repo_manifest
|
|
174
|
+
? [
|
|
175
|
+
...new Set(buildPathLookup(input.bundle.repo_manifest, dispositionMap).values()),
|
|
176
|
+
].sort((a, b) => a.localeCompare(b))
|
|
177
|
+
: [];
|
|
178
|
+
return computeAuditScope({
|
|
179
|
+
since,
|
|
180
|
+
changed: changedFiles(input.root, since),
|
|
181
|
+
includedFiles,
|
|
182
|
+
graphBundle: input.bundle.graph_bundle,
|
|
183
|
+
budget: input.budget,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Apply a delta scope to a freshly-built coverage matrix. In-scope files (seeds
|
|
188
|
+
* + expanded neighbours) keep their fresh `pending` status to be re-audited.
|
|
189
|
+
* Out-of-scope files inherit a prior `complete` record verbatim when present (so
|
|
190
|
+
* previously-finished work is preserved, not re-run), and are otherwise excluded
|
|
191
|
+
* from this run with `classification_status: "out_of_scope_delta"`. Deterministic
|
|
192
|
+
* exclusions (non-auditable/trivial) are left untouched. A full scope is a no-op.
|
|
193
|
+
*/
|
|
194
|
+
export function applyScopeToCoverage(coverage, scope, priorCoverage) {
|
|
195
|
+
if (scope.mode !== "delta") {
|
|
196
|
+
return coverage;
|
|
197
|
+
}
|
|
198
|
+
const inScope = new Set([
|
|
199
|
+
...scope.seed_files,
|
|
200
|
+
...scope.expanded_files,
|
|
201
|
+
]);
|
|
202
|
+
const priorByPath = new Map((priorCoverage?.files ?? []).map((file) => [file.path, file]));
|
|
203
|
+
for (const file of coverage.files) {
|
|
204
|
+
if (file.audit_status === "excluded") {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (inScope.has(file.path)) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const prior = priorByPath.get(file.path);
|
|
211
|
+
if (prior && prior.audit_status === "complete") {
|
|
212
|
+
file.required_lenses = [...prior.required_lenses];
|
|
213
|
+
file.completed_lenses = [...prior.completed_lenses];
|
|
214
|
+
file.unit_ids = [...prior.unit_ids];
|
|
215
|
+
file.audit_status = "complete";
|
|
216
|
+
file.classification_status = prior.classification_status;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
file.required_lenses = [];
|
|
220
|
+
file.completed_lenses = [];
|
|
221
|
+
file.unit_ids = [];
|
|
222
|
+
file.audit_status = "excluded";
|
|
223
|
+
file.classification_status = "out_of_scope_delta";
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return coverage;
|
|
227
|
+
}
|
|
@@ -29,6 +29,7 @@ export function deriveAuditState(bundle) {
|
|
|
29
29
|
"critical_flows.json",
|
|
30
30
|
"risk_register.json",
|
|
31
31
|
], structureReady)));
|
|
32
|
+
obligations.push(obligation("graph_enrichment_current", staleOrSatisfied(staleArtifacts, ["analyzer_capability.json"], has(bundle.analyzer_capability))));
|
|
32
33
|
obligations.push(obligation("design_assessment_current", staleOrSatisfied(staleArtifacts, ["design_assessment.json"], has(bundle.design_assessment))));
|
|
33
34
|
obligations.push(obligation("design_review_completed", bundle.design_assessment?.reviewed ? "satisfied" : "missing"));
|
|
34
35
|
const planningReady = has(bundle.coverage_matrix) &&
|
|
@@ -69,6 +70,7 @@ export function deriveAuditState(bundle) {
|
|
|
69
70
|
? "No deterministic runtime validation tasks were planned."
|
|
70
71
|
: undefined));
|
|
71
72
|
obligations.push(obligation("synthesis_current", staleOrSatisfied(staleArtifacts, ["audit-report.md"], has(bundle.audit_report))));
|
|
73
|
+
obligations.push(obligation("synthesis_narrative_current", staleOrSatisfied(staleArtifacts, ["synthesis-narrative.json"], has(bundle.synthesis_narrative))));
|
|
72
74
|
let status = "not_started";
|
|
73
75
|
if (!has(bundle.repo_manifest)) {
|
|
74
76
|
status = "not_started";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
|
|
2
2
|
import type { AuditTask, CoverageMatrix, Lens } from "../types.js";
|
|
3
|
-
import type { CriticalFlowManifest } from "
|
|
3
|
+
import type { CriticalFlowManifest } from "@audit-tools/shared";
|
|
4
4
|
export interface UnitLineIndex {
|
|
5
5
|
[path: string]: number;
|
|
6
6
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { claimFlowReviewBlocks } from "./flowPlanning.js";
|
|
2
2
|
import { isTrivialAuditPath } from "./trivialAudit.js";
|
|
3
|
-
import { LENS_ORDER } from "./
|
|
3
|
+
import { LENS_ORDER, priorityRank } from "./auditTaskUtils.js";
|
|
4
4
|
import { isTestPath, normalizeExtractorPath, } from "../extractors/pathPatterns.js";
|
|
5
5
|
function taskPriority(hasExternalSignal, lens, isCriticalFlow = false) {
|
|
6
6
|
if (isCriticalFlow) {
|
|
@@ -17,17 +17,6 @@ function taskPriority(hasExternalSignal, lens, isCriticalFlow = false) {
|
|
|
17
17
|
}
|
|
18
18
|
return lens === "security" || lens === "data_integrity" ? "medium" : "low";
|
|
19
19
|
}
|
|
20
|
-
function priorityRank(priority) {
|
|
21
|
-
switch (priority) {
|
|
22
|
-
case "high":
|
|
23
|
-
return 3;
|
|
24
|
-
case "medium":
|
|
25
|
-
return 2;
|
|
26
|
-
case "low":
|
|
27
|
-
default:
|
|
28
|
-
return 1;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
20
|
function pickAnalyzerLens(category) {
|
|
32
21
|
const normalized = category.toLowerCase();
|
|
33
22
|
if (normalized.includes("security") ||
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class UnionFind {
|
|
2
|
+
parent;
|
|
3
|
+
constructor(keys) {
|
|
4
|
+
this.parent = new Map([...keys].map((key) => [key, key]));
|
|
5
|
+
}
|
|
6
|
+
find(key) {
|
|
7
|
+
const current = this.parent.get(key) ?? key;
|
|
8
|
+
if (current === key)
|
|
9
|
+
return key;
|
|
10
|
+
const root = this.find(current);
|
|
11
|
+
this.parent.set(key, root);
|
|
12
|
+
return root;
|
|
13
|
+
}
|
|
14
|
+
union(a, b) {
|
|
15
|
+
const rootA = this.find(a);
|
|
16
|
+
const rootB = this.find(b);
|
|
17
|
+
if (rootA === rootB)
|
|
18
|
+
return;
|
|
19
|
+
const [keep, move] = rootA.localeCompare(rootB) <= 0 ? [rootA, rootB] : [rootB, rootA];
|
|
20
|
+
this.parent.set(move, keep);
|
|
21
|
+
}
|
|
22
|
+
groups() {
|
|
23
|
+
const result = new Map();
|
|
24
|
+
for (const key of this.parent.keys()) {
|
|
25
|
+
const root = this.find(key);
|
|
26
|
+
const group = result.get(root) ?? [];
|
|
27
|
+
group.push(key);
|
|
28
|
+
result.set(root, group);
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Lens, RepoManifest, UnitManifest } from "../types.js";
|
|
2
|
-
import type { FileDisposition } from "
|
|
3
|
-
export
|
|
2
|
+
import type { FileDisposition } from "@audit-tools/shared";
|
|
3
|
+
export { LENS_ORDER, sortLenses } from "./auditTaskUtils.js";
|
|
4
4
|
export declare function deriveRequiredLensesForPath(path: string, options?: {
|
|
5
5
|
isBrowserExtensionProject?: boolean;
|
|
6
6
|
}): Lens[];
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { deriveBrowserExtensionLensesForPath, hasBrowserExtensionManifestFile, inferBrowserExtensionUnitKind, } from "../extractors/browserExtension.js";
|
|
2
2
|
import { bucketFile } from "../extractors/bucketing.js";
|
|
3
|
-
import { isAuditExcludedStatus } from "../extractors/disposition.js";
|
|
3
|
+
import { buildDispositionMap, isAuditExcludedStatus } from "../extractors/disposition.js";
|
|
4
4
|
import { pathTokens, normalizeExtractorPath } from "../extractors/pathPatterns.js";
|
|
5
|
+
import { sortLenses } from "./auditTaskUtils.js";
|
|
5
6
|
const LENS_MAP = {
|
|
6
7
|
runtime: ["correctness", "maintainability", "tests", "observability"],
|
|
7
8
|
interface: ["correctness", "security", "reliability", "tests", "observability"],
|
|
@@ -76,22 +77,7 @@ function inferUnitId(path, kind) {
|
|
|
76
77
|
}
|
|
77
78
|
return `${kind}-${path.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
|
|
78
79
|
}
|
|
79
|
-
export
|
|
80
|
-
"security",
|
|
81
|
-
"correctness",
|
|
82
|
-
"reliability",
|
|
83
|
-
"data_integrity",
|
|
84
|
-
"performance",
|
|
85
|
-
"operability",
|
|
86
|
-
"config_deployment",
|
|
87
|
-
"observability",
|
|
88
|
-
"maintainability",
|
|
89
|
-
"tests",
|
|
90
|
-
];
|
|
91
|
-
function sortLenses(lenses) {
|
|
92
|
-
const set = new Set(lenses);
|
|
93
|
-
return LENS_ORDER.filter((lens) => set.has(lens));
|
|
94
|
-
}
|
|
80
|
+
export { LENS_ORDER, sortLenses } from "./auditTaskUtils.js";
|
|
95
81
|
function applyExtensionLensGuards(path, lenses) {
|
|
96
82
|
const n = path.toLowerCase();
|
|
97
83
|
if (n.endsWith(".schema.json") || n.endsWith(".schema.ts")) {
|
|
@@ -142,7 +128,7 @@ function inferCriticalFlows(files, requiredLenses) {
|
|
|
142
128
|
export function buildUnitManifest(repoManifest, disposition) {
|
|
143
129
|
const units = new Map();
|
|
144
130
|
const isBrowserExtensionProject = hasBrowserExtensionManifestFile(repoManifest);
|
|
145
|
-
const dispositionMap =
|
|
131
|
+
const dispositionMap = buildDispositionMap(disposition);
|
|
146
132
|
for (const file of repoManifest.files) {
|
|
147
133
|
const status = dispositionMap.get(file.path);
|
|
148
134
|
if (file.excluded || (status && isAuditExcludedStatus(status))) {
|
|
@@ -2,6 +2,20 @@ import { usesDeferredWorkerCommand, } from "../types/workerSession.js";
|
|
|
2
2
|
function renderArgv(task) {
|
|
3
3
|
return JSON.stringify(task.worker_command);
|
|
4
4
|
}
|
|
5
|
+
function renderAccessSection(access) {
|
|
6
|
+
const lines = ["", "## File access"];
|
|
7
|
+
if (access.read_paths.length > 0) {
|
|
8
|
+
lines.push(`Read: ${access.read_paths.join(", ")}`);
|
|
9
|
+
}
|
|
10
|
+
if (access.write_paths.length > 0) {
|
|
11
|
+
lines.push(`Write: ${access.write_paths.join(", ")}`);
|
|
12
|
+
}
|
|
13
|
+
if (access.forbidden_patterns && access.forbidden_patterns.length > 0) {
|
|
14
|
+
lines.push(`Forbidden: ${access.forbidden_patterns.join(", ")}`);
|
|
15
|
+
}
|
|
16
|
+
lines.push("Do not read or write files outside these paths.");
|
|
17
|
+
return lines;
|
|
18
|
+
}
|
|
5
19
|
export function renderWorkerPrompt(task) {
|
|
6
20
|
const commandArgv = renderArgv(task);
|
|
7
21
|
if (task.preferred_executor === "agent" && task.audit_results_path) {
|
|
@@ -12,7 +26,7 @@ export function renderWorkerPrompt(task) {
|
|
|
12
26
|
`Read: ${tasksPath}`,
|
|
13
27
|
"Scope: review only the tasks listed in the Read file. Do not add tasks,",
|
|
14
28
|
"edit source files, remediate findings, run unrelated audits, or write result_path.",
|
|
15
|
-
"
|
|
29
|
+
"Use host Read and Grep tools for source inspection. Do not use shell search commands.",
|
|
16
30
|
"For each listed task: read the assigned file_paths under the specified lens,",
|
|
17
31
|
"using targeted reads/searches where they give complete enough evidence without loading unrelated context,",
|
|
18
32
|
"and emit exactly one AuditResult object with:",
|
|
@@ -34,6 +48,9 @@ export function renderWorkerPrompt(task) {
|
|
|
34
48
|
else {
|
|
35
49
|
lines.push("After writing audit_results_path, execute worker_command from task.json exactly.", "The worker command ingests audit_results_path and writes result_path.", `Command: ${commandArgv}`);
|
|
36
50
|
}
|
|
51
|
+
if (task.access) {
|
|
52
|
+
lines.push(...renderAccessSection(task.access));
|
|
53
|
+
}
|
|
37
54
|
return lines.join("\n");
|
|
38
55
|
}
|
|
39
56
|
return [
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { FreshSessionProvider, LaunchFreshSessionInput } from "
|
|
2
|
-
import type { ClaudeCodeConfig } from "../types/sessionConfig.js";
|
|
1
|
+
import type { FreshSessionProvider, LaunchFreshSessionInput, ClaudeCodeConfig, OpenTokenConfig } from "@audit-tools/shared";
|
|
3
2
|
import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
|
|
4
3
|
export declare const ACTIVE_CLAUDE_CODE_SESSION_MESSAGE: string;
|
|
5
4
|
export declare class ClaudeCodeProvider implements FreshSessionProvider {
|
|
6
5
|
name: string;
|
|
7
6
|
private readonly config;
|
|
7
|
+
private readonly opentoken;
|
|
8
8
|
private readonly launchCommand;
|
|
9
|
-
constructor(config?: ClaudeCodeConfig, launchCommand?: typeof spawnLoggedCommand);
|
|
10
|
-
launch(input: LaunchFreshSessionInput): Promise<import("
|
|
9
|
+
constructor(config?: ClaudeCodeConfig, launchCommand?: typeof spawnLoggedCommand, opentoken?: OpenTokenConfig);
|
|
10
|
+
launch(input: LaunchFreshSessionInput): Promise<import("@audit-tools/shared").LaunchFreshSessionResult>;
|
|
11
11
|
}
|
|
@@ -6,10 +6,12 @@ export const ACTIVE_CLAUDE_CODE_SESSION_MESSAGE = "claude-code provider cannot b
|
|
|
6
6
|
export class ClaudeCodeProvider {
|
|
7
7
|
name = "claude-code";
|
|
8
8
|
config;
|
|
9
|
+
opentoken;
|
|
9
10
|
launchCommand;
|
|
10
|
-
constructor(config = {}, launchCommand = spawnLoggedCommand) {
|
|
11
|
+
constructor(config = {}, launchCommand = spawnLoggedCommand, opentoken = {}) {
|
|
11
12
|
this.config = config;
|
|
12
13
|
this.launchCommand = launchCommand;
|
|
14
|
+
this.opentoken = opentoken;
|
|
13
15
|
}
|
|
14
16
|
async launch(input) {
|
|
15
17
|
if (process.env.CLAUDECODE) {
|
|
@@ -17,14 +19,18 @@ export class ClaudeCodeProvider {
|
|
|
17
19
|
}
|
|
18
20
|
const prompt = await readFile(input.promptPath, "utf8");
|
|
19
21
|
const command = this.config.command ?? "claude";
|
|
22
|
+
const promptFlag = this.config.prompt_flag ?? "-p";
|
|
20
23
|
const args = [
|
|
21
|
-
|
|
24
|
+
promptFlag,
|
|
22
25
|
prompt,
|
|
23
26
|
...(this.config.extra_args ?? []),
|
|
24
27
|
...(this.config.dangerously_skip_permissions
|
|
25
28
|
? ["--dangerously-skip-permissions"]
|
|
26
29
|
: []),
|
|
27
30
|
];
|
|
28
|
-
return await this.launchCommand(command, args, input
|
|
31
|
+
return await this.launchCommand(command, args, input, undefined, {
|
|
32
|
+
opentoken: this.opentoken.enabled,
|
|
33
|
+
opentokenCommand: this.opentoken.command,
|
|
34
|
+
});
|
|
29
35
|
}
|
|
30
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { LOCAL_SUBPROCESS_PROVIDER_NAME } from "@audit-tools/shared";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { LOCAL_SUBPROCESS_PROVIDER_NAME } from "@audit-tools/shared";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { FreshSessionProvider } from "
|
|
2
|
-
import type { ResolvedProviderName, SessionConfig } from "../types/sessionConfig.js";
|
|
1
|
+
import type { FreshSessionProvider, ResolvedProviderName, SessionConfig } from "@audit-tools/shared";
|
|
3
2
|
export declare function resolveFreshSessionProviderName(name: string | undefined, sessionConfig?: SessionConfig, options?: {
|
|
4
3
|
env?: NodeJS.ProcessEnv;
|
|
5
4
|
commandExists?: (command: string) => boolean;
|
package/dist/providers/index.js
CHANGED
|
@@ -64,6 +64,7 @@ export function resolveFreshSessionProviderName(name, sessionConfig = {}, option
|
|
|
64
64
|
}
|
|
65
65
|
export function createFreshSessionProvider(name, sessionConfig = {}) {
|
|
66
66
|
const providerName = resolveFreshSessionProviderName(name, sessionConfig);
|
|
67
|
+
const opentoken = sessionConfig.opentoken ?? {};
|
|
67
68
|
if (providerName === "local-subprocess" &&
|
|
68
69
|
(name ?? sessionConfig.provider) === "auto") {
|
|
69
70
|
process.stderr.write("audit-code: auto provider resolved to local-subprocess — no capable agent provider detected. " +
|
|
@@ -77,16 +78,16 @@ export function createFreshSessionProvider(name, sessionConfig = {}) {
|
|
|
77
78
|
if (!sessionConfig.subprocess_template?.command_template?.length) {
|
|
78
79
|
throw new Error("subprocess-template provider requires session-config.json with subprocess_template.command_template.");
|
|
79
80
|
}
|
|
80
|
-
return new SubprocessTemplateProvider(sessionConfig.subprocess_template);
|
|
81
|
+
return new SubprocessTemplateProvider(sessionConfig.subprocess_template, undefined, opentoken);
|
|
81
82
|
case "claude-code":
|
|
82
|
-
return new ClaudeCodeProvider(sessionConfig.claude_code);
|
|
83
|
+
return new ClaudeCodeProvider(sessionConfig.claude_code, undefined, opentoken);
|
|
83
84
|
case "opencode":
|
|
84
|
-
return new OpenCodeProvider(sessionConfig.opencode);
|
|
85
|
+
return new OpenCodeProvider(sessionConfig.opencode, opentoken);
|
|
85
86
|
case "vscode-task":
|
|
86
87
|
if (!sessionConfig.vscode_task?.command_template?.length) {
|
|
87
88
|
throw new Error("vscode-task provider requires session-config.json with vscode_task.command_template.");
|
|
88
89
|
}
|
|
89
|
-
return new VSCodeTaskProvider(sessionConfig.vscode_task);
|
|
90
|
+
return new VSCodeTaskProvider(sessionConfig.vscode_task, opentoken);
|
|
90
91
|
default:
|
|
91
92
|
throw new Error(`Unknown provider: ${providerName}`);
|
|
92
93
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { FreshSessionProvider, LaunchFreshSessionInput } from "
|
|
1
|
+
import type { FreshSessionProvider, LaunchFreshSessionInput } from "@audit-tools/shared";
|
|
2
2
|
import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
|
|
3
3
|
export declare const MISSING_WORKER_COMMAND_MESSAGE = "local-subprocess provider requires task.worker_command.";
|
|
4
4
|
export declare class LocalSubprocessProvider implements FreshSessionProvider {
|
|
5
5
|
name: string;
|
|
6
6
|
private readonly launchCommand;
|
|
7
7
|
constructor(launchCommand?: typeof spawnLoggedCommand);
|
|
8
|
-
launch(input: LaunchFreshSessionInput): Promise<import("
|
|
8
|
+
launch(input: LaunchFreshSessionInput): Promise<import("@audit-tools/shared").LaunchFreshSessionResult>;
|
|
9
9
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readJsonFile } from "
|
|
1
|
+
import { readJsonFile } from "@audit-tools/shared";
|
|
2
2
|
import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
|
|
3
3
|
export const MISSING_WORKER_COMMAND_MESSAGE = "local-subprocess provider requires task.worker_command.";
|
|
4
4
|
export class LocalSubprocessProvider {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { FreshSessionProvider, LaunchFreshSessionInput } from "
|
|
2
|
-
import type { OpenCodeConfig } from "../types/sessionConfig.js";
|
|
1
|
+
import type { FreshSessionProvider, LaunchFreshSessionInput, OpenCodeConfig, OpenTokenConfig } from "@audit-tools/shared";
|
|
3
2
|
export declare class OpenCodeProvider implements FreshSessionProvider {
|
|
4
3
|
name: string;
|
|
5
4
|
private readonly config;
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
private readonly opentoken;
|
|
6
|
+
constructor(config?: OpenCodeConfig, opentoken?: OpenTokenConfig);
|
|
7
|
+
launch(input: LaunchFreshSessionInput): Promise<import("@audit-tools/shared").LaunchFreshSessionResult>;
|
|
8
8
|
}
|
|
@@ -22,14 +22,19 @@ function quoteCmdArg(value) {
|
|
|
22
22
|
export class OpenCodeProvider {
|
|
23
23
|
name = "opencode";
|
|
24
24
|
config;
|
|
25
|
-
|
|
25
|
+
opentoken;
|
|
26
|
+
constructor(config = {}, opentoken = {}) {
|
|
26
27
|
this.config = config;
|
|
28
|
+
this.opentoken = opentoken;
|
|
27
29
|
}
|
|
28
30
|
async launch(input) {
|
|
29
31
|
const prompt = await readFile(input.promptPath, "utf8");
|
|
30
32
|
const baseCommand = this.config.command ?? "opencode";
|
|
31
33
|
const baseArgs = ["run", prompt, ...(this.config.extra_args ?? [])];
|
|
32
34
|
const resolved = resolveOpenCodeSpawnCommand(baseCommand, baseArgs);
|
|
33
|
-
return await spawnLoggedCommand(resolved.command, resolved.args, input
|
|
35
|
+
return await spawnLoggedCommand(resolved.command, resolved.args, input, undefined, {
|
|
36
|
+
opentoken: this.opentoken.enabled,
|
|
37
|
+
opentokenCommand: this.opentoken.command,
|
|
38
|
+
});
|
|
34
39
|
}
|
|
35
40
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { createWriteStream } from "node:fs";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
import type { LaunchFreshSessionInput, LaunchFreshSessionResult } from "
|
|
3
|
+
import type { LaunchFreshSessionInput, LaunchFreshSessionResult } from "@audit-tools/shared";
|
|
4
4
|
interface SpawnLoggedCommandOptions {
|
|
5
5
|
createWriteStream?: typeof createWriteStream;
|
|
6
6
|
spawn?: typeof spawn;
|
|
7
7
|
killGraceMs?: number;
|
|
8
|
+
opentoken?: boolean;
|
|
9
|
+
opentokenCommand?: string;
|
|
8
10
|
}
|
|
9
11
|
export declare function spawnLoggedCommand(command: string, args: string[], input: LaunchFreshSessionInput, env?: Record<string, string>, options?: SpawnLoggedCommandOptions): Promise<LaunchFreshSessionResult>;
|
|
10
12
|
export {};
|
|
@@ -6,6 +6,22 @@ const FORCE_KILL_GRACE_MS = 1_000;
|
|
|
6
6
|
function formatCommand(command, args) {
|
|
7
7
|
return [command, ...args].join(" ");
|
|
8
8
|
}
|
|
9
|
+
function quoteCmdArg(value) {
|
|
10
|
+
if (/^[A-Za-z0-9_./:=@+-]+$/.test(value))
|
|
11
|
+
return value;
|
|
12
|
+
return `"${value.replace(/(["^&|<>%])/g, "^$1")}"`;
|
|
13
|
+
}
|
|
14
|
+
function applyOpenTokenWrap(command, args, opentokenCommand, platform = process.platform) {
|
|
15
|
+
if (platform === "win32") {
|
|
16
|
+
const shell = process.env.ComSpec ?? "cmd.exe";
|
|
17
|
+
const inner = [command, ...args].map(quoteCmdArg).join(" ");
|
|
18
|
+
return {
|
|
19
|
+
command: shell,
|
|
20
|
+
args: ["/d", "/s", "/c", `${opentokenCommand} wrap ${inner}`],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return { command: opentokenCommand, args: ["wrap", command, ...args] };
|
|
24
|
+
}
|
|
9
25
|
// On Windows `command` must be the resolved .cmd / .exe path because `spawn`
|
|
10
26
|
// does not consult PATH for executables without a shell. Callers should use
|
|
11
27
|
// `platformCommand()` (scripts/smoke-packaged-audit-code.mjs) or similar to
|
|
@@ -14,6 +30,11 @@ export async function spawnLoggedCommand(command, args, input, env, options = {}
|
|
|
14
30
|
const openWriteStream = options.createWriteStream ?? createWriteStream;
|
|
15
31
|
const spawnProcess = options.spawn ?? spawn;
|
|
16
32
|
const killGraceMs = options.killGraceMs ?? FORCE_KILL_GRACE_MS;
|
|
33
|
+
if (options.opentoken) {
|
|
34
|
+
const wrapped = applyOpenTokenWrap(command, args, options.opentokenCommand ?? "opentoken");
|
|
35
|
+
command = wrapped.command;
|
|
36
|
+
args = wrapped.args;
|
|
37
|
+
}
|
|
17
38
|
return await new Promise((resolve, reject) => {
|
|
18
39
|
const stdoutLog = openWriteStream(input.stdoutPath, { flags: "a" });
|
|
19
40
|
const stderrLog = openWriteStream(input.stderrPath, { flags: "a" });
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { FreshSessionProvider, LaunchFreshSessionInput } from "
|
|
2
|
-
import type { SubprocessTemplateConfig } from "../types/sessionConfig.js";
|
|
1
|
+
import type { FreshSessionProvider, LaunchFreshSessionInput, SubprocessTemplateConfig, OpenTokenConfig } from "@audit-tools/shared";
|
|
3
2
|
export declare class SubprocessTemplateProvider implements FreshSessionProvider {
|
|
4
3
|
name: string;
|
|
5
4
|
private readonly config;
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
private readonly opentoken;
|
|
6
|
+
constructor(config: SubprocessTemplateConfig, name?: string, opentoken?: OpenTokenConfig);
|
|
7
|
+
launch(input: LaunchFreshSessionInput): Promise<import("@audit-tools/shared").LaunchFreshSessionResult>;
|
|
8
8
|
}
|