open-local-audit 0.61.0 → 0.63.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 +12 -0
- package/README.md +42 -0
- package/dist/batch.d.ts +1 -0
- package/dist/batch.js +9 -3
- package/dist/batch.js.map +1 -1
- package/dist/cli.js +120 -167
- package/dist/cli.js.map +1 -1
- package/dist/discovery-runner.d.ts +28 -0
- package/dist/discovery-runner.js +136 -0
- package/dist/discovery-runner.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/output.d.ts +1 -0
- package/dist/output.js +20 -3
- package/dist/output.js.map +1 -1
- package/dist/shortlist-runner.d.ts +10 -0
- package/dist/shortlist-runner.js +25 -0
- package/dist/shortlist-runner.js.map +1 -0
- package/dist/workflow-config.d.ts +151 -0
- package/dist/workflow-config.js +104 -0
- package/dist/workflow-config.js.map +1 -0
- package/dist/workflow-output.d.ts +5 -0
- package/dist/workflow-output.js +70 -0
- package/dist/workflow-output.js.map +1 -0
- package/dist/workflow-paths.d.ts +19 -0
- package/dist/workflow-paths.js +111 -0
- package/dist/workflow-paths.js.map +1 -0
- package/dist/workflow-preflight.d.ts +38 -0
- package/dist/workflow-preflight.js +206 -0
- package/dist/workflow-preflight.js.map +1 -0
- package/dist/workflow.d.ts +85 -0
- package/dist/workflow.js +382 -0
- package/dist/workflow.js.map +1 -0
- package/docs/architecture/workflow-command.md +106 -0
- package/docs/architecture/workflow-preflight.md +131 -0
- package/package.json +4 -2
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { runDiscovery, type DiscoveryRunResult } from "./discovery-runner.js";
|
|
2
|
+
import { packageReport, type ReportPackResult } from "./report-pack.js";
|
|
3
|
+
import { summarizeReviewCsvFile } from "./review.js";
|
|
4
|
+
import { type ShortlistRunOptions } from "./shortlist-runner.js";
|
|
5
|
+
import type { ShortlistResult } from "./shortlist.js";
|
|
6
|
+
import { resolveGoogleMapsApiKey } from "./secrets.js";
|
|
7
|
+
import { readWorkflowConfig, type ResolvedWorkflowConfig, type WorkflowManagedPaths } from "./workflow-config.js";
|
|
8
|
+
export type WorkflowStatus = "success" | "failed";
|
|
9
|
+
export type WorkflowStageStatus = "success" | "failed" | "skipped" | "not-run";
|
|
10
|
+
export type WorkflowStageName = "discovery" | "shortlist" | "review" | "packaging";
|
|
11
|
+
export type WorkflowPackageStatus = "packaged" | "skipped" | "failed";
|
|
12
|
+
export interface WorkflowDiscoveryStageSummary {
|
|
13
|
+
status: WorkflowStageStatus;
|
|
14
|
+
totalCandidates?: number;
|
|
15
|
+
suppressedCandidates?: number;
|
|
16
|
+
audited?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface WorkflowShortlistStageSummary {
|
|
19
|
+
status: WorkflowStageStatus;
|
|
20
|
+
totalRows?: number;
|
|
21
|
+
suppressedRows?: number;
|
|
22
|
+
filteredRows?: number;
|
|
23
|
+
selected?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface WorkflowReviewStageSummary {
|
|
26
|
+
status: WorkflowStageStatus;
|
|
27
|
+
totalRows?: number;
|
|
28
|
+
reviewedRows?: number;
|
|
29
|
+
actionableLeads?: number;
|
|
30
|
+
staleRows?: number;
|
|
31
|
+
invalidReviewedAtRows?: number;
|
|
32
|
+
unreviewedRows?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface WorkflowPackagingStageSummary {
|
|
35
|
+
status: WorkflowStageStatus;
|
|
36
|
+
packaged?: number;
|
|
37
|
+
skipped?: number;
|
|
38
|
+
failed?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface WorkflowPackageEntry {
|
|
41
|
+
leadKey: string;
|
|
42
|
+
companyName: string;
|
|
43
|
+
status: WorkflowPackageStatus;
|
|
44
|
+
outDir?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface WorkflowPackageSummary {
|
|
48
|
+
packaged: number;
|
|
49
|
+
skipped: number;
|
|
50
|
+
failed: number;
|
|
51
|
+
entries: WorkflowPackageEntry[];
|
|
52
|
+
}
|
|
53
|
+
export interface WorkflowSummary {
|
|
54
|
+
version: 1;
|
|
55
|
+
status: WorkflowStatus;
|
|
56
|
+
stages: {
|
|
57
|
+
discovery: WorkflowDiscoveryStageSummary;
|
|
58
|
+
shortlist: WorkflowShortlistStageSummary;
|
|
59
|
+
review: WorkflowReviewStageSummary;
|
|
60
|
+
packaging: WorkflowPackagingStageSummary;
|
|
61
|
+
};
|
|
62
|
+
outputs: WorkflowManagedPaths;
|
|
63
|
+
discoveredLeads: number;
|
|
64
|
+
selectedLeads: number;
|
|
65
|
+
packages: WorkflowPackageSummary;
|
|
66
|
+
error?: {
|
|
67
|
+
stage: WorkflowStageName;
|
|
68
|
+
message: string;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export interface WorkflowDependencies {
|
|
72
|
+
readWorkflowConfig: typeof readWorkflowConfig;
|
|
73
|
+
runDiscovery: (options: Parameters<typeof runDiscovery>[0]) => Promise<DiscoveryRunResult>;
|
|
74
|
+
runShortlistReport: (options: ShortlistRunOptions) => Promise<ShortlistResult>;
|
|
75
|
+
summarizeReviewCsvFile: typeof summarizeReviewCsvFile;
|
|
76
|
+
packageReport: (options: Parameters<typeof packageReport>[0]) => Promise<ReportPackResult>;
|
|
77
|
+
resolveGoogleMapsApiKey: typeof resolveGoogleMapsApiKey;
|
|
78
|
+
}
|
|
79
|
+
export declare class WorkflowRunError extends Error {
|
|
80
|
+
readonly summary: WorkflowSummary;
|
|
81
|
+
constructor(summary: WorkflowSummary);
|
|
82
|
+
}
|
|
83
|
+
export declare function safeLeadSlug(leadKey: string, companyName: string): string;
|
|
84
|
+
export declare function runWorkflow(configPath: string, dependencies?: Partial<WorkflowDependencies>): Promise<WorkflowSummary>;
|
|
85
|
+
export declare function runResolvedWorkflow(config: ResolvedWorkflowConfig, dependencies?: Partial<WorkflowDependencies>): Promise<WorkflowSummary>;
|
package/dist/workflow.js
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { lstat, mkdir, mkdtemp, realpath, rename, rm } from "node:fs/promises";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { runDiscovery } from "./discovery-runner.js";
|
|
5
|
+
import { packageReport } from "./report-pack.js";
|
|
6
|
+
import { summarizeReviewCsvFile } from "./review.js";
|
|
7
|
+
import { runShortlistReport } from "./shortlist-runner.js";
|
|
8
|
+
import { resolveGoogleMapsApiKey } from "./secrets.js";
|
|
9
|
+
import { readWorkflowConfig } from "./workflow-config.js";
|
|
10
|
+
import { prepareWorkflowManagedDirectories } from "./workflow-paths.js";
|
|
11
|
+
import { writeWorkflowOutputFile } from "./workflow-output.js";
|
|
12
|
+
export class WorkflowRunError extends Error {
|
|
13
|
+
summary;
|
|
14
|
+
constructor(summary) {
|
|
15
|
+
super(summary.error ? `Workflow failed during ${summary.error.stage}: ${summary.error.message}` : "Workflow failed");
|
|
16
|
+
this.name = "WorkflowRunError";
|
|
17
|
+
this.summary = summary;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const defaultDependencies = {
|
|
21
|
+
readWorkflowConfig,
|
|
22
|
+
runDiscovery,
|
|
23
|
+
runShortlistReport,
|
|
24
|
+
summarizeReviewCsvFile,
|
|
25
|
+
packageReport,
|
|
26
|
+
resolveGoogleMapsApiKey
|
|
27
|
+
};
|
|
28
|
+
const packageReportFileNames = [
|
|
29
|
+
"open-local-audit-report.json",
|
|
30
|
+
"open-local-audit-report.md",
|
|
31
|
+
"open-local-audit-report.html",
|
|
32
|
+
"open-local-audit-report.pdf"
|
|
33
|
+
];
|
|
34
|
+
function slugify(value) {
|
|
35
|
+
return value
|
|
36
|
+
.normalize("NFKD")
|
|
37
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
38
|
+
.replace(/[^A-Za-z0-9]+/g, "-")
|
|
39
|
+
.replace(/^-+|-+$/g, "")
|
|
40
|
+
.toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
function slugSourceFromLeadKey(leadKey) {
|
|
43
|
+
const trimmed = leadKey.trim();
|
|
44
|
+
if (!trimmed) {
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
if (trimmed.startsWith("url:")) {
|
|
48
|
+
const rawUrl = trimmed.slice(4);
|
|
49
|
+
try {
|
|
50
|
+
const parsed = new URL(rawUrl);
|
|
51
|
+
return `${parsed.hostname}${parsed.pathname}`.replace(/^www\./i, "");
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return rawUrl;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return trimmed.replace(/^[a-z]+:/i, "");
|
|
58
|
+
}
|
|
59
|
+
export function safeLeadSlug(leadKey, companyName) {
|
|
60
|
+
const companySlug = slugify(companyName.trim());
|
|
61
|
+
if (companySlug) {
|
|
62
|
+
return companySlug;
|
|
63
|
+
}
|
|
64
|
+
const leadSlug = slugify(slugSourceFromLeadKey(leadKey));
|
|
65
|
+
return leadSlug || "lead";
|
|
66
|
+
}
|
|
67
|
+
function sanitizeErrorMessage(error, knownSecrets = []) {
|
|
68
|
+
if (error instanceof Error) {
|
|
69
|
+
const message = error.message.trim();
|
|
70
|
+
return knownSecrets
|
|
71
|
+
.filter((secret) => secret.length > 0)
|
|
72
|
+
.reduce((sanitized, secret) => sanitized.split(secret).join("[REDACTED]"), message || "Unknown error");
|
|
73
|
+
}
|
|
74
|
+
return "Unknown error";
|
|
75
|
+
}
|
|
76
|
+
function createInitialSummary(config) {
|
|
77
|
+
return {
|
|
78
|
+
version: 1,
|
|
79
|
+
status: "success",
|
|
80
|
+
stages: {
|
|
81
|
+
discovery: { status: "not-run" },
|
|
82
|
+
shortlist: { status: "not-run" },
|
|
83
|
+
review: { status: config.review ? "not-run" : "skipped" },
|
|
84
|
+
packaging: { status: config.packageReports ? "not-run" : "skipped" }
|
|
85
|
+
},
|
|
86
|
+
outputs: config.paths,
|
|
87
|
+
discoveredLeads: 0,
|
|
88
|
+
selectedLeads: 0,
|
|
89
|
+
packages: {
|
|
90
|
+
packaged: 0,
|
|
91
|
+
skipped: 0,
|
|
92
|
+
failed: 0,
|
|
93
|
+
entries: []
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function writePrettyJson(path, value) {
|
|
98
|
+
await mkdir(dirname(path), { recursive: true });
|
|
99
|
+
await writeWorkflowOutputFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
100
|
+
}
|
|
101
|
+
async function writeWorkflowSummary(summary) {
|
|
102
|
+
await writePrettyJson(summary.outputs.workflowSummaryJson, summary);
|
|
103
|
+
}
|
|
104
|
+
async function throwPersistedWorkflowFailure(summary) {
|
|
105
|
+
try {
|
|
106
|
+
await writeWorkflowSummary(summary);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// The workflow failure remains authoritative if its summary cannot be persisted.
|
|
110
|
+
}
|
|
111
|
+
throw new WorkflowRunError(summary);
|
|
112
|
+
}
|
|
113
|
+
async function throwStageFailure(summary, stage, error, knownSecrets) {
|
|
114
|
+
markStageFailure(summary, stage, sanitizeErrorMessage(error, knownSecrets));
|
|
115
|
+
return throwPersistedWorkflowFailure(summary);
|
|
116
|
+
}
|
|
117
|
+
function markStageFailure(summary, stage, message) {
|
|
118
|
+
summary.status = "failed";
|
|
119
|
+
summary.stages[stage].status = "failed";
|
|
120
|
+
summary.error = {
|
|
121
|
+
stage,
|
|
122
|
+
message
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
async function resolvePackageInputDir(reportsDir, reportPath) {
|
|
126
|
+
const resolvedReportPath = resolve(reportsDir, reportPath);
|
|
127
|
+
const relativePath = relative(reportsDir, resolvedReportPath);
|
|
128
|
+
if (!relativePath || relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
129
|
+
throw new Error("Report path escapes reports directory");
|
|
130
|
+
}
|
|
131
|
+
const inputDir = dirname(resolvedReportPath);
|
|
132
|
+
const [realReportsDir, realInputDir] = await Promise.all([realpath(reportsDir), realpath(inputDir)]);
|
|
133
|
+
const realRelativePath = relative(realReportsDir, realInputDir);
|
|
134
|
+
if (realRelativePath.startsWith("..") || isAbsolute(realRelativePath)) {
|
|
135
|
+
throw new Error("Report path escapes reports directory");
|
|
136
|
+
}
|
|
137
|
+
return { inputDir, realInputDir };
|
|
138
|
+
}
|
|
139
|
+
async function validatePackageSourceFiles(inputDir, realInputDir) {
|
|
140
|
+
for (const fileName of packageReportFileNames) {
|
|
141
|
+
const sourcePath = join(inputDir, fileName);
|
|
142
|
+
try {
|
|
143
|
+
const sourceInfo = await lstat(sourcePath);
|
|
144
|
+
if (sourceInfo.isSymbolicLink()) {
|
|
145
|
+
throw new Error("Linked report files are not allowed");
|
|
146
|
+
}
|
|
147
|
+
const realSourcePath = await realpath(sourcePath);
|
|
148
|
+
const relativeSourcePath = relative(realInputDir, realSourcePath);
|
|
149
|
+
if (relativeSourcePath.startsWith("..") || isAbsolute(relativeSourcePath)) {
|
|
150
|
+
throw new Error("Report file escapes input directory");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function promotePackage(tempDir, finalDir) {
|
|
162
|
+
const backupDir = `${finalDir}.backup-${randomUUID()}`;
|
|
163
|
+
let hasBackup = false;
|
|
164
|
+
let promoted = false;
|
|
165
|
+
try {
|
|
166
|
+
await rename(finalDir, backupDir);
|
|
167
|
+
hasBackup = true;
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
await rename(tempDir, finalDir);
|
|
176
|
+
promoted = true;
|
|
177
|
+
if (hasBackup) {
|
|
178
|
+
await rm(backupDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (hasBackup) {
|
|
183
|
+
try {
|
|
184
|
+
if (promoted) {
|
|
185
|
+
await rename(finalDir, tempDir);
|
|
186
|
+
}
|
|
187
|
+
await rename(backupDir, finalDir);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Best-effort rollback; the original promotion error remains authoritative.
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function updateDiscoveryStage(summary, result) {
|
|
197
|
+
summary.stages.discovery = {
|
|
198
|
+
status: "success",
|
|
199
|
+
totalCandidates: result.summary.totalCandidates,
|
|
200
|
+
suppressedCandidates: result.summary.suppressedCandidates,
|
|
201
|
+
audited: result.summary.audited
|
|
202
|
+
};
|
|
203
|
+
summary.discoveredLeads = result.rows.length;
|
|
204
|
+
}
|
|
205
|
+
function updateShortlistStage(summary, result) {
|
|
206
|
+
summary.stages.shortlist = {
|
|
207
|
+
status: "success",
|
|
208
|
+
totalRows: result.totalRows,
|
|
209
|
+
suppressedRows: result.suppressedRows,
|
|
210
|
+
filteredRows: result.filteredRows,
|
|
211
|
+
selected: result.selected
|
|
212
|
+
};
|
|
213
|
+
summary.selectedLeads = result.selected;
|
|
214
|
+
}
|
|
215
|
+
function updateReviewStage(summary, result) {
|
|
216
|
+
summary.stages.review = {
|
|
217
|
+
status: "success",
|
|
218
|
+
totalRows: result.totalRows,
|
|
219
|
+
reviewedRows: result.reviewedRows,
|
|
220
|
+
actionableLeads: result.actionableLeadKeys.length,
|
|
221
|
+
staleRows: result.staleRows,
|
|
222
|
+
invalidReviewedAtRows: result.invalidReviewedAtRows,
|
|
223
|
+
unreviewedRows: result.unreviewedRows
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function updatePackagingStage(summary) {
|
|
227
|
+
summary.stages.packaging = {
|
|
228
|
+
status: summary.packages.failed > 0 ? "failed" : "success",
|
|
229
|
+
packaged: summary.packages.packaged,
|
|
230
|
+
skipped: summary.packages.skipped,
|
|
231
|
+
failed: summary.packages.failed
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function nextUniqueSlug(baseSlug, counts) {
|
|
235
|
+
const nextCount = (counts.get(baseSlug) ?? 0) + 1;
|
|
236
|
+
counts.set(baseSlug, nextCount);
|
|
237
|
+
return nextCount === 1 ? baseSlug : `${baseSlug}-${nextCount}`;
|
|
238
|
+
}
|
|
239
|
+
export async function runWorkflow(configPath, dependencies = {}) {
|
|
240
|
+
const resolvedDependencies = {
|
|
241
|
+
...defaultDependencies,
|
|
242
|
+
...dependencies
|
|
243
|
+
};
|
|
244
|
+
const config = await resolvedDependencies.readWorkflowConfig(configPath);
|
|
245
|
+
return await runResolvedWorkflow(config, resolvedDependencies);
|
|
246
|
+
}
|
|
247
|
+
export async function runResolvedWorkflow(config, dependencies = {}) {
|
|
248
|
+
const resolvedDependencies = {
|
|
249
|
+
...defaultDependencies,
|
|
250
|
+
...dependencies
|
|
251
|
+
};
|
|
252
|
+
await prepareWorkflowManagedDirectories(config);
|
|
253
|
+
const summary = createInitialSummary(config);
|
|
254
|
+
const knownSecrets = [];
|
|
255
|
+
try {
|
|
256
|
+
const googleApiKey = config.discovery.provider === "google-places" ? resolvedDependencies.resolveGoogleMapsApiKey() : undefined;
|
|
257
|
+
if (googleApiKey) {
|
|
258
|
+
knownSecrets.push(googleApiKey);
|
|
259
|
+
}
|
|
260
|
+
const discoveryResult = await resolvedDependencies.runDiscovery({
|
|
261
|
+
provider: config.discovery.provider,
|
|
262
|
+
...(config.discovery.provider === "manual-csv" ? { input: config.discovery.input } : {}),
|
|
263
|
+
...(config.discovery.provider === "google-places" ? { query: config.discovery.query } : {}),
|
|
264
|
+
profile: config.discovery.profile,
|
|
265
|
+
outDir: config.paths.reportsDir,
|
|
266
|
+
managedOutputRoot: config.paths.reportsDir,
|
|
267
|
+
exportCsv: config.paths.leadsCsv,
|
|
268
|
+
summaryJson: config.paths.discoverySummaryJson,
|
|
269
|
+
reviewCsv: config.review?.csv,
|
|
270
|
+
dryRun: false,
|
|
271
|
+
concurrency: config.discovery.concurrency,
|
|
272
|
+
...(config.discovery.maxAudits !== undefined ? { maxAudits: config.discovery.maxAudits } : {}),
|
|
273
|
+
...(config.discovery.provider === "google-places" ? { limit: config.discovery.limit } : {}),
|
|
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) {
|
|
287
|
+
const reviewSummary = await resolvedDependencies.summarizeReviewCsvFile(config.review.csv, {
|
|
288
|
+
staleBefore: config.review.staleBefore
|
|
289
|
+
});
|
|
290
|
+
await writePrettyJson(config.paths.reviewSummaryJson, reviewSummary);
|
|
291
|
+
updateReviewStage(summary, reviewSummary);
|
|
292
|
+
}
|
|
293
|
+
if (config.packageReports) {
|
|
294
|
+
const slugCounts = new Map();
|
|
295
|
+
for (const lead of shortlistResult.leads) {
|
|
296
|
+
const packageEntry = await packageLead(config.paths, lead, slugCounts, resolvedDependencies.packageReport, knownSecrets);
|
|
297
|
+
summary.packages.entries.push(packageEntry);
|
|
298
|
+
if (packageEntry.status === "packaged") {
|
|
299
|
+
summary.packages.packaged += 1;
|
|
300
|
+
}
|
|
301
|
+
else if (packageEntry.status === "skipped") {
|
|
302
|
+
summary.packages.skipped += 1;
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
summary.packages.failed += 1;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
updatePackagingStage(summary);
|
|
309
|
+
if (summary.packages.failed > 0) {
|
|
310
|
+
summary.status = "failed";
|
|
311
|
+
summary.error = {
|
|
312
|
+
stage: "packaging",
|
|
313
|
+
message: `${summary.packages.failed} package ${summary.packages.failed === 1 ? "entry" : "entries"} failed`
|
|
314
|
+
};
|
|
315
|
+
await throwPersistedWorkflowFailure(summary);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
if (error instanceof WorkflowRunError) {
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
const failedStage = summary.stages.discovery.status !== "success"
|
|
324
|
+
? "discovery"
|
|
325
|
+
: summary.stages.shortlist.status !== "success"
|
|
326
|
+
? "shortlist"
|
|
327
|
+
: summary.stages.review.status === "not-run"
|
|
328
|
+
? "review"
|
|
329
|
+
: "packaging";
|
|
330
|
+
await throwStageFailure(summary, failedStage, error, knownSecrets);
|
|
331
|
+
}
|
|
332
|
+
await writeWorkflowSummary(summary);
|
|
333
|
+
return summary;
|
|
334
|
+
}
|
|
335
|
+
async function packageLead(paths, lead, slugCounts, packageReportDependency, knownSecrets) {
|
|
336
|
+
const reportPath = lead.reportPath.trim();
|
|
337
|
+
if (!reportPath) {
|
|
338
|
+
return {
|
|
339
|
+
leadKey: lead.leadKey,
|
|
340
|
+
companyName: lead.companyName,
|
|
341
|
+
status: "skipped"
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
const slug = nextUniqueSlug(safeLeadSlug(lead.leadKey, lead.companyName), slugCounts);
|
|
345
|
+
const outDir = join(paths.packagesDir, slug);
|
|
346
|
+
let tempDir;
|
|
347
|
+
try {
|
|
348
|
+
const { inputDir, realInputDir } = await resolvePackageInputDir(paths.reportsDir, reportPath);
|
|
349
|
+
await validatePackageSourceFiles(inputDir, realInputDir);
|
|
350
|
+
await mkdir(paths.packagesDir, { recursive: true });
|
|
351
|
+
tempDir = await mkdtemp(join(paths.packagesDir, `.${slug}-tmp-`));
|
|
352
|
+
await packageReportDependency({
|
|
353
|
+
inputDir,
|
|
354
|
+
outDir: tempDir
|
|
355
|
+
});
|
|
356
|
+
await promotePackage(tempDir, outDir);
|
|
357
|
+
tempDir = undefined;
|
|
358
|
+
return {
|
|
359
|
+
leadKey: lead.leadKey,
|
|
360
|
+
companyName: lead.companyName,
|
|
361
|
+
status: "packaged",
|
|
362
|
+
outDir
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
if (tempDir) {
|
|
367
|
+
try {
|
|
368
|
+
await rm(tempDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
// Keep the package failure useful even if temporary output cannot be removed.
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
leadKey: lead.leadKey,
|
|
376
|
+
companyName: lead.companyName,
|
|
377
|
+
status: "failed",
|
|
378
|
+
error: sanitizeErrorMessage(error, knownSecrets)
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Workflow Command
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
The `workflow` command runs the existing local lead discovery, shortlist, review summary, and report packaging capabilities from one versioned JSON configuration file. It writes local artifacts; it does not send outreach, synchronize CRM records, or upload reports. A Google Places discovery run is the exception to local processing: it calls the configured Google service through the existing provider.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
open-local-audit workflow --config workflow.json
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The command does not store Google Places API keys. The Google provider resolves its key through the existing key resolver at runtime.
|
|
12
|
+
|
|
13
|
+
## Configuration
|
|
14
|
+
|
|
15
|
+
The configuration uses a strict JSON contract. Only version `1` is accepted. Relative `outDir`, manual CSV `input`, and optional review `csv` paths are resolved from the configuration file directory.
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"version": 1,
|
|
20
|
+
"outDir": "./workflow-output",
|
|
21
|
+
"discovery": {
|
|
22
|
+
"provider": "manual-csv",
|
|
23
|
+
"input": "./places.csv",
|
|
24
|
+
"profile": "dental",
|
|
25
|
+
"concurrency": 2,
|
|
26
|
+
"maxAudits": 10
|
|
27
|
+
},
|
|
28
|
+
"shortlist": {
|
|
29
|
+
"top": 10,
|
|
30
|
+
"minOpportunityScore": 70,
|
|
31
|
+
"sort": "opportunity-desc"
|
|
32
|
+
},
|
|
33
|
+
"review": {
|
|
34
|
+
"csv": "./review.csv",
|
|
35
|
+
"staleBefore": "2026-07-01"
|
|
36
|
+
},
|
|
37
|
+
"packageReports": true
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`version`, `outDir`, `discovery`, and `shortlist` are required. `review` and `packageReports` are optional. Unknown fields and invalid values are rejected before output files are created. `manual-csv` discovery requires `input`; `google-places` discovery requires `query` and resolves `GOOGLE_MAPS_API_KEY` only for that provider.
|
|
42
|
+
|
|
43
|
+
The discovery object accepts either the existing `manual-csv` input or the existing `google-places` provider and its query. Google Places workflows continue to require `GOOGLE_MAPS_API_KEY`, display the billing warning, and use the existing candidate and audit limits.
|
|
44
|
+
|
|
45
|
+
## Managed Outputs
|
|
46
|
+
|
|
47
|
+
The command writes predictable paths below `outDir`:
|
|
48
|
+
|
|
49
|
+
- `leads.csv`
|
|
50
|
+
- `discovery-summary.json`
|
|
51
|
+
- `shortlist.csv`
|
|
52
|
+
- `shortlist-summary.json`
|
|
53
|
+
- `review-summary.json` when review configuration is present
|
|
54
|
+
- `packages/<safe-lead-slug>/` for selected leads with successful report artifacts when packaging is enabled
|
|
55
|
+
- `workflow-summary.json`
|
|
56
|
+
|
|
57
|
+
The same configuration resolves to the same managed output paths, so rerun destinations are deterministic. Reruns replace only those managed outputs and do not delete unrelated files. Existing operator decisions in the configured review CSV remain authoritative and are preserved by the discovery merge behavior.
|
|
58
|
+
|
|
59
|
+
## Execution Model
|
|
60
|
+
|
|
61
|
+
The workflow runs these dependent stages in order:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
discovery
|
|
65
|
+
-> shortlist
|
|
66
|
+
-> review summary (optional)
|
|
67
|
+
-> selected report packages (optional)
|
|
68
|
+
-> workflow summary
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The implementation reuses the same application services as the individual CLI commands. It does not duplicate discovery, ranking, review, or packaging rules.
|
|
72
|
+
|
|
73
|
+
Configuration validation completes before any output is written and returns exit code `1` when invalid. A discovery, shortlist, or review summary failure stops dependent stages and returns exit code `1`. Report packages are independent: one package failure does not prevent other selected reports from being packaged, but the final workflow status is `failed` and the command returns exit code `1`.
|
|
74
|
+
|
|
75
|
+
A selected lead without a successful report path is recorded as `skipped`, not failed.
|
|
76
|
+
Package directory names use a deterministic, file-system-safe slug derived from the selected lead identity; raw lead keys are not used as paths.
|
|
77
|
+
|
|
78
|
+
## Workflow Summary
|
|
79
|
+
|
|
80
|
+
`workflow-summary.json` is written after configuration has been validated, the output directory has been prepared, and execution reaches a managed workflow stage. It contains:
|
|
81
|
+
|
|
82
|
+
- overall `status`;
|
|
83
|
+
- per-stage status and counts;
|
|
84
|
+
- generated output paths;
|
|
85
|
+
- discovered and selected lead counts;
|
|
86
|
+
- package success, skipped, and failure counts;
|
|
87
|
+
- sanitized failure messages.
|
|
88
|
+
|
|
89
|
+
The summary does not include the full configuration, environment variables, API keys, raw Google Places responses, or website response bodies.
|
|
90
|
+
|
|
91
|
+
## Compatibility
|
|
92
|
+
|
|
93
|
+
The existing `discover`, `shortlist`, `review`, and `package-report` commands keep their current flags, outputs, and exit behavior. The workflow command is additive and uses configuration contract version `1` so future incompatible configuration changes can be rejected explicitly. `workflow --check` is also additive: it preflights the same configuration without changing normal workflow execution. Its read-only behavior, exit semantics, and versioned report contract are defined in the [workflow preflight contract](./workflow-preflight.md).
|
|
94
|
+
|
|
95
|
+
## Verification
|
|
96
|
+
|
|
97
|
+
Coverage for this command includes:
|
|
98
|
+
|
|
99
|
+
- strict configuration validation and unknown-field rejection;
|
|
100
|
+
- configuration-relative path resolution;
|
|
101
|
+
- a successful manual CSV workflow using local fixtures;
|
|
102
|
+
- fail-fast behavior for discovery, shortlist, and review summary failures;
|
|
103
|
+
- partial package failure reporting and exit behavior;
|
|
104
|
+
- review decision preservation across repeated runs;
|
|
105
|
+
- CLI help and invalid-configuration errors;
|
|
106
|
+
- the full release gate, including build, dependency audit, package dry run, and clean consumer installation.
|