sentinelayer-cli 0.8.11 → 0.8.12
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/package.json +4 -4
- package/src/agents/jules/stream.js +2 -12
- package/src/audit/orchestrator.js +471 -114
- package/src/audit/persona-loop.js +1342 -0
- package/src/audit/registry.js +58 -2
- package/src/commands/audit.js +42 -1
- package/src/commands/legacy-args.js +28 -1
- package/src/commands/session.js +80 -20
- package/src/cost/history.js +41 -21
- package/src/events/schema.js +27 -1
- package/src/legacy-cli.js +76 -1
- package/src/review/omargate-cache.js +285 -0
- package/src/review/omargate-orchestrator.js +586 -3
- package/src/review/report.js +128 -2
- package/src/session/senti-naming.js +36 -0
- package/src/session/sync.js +23 -0
package/src/legacy-cli.js
CHANGED
|
@@ -1237,7 +1237,10 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1237
1237
|
deterministic: {
|
|
1238
1238
|
summary: detSummary,
|
|
1239
1239
|
findings: detFindings,
|
|
1240
|
+
scope: deterministic.scope || {},
|
|
1241
|
+
layers: deterministic.layers || {},
|
|
1240
1242
|
metadata: deterministic.metadata || {},
|
|
1243
|
+
artifacts: deterministic.artifacts || {},
|
|
1241
1244
|
},
|
|
1242
1245
|
onEvent: streamHandler,
|
|
1243
1246
|
includeOnly: includeOnly.length > 0 ? includeOnly : null,
|
|
@@ -1331,6 +1334,7 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1331
1334
|
const combinedP1 = combinedSummary.P1 || 0;
|
|
1332
1335
|
const combinedP2 = combinedSummary.P2 || 0;
|
|
1333
1336
|
const combinedP3 = combinedSummary.P3 || 0;
|
|
1337
|
+
const omargateRunId = orchestratorResult?.runId || deterministic.runId;
|
|
1334
1338
|
|
|
1335
1339
|
// Write per-phase artifacts alongside REVIEW_DETERMINISTIC so post-mortems
|
|
1336
1340
|
// can inspect exactly what each layer contributed.
|
|
@@ -1376,6 +1380,7 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1376
1380
|
const report = `# Local Omar Gate Deep Scan
|
|
1377
1381
|
|
|
1378
1382
|
Generated: ${nowIso()}
|
|
1383
|
+
Run ID: ${omargateRunId}
|
|
1379
1384
|
Target: ${targetPath}
|
|
1380
1385
|
Elapsed: ${totalElapsed}
|
|
1381
1386
|
|
|
@@ -1395,12 +1400,24 @@ ${formatFindingsMarkdown(allFindings)}
|
|
|
1395
1400
|
const reportPath = await writeLocalCommandReport(targetPath, "omargate-deep", report, {
|
|
1396
1401
|
outputDir: outputDirArg,
|
|
1397
1402
|
});
|
|
1403
|
+
const { writeOmarGateDeterministicCache } = await import("./review/omargate-cache.js");
|
|
1404
|
+
const deterministicCache = await writeOmarGateDeterministicCache({
|
|
1405
|
+
targetPath,
|
|
1406
|
+
outputDir: outputDirArg,
|
|
1407
|
+
runId: omargateRunId,
|
|
1408
|
+
deterministic,
|
|
1409
|
+
reportPath,
|
|
1410
|
+
});
|
|
1411
|
+
artifactPaths.deterministicCache = deterministicCache.artifactPath;
|
|
1412
|
+
artifactPaths.latestOmarGate = deterministicCache.latestPath;
|
|
1413
|
+
|
|
1398
1414
|
if (asJson) {
|
|
1399
1415
|
console.log(
|
|
1400
1416
|
JSON.stringify(
|
|
1401
1417
|
{
|
|
1402
1418
|
command: "/omargate deep",
|
|
1403
1419
|
targetPath,
|
|
1420
|
+
runId: omargateRunId,
|
|
1404
1421
|
reportPath,
|
|
1405
1422
|
scannedFiles,
|
|
1406
1423
|
p0: combinedP0,
|
|
@@ -1457,6 +1474,7 @@ async function runLocalAuditCommand(args) {
|
|
|
1457
1474
|
const asJson = hasCommandOption(args, "--json");
|
|
1458
1475
|
const pathArg = getCommandOptionValue(args, "--path") || ".";
|
|
1459
1476
|
const outputDirArg = getCommandOptionValue(args, "--output-dir") || "";
|
|
1477
|
+
const reuseOmarGate = getCommandOptionValue(args, "--reuse-omargate") || "";
|
|
1460
1478
|
const targetPath = path.resolve(process.cwd(), pathArg);
|
|
1461
1479
|
if (!fs.existsSync(targetPath) || !fs.statSync(targetPath).isDirectory()) {
|
|
1462
1480
|
throw new Error(`Invalid --path target: ${targetPath}`);
|
|
@@ -1467,6 +1485,16 @@ async function runLocalAuditCommand(args) {
|
|
|
1467
1485
|
printInfo(`Target: ${targetPath}`);
|
|
1468
1486
|
}
|
|
1469
1487
|
|
|
1488
|
+
const buildScanFromOmarGateCache = (cache) => {
|
|
1489
|
+
const findings = Array.isArray(cache?.findings) ? cache.findings : [];
|
|
1490
|
+
return {
|
|
1491
|
+
scannedFiles: Number(cache?.scope?.scannedFiles || findings.length || 0),
|
|
1492
|
+
findings,
|
|
1493
|
+
p1: findings.filter((item) => item.severity === "P1").length,
|
|
1494
|
+
p2: findings.filter((item) => item.severity === "P2").length,
|
|
1495
|
+
};
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1470
1498
|
const requiredChecks = [
|
|
1471
1499
|
{
|
|
1472
1500
|
key: ".github/workflows/omar-gate.yml",
|
|
@@ -1488,7 +1516,44 @@ async function runLocalAuditCommand(args) {
|
|
|
1488
1516
|
},
|
|
1489
1517
|
];
|
|
1490
1518
|
|
|
1491
|
-
|
|
1519
|
+
let omargateReuse = {
|
|
1520
|
+
requested: reuseOmarGate || "",
|
|
1521
|
+
used: false,
|
|
1522
|
+
runId: "",
|
|
1523
|
+
artifactPath: "",
|
|
1524
|
+
reason: reuseOmarGate ? "not_found" : "not_requested",
|
|
1525
|
+
};
|
|
1526
|
+
let scan = null;
|
|
1527
|
+
if (reuseOmarGate) {
|
|
1528
|
+
const { loadOmarGateDeterministicCache } = await import("./review/omargate-cache.js");
|
|
1529
|
+
const reused = await loadOmarGateDeterministicCache({
|
|
1530
|
+
targetPath,
|
|
1531
|
+
outputDir: outputDirArg,
|
|
1532
|
+
runIdOrLatest: reuseOmarGate,
|
|
1533
|
+
});
|
|
1534
|
+
if (reused.found) {
|
|
1535
|
+
scan = buildScanFromOmarGateCache(reused.cache);
|
|
1536
|
+
omargateReuse = {
|
|
1537
|
+
requested: reuseOmarGate,
|
|
1538
|
+
used: true,
|
|
1539
|
+
runId: reused.runId,
|
|
1540
|
+
deterministicRunId: reused.cache?.deterministicRunId || "",
|
|
1541
|
+
artifactPath: reused.artifactPath,
|
|
1542
|
+
reason: "",
|
|
1543
|
+
};
|
|
1544
|
+
} else {
|
|
1545
|
+
omargateReuse = {
|
|
1546
|
+
requested: reuseOmarGate,
|
|
1547
|
+
used: false,
|
|
1548
|
+
runId: "",
|
|
1549
|
+
artifactPath: "",
|
|
1550
|
+
reason: reused.reason || "not_found",
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
if (!scan) {
|
|
1555
|
+
scan = await runCredentialScan(targetPath);
|
|
1556
|
+
}
|
|
1492
1557
|
const failedP1Checks = requiredChecks.filter((item) => !item.ok && item.severity === "P1").length;
|
|
1493
1558
|
const failedP2Checks = requiredChecks.filter((item) => !item.ok && item.severity === "P2").length;
|
|
1494
1559
|
const totalP1 = scan.p1 + failedP1Checks;
|
|
@@ -1506,11 +1571,13 @@ async function runLocalAuditCommand(args) {
|
|
|
1506
1571
|
Generated: ${nowIso()}
|
|
1507
1572
|
Target: ${targetPath}
|
|
1508
1573
|
Overall status: ${overallStatus}
|
|
1574
|
+
OmarGate reuse: ${omargateReuse.used ? `yes (${omargateReuse.runId})` : omargateReuse.requested ? `requested ${omargateReuse.requested} (${omargateReuse.reason})` : "no"}
|
|
1509
1575
|
|
|
1510
1576
|
Readiness checks:
|
|
1511
1577
|
${checkText}
|
|
1512
1578
|
|
|
1513
1579
|
Scan summary:
|
|
1580
|
+
- Reused OmarGate run: ${omargateReuse.used ? omargateReuse.runId : "n/a"}
|
|
1514
1581
|
- Files scanned: ${scan.scannedFiles}
|
|
1515
1582
|
- P1 findings: ${scan.p1}
|
|
1516
1583
|
- P2 findings: ${scan.p2}
|
|
@@ -1536,6 +1603,9 @@ ${formatFindingsMarkdown(scan.findings)}
|
|
|
1536
1603
|
p1Total: totalP1,
|
|
1537
1604
|
p2Total: totalP2,
|
|
1538
1605
|
blocking: totalP1 > 0,
|
|
1606
|
+
omargateReuse,
|
|
1607
|
+
reusedOmarGateRunId: omargateReuse.used ? omargateReuse.runId : "",
|
|
1608
|
+
reusedOmarGateDeterministicPath: omargateReuse.used ? omargateReuse.artifactPath : "",
|
|
1539
1609
|
},
|
|
1540
1610
|
null,
|
|
1541
1611
|
2
|
|
@@ -1543,6 +1613,11 @@ ${formatFindingsMarkdown(scan.findings)}
|
|
|
1543
1613
|
);
|
|
1544
1614
|
} else {
|
|
1545
1615
|
console.log(pc.cyan(`Report: ${reportPath}`));
|
|
1616
|
+
if (omargateReuse.used) {
|
|
1617
|
+
console.log(pc.gray(`Reused OmarGate run: ${omargateReuse.runId}`));
|
|
1618
|
+
} else if (omargateReuse.requested) {
|
|
1619
|
+
console.log(pc.gray(`OmarGate reuse unavailable: ${omargateReuse.reason}`));
|
|
1620
|
+
}
|
|
1546
1621
|
console.log(`Overall status: ${overallStatus}`);
|
|
1547
1622
|
console.log(`P1 total: ${totalP1}`);
|
|
1548
1623
|
console.log(`P2 total: ${totalP2}`);
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import fsp from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { resolveOutputRoot } from "../config/service.js";
|
|
5
|
+
|
|
6
|
+
const CACHE_SCHEMA_VERSION = "1.0.0";
|
|
7
|
+
const CACHE_KIND = "omargate-deterministic-cache";
|
|
8
|
+
const LATEST_INDEX_NAME = "latest-omargate.json";
|
|
9
|
+
|
|
10
|
+
function normalizeString(value) {
|
|
11
|
+
return String(value || "").trim();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizeTargetPath(value) {
|
|
15
|
+
return path.resolve(String(value || "."));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeSummary(value = {}) {
|
|
19
|
+
const summary = value && typeof value === "object" ? value : {};
|
|
20
|
+
const P0 = Math.max(0, Math.floor(Number(summary.P0 || 0)));
|
|
21
|
+
const P1 = Math.max(0, Math.floor(Number(summary.P1 || 0)));
|
|
22
|
+
const P2 = Math.max(0, Math.floor(Number(summary.P2 || 0)));
|
|
23
|
+
const P3 = Math.max(0, Math.floor(Number(summary.P3 || 0)));
|
|
24
|
+
return {
|
|
25
|
+
P0,
|
|
26
|
+
P1,
|
|
27
|
+
P2,
|
|
28
|
+
P3,
|
|
29
|
+
blocking: summary.blocking === undefined ? P0 > 0 || P1 > 0 : Boolean(summary.blocking),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function sanitizeRunId(value) {
|
|
34
|
+
const normalized = normalizeString(value);
|
|
35
|
+
if (!normalized) {
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
return normalized.replace(/[^A-Za-z0-9._-]/g, "-").replace(/^-+|-+$/g, "");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isSafeRequestedRunId(requested, sanitized) {
|
|
42
|
+
return Boolean(requested) && requested === sanitized && sanitized !== "." && sanitized !== "..";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function deterministicCachePath(outputRoot, runId) {
|
|
46
|
+
return path.join(outputRoot, "runs", runId, "deterministic.json");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function readJsonFile(filePath) {
|
|
50
|
+
const content = await fsp.readFile(filePath, "utf-8");
|
|
51
|
+
return JSON.parse(content);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isCacheForTarget(cache, normalizedTargetPath) {
|
|
55
|
+
const cacheTarget = normalizeString(cache?.targetPath);
|
|
56
|
+
if (!cacheTarget) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return path.resolve(cacheTarget) === normalizedTargetPath;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildMissingResult({ outputRoot, requested = "latest", reason = "not_found" } = {}) {
|
|
63
|
+
return {
|
|
64
|
+
found: false,
|
|
65
|
+
requested,
|
|
66
|
+
reason,
|
|
67
|
+
outputRoot,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function loadCacheFile({ filePath, outputRoot, requested, normalizedTargetPath }) {
|
|
72
|
+
let cache;
|
|
73
|
+
try {
|
|
74
|
+
cache = await readJsonFile(filePath);
|
|
75
|
+
} catch {
|
|
76
|
+
return buildMissingResult({
|
|
77
|
+
outputRoot,
|
|
78
|
+
requested,
|
|
79
|
+
reason: "malformed_or_missing_cache",
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (cache?.kind !== CACHE_KIND) {
|
|
84
|
+
return buildMissingResult({
|
|
85
|
+
outputRoot,
|
|
86
|
+
requested,
|
|
87
|
+
reason: "invalid_cache_kind",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (!isCacheForTarget(cache, normalizedTargetPath)) {
|
|
91
|
+
return buildMissingResult({
|
|
92
|
+
outputRoot,
|
|
93
|
+
requested,
|
|
94
|
+
reason: "target_mismatch",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
found: true,
|
|
100
|
+
requested,
|
|
101
|
+
runId: normalizeString(cache.runId),
|
|
102
|
+
artifactPath: filePath,
|
|
103
|
+
outputRoot,
|
|
104
|
+
cache,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function loadLatestFromIndex({ outputRoot, normalizedTargetPath }) {
|
|
109
|
+
const latestPath = path.join(outputRoot, "runs", LATEST_INDEX_NAME);
|
|
110
|
+
let index;
|
|
111
|
+
try {
|
|
112
|
+
index = await readJsonFile(latestPath);
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const runId = sanitizeRunId(index?.runId);
|
|
118
|
+
if (!runId || !isCacheForTarget(index, normalizedTargetPath)) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const artifactPath = normalizeString(index.artifactPath) || deterministicCachePath(outputRoot, runId);
|
|
122
|
+
const loaded = await loadCacheFile({
|
|
123
|
+
filePath: artifactPath,
|
|
124
|
+
outputRoot,
|
|
125
|
+
requested: "latest",
|
|
126
|
+
normalizedTargetPath,
|
|
127
|
+
});
|
|
128
|
+
return loaded.found ? loaded : null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function loadLatestByScanning({ outputRoot, normalizedTargetPath }) {
|
|
132
|
+
const runsDir = path.join(outputRoot, "runs");
|
|
133
|
+
let entries = [];
|
|
134
|
+
try {
|
|
135
|
+
entries = await fsp.readdir(runsDir, { withFileTypes: true });
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const candidates = [];
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
if (!entry.isDirectory()) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const runId = sanitizeRunId(entry.name);
|
|
146
|
+
if (!isSafeRequestedRunId(entry.name, runId)) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const artifactPath = deterministicCachePath(outputRoot, runId);
|
|
150
|
+
try {
|
|
151
|
+
const stat = await fsp.stat(artifactPath);
|
|
152
|
+
candidates.push({ runId, artifactPath, mtimeMs: Number(stat.mtimeMs || 0) });
|
|
153
|
+
} catch {
|
|
154
|
+
// Ignore incomplete run directories.
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
candidates.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
|
159
|
+
for (const candidate of candidates) {
|
|
160
|
+
const loaded = await loadCacheFile({
|
|
161
|
+
filePath: candidate.artifactPath,
|
|
162
|
+
outputRoot,
|
|
163
|
+
requested: "latest",
|
|
164
|
+
normalizedTargetPath,
|
|
165
|
+
});
|
|
166
|
+
if (loaded.found) {
|
|
167
|
+
return loaded;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export async function writeOmarGateDeterministicCache({
|
|
174
|
+
targetPath,
|
|
175
|
+
outputDir = "",
|
|
176
|
+
runId,
|
|
177
|
+
deterministic = {},
|
|
178
|
+
reportPath = "",
|
|
179
|
+
} = {}) {
|
|
180
|
+
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
181
|
+
const outputRoot = await resolveOutputRoot({
|
|
182
|
+
cwd: normalizedTargetPath,
|
|
183
|
+
outputDirOverride: outputDir,
|
|
184
|
+
env: process.env,
|
|
185
|
+
});
|
|
186
|
+
const normalizedRunId = sanitizeRunId(runId || deterministic?.runId);
|
|
187
|
+
if (!normalizedRunId) {
|
|
188
|
+
throw new Error("OmarGate deterministic cache requires a runId.");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const runDirectory = path.join(outputRoot, "runs", normalizedRunId);
|
|
192
|
+
const artifactPath = path.join(runDirectory, "deterministic.json");
|
|
193
|
+
const latestPath = path.join(outputRoot, "runs", LATEST_INDEX_NAME);
|
|
194
|
+
await fsp.mkdir(runDirectory, { recursive: true });
|
|
195
|
+
|
|
196
|
+
const cache = {
|
|
197
|
+
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
198
|
+
kind: CACHE_KIND,
|
|
199
|
+
runId: normalizedRunId,
|
|
200
|
+
targetPath: normalizedTargetPath,
|
|
201
|
+
generatedAt: new Date().toISOString(),
|
|
202
|
+
deterministicRunId: normalizeString(deterministic?.runId),
|
|
203
|
+
mode: normalizeString(deterministic?.mode) || "full",
|
|
204
|
+
summary: normalizeSummary(deterministic?.summary),
|
|
205
|
+
findings: Array.isArray(deterministic?.findings) ? deterministic.findings : [],
|
|
206
|
+
scope: deterministic?.scope && typeof deterministic.scope === "object" ? deterministic.scope : {},
|
|
207
|
+
layers: deterministic?.layers && typeof deterministic.layers === "object" ? deterministic.layers : {},
|
|
208
|
+
metadata: deterministic?.metadata && typeof deterministic.metadata === "object" ? deterministic.metadata : {},
|
|
209
|
+
artifacts: deterministic?.artifacts && typeof deterministic.artifacts === "object" ? deterministic.artifacts : {},
|
|
210
|
+
source: {
|
|
211
|
+
command: "/omargate deep",
|
|
212
|
+
reportPath: normalizeString(reportPath),
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
await fsp.writeFile(artifactPath, `${JSON.stringify(cache, null, 2)}\n`, "utf-8");
|
|
216
|
+
await fsp.writeFile(
|
|
217
|
+
latestPath,
|
|
218
|
+
`${JSON.stringify(
|
|
219
|
+
{
|
|
220
|
+
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
221
|
+
kind: "omargate-latest-index",
|
|
222
|
+
runId: normalizedRunId,
|
|
223
|
+
targetPath: normalizedTargetPath,
|
|
224
|
+
artifactPath,
|
|
225
|
+
updatedAt: cache.generatedAt,
|
|
226
|
+
},
|
|
227
|
+
null,
|
|
228
|
+
2
|
|
229
|
+
)}\n`,
|
|
230
|
+
"utf-8"
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
runId: normalizedRunId,
|
|
235
|
+
outputRoot,
|
|
236
|
+
runDirectory,
|
|
237
|
+
artifactPath,
|
|
238
|
+
latestPath,
|
|
239
|
+
cache,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export async function loadOmarGateDeterministicCache({
|
|
244
|
+
targetPath,
|
|
245
|
+
outputDir = "",
|
|
246
|
+
runIdOrLatest = "latest",
|
|
247
|
+
} = {}) {
|
|
248
|
+
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
249
|
+
const outputRoot = await resolveOutputRoot({
|
|
250
|
+
cwd: normalizedTargetPath,
|
|
251
|
+
outputDirOverride: outputDir,
|
|
252
|
+
env: process.env,
|
|
253
|
+
});
|
|
254
|
+
const requested = normalizeString(runIdOrLatest) || "latest";
|
|
255
|
+
|
|
256
|
+
if (requested.toLowerCase() === "latest") {
|
|
257
|
+
const latestFromIndex = await loadLatestFromIndex({
|
|
258
|
+
outputRoot,
|
|
259
|
+
normalizedTargetPath,
|
|
260
|
+
});
|
|
261
|
+
if (latestFromIndex) {
|
|
262
|
+
return latestFromIndex;
|
|
263
|
+
}
|
|
264
|
+
const latestFromScan = await loadLatestByScanning({
|
|
265
|
+
outputRoot,
|
|
266
|
+
normalizedTargetPath,
|
|
267
|
+
});
|
|
268
|
+
return latestFromScan || buildMissingResult({ outputRoot, requested, reason: "not_found" });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const runId = sanitizeRunId(requested);
|
|
272
|
+
if (!isSafeRequestedRunId(requested, runId)) {
|
|
273
|
+
return buildMissingResult({
|
|
274
|
+
outputRoot,
|
|
275
|
+
requested,
|
|
276
|
+
reason: "invalid_run_id",
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return loadCacheFile({
|
|
280
|
+
filePath: deterministicCachePath(outputRoot, runId),
|
|
281
|
+
outputRoot,
|
|
282
|
+
requested,
|
|
283
|
+
normalizedTargetPath,
|
|
284
|
+
});
|
|
285
|
+
}
|