open-local-audit 0.63.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +20 -1
- package/dist/cli.js +39 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/workflow-paths.js +3 -2
- package/dist/workflow-paths.js.map +1 -1
- package/dist/workflow-plan.d.ts +47 -0
- package/dist/workflow-plan.js +186 -0
- package/dist/workflow-plan.js.map +1 -0
- package/dist/workflow-preflight.d.ts +5 -0
- package/dist/workflow-preflight.js +27 -19
- package/dist/workflow-preflight.js.map +1 -1
- package/dist/workflow.d.ts +5 -2
- package/dist/workflow.js +369 -38
- package/dist/workflow.js.map +1 -1
- package/docs/architecture/workflow-command.md +7 -2
- package/docs/architecture/workflow-plan.md +229 -0
- package/docs/architecture/workflow-preflight.md +4 -2
- package/docs/architecture/workflow-resume.md +66 -0
- package/package.json +4 -2
|
@@ -104,9 +104,12 @@ export function renderWorkflowPreflightJson(report) {
|
|
|
104
104
|
return `${JSON.stringify(report, null, 2)}\n`;
|
|
105
105
|
}
|
|
106
106
|
export async function runWorkflowPreflight(configPath) {
|
|
107
|
-
return
|
|
107
|
+
return (await runWorkflowPreflightEvaluationWithDependencies(configPath)).report;
|
|
108
108
|
}
|
|
109
109
|
export async function runWorkflowPreflightWithDependencies(configPath, overrides = {}) {
|
|
110
|
+
return (await runWorkflowPreflightEvaluationWithDependencies(configPath, overrides)).report;
|
|
111
|
+
}
|
|
112
|
+
export async function runWorkflowPreflightEvaluationWithDependencies(configPath, overrides = {}) {
|
|
110
113
|
const dependencies = { ...defaultDependencies, ...overrides };
|
|
111
114
|
let config;
|
|
112
115
|
try {
|
|
@@ -117,15 +120,17 @@ export async function runWorkflowPreflightWithDependencies(configPath, overrides
|
|
|
117
120
|
throw error;
|
|
118
121
|
}
|
|
119
122
|
return {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
report: {
|
|
124
|
+
version: 1,
|
|
125
|
+
status: "blocked",
|
|
126
|
+
checks: [
|
|
127
|
+
{
|
|
128
|
+
id: "configuration",
|
|
129
|
+
status: "fail",
|
|
130
|
+
message: "Workflow configuration could not be read or validated"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
129
134
|
};
|
|
130
135
|
}
|
|
131
136
|
const checks = [
|
|
@@ -191,15 +196,18 @@ export async function runWorkflowPreflightWithDependencies(configPath, overrides
|
|
|
191
196
|
...(config.packageReports ? ["packaging"] : [])
|
|
192
197
|
];
|
|
193
198
|
return {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
199
|
+
config,
|
|
200
|
+
report: {
|
|
201
|
+
version: 1,
|
|
202
|
+
status: reportStatus(checks),
|
|
203
|
+
checks,
|
|
204
|
+
provider: config.discovery.provider,
|
|
205
|
+
stages,
|
|
206
|
+
outputs: { outDir: config.outDir, ...config.paths },
|
|
207
|
+
limits: {
|
|
208
|
+
maxCandidates: config.discovery.provider === "google-places" ? config.discovery.limit : null,
|
|
209
|
+
maxAudits: config.discovery.maxAudits ?? null
|
|
210
|
+
}
|
|
203
211
|
}
|
|
204
212
|
};
|
|
205
213
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-preflight.js","sourceRoot":"","sources":["../src/workflow-preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAElE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-preflight.js","sourceRoot":"","sources":["../src/workflow-preflight.ts"],"names":[],"mappings":"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"}
|
package/dist/workflow.d.ts
CHANGED
|
@@ -76,10 +76,13 @@ export interface WorkflowDependencies {
|
|
|
76
76
|
packageReport: (options: Parameters<typeof packageReport>[0]) => Promise<ReportPackResult>;
|
|
77
77
|
resolveGoogleMapsApiKey: typeof resolveGoogleMapsApiKey;
|
|
78
78
|
}
|
|
79
|
+
export interface WorkflowRunOptions {
|
|
80
|
+
resume?: boolean;
|
|
81
|
+
}
|
|
79
82
|
export declare class WorkflowRunError extends Error {
|
|
80
83
|
readonly summary: WorkflowSummary;
|
|
81
84
|
constructor(summary: WorkflowSummary);
|
|
82
85
|
}
|
|
83
86
|
export declare function safeLeadSlug(leadKey: string, companyName: string): string;
|
|
84
|
-
export declare function runWorkflow(configPath: string, dependencies?: Partial<WorkflowDependencies
|
|
85
|
-
export declare function runResolvedWorkflow(config: ResolvedWorkflowConfig, dependencies?: Partial<WorkflowDependencies
|
|
87
|
+
export declare function runWorkflow(configPath: string, dependencies?: Partial<WorkflowDependencies>, options?: WorkflowRunOptions): Promise<WorkflowSummary>;
|
|
88
|
+
export declare function runResolvedWorkflow(config: ResolvedWorkflowConfig, dependencies?: Partial<WorkflowDependencies>, options?: WorkflowRunOptions): Promise<WorkflowSummary>;
|
package/dist/workflow.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { lstat, mkdir, mkdtemp, realpath, rename, rm } from "node:fs/promises";
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { lstat, mkdir, mkdtemp, readFile, realpath, rename, rm } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
4
|
import { runDiscovery } from "./discovery-runner.js";
|
|
5
5
|
import { packageReport } from "./report-pack.js";
|
|
@@ -101,6 +101,323 @@ async function writePrettyJson(path, value) {
|
|
|
101
101
|
async function writeWorkflowSummary(summary) {
|
|
102
102
|
await writePrettyJson(summary.outputs.workflowSummaryJson, summary);
|
|
103
103
|
}
|
|
104
|
+
function stableJson(value) {
|
|
105
|
+
if (Array.isArray(value)) {
|
|
106
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
107
|
+
}
|
|
108
|
+
if (value && typeof value === "object") {
|
|
109
|
+
return `{${Object.entries(value)
|
|
110
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
111
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
|
|
112
|
+
.join(",")}}`;
|
|
113
|
+
}
|
|
114
|
+
return JSON.stringify(value);
|
|
115
|
+
}
|
|
116
|
+
function workflowConfigFingerprint(config) {
|
|
117
|
+
const { paths: _paths, ...effectiveConfig } = config;
|
|
118
|
+
return createHash("sha256").update(stableJson(effectiveConfig)).digest("hex");
|
|
119
|
+
}
|
|
120
|
+
function workflowCheckpointPath(config) {
|
|
121
|
+
return join(config.outDir, "workflow-checkpoint.json");
|
|
122
|
+
}
|
|
123
|
+
function isRegularFile(info) {
|
|
124
|
+
return info.isFile() && !info.isSymbolicLink();
|
|
125
|
+
}
|
|
126
|
+
async function hashManagedFile(path) {
|
|
127
|
+
try {
|
|
128
|
+
const info = await lstat(path);
|
|
129
|
+
if (!isRegularFile(info)) {
|
|
130
|
+
throw new Error("Workflow checkpoint managed artifact must be a regular file");
|
|
131
|
+
}
|
|
132
|
+
return createHash("sha256").update(await readFile(path)).digest("hex");
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function isMissingPath(error) {
|
|
142
|
+
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
143
|
+
}
|
|
144
|
+
function packageSourceIntegrityId(index, fileName) {
|
|
145
|
+
return `package-source-${index}-${fileName}`;
|
|
146
|
+
}
|
|
147
|
+
function isPackageSourceIntegrityId(id, shortlistLeads) {
|
|
148
|
+
const match = /^package-source-(\d+)-(.+)$/.exec(id);
|
|
149
|
+
if (!match) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const index = Number(match[1]);
|
|
153
|
+
return (Number.isSafeInteger(index) &&
|
|
154
|
+
index >= 0 &&
|
|
155
|
+
index < shortlistLeads.length &&
|
|
156
|
+
shortlistLeads[index].reportPath.trim().length > 0 &&
|
|
157
|
+
packageReportFileNames.includes(match[2]));
|
|
158
|
+
}
|
|
159
|
+
async function validateCheckpointPackageSources(config, checkpoint) {
|
|
160
|
+
if (!config.packageReports || checkpoint.summary.stages.packaging.status === "success") {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
for (const [index, lead] of checkpoint.shortlistLeads.entries()) {
|
|
164
|
+
if (!lead.reportPath.trim()) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const source = await resolvePackageInputDir(config.paths.reportsDir, lead.reportPath);
|
|
168
|
+
await validatePackageSourceFiles(source.inputDir, source.realInputDir);
|
|
169
|
+
for (const fileName of packageReportFileNames) {
|
|
170
|
+
const id = packageSourceIntegrityId(index, fileName);
|
|
171
|
+
const expectedHash = checkpoint.integrity[id];
|
|
172
|
+
const actualHash = await hashManagedFile(join(source.inputDir, fileName));
|
|
173
|
+
if ((actualHash && !expectedHash) || (expectedHash && actualHash !== expectedHash)) {
|
|
174
|
+
throw new Error("Workflow checkpoint managed artifacts do not match");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function addPackageSourceIntegrity(config, summary, shortlistLeads, integrity) {
|
|
180
|
+
if (!config.packageReports || summary.stages.shortlist.status !== "success") {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
for (const [index, lead] of shortlistLeads.entries()) {
|
|
184
|
+
if (!lead.reportPath.trim()) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
let source;
|
|
188
|
+
try {
|
|
189
|
+
source = await resolvePackageInputDir(config.paths.reportsDir, lead.reportPath);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (isMissingPath(error)) {
|
|
193
|
+
throw new Error("Checkpoint package source report is missing");
|
|
194
|
+
}
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
await validatePackageSourceFiles(source.inputDir, source.realInputDir);
|
|
198
|
+
for (const fileName of packageReportFileNames) {
|
|
199
|
+
const hash = await hashManagedFile(join(source.inputDir, fileName));
|
|
200
|
+
if (!hash && fileName === "open-local-audit-report.json") {
|
|
201
|
+
throw new Error("Checkpoint package source report is missing");
|
|
202
|
+
}
|
|
203
|
+
if (hash) {
|
|
204
|
+
integrity[packageSourceIntegrityId(index, fileName)] = hash;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function checkpointArtifactPaths(config, summary) {
|
|
210
|
+
const paths = {};
|
|
211
|
+
if (summary.stages.discovery.status === "success") {
|
|
212
|
+
paths.leadsCsv = config.paths.leadsCsv;
|
|
213
|
+
paths.discoverySummaryJson = config.paths.discoverySummaryJson;
|
|
214
|
+
}
|
|
215
|
+
if (summary.stages.shortlist.status === "success") {
|
|
216
|
+
paths.shortlistCsv = config.paths.shortlistCsv;
|
|
217
|
+
paths.shortlistSummaryJson = config.paths.shortlistSummaryJson;
|
|
218
|
+
}
|
|
219
|
+
if (summary.stages.review.status === "success") {
|
|
220
|
+
paths.reviewSummaryJson = config.paths.reviewSummaryJson;
|
|
221
|
+
}
|
|
222
|
+
return paths;
|
|
223
|
+
}
|
|
224
|
+
async function createCheckpoint(config, summary, shortlistLeads) {
|
|
225
|
+
const integrity = {};
|
|
226
|
+
for (const [id, path] of Object.entries(checkpointArtifactPaths(config, summary))) {
|
|
227
|
+
const hash = await hashManagedFile(path);
|
|
228
|
+
if (hash) {
|
|
229
|
+
integrity[id] = hash;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
await addPackageSourceIntegrity(config, summary, shortlistLeads, integrity);
|
|
233
|
+
return {
|
|
234
|
+
version: 1,
|
|
235
|
+
configFingerprint: workflowConfigFingerprint(config),
|
|
236
|
+
summary,
|
|
237
|
+
integrity,
|
|
238
|
+
shortlistLeads
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async function writeWorkflowCheckpoint(config, summary, shortlistLeads) {
|
|
242
|
+
await writePrettyJson(workflowCheckpointPath(config), await createCheckpoint(config, summary, shortlistLeads));
|
|
243
|
+
}
|
|
244
|
+
function isRecord(value) {
|
|
245
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
246
|
+
}
|
|
247
|
+
function hasOnlyKeys(value, keys) {
|
|
248
|
+
return Object.keys(value).every((key) => keys.includes(key));
|
|
249
|
+
}
|
|
250
|
+
function isCount(value) {
|
|
251
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
252
|
+
}
|
|
253
|
+
function isStageSummary(value, keys) {
|
|
254
|
+
return (isRecord(value) &&
|
|
255
|
+
hasOnlyKeys(value, ["status", ...keys]) &&
|
|
256
|
+
typeof value.status === "string" &&
|
|
257
|
+
keys.every((key) => value[key] === undefined || isCount(value[key])));
|
|
258
|
+
}
|
|
259
|
+
function isShortlistLeadSnapshot(value) {
|
|
260
|
+
if (!isRecord(value)) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
const stringKeys = [
|
|
264
|
+
"companyName",
|
|
265
|
+
"website",
|
|
266
|
+
"segment",
|
|
267
|
+
"profile",
|
|
268
|
+
"priority",
|
|
269
|
+
"auditStatus",
|
|
270
|
+
"hasWebsite",
|
|
271
|
+
"source",
|
|
272
|
+
"topFinding",
|
|
273
|
+
"contactConfidence",
|
|
274
|
+
"preferredContactChannel",
|
|
275
|
+
"contactabilityReason",
|
|
276
|
+
"reason",
|
|
277
|
+
"reportPath",
|
|
278
|
+
"leadKey",
|
|
279
|
+
"reviewStatus",
|
|
280
|
+
"reviewReason",
|
|
281
|
+
"lastReviewedAt"
|
|
282
|
+
];
|
|
283
|
+
return (hasOnlyKeys(value, ["rank", ...stringKeys, "score", "opportunityScore"]) &&
|
|
284
|
+
typeof value.rank === "number" &&
|
|
285
|
+
Number.isSafeInteger(value.rank) &&
|
|
286
|
+
value.rank > 0 &&
|
|
287
|
+
stringKeys.every((key) => typeof value[key] === "string") &&
|
|
288
|
+
(value.score === undefined || typeof value.score === "number") &&
|
|
289
|
+
(value.opportunityScore === undefined || typeof value.opportunityScore === "number"));
|
|
290
|
+
}
|
|
291
|
+
function isCompletedPackageEntries(entries, shortlistLeads, packagesDir) {
|
|
292
|
+
if (entries.length !== shortlistLeads.length) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
const slugCounts = new Map();
|
|
296
|
+
return entries.every((entry, index) => {
|
|
297
|
+
if (!isRecord(entry)) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
const lead = shortlistLeads[index];
|
|
301
|
+
if (entry.leadKey !== lead.leadKey || entry.companyName !== lead.companyName) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
if (!lead.reportPath.trim()) {
|
|
305
|
+
return hasOnlyKeys(entry, ["leadKey", "companyName", "status"]) && entry.status === "skipped";
|
|
306
|
+
}
|
|
307
|
+
const slug = nextUniqueSlug(safeLeadSlug(lead.leadKey, lead.companyName), slugCounts);
|
|
308
|
+
return (hasOnlyKeys(entry, ["leadKey", "companyName", "status", "outDir"]) &&
|
|
309
|
+
entry.status === "packaged" &&
|
|
310
|
+
entry.outDir === join(packagesDir, slug));
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
function isWorkflowCheckpoint(value, config) {
|
|
314
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ["version", "configFingerprint", "summary", "integrity", "shortlistLeads"])) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
const checkpoint = value;
|
|
318
|
+
const summary = checkpoint.summary;
|
|
319
|
+
if (!isRecord(summary) || !hasOnlyKeys(summary, ["version", "status", "stages", "outputs", "discoveredLeads", "selectedLeads", "packages"])) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
const stages = summary.stages;
|
|
323
|
+
const packages = summary.packages;
|
|
324
|
+
const integrity = checkpoint.integrity;
|
|
325
|
+
if (!isRecord(stages) ||
|
|
326
|
+
!hasOnlyKeys(stages, ["discovery", "shortlist", "review", "packaging"]) ||
|
|
327
|
+
!isRecord(packages) ||
|
|
328
|
+
!hasOnlyKeys(packages, ["packaged", "skipped", "failed", "entries"]) ||
|
|
329
|
+
!isRecord(integrity) ||
|
|
330
|
+
!isRecord(summary.outputs)) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
const stageSummaries = {
|
|
334
|
+
discovery: isStageSummary(stages.discovery, ["totalCandidates", "suppressedCandidates", "audited"]),
|
|
335
|
+
shortlist: isStageSummary(stages.shortlist, ["totalRows", "suppressedRows", "filteredRows", "selected"]),
|
|
336
|
+
review: isStageSummary(stages.review, ["totalRows", "reviewedRows", "actionableLeads", "staleRows", "invalidReviewedAtRows", "unreviewedRows"]),
|
|
337
|
+
packaging: isStageSummary(stages.packaging, ["packaged", "skipped", "failed"])
|
|
338
|
+
};
|
|
339
|
+
const expectedIntegrity = checkpointArtifactPaths(config, summary);
|
|
340
|
+
const expectedOutputEntries = Object.entries(config.paths);
|
|
341
|
+
const checkpointOutputs = summary.outputs;
|
|
342
|
+
const outputsMatch = hasOnlyKeys(checkpointOutputs, expectedOutputEntries.map(([key]) => key)) && expectedOutputEntries.every(([key, path]) => checkpointOutputs[key] === path);
|
|
343
|
+
const stageStatus = (name) => stages[name].status;
|
|
344
|
+
if (!Array.isArray(checkpoint.shortlistLeads)) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
const shortlistLeads = checkpoint.shortlistLeads;
|
|
348
|
+
const packagesValid = isCount(packages.packaged) &&
|
|
349
|
+
isCount(packages.skipped) &&
|
|
350
|
+
packages.failed === 0 &&
|
|
351
|
+
Array.isArray(packages.entries) &&
|
|
352
|
+
packages.entries.length === packages.packaged + packages.skipped;
|
|
353
|
+
const completedPackagesValid = stageStatus("packaging") !== "success" ||
|
|
354
|
+
packages.entries.filter((entry) => isRecord(entry) && entry.status === "packaged").length === packages.packaged &&
|
|
355
|
+
packages.entries.filter((entry) => isRecord(entry) && entry.status === "skipped").length === packages.skipped &&
|
|
356
|
+
isCompletedPackageEntries(packages.entries, shortlistLeads, config.paths.packagesDir);
|
|
357
|
+
const packagingCountersMatch = stageStatus("packaging") !== "success" ||
|
|
358
|
+
(stages.packaging.packaged === packages.packaged &&
|
|
359
|
+
stages.packaging.skipped === packages.skipped &&
|
|
360
|
+
stages.packaging.failed === packages.failed);
|
|
361
|
+
return (checkpoint.version === 1 &&
|
|
362
|
+
typeof checkpoint.configFingerprint === "string" &&
|
|
363
|
+
/^[a-f0-9]{64}$/.test(checkpoint.configFingerprint) &&
|
|
364
|
+
summary.version === 1 &&
|
|
365
|
+
summary.status === "success" &&
|
|
366
|
+
stageSummaries.discovery &&
|
|
367
|
+
stageSummaries.shortlist &&
|
|
368
|
+
stageSummaries.review &&
|
|
369
|
+
stageSummaries.packaging &&
|
|
370
|
+
outputsMatch &&
|
|
371
|
+
isCount(summary.discoveredLeads) &&
|
|
372
|
+
isCount(summary.selectedLeads) &&
|
|
373
|
+
packagesValid &&
|
|
374
|
+
completedPackagesValid &&
|
|
375
|
+
packagingCountersMatch &&
|
|
376
|
+
stageStatus("discovery") === "success" &&
|
|
377
|
+
(stageStatus("shortlist") === "success" || stageStatus("shortlist") === "not-run") &&
|
|
378
|
+
(config.review ? stageStatus("review") === "success" || stageStatus("review") === "not-run" : stageStatus("review") === "skipped") &&
|
|
379
|
+
(config.packageReports
|
|
380
|
+
? stageStatus("packaging") === "success" || stageStatus("packaging") === "not-run"
|
|
381
|
+
: stageStatus("packaging") === "skipped") &&
|
|
382
|
+
(stageStatus("shortlist") === "success" ||
|
|
383
|
+
(stageStatus("review") !== "success" && stageStatus("packaging") !== "success" && summary.selectedLeads === 0 && shortlistLeads.length === 0)) &&
|
|
384
|
+
(stageStatus("review") !== "success" || stageStatus("shortlist") === "success") &&
|
|
385
|
+
(stageStatus("packaging") !== "success" ||
|
|
386
|
+
(stageStatus("shortlist") === "success" && (!config.review || stageStatus("review") === "success"))) &&
|
|
387
|
+
shortlistLeads.every(isShortlistLeadSnapshot) &&
|
|
388
|
+
(stageStatus("shortlist") !== "success" ||
|
|
389
|
+
(summary.selectedLeads === shortlistLeads.length && stages.shortlist.selected === shortlistLeads.length)) &&
|
|
390
|
+
Object.keys(integrity).every((id) => Object.hasOwn(expectedIntegrity, id) || isPackageSourceIntegrityId(id, shortlistLeads)) &&
|
|
391
|
+
Object.entries(expectedIntegrity).every(([id]) => typeof integrity[id] === "string" && /^[a-f0-9]{64}$/.test(integrity[id])) &&
|
|
392
|
+
Object.entries(integrity).every(([, hash]) => typeof hash === "string" && /^[a-f0-9]{64}$/.test(hash)) &&
|
|
393
|
+
(!config.packageReports ||
|
|
394
|
+
stageStatus("packaging") === "success" ||
|
|
395
|
+
shortlistLeads.every((lead, index) => !lead.reportPath.trim() ||
|
|
396
|
+
typeof integrity[packageSourceIntegrityId(index, "open-local-audit-report.json")] === "string")));
|
|
397
|
+
}
|
|
398
|
+
async function readWorkflowCheckpoint(config) {
|
|
399
|
+
let value;
|
|
400
|
+
try {
|
|
401
|
+
value = JSON.parse(await readFile(workflowCheckpointPath(config), "utf8"));
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
throw new Error("Workflow checkpoint is missing or invalid");
|
|
405
|
+
}
|
|
406
|
+
if (!isWorkflowCheckpoint(value, config)) {
|
|
407
|
+
throw new Error("Workflow checkpoint is missing or invalid");
|
|
408
|
+
}
|
|
409
|
+
if (value.configFingerprint !== workflowConfigFingerprint(config)) {
|
|
410
|
+
throw new Error("Workflow checkpoint does not match the current configuration");
|
|
411
|
+
}
|
|
412
|
+
for (const [id, path] of Object.entries(checkpointArtifactPaths(config, value.summary))) {
|
|
413
|
+
const expectedHash = value.integrity[id];
|
|
414
|
+
if (!expectedHash || (await hashManagedFile(path)) !== expectedHash) {
|
|
415
|
+
throw new Error("Workflow checkpoint managed artifacts do not match");
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
await validateCheckpointPackageSources(config, value);
|
|
419
|
+
return value;
|
|
420
|
+
}
|
|
104
421
|
async function throwPersistedWorkflowFailure(summary) {
|
|
105
422
|
try {
|
|
106
423
|
await writeWorkflowSummary(summary);
|
|
@@ -236,63 +553,76 @@ function nextUniqueSlug(baseSlug, counts) {
|
|
|
236
553
|
counts.set(baseSlug, nextCount);
|
|
237
554
|
return nextCount === 1 ? baseSlug : `${baseSlug}-${nextCount}`;
|
|
238
555
|
}
|
|
239
|
-
export async function runWorkflow(configPath, dependencies = {}) {
|
|
556
|
+
export async function runWorkflow(configPath, dependencies = {}, options = {}) {
|
|
240
557
|
const resolvedDependencies = {
|
|
241
558
|
...defaultDependencies,
|
|
242
559
|
...dependencies
|
|
243
560
|
};
|
|
244
561
|
const config = await resolvedDependencies.readWorkflowConfig(configPath);
|
|
245
|
-
return await runResolvedWorkflow(config, resolvedDependencies);
|
|
562
|
+
return await runResolvedWorkflow(config, resolvedDependencies, options);
|
|
246
563
|
}
|
|
247
|
-
export async function runResolvedWorkflow(config, dependencies = {}) {
|
|
564
|
+
export async function runResolvedWorkflow(config, dependencies = {}, options = {}) {
|
|
248
565
|
const resolvedDependencies = {
|
|
249
566
|
...defaultDependencies,
|
|
250
567
|
...dependencies
|
|
251
568
|
};
|
|
569
|
+
const checkpoint = options.resume ? await readWorkflowCheckpoint(config) : undefined;
|
|
252
570
|
await prepareWorkflowManagedDirectories(config);
|
|
253
|
-
const summary =
|
|
571
|
+
const summary = checkpoint
|
|
572
|
+
? { ...checkpoint.summary, outputs: config.paths, error: undefined, status: "success" }
|
|
573
|
+
: createInitialSummary(config);
|
|
574
|
+
let shortlistLeads = checkpoint?.shortlistLeads ?? [];
|
|
254
575
|
const knownSecrets = [];
|
|
255
576
|
try {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
577
|
+
if (summary.stages.discovery.status !== "success") {
|
|
578
|
+
const googleApiKey = config.discovery.provider === "google-places" ? resolvedDependencies.resolveGoogleMapsApiKey() : undefined;
|
|
579
|
+
if (googleApiKey) {
|
|
580
|
+
knownSecrets.push(googleApiKey);
|
|
581
|
+
}
|
|
582
|
+
const discoveryResult = await resolvedDependencies.runDiscovery({
|
|
583
|
+
provider: config.discovery.provider,
|
|
584
|
+
...(config.discovery.provider === "manual-csv" ? { input: config.discovery.input } : {}),
|
|
585
|
+
...(config.discovery.provider === "google-places" ? { query: config.discovery.query } : {}),
|
|
586
|
+
profile: config.discovery.profile,
|
|
587
|
+
outDir: config.paths.reportsDir,
|
|
588
|
+
managedOutputRoot: config.paths.reportsDir,
|
|
589
|
+
exportCsv: config.paths.leadsCsv,
|
|
590
|
+
summaryJson: config.paths.discoverySummaryJson,
|
|
591
|
+
reviewCsv: config.review?.csv,
|
|
592
|
+
dryRun: false,
|
|
593
|
+
concurrency: config.discovery.concurrency,
|
|
594
|
+
...(config.discovery.maxAudits !== undefined ? { maxAudits: config.discovery.maxAudits } : {}),
|
|
595
|
+
...(config.discovery.provider === "google-places" ? { limit: config.discovery.limit } : {}),
|
|
596
|
+
...(googleApiKey !== undefined ? { apiKey: googleApiKey } : {})
|
|
597
|
+
});
|
|
598
|
+
updateDiscoveryStage(summary, discoveryResult);
|
|
599
|
+
await writeWorkflowCheckpoint(config, summary, shortlistLeads);
|
|
259
600
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
...(googleApiKey !== undefined ? { apiKey: googleApiKey } : {})
|
|
275
|
-
});
|
|
276
|
-
updateDiscoveryStage(summary, discoveryResult);
|
|
277
|
-
const shortlistResult = await resolvedDependencies.runShortlistReport({
|
|
278
|
-
input: config.paths.leadsCsv,
|
|
279
|
-
out: config.paths.shortlistCsv,
|
|
280
|
-
summaryJson: config.paths.shortlistSummaryJson,
|
|
281
|
-
reviewCsv: config.review?.csv,
|
|
282
|
-
format: "csv",
|
|
283
|
-
shortlist: config.shortlist
|
|
284
|
-
});
|
|
285
|
-
updateShortlistStage(summary, shortlistResult);
|
|
286
|
-
if (config.review) {
|
|
601
|
+
if (summary.stages.shortlist.status !== "success") {
|
|
602
|
+
const shortlistResult = await resolvedDependencies.runShortlistReport({
|
|
603
|
+
input: config.paths.leadsCsv,
|
|
604
|
+
out: config.paths.shortlistCsv,
|
|
605
|
+
summaryJson: config.paths.shortlistSummaryJson,
|
|
606
|
+
reviewCsv: config.review?.csv,
|
|
607
|
+
format: "csv",
|
|
608
|
+
shortlist: config.shortlist
|
|
609
|
+
});
|
|
610
|
+
updateShortlistStage(summary, shortlistResult);
|
|
611
|
+
shortlistLeads = shortlistResult.leads;
|
|
612
|
+
await writeWorkflowCheckpoint(config, summary, shortlistLeads);
|
|
613
|
+
}
|
|
614
|
+
if (config.review && summary.stages.review.status !== "success") {
|
|
287
615
|
const reviewSummary = await resolvedDependencies.summarizeReviewCsvFile(config.review.csv, {
|
|
288
616
|
staleBefore: config.review.staleBefore
|
|
289
617
|
});
|
|
290
618
|
await writePrettyJson(config.paths.reviewSummaryJson, reviewSummary);
|
|
291
619
|
updateReviewStage(summary, reviewSummary);
|
|
620
|
+
await writeWorkflowCheckpoint(config, summary, shortlistLeads);
|
|
292
621
|
}
|
|
293
|
-
if (config.packageReports) {
|
|
622
|
+
if (config.packageReports && summary.stages.packaging.status !== "success") {
|
|
623
|
+
summary.packages = { packaged: 0, skipped: 0, failed: 0, entries: [] };
|
|
294
624
|
const slugCounts = new Map();
|
|
295
|
-
for (const lead of
|
|
625
|
+
for (const lead of shortlistLeads) {
|
|
296
626
|
const packageEntry = await packageLead(config.paths, lead, slugCounts, resolvedDependencies.packageReport, knownSecrets);
|
|
297
627
|
summary.packages.entries.push(packageEntry);
|
|
298
628
|
if (packageEntry.status === "packaged") {
|
|
@@ -314,6 +644,7 @@ export async function runResolvedWorkflow(config, dependencies = {}) {
|
|
|
314
644
|
};
|
|
315
645
|
await throwPersistedWorkflowFailure(summary);
|
|
316
646
|
}
|
|
647
|
+
await writeWorkflowCheckpoint(config, summary, shortlistLeads);
|
|
317
648
|
}
|
|
318
649
|
}
|
|
319
650
|
catch (error) {
|