@open-agent-toolkit/cli 0.2.73 → 0.2.75
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/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/configuration.md +3 -56
- package/assets/docs/cli-utilities/tool-packs.md +4 -4
- package/assets/docs/contributing/code.md +2 -2
- package/assets/docs/contributing/index.md +0 -1
- package/assets/docs/reference/cli-reference.md +1 -1
- package/assets/docs/reference/troubleshooting.md +17 -40
- package/assets/docs/workflows/projects/artifacts.md +16 -34
- package/assets/docs/workflows/projects/lifecycle.md +20 -19
- package/assets/docs/workflows/skills/explainer-kit.md +180 -440
- package/assets/docs/workflows/skills/index.md +1 -2
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/explainer-kit/SKILL.md +96 -171
- package/assets/skills/explainer-kit/recipes/engineer-tour.json +1 -22
- package/assets/skills/explainer-kit/recipes/program-recap.json +1 -29
- package/assets/skills/explainer-kit/recipes/project-explainer.json +1 -22
- package/assets/skills/explainer-kit/recipes/project-recap.v2.json +1 -43
- package/assets/skills/explainer-kit/references/fact-base-contract.md +76 -101
- package/assets/skills/explainer-kit/references/recap-authoring.md +99 -0
- package/assets/skills/explainer-kit/schemas/manifest.schema.json +13 -135
- package/assets/skills/explainer-kit/scripts/bundle.mjs +712 -0
- package/assets/skills/explainer-kit/scripts/lib/claim-subject.mjs +40 -0
- package/assets/skills/explainer-kit/scripts/lib/contracts.mjs +113 -1504
- package/assets/skills/explainer-kit/scripts/lib/failure.mjs +13 -0
- package/assets/skills/explainer-kit/scripts/lib/package-coverage.mjs +57 -403
- package/assets/skills/explainer-kit/scripts/lib/qa-result.mjs +168 -0
- package/assets/skills/explainer-kit/scripts/lib/qa.mjs +90 -488
- package/assets/skills/explainer-kit/scripts/lib/recipes.mjs +28 -545
- package/assets/skills/explainer-kit/scripts/lib/run-package.mjs +120 -0
- package/assets/skills/explainer-kit/scripts/lib/sanitize.mjs +42 -0
- package/assets/skills/explainer-kit/scripts/record.mjs +335 -0
- package/assets/skills/explainer-kit/scripts/verify.mjs +648 -0
- package/assets/skills/oat-explainer-kit/SKILL.md +57 -117
- package/assets/skills/oat-explainer-kit/references/config-contract.md +20 -64
- package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +85 -237
- package/assets/skills/oat-explainer-kit/scripts/bind-project-sources.mjs +18 -10
- package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +1 -0
- package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +256 -21
- package/assets/skills/oat-explainer-kit/scripts/persist-intent.mjs +82 -3
- package/assets/skills/oat-explainer-kit/scripts/resolve-config.mjs +0 -162
- package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +57 -123
- package/assets/skills/oat-project-autonomous/SKILL.md +14 -13
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +165 -165
- package/assets/skills/oat-project-complete/SKILL.md +168 -349
- package/assets/skills/oat-project-complete/scripts/consume-persisted-recap-intent.mjs +125 -0
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +0 -30
- package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +4 -37
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +17 -16
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +9 -174
- package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +1 -2
- package/assets/skills/oat-project-document/SKILL.md +1 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +44 -56
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-lite/SKILL.md +1 -1
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-plan/SKILL.md +5 -6
- package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-quick-start/SKILL.md +1 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-summary/SKILL.md +35 -25
- package/assets/skills/oat-wave-execute/SKILL.md +11 -36
- package/assets/skills/oat-wave-program/SKILL.md +13 -37
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +9 -113
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +32 -184
- package/dist/commands/project/archive/explainer-package-coverage.d.ts +3 -8
- package/dist/commands/project/archive/explainer-package-coverage.d.ts.map +1 -1
- package/dist/commands/project/archive/explainer-package-coverage.js +1 -1
- package/dist/config/oat-config.d.ts +0 -11
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +0 -34
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +0 -8
- package/dist/release/public-package-contract.d.ts.map +1 -1
- package/dist/release/public-package-contract.js +2 -10
- package/dist/validation/project-state.d.ts.map +1 -1
- package/dist/validation/project-state.js +42 -3
- package/package.json +2 -2
- package/assets/docs/contributing/explainer-kit-verification.md +0 -133
- package/assets/docs/workflows/skills/explainer-kit-providers.md +0 -155
- package/assets/skills/explainer-kit/briefs/deep-dive.md +0 -35
- package/assets/skills/explainer-kit/briefs/project-page.md +0 -38
- package/assets/skills/explainer-kit/briefs/supporting-diagram.md +0 -28
- package/assets/skills/explainer-kit/briefs/walkthrough-deck.md +0 -35
- package/assets/skills/explainer-kit/examples/project-explainer/content.md +0 -31
- package/assets/skills/explainer-kit/examples/project-explainer/fact-base.md +0 -22
- package/assets/skills/explainer-kit/examples/project-recap/content.md +0 -77
- package/assets/skills/explainer-kit/examples/project-recap/fact-base.json +0 -104
- package/assets/skills/explainer-kit/examples/project-recap/fact-base.md +0 -44
- package/assets/skills/explainer-kit/examples/theme-bundle.json +0 -87
- package/assets/skills/explainer-kit/recipes/project-recap.v1.json +0 -93
- package/assets/skills/explainer-kit/references/contracts.md +0 -324
- package/assets/skills/explainer-kit/references/destination-contract.md +0 -210
- package/assets/skills/explainer-kit/references/extension-contract.md +0 -80
- package/assets/skills/explainer-kit/references/golden-conformance.md +0 -80
- package/assets/skills/explainer-kit/references/visual-review.md +0 -71
- package/assets/skills/explainer-kit/schemas/author-request.v2.schema.json +0 -212
- package/assets/skills/explainer-kit/schemas/author-request.v3.schema.json +0 -241
- package/assets/skills/explainer-kit/schemas/author-result.v2.schema.json +0 -52
- package/assets/skills/explainer-kit/schemas/build-record.schema.json +0 -98
- package/assets/skills/explainer-kit/schemas/durability-evidence.schema.json +0 -53
- package/assets/skills/explainer-kit/schemas/publish-receipt.v1.schema.json +0 -97
- package/assets/skills/explainer-kit/schemas/publish-receipt.v2.schema.json +0 -215
- package/assets/skills/explainer-kit/schemas/publish-request.v1.schema.json +0 -32
- package/assets/skills/explainer-kit/schemas/publish-request.v2.schema.json +0 -34
- package/assets/skills/explainer-kit/schemas/run-request.schema.json +0 -147
- package/assets/skills/explainer-kit/schemas/set-plan.v1.schema.json +0 -149
- package/assets/skills/explainer-kit/schemas/terminal-evidence.v1.schema.json +0 -81
- package/assets/skills/explainer-kit/schemas/visual-review-evidence.v1.schema.json +0 -66
- package/assets/skills/explainer-kit/schemas/visual-review-request.v1.schema.json +0 -117
- package/assets/skills/explainer-kit/schemas/visual-review-result.v1.schema.json +0 -80
- package/assets/skills/explainer-kit/scripts/lib/catalog.mjs +0 -349
- package/assets/skills/explainer-kit/scripts/lib/content-approval.mjs +0 -418
- package/assets/skills/explainer-kit/scripts/lib/diagram.mjs +0 -514
- package/assets/skills/explainer-kit/scripts/lib/durability.mjs +0 -643
- package/assets/skills/explainer-kit/scripts/lib/fact-base.mjs +0 -664
- package/assets/skills/explainer-kit/scripts/lib/internal-references.mjs +0 -538
- package/assets/skills/explainer-kit/scripts/lib/markdown.mjs +0 -414
- package/assets/skills/explainer-kit/scripts/lib/publication-policy.mjs +0 -54
- package/assets/skills/explainer-kit/scripts/lib/records.mjs +0 -954
- package/assets/skills/explainer-kit/scripts/lib/render.mjs +0 -703
- package/assets/skills/explainer-kit/scripts/lib/s3-roots.mjs +0 -353
- package/assets/skills/explainer-kit/scripts/lib/s3-static.mjs +0 -759
- package/assets/skills/explainer-kit/scripts/lib/set-plan.mjs +0 -209
- package/assets/skills/explainer-kit/scripts/lib/source-backlinks.mjs +0 -218
- package/assets/skills/explainer-kit/scripts/lib/terminal-evidence.mjs +0 -157
- package/assets/skills/explainer-kit/scripts/lib/visual-review.mjs +0 -400
- package/assets/skills/explainer-kit/scripts/publish.mjs +0 -76
- package/assets/skills/explainer-kit/scripts/record-durability.mjs +0 -64
- package/assets/skills/explainer-kit/scripts/render-qa.mjs +0 -461
- package/assets/skills/explainer-kit/scripts/run.mjs +0 -3038
- package/assets/skills/explainer-kit/scripts/validate.mjs +0 -59
- package/assets/skills/oat-explainer-kit/references/author-callback.md +0 -53
- package/assets/skills/oat-explainer-kit/references/migration.md +0 -144
- package/assets/skills/oat-explainer-kit/references/visual-review-callback.md +0 -83
- package/assets/skills/oat-explainer-kit/scripts/derive-destination.mjs +0 -91
- package/assets/skills/oat-explainer-kit/scripts/finalize-tracked-run.mjs +0 -531
- package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +0 -371
- package/assets/skills/oat-explainer-kit/scripts/run.mjs +0 -905
- package/dist/commands/project/archive/explainer-source-backlinks.d.ts +0 -18
- package/dist/commands/project/archive/explainer-source-backlinks.d.ts.map +0 -1
- package/dist/commands/project/archive/explainer-source-backlinks.js +0 -27
- package/dist/commands/project/archive/explainer-terminal-evidence.d.ts +0 -29
- package/dist/commands/project/archive/explainer-terminal-evidence.d.ts.map +0 -1
- package/dist/commands/project/archive/explainer-terminal-evidence.js +0 -37
|
@@ -1,905 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { readFile } from 'node:fs/promises';
|
|
4
|
-
import { basename, dirname, join, resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
bindProjectSources,
|
|
9
|
-
bindRepositorySources,
|
|
10
|
-
} from './bind-project-sources.mjs';
|
|
11
|
-
import { checkCoreCompatibility } from './check-core.mjs';
|
|
12
|
-
import { deriveExplainerDestination } from './derive-destination.mjs';
|
|
13
|
-
import {
|
|
14
|
-
resolveExplainerConfig,
|
|
15
|
-
toExplainerRunRequest,
|
|
16
|
-
} from './resolve-config.mjs';
|
|
17
|
-
import { resolveExplainerOutputRoot } from './resolve-paths.mjs';
|
|
18
|
-
|
|
19
|
-
export const MINIMUM_CORE_VERSION = '2.1.0';
|
|
20
|
-
const ADAPTER_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
21
|
-
|
|
22
|
-
export async function runOatExplainer({
|
|
23
|
-
adapterRoot = ADAPTER_ROOT,
|
|
24
|
-
userSkillsRoot,
|
|
25
|
-
repoRoot,
|
|
26
|
-
invocation,
|
|
27
|
-
activeProject,
|
|
28
|
-
recipe,
|
|
29
|
-
slug,
|
|
30
|
-
suppliedFactBasePath,
|
|
31
|
-
runtimeOverrides = {},
|
|
32
|
-
getConfig,
|
|
33
|
-
mode = 'unattended',
|
|
34
|
-
durabilityStrategy = 'none',
|
|
35
|
-
artDirection,
|
|
36
|
-
defaultMode,
|
|
37
|
-
renderStrategy,
|
|
38
|
-
retainRawArtDirection = false,
|
|
39
|
-
planSet,
|
|
40
|
-
planSetModulePath,
|
|
41
|
-
author,
|
|
42
|
-
authorModulePath,
|
|
43
|
-
critic,
|
|
44
|
-
criticModulePath,
|
|
45
|
-
browserSession,
|
|
46
|
-
browserSessionModulePath,
|
|
47
|
-
browserProbe,
|
|
48
|
-
browserProbeModulePath,
|
|
49
|
-
visualCritic,
|
|
50
|
-
visualCriticModulePath,
|
|
51
|
-
coreOptions = {},
|
|
52
|
-
}) {
|
|
53
|
-
const compatibility = await checkCoreCompatibility({
|
|
54
|
-
adapterRoot,
|
|
55
|
-
...(userSkillsRoot && { userSkillsRoot }),
|
|
56
|
-
minimumVersion: MINIMUM_CORE_VERSION,
|
|
57
|
-
});
|
|
58
|
-
if (!compatibility.ok) {
|
|
59
|
-
const error = new Error(
|
|
60
|
-
`${compatibility.message} Run \`${compatibility.guidance}\`.`,
|
|
61
|
-
);
|
|
62
|
-
error.code =
|
|
63
|
-
compatibility.code === 'missing'
|
|
64
|
-
? 'E_CORE_MISSING'
|
|
65
|
-
: 'E_CORE_INCOMPATIBLE';
|
|
66
|
-
error.compatibility = compatibility;
|
|
67
|
-
throw error;
|
|
68
|
-
}
|
|
69
|
-
if (!supportsAdaptiveSetPlanning(compatibility.installedVersion)) {
|
|
70
|
-
const error = new Error(
|
|
71
|
-
`Installed explainer-kit ${compatibility.installedVersion} does not provide adaptive set planning. Run \`oat tools update --pack utility --scope user\`.`,
|
|
72
|
-
);
|
|
73
|
-
error.code = 'E_CORE_INCOMPATIBLE';
|
|
74
|
-
error.compatibility = compatibility;
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const resolvedConfig = await resolveExplainerConfig({
|
|
79
|
-
repoRoot,
|
|
80
|
-
runtimeOverrides,
|
|
81
|
-
...(getConfig && { getConfig }),
|
|
82
|
-
});
|
|
83
|
-
const outputRoot = await resolveExplainerOutputRoot({
|
|
84
|
-
repoRoot,
|
|
85
|
-
invocation,
|
|
86
|
-
activeProject,
|
|
87
|
-
slug,
|
|
88
|
-
});
|
|
89
|
-
const destination = resolvedConfig.publish
|
|
90
|
-
? deriveExplainerDestination({
|
|
91
|
-
invocation,
|
|
92
|
-
...(invocation === 'project' && {
|
|
93
|
-
projectSlug: basename(activeProject),
|
|
94
|
-
}),
|
|
95
|
-
s3Uri: resolvedConfig.publish.s3Uri,
|
|
96
|
-
publicBaseUrl: resolvedConfig.publish.publicBaseUrl,
|
|
97
|
-
})
|
|
98
|
-
: null;
|
|
99
|
-
let bound;
|
|
100
|
-
if (invocation === 'project') {
|
|
101
|
-
if (!activeProject) {
|
|
102
|
-
throw new Error(
|
|
103
|
-
'Project artifact binding requires an activeProject input.',
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
bound = await bindProjectSources({
|
|
107
|
-
projectRoot: resolve(repoRoot, activeProject),
|
|
108
|
-
repoRoot,
|
|
109
|
-
recipe,
|
|
110
|
-
suppliedFactBasePath,
|
|
111
|
-
});
|
|
112
|
-
} else if (invocation === 'repo') {
|
|
113
|
-
bound = await bindRepositorySources({
|
|
114
|
-
repoRoot,
|
|
115
|
-
suppliedFactBasePath,
|
|
116
|
-
});
|
|
117
|
-
} else {
|
|
118
|
-
throw new Error(
|
|
119
|
-
'The OAT adapter accepts project or repository invocations; direct callers invoke the core with an explicit output root.',
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
const requestConfig = destination
|
|
123
|
-
? {
|
|
124
|
-
...resolvedConfig,
|
|
125
|
-
publish: {
|
|
126
|
-
...resolvedConfig.publish,
|
|
127
|
-
...destination,
|
|
128
|
-
},
|
|
129
|
-
}
|
|
130
|
-
: resolvedConfig;
|
|
131
|
-
const request = toExplainerRunRequest({
|
|
132
|
-
resolvedConfig: requestConfig,
|
|
133
|
-
recipe,
|
|
134
|
-
slug,
|
|
135
|
-
outputRoot,
|
|
136
|
-
factBase: bound.factBase,
|
|
137
|
-
mode,
|
|
138
|
-
durabilityStrategy,
|
|
139
|
-
artDirection,
|
|
140
|
-
defaultMode,
|
|
141
|
-
renderStrategy,
|
|
142
|
-
retainRawArtDirection,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
const coreModulePath = join(compatibility.coreRoot, 'scripts', 'run.mjs');
|
|
146
|
-
let core;
|
|
147
|
-
try {
|
|
148
|
-
core = await import(pathToFileURL(coreModulePath).href);
|
|
149
|
-
} catch (cause) {
|
|
150
|
-
const error = new Error(
|
|
151
|
-
`Compatible explainer-kit is missing its run entry point. Run \`${compatibility.guidance ?? 'oat tools update --pack utility --scope user'}\`.`,
|
|
152
|
-
{ cause },
|
|
153
|
-
);
|
|
154
|
-
error.code = 'E_CORE_INCOMPATIBLE';
|
|
155
|
-
throw error;
|
|
156
|
-
}
|
|
157
|
-
if (typeof core.runExplainer !== 'function') {
|
|
158
|
-
const error = new Error(
|
|
159
|
-
'Compatible explainer-kit does not export runExplainer(request, options).',
|
|
160
|
-
);
|
|
161
|
-
error.code = 'E_CORE_INCOMPATIBLE';
|
|
162
|
-
throw error;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const lifecycleAuthor = await resolveLifecycleAuthor({
|
|
166
|
-
author,
|
|
167
|
-
authorModulePath,
|
|
168
|
-
coreOptions,
|
|
169
|
-
});
|
|
170
|
-
const lifecycleSetPlanner = await resolveLifecycleSetPlanner({
|
|
171
|
-
planSet,
|
|
172
|
-
planSetModulePath,
|
|
173
|
-
coreOptions,
|
|
174
|
-
required: recipe === 'project-recap' && mode === 'unattended',
|
|
175
|
-
});
|
|
176
|
-
const lifecycleCritic = await resolveLifecycleCritic({
|
|
177
|
-
critic,
|
|
178
|
-
criticModulePath,
|
|
179
|
-
coreOptions,
|
|
180
|
-
});
|
|
181
|
-
const reviewProvidersRequired =
|
|
182
|
-
recipe === 'project-recap' && mode === 'unattended';
|
|
183
|
-
const lifecycleBrowserSession = await resolveLifecycleBrowserSession({
|
|
184
|
-
core,
|
|
185
|
-
browserSession,
|
|
186
|
-
browserSessionModulePath,
|
|
187
|
-
browserProbe,
|
|
188
|
-
browserProbeModulePath,
|
|
189
|
-
coreOptions,
|
|
190
|
-
required: reviewProvidersRequired,
|
|
191
|
-
});
|
|
192
|
-
const lifecycleVisualCritic = await resolveLifecycleVisualCritic({
|
|
193
|
-
visualCritic,
|
|
194
|
-
visualCriticModulePath,
|
|
195
|
-
coreOptions,
|
|
196
|
-
required: reviewProvidersRequired,
|
|
197
|
-
});
|
|
198
|
-
assertDistinctProviderRoles({
|
|
199
|
-
author: lifecycleAuthor,
|
|
200
|
-
factCritic: lifecycleCritic,
|
|
201
|
-
browserProbe: lifecycleBrowserSession?.probe,
|
|
202
|
-
visualCritic: lifecycleVisualCritic,
|
|
203
|
-
});
|
|
204
|
-
const result = await core.runExplainer(request, {
|
|
205
|
-
...coreOptions,
|
|
206
|
-
...(lifecycleSetPlanner && { planSet: lifecycleSetPlanner }),
|
|
207
|
-
...(lifecycleAuthor && { author: lifecycleAuthor }),
|
|
208
|
-
...(lifecycleCritic && { critic: lifecycleCritic }),
|
|
209
|
-
...(lifecycleBrowserSession && {
|
|
210
|
-
browserSession: lifecycleBrowserSession,
|
|
211
|
-
}),
|
|
212
|
-
...(lifecycleVisualCritic && { visualCritic: lifecycleVisualCritic }),
|
|
213
|
-
...(bound.sourceLoader && { sourceLoader: bound.sourceLoader }),
|
|
214
|
-
...(bound.sourceProvenance && {
|
|
215
|
-
sourceProvenance: bound.sourceProvenance,
|
|
216
|
-
}),
|
|
217
|
-
reviewedSource: bound.reviewedSource,
|
|
218
|
-
});
|
|
219
|
-
const manifest = await readManifest(result, request);
|
|
220
|
-
return {
|
|
221
|
-
compatibility,
|
|
222
|
-
request,
|
|
223
|
-
manifest,
|
|
224
|
-
result,
|
|
225
|
-
marking: result.marking ?? null,
|
|
226
|
-
publication: result.publication ?? null,
|
|
227
|
-
outputRoot,
|
|
228
|
-
destination,
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
export function supportsAdaptiveSetPlanning(version) {
|
|
233
|
-
if (typeof version !== 'string') return false;
|
|
234
|
-
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-|$)/);
|
|
235
|
-
if (!match) return false;
|
|
236
|
-
const [major, minor, patch] = match.slice(1).map(Number);
|
|
237
|
-
const [minimumMajor, minimumMinor, minimumPatch] =
|
|
238
|
-
MINIMUM_CORE_VERSION.split('.').map(Number);
|
|
239
|
-
return (
|
|
240
|
-
major === minimumMajor &&
|
|
241
|
-
(minor > minimumMinor || (minor === minimumMinor && patch >= minimumPatch))
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
async function resolveLifecycleSetPlanner({
|
|
246
|
-
planSet,
|
|
247
|
-
planSetModulePath,
|
|
248
|
-
coreOptions,
|
|
249
|
-
required,
|
|
250
|
-
}) {
|
|
251
|
-
if (coreOptions?.planSet !== undefined) {
|
|
252
|
-
throw new TypeError(
|
|
253
|
-
'coreOptions.planSet is not supported at the OAT adapter boundary; supply planSet directly.',
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
if (planSet !== undefined && typeof planSet !== 'function') {
|
|
257
|
-
throw new TypeError('planSet must be a function when supplied.');
|
|
258
|
-
}
|
|
259
|
-
if (planSet !== undefined && planSetModulePath !== undefined) {
|
|
260
|
-
throw new Error(
|
|
261
|
-
'Supply only one provider-neutral set planner callback or set planner module entry point.',
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
if (planSet === undefined && planSetModulePath === undefined) {
|
|
265
|
-
if (!required) return null;
|
|
266
|
-
const error = new Error(
|
|
267
|
-
'Unattended OAT project recaps require exactly one provider-neutral set planner callback or set planner module entry point.',
|
|
268
|
-
);
|
|
269
|
-
error.code = 'E_SET_PLANNER_REQUIRED';
|
|
270
|
-
throw error;
|
|
271
|
-
}
|
|
272
|
-
if (planSetModulePath === undefined) {
|
|
273
|
-
return planSet;
|
|
274
|
-
}
|
|
275
|
-
if (
|
|
276
|
-
typeof planSetModulePath !== 'string' ||
|
|
277
|
-
planSetModulePath.trim().length === 0
|
|
278
|
-
) {
|
|
279
|
-
throw new TypeError('planSetModulePath must be a non-empty path.');
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
let plannerModule;
|
|
283
|
-
try {
|
|
284
|
-
plannerModule = await import(
|
|
285
|
-
pathToFileURL(resolve(planSetModulePath.trim())).href
|
|
286
|
-
);
|
|
287
|
-
} catch (cause) {
|
|
288
|
-
throw new Error(
|
|
289
|
-
`Unable to load provider-neutral set planner module at ${planSetModulePath}.`,
|
|
290
|
-
{ cause },
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
if (typeof plannerModule.planSet !== 'function') {
|
|
294
|
-
throw new TypeError(
|
|
295
|
-
'Provider-neutral set planner module must export a planSet function.',
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
return plannerModule.planSet;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
async function resolveLifecycleAuthor({
|
|
302
|
-
author,
|
|
303
|
-
authorModulePath,
|
|
304
|
-
coreOptions,
|
|
305
|
-
}) {
|
|
306
|
-
if (coreOptions?.author !== undefined) {
|
|
307
|
-
throw new TypeError(
|
|
308
|
-
'coreOptions.author is not supported at the OAT adapter boundary; supply author directly.',
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
if (author !== undefined && typeof author !== 'function') {
|
|
312
|
-
throw new TypeError('author must be a function when supplied.');
|
|
313
|
-
}
|
|
314
|
-
if (author !== undefined && authorModulePath !== undefined) {
|
|
315
|
-
throw new Error(
|
|
316
|
-
'Supply only one provider-neutral author callback or author module entry point.',
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (author === undefined && authorModulePath === undefined) {
|
|
321
|
-
const error = new Error(
|
|
322
|
-
'Unattended and interactive OAT explainer runs require exactly one provider-neutral author callback or author module entry point.',
|
|
323
|
-
);
|
|
324
|
-
error.code = 'E_AUTHOR_REQUIRED';
|
|
325
|
-
throw error;
|
|
326
|
-
}
|
|
327
|
-
if (authorModulePath === undefined) {
|
|
328
|
-
return author;
|
|
329
|
-
}
|
|
330
|
-
if (
|
|
331
|
-
typeof authorModulePath !== 'string' ||
|
|
332
|
-
authorModulePath.trim().length === 0
|
|
333
|
-
) {
|
|
334
|
-
throw new TypeError('authorModulePath must be a non-empty path.');
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
let authorModule;
|
|
338
|
-
try {
|
|
339
|
-
authorModule = await import(
|
|
340
|
-
pathToFileURL(resolve(authorModulePath.trim())).href
|
|
341
|
-
);
|
|
342
|
-
} catch (cause) {
|
|
343
|
-
throw new Error(
|
|
344
|
-
`Unable to load provider-neutral author module at ${authorModulePath}.`,
|
|
345
|
-
{ cause },
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
if (typeof authorModule.author !== 'function') {
|
|
349
|
-
throw new TypeError(
|
|
350
|
-
'Provider-neutral author module must export an author function.',
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
return authorModule.author;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
async function resolveLifecycleCritic({
|
|
357
|
-
critic,
|
|
358
|
-
criticModulePath,
|
|
359
|
-
coreOptions,
|
|
360
|
-
}) {
|
|
361
|
-
const candidates = [
|
|
362
|
-
typeof critic === 'function' ? critic : null,
|
|
363
|
-
typeof coreOptions?.critic === 'function' ? coreOptions.critic : null,
|
|
364
|
-
criticModulePath ? criticModulePath : null,
|
|
365
|
-
].filter(Boolean);
|
|
366
|
-
if (candidates.length > 1) {
|
|
367
|
-
throw new Error(
|
|
368
|
-
'Supply only one provider-neutral critic callback or critic module entry point.',
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
if (critic !== undefined && typeof critic !== 'function') {
|
|
372
|
-
throw new TypeError('critic must be a function when supplied.');
|
|
373
|
-
}
|
|
374
|
-
if (
|
|
375
|
-
coreOptions?.critic !== undefined &&
|
|
376
|
-
typeof coreOptions.critic !== 'function'
|
|
377
|
-
) {
|
|
378
|
-
throw new TypeError('coreOptions.critic must be a function when supplied.');
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
let callback = critic ?? coreOptions?.critic;
|
|
382
|
-
if (criticModulePath !== undefined) {
|
|
383
|
-
if (
|
|
384
|
-
typeof criticModulePath !== 'string' ||
|
|
385
|
-
criticModulePath.trim().length === 0
|
|
386
|
-
) {
|
|
387
|
-
throw new TypeError('criticModulePath must be a non-empty path.');
|
|
388
|
-
}
|
|
389
|
-
let criticModule;
|
|
390
|
-
try {
|
|
391
|
-
criticModule = await import(
|
|
392
|
-
pathToFileURL(resolve(criticModulePath)).href
|
|
393
|
-
);
|
|
394
|
-
} catch (cause) {
|
|
395
|
-
throw new Error(
|
|
396
|
-
`Unable to load provider-neutral critic module at ${criticModulePath}.`,
|
|
397
|
-
{ cause },
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
if (typeof criticModule.critic !== 'function') {
|
|
401
|
-
throw new TypeError(
|
|
402
|
-
'Provider-neutral critic module must export a critic function.',
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
callback = criticModule.critic;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
if (!callback) {
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
const wrapped = async (request) => {
|
|
412
|
-
const result = await callback(request);
|
|
413
|
-
if (
|
|
414
|
-
!result ||
|
|
415
|
-
typeof result !== 'object' ||
|
|
416
|
-
typeof result.criticId !== 'string' ||
|
|
417
|
-
!/^[a-z0-9][a-z0-9._-]*$/.test(result.criticId) ||
|
|
418
|
-
!Array.isArray(result.findings) ||
|
|
419
|
-
(result.executedAt !== undefined &&
|
|
420
|
-
(typeof result.executedAt !== 'string' ||
|
|
421
|
-
Number.isNaN(Date.parse(result.executedAt))))
|
|
422
|
-
) {
|
|
423
|
-
throw new Error(
|
|
424
|
-
'Provider-neutral critic result does not match the critic result contract.',
|
|
425
|
-
);
|
|
426
|
-
}
|
|
427
|
-
return result;
|
|
428
|
-
};
|
|
429
|
-
return markProviderIdentity(wrapped, callback);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
async function resolveLifecycleBrowserSession({
|
|
433
|
-
core,
|
|
434
|
-
browserSession,
|
|
435
|
-
browserSessionModulePath,
|
|
436
|
-
browserProbe,
|
|
437
|
-
browserProbeModulePath,
|
|
438
|
-
coreOptions,
|
|
439
|
-
required,
|
|
440
|
-
}) {
|
|
441
|
-
if (coreOptions?.browserProbe !== undefined) {
|
|
442
|
-
throw new TypeError(
|
|
443
|
-
'coreOptions.browserProbe is not supported at the OAT adapter boundary; supply browserSession directly.',
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
if (browserProbe !== undefined || browserProbeModulePath !== undefined) {
|
|
447
|
-
throw new TypeError(
|
|
448
|
-
'Bare browserProbe callbacks are not supported at the OAT adapter boundary; supply a branded browserSession created by createBrowserProbeSession().',
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
if (coreOptions?.browserSession !== undefined) {
|
|
452
|
-
throw new TypeError(
|
|
453
|
-
'coreOptions.browserSession is not supported at the OAT adapter boundary; supply browserSession directly.',
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
|
-
if (browserSession !== undefined && browserSessionModulePath !== undefined) {
|
|
457
|
-
throw new Error(
|
|
458
|
-
'Supply only one browser session descriptor or browser session module entry point.',
|
|
459
|
-
);
|
|
460
|
-
}
|
|
461
|
-
let resolvedSession = browserSession;
|
|
462
|
-
if (browserSessionModulePath !== undefined) {
|
|
463
|
-
if (
|
|
464
|
-
typeof browserSessionModulePath !== 'string' ||
|
|
465
|
-
browserSessionModulePath.trim().length === 0
|
|
466
|
-
) {
|
|
467
|
-
throw new TypeError('browserSessionModulePath must be a non-empty path.');
|
|
468
|
-
}
|
|
469
|
-
let sessionModule;
|
|
470
|
-
try {
|
|
471
|
-
sessionModule = await import(
|
|
472
|
-
pathToFileURL(resolve(browserSessionModulePath.trim())).href
|
|
473
|
-
);
|
|
474
|
-
} catch (cause) {
|
|
475
|
-
throw new Error(
|
|
476
|
-
`Unable to load browser session module at ${browserSessionModulePath}.`,
|
|
477
|
-
{ cause },
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
resolvedSession = sessionModule.browserSession;
|
|
481
|
-
}
|
|
482
|
-
if (!resolvedSession) {
|
|
483
|
-
if (!required) return null;
|
|
484
|
-
const error = new Error(
|
|
485
|
-
'Unattended OAT project recaps require exactly one branded launched-Chromium browser session descriptor or browser session module entry point.',
|
|
486
|
-
);
|
|
487
|
-
error.code = 'E_BROWSER_PROBE_REQUIRED';
|
|
488
|
-
throw error;
|
|
489
|
-
}
|
|
490
|
-
if (typeof core.assertBrowserProbeSession !== 'function') {
|
|
491
|
-
const error = new Error(
|
|
492
|
-
'Compatible explainer-kit does not export browser session validation.',
|
|
493
|
-
);
|
|
494
|
-
error.code = 'E_CORE_INCOMPATIBLE';
|
|
495
|
-
throw error;
|
|
496
|
-
}
|
|
497
|
-
try {
|
|
498
|
-
return core.assertBrowserProbeSession(resolvedSession, {
|
|
499
|
-
allowFixture: !required,
|
|
500
|
-
});
|
|
501
|
-
} catch {
|
|
502
|
-
const error = new Error('Browser session validation failed.');
|
|
503
|
-
error.code = 'E_BROWSER_PROBE_REQUIRED';
|
|
504
|
-
throw error;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
async function resolveLifecycleVisualCritic({
|
|
509
|
-
visualCritic,
|
|
510
|
-
visualCriticModulePath,
|
|
511
|
-
coreOptions,
|
|
512
|
-
required,
|
|
513
|
-
}) {
|
|
514
|
-
if (coreOptions?.visualCritic !== undefined) {
|
|
515
|
-
throw new TypeError(
|
|
516
|
-
'coreOptions.visualCritic is not supported at the OAT adapter boundary; supply visualCritic directly.',
|
|
517
|
-
);
|
|
518
|
-
}
|
|
519
|
-
const callback = await resolveProviderCallback({
|
|
520
|
-
callback: visualCritic,
|
|
521
|
-
modulePath: visualCriticModulePath,
|
|
522
|
-
exportName: 'visualCritic',
|
|
523
|
-
label: 'visual critic',
|
|
524
|
-
});
|
|
525
|
-
if (!callback) {
|
|
526
|
-
if (!required) return null;
|
|
527
|
-
const error = new Error(
|
|
528
|
-
'Unattended OAT project recaps require exactly one provider-neutral visual critic callback or visual critic module entry point.',
|
|
529
|
-
);
|
|
530
|
-
error.code = 'E_VISUAL_CRITIC_REQUIRED';
|
|
531
|
-
throw error;
|
|
532
|
-
}
|
|
533
|
-
const wrapped = async (request, evidenceInput) => {
|
|
534
|
-
const result = await callback(request, evidenceInput);
|
|
535
|
-
if (
|
|
536
|
-
!result ||
|
|
537
|
-
typeof result !== 'object' ||
|
|
538
|
-
result.schemaVersion !== 'explainer-kit.visual-review-result/v1' ||
|
|
539
|
-
typeof result.reviewId !== 'string' ||
|
|
540
|
-
!/^[a-z0-9][a-z0-9._-]*$/.test(result.reviewId) ||
|
|
541
|
-
result.requestId !== request?.requestId ||
|
|
542
|
-
result.requestHash !== request?.requestHash ||
|
|
543
|
-
typeof result.reviewedAt !== 'string' ||
|
|
544
|
-
Number.isNaN(Date.parse(result.reviewedAt)) ||
|
|
545
|
-
!['pass', 'correct', 'fail'].includes(result.disposition) ||
|
|
546
|
-
!Array.isArray(result.artifactIds) ||
|
|
547
|
-
result.artifactIds.length !== request?.renderedArtifacts?.length ||
|
|
548
|
-
result.artifactIds.some(
|
|
549
|
-
(artifactId, index) =>
|
|
550
|
-
artifactId !== request.renderedArtifacts[index]?.artifactId,
|
|
551
|
-
) ||
|
|
552
|
-
!Array.isArray(result.findings) ||
|
|
553
|
-
result.findings.some((finding) => !finding || typeof finding !== 'object')
|
|
554
|
-
) {
|
|
555
|
-
throw new Error(
|
|
556
|
-
'Provider-neutral visual critic result does not match the visual review result contract.',
|
|
557
|
-
);
|
|
558
|
-
}
|
|
559
|
-
return result;
|
|
560
|
-
};
|
|
561
|
-
return markProviderIdentity(wrapped, callback);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
async function resolveProviderCallback({
|
|
565
|
-
callback,
|
|
566
|
-
modulePath,
|
|
567
|
-
exportName,
|
|
568
|
-
label,
|
|
569
|
-
}) {
|
|
570
|
-
if (callback !== undefined && typeof callback !== 'function') {
|
|
571
|
-
throw new TypeError(`${exportName} must be a function when supplied.`);
|
|
572
|
-
}
|
|
573
|
-
if (callback !== undefined && modulePath !== undefined) {
|
|
574
|
-
throw new Error(
|
|
575
|
-
`Supply only one provider-neutral ${label} callback or ${label} module entry point.`,
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
if (modulePath === undefined) return callback ?? null;
|
|
579
|
-
if (typeof modulePath !== 'string' || modulePath.trim().length === 0) {
|
|
580
|
-
throw new TypeError(`${exportName}ModulePath must be a non-empty path.`);
|
|
581
|
-
}
|
|
582
|
-
let providerModule;
|
|
583
|
-
try {
|
|
584
|
-
providerModule = await import(
|
|
585
|
-
pathToFileURL(resolve(modulePath.trim())).href
|
|
586
|
-
);
|
|
587
|
-
} catch (cause) {
|
|
588
|
-
throw new Error(
|
|
589
|
-
`Unable to load provider-neutral ${label} module at ${modulePath}.`,
|
|
590
|
-
{ cause },
|
|
591
|
-
);
|
|
592
|
-
}
|
|
593
|
-
if (typeof providerModule[exportName] !== 'function') {
|
|
594
|
-
throw new TypeError(
|
|
595
|
-
`Provider-neutral ${label} module must export a ${exportName} function.`,
|
|
596
|
-
);
|
|
597
|
-
}
|
|
598
|
-
return providerModule[exportName];
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
const PROVIDER_IDENTITY = Symbol('oat-explainer-provider-identity');
|
|
602
|
-
|
|
603
|
-
function markProviderIdentity(wrapper, callback) {
|
|
604
|
-
Object.defineProperty(wrapper, PROVIDER_IDENTITY, { value: callback });
|
|
605
|
-
return wrapper;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function providerIdentity(callback) {
|
|
609
|
-
return callback?.[PROVIDER_IDENTITY] ?? callback;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
function assertDistinctProviderRoles(callbacks) {
|
|
613
|
-
const entries = Object.entries(callbacks).filter(([, callback]) => callback);
|
|
614
|
-
for (let left = 0; left < entries.length; left += 1) {
|
|
615
|
-
for (let right = left + 1; right < entries.length; right += 1) {
|
|
616
|
-
if (
|
|
617
|
-
providerIdentity(entries[left][1]) ===
|
|
618
|
-
providerIdentity(entries[right][1])
|
|
619
|
-
) {
|
|
620
|
-
throw new Error(
|
|
621
|
-
`Provider roles ${entries[left][0]} and ${entries[right][0]} must use distinct callback identities.`,
|
|
622
|
-
);
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
async function readManifest(result, request) {
|
|
629
|
-
if (
|
|
630
|
-
!result ||
|
|
631
|
-
typeof result !== 'object' ||
|
|
632
|
-
typeof result.manifestPath !== 'string'
|
|
633
|
-
) {
|
|
634
|
-
throw new Error('explainer-kit returned no manifestPath.');
|
|
635
|
-
}
|
|
636
|
-
let manifest;
|
|
637
|
-
try {
|
|
638
|
-
manifest = JSON.parse(await readFile(result.manifestPath, 'utf8'));
|
|
639
|
-
} catch (error) {
|
|
640
|
-
if (
|
|
641
|
-
error?.code === 'ENOENT' &&
|
|
642
|
-
(result.outcome === 'failed' || result.outcome === 'incomplete')
|
|
643
|
-
) {
|
|
644
|
-
return null;
|
|
645
|
-
}
|
|
646
|
-
throw error;
|
|
647
|
-
}
|
|
648
|
-
if (manifest.schemaVersion !== 'explainer-kit.manifest/v1') {
|
|
649
|
-
throw new Error(
|
|
650
|
-
`Unsupported core manifest version: ${manifest.schemaVersion ?? 'missing'}.`,
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
if (
|
|
654
|
-
manifest.recipe?.id !== request.recipe.id ||
|
|
655
|
-
manifest.recipe?.version !== request.recipe.version
|
|
656
|
-
) {
|
|
657
|
-
throw new Error(
|
|
658
|
-
'Core manifest recipe does not match the normalized request.',
|
|
659
|
-
);
|
|
660
|
-
}
|
|
661
|
-
return manifest;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
export async function runOatExplainerCli(
|
|
665
|
-
argv = process.argv.slice(2),
|
|
666
|
-
io = console,
|
|
667
|
-
run = runOatExplainer,
|
|
668
|
-
) {
|
|
669
|
-
try {
|
|
670
|
-
if (argv.length !== 2 || argv[0] !== '--context') {
|
|
671
|
-
throw new Error('Usage: run.mjs --context <adapter-context.json>');
|
|
672
|
-
}
|
|
673
|
-
const context = JSON.parse(await readFile(argv[1], 'utf8'));
|
|
674
|
-
const result = await run(context);
|
|
675
|
-
io.log(JSON.stringify(projectAdapterCliResult(result), null, 2));
|
|
676
|
-
return result.result.outcome === 'failed' ? 1 : 0;
|
|
677
|
-
} catch {
|
|
678
|
-
io.error(
|
|
679
|
-
JSON.stringify(
|
|
680
|
-
{
|
|
681
|
-
outcome: 'failed',
|
|
682
|
-
reasons: [
|
|
683
|
-
{
|
|
684
|
-
stage: 'finalization',
|
|
685
|
-
kind: 'pipeline-failure',
|
|
686
|
-
count: 1,
|
|
687
|
-
},
|
|
688
|
-
],
|
|
689
|
-
},
|
|
690
|
-
null,
|
|
691
|
-
2,
|
|
692
|
-
),
|
|
693
|
-
);
|
|
694
|
-
return 1;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
function projectAdapterCliResult(value) {
|
|
699
|
-
if (!isObject(value) || !isObject(value.result)) {
|
|
700
|
-
return {
|
|
701
|
-
result: {
|
|
702
|
-
outcome: 'failed',
|
|
703
|
-
reasons: [
|
|
704
|
-
{
|
|
705
|
-
stage: 'finalization',
|
|
706
|
-
kind: 'pipeline-failure',
|
|
707
|
-
count: 1,
|
|
708
|
-
},
|
|
709
|
-
],
|
|
710
|
-
},
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
const projected = {};
|
|
714
|
-
for (const key of ['compatibility', 'request', 'manifest', 'destination']) {
|
|
715
|
-
if (isObject(value[key])) projected[key] = structuredClone(value[key]);
|
|
716
|
-
}
|
|
717
|
-
for (const key of ['marking', 'outputRoot']) {
|
|
718
|
-
if (typeof value[key] === 'string' || value[key] === null) {
|
|
719
|
-
projected[key] = value[key];
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
projected.result = projectAdapterRunResult(value.result);
|
|
723
|
-
const publication = projectAdapterPublication(value.publication);
|
|
724
|
-
if (publication) projected.publication = publication;
|
|
725
|
-
return projected;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
function projectAdapterRunResult(value) {
|
|
729
|
-
const projected = {};
|
|
730
|
-
for (const key of [
|
|
731
|
-
'runId',
|
|
732
|
-
'runRoot',
|
|
733
|
-
'manifestPath',
|
|
734
|
-
'buildRecordPath',
|
|
735
|
-
'outcome',
|
|
736
|
-
'marking',
|
|
737
|
-
]) {
|
|
738
|
-
if (typeof value[key] === 'string') projected[key] = value[key];
|
|
739
|
-
}
|
|
740
|
-
if (Array.isArray(value.warnings)) {
|
|
741
|
-
projected.warnings = [
|
|
742
|
-
...new Set(value.warnings.filter(isAdapterWarningCode)),
|
|
743
|
-
];
|
|
744
|
-
}
|
|
745
|
-
for (const key of ['discovery', 'approval']) {
|
|
746
|
-
if (isObject(value[key])) projected[key] = structuredClone(value[key]);
|
|
747
|
-
}
|
|
748
|
-
const reasons = projectAdapterReasons(value.reasons);
|
|
749
|
-
if (reasons.length > 0) projected.reasons = reasons;
|
|
750
|
-
const visualReview = projectAdapterVisualReview(value.visualReview);
|
|
751
|
-
if (visualReview) projected.visualReview = visualReview;
|
|
752
|
-
const publication = projectAdapterPublication(value.publication);
|
|
753
|
-
if (publication) projected.publication = publication;
|
|
754
|
-
return projected;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function projectAdapterVisualReview(value) {
|
|
758
|
-
if (!isObject(value)) return null;
|
|
759
|
-
const reasons = projectAdapterReasons(value.reasons);
|
|
760
|
-
if (
|
|
761
|
-
value.schemaVersion !== 'explainer-kit.visual-review-evidence/v1' ||
|
|
762
|
-
typeof value.requestHash !== 'string' ||
|
|
763
|
-
![1, 2].includes(value.attempt) ||
|
|
764
|
-
!['pass', 'correct', 'failed'].includes(value.disposition) ||
|
|
765
|
-
!Array.isArray(value.reasons) ||
|
|
766
|
-
reasons.length !== value.reasons.length
|
|
767
|
-
) {
|
|
768
|
-
return null;
|
|
769
|
-
}
|
|
770
|
-
return {
|
|
771
|
-
schemaVersion: value.schemaVersion,
|
|
772
|
-
requestHash: value.requestHash,
|
|
773
|
-
attempt: value.attempt,
|
|
774
|
-
disposition: value.disposition,
|
|
775
|
-
reasons,
|
|
776
|
-
};
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
function projectAdapterPublication(value) {
|
|
780
|
-
if (!isObject(value)) return null;
|
|
781
|
-
if (
|
|
782
|
-
value.schemaVersion !== 'explainer-kit.publish-summary/v1' &&
|
|
783
|
-
value.schemaVersion !== 'explainer-kit.publish-summary/v2'
|
|
784
|
-
) {
|
|
785
|
-
return null;
|
|
786
|
-
}
|
|
787
|
-
return {
|
|
788
|
-
schemaVersion: value.schemaVersion,
|
|
789
|
-
...(typeof value.receiptSchemaVersion === 'string' && {
|
|
790
|
-
receiptSchemaVersion: value.receiptSchemaVersion,
|
|
791
|
-
}),
|
|
792
|
-
...(typeof value.publicAccess === 'string' && {
|
|
793
|
-
publicAccess: value.publicAccess,
|
|
794
|
-
}),
|
|
795
|
-
...(Array.isArray(value.artifacts) && {
|
|
796
|
-
artifacts: value.artifacts.map((artifact) =>
|
|
797
|
-
isObject(artifact)
|
|
798
|
-
? {
|
|
799
|
-
...pickAdapterStringFields(artifact, [
|
|
800
|
-
'relativePath',
|
|
801
|
-
'publicUrl',
|
|
802
|
-
's3Uri',
|
|
803
|
-
'hash',
|
|
804
|
-
'contentType',
|
|
805
|
-
]),
|
|
806
|
-
...(isObject(artifact.source) && {
|
|
807
|
-
source: pickAdapterStringFields(artifact.source, [
|
|
808
|
-
'kind',
|
|
809
|
-
'artifactId',
|
|
810
|
-
'name',
|
|
811
|
-
]),
|
|
812
|
-
}),
|
|
813
|
-
...(isObject(artifact.objectVerification) && {
|
|
814
|
-
objectVerification: projectAdapterVerification(
|
|
815
|
-
artifact.objectVerification,
|
|
816
|
-
),
|
|
817
|
-
}),
|
|
818
|
-
...(isObject(artifact.publicVerification) && {
|
|
819
|
-
publicVerification: projectAdapterVerification(
|
|
820
|
-
artifact.publicVerification,
|
|
821
|
-
),
|
|
822
|
-
}),
|
|
823
|
-
}
|
|
824
|
-
: {},
|
|
825
|
-
),
|
|
826
|
-
}),
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
function projectAdapterVerification(value) {
|
|
831
|
-
return {
|
|
832
|
-
...pickAdapterStringFields(value, ['status', 'method', 'hash']),
|
|
833
|
-
...(Number.isInteger(value.httpStatus) && {
|
|
834
|
-
httpStatus: value.httpStatus,
|
|
835
|
-
}),
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
function projectAdapterReasons(value) {
|
|
840
|
-
if (!Array.isArray(value)) return [];
|
|
841
|
-
return value.flatMap((reason) => {
|
|
842
|
-
if (
|
|
843
|
-
!isObject(reason) ||
|
|
844
|
-
![
|
|
845
|
-
'planning',
|
|
846
|
-
'authoring',
|
|
847
|
-
'rendering',
|
|
848
|
-
'link-validation',
|
|
849
|
-
'browser-review',
|
|
850
|
-
'visual-review',
|
|
851
|
-
'durability',
|
|
852
|
-
'finalization',
|
|
853
|
-
].includes(reason.stage) ||
|
|
854
|
-
![
|
|
855
|
-
'finding',
|
|
856
|
-
'provider-failure',
|
|
857
|
-
'pipeline-failure',
|
|
858
|
-
'superseded',
|
|
859
|
-
].includes(reason.kind) ||
|
|
860
|
-
!Number.isInteger(reason.count) ||
|
|
861
|
-
reason.count < 1 ||
|
|
862
|
-
reason.count > 50
|
|
863
|
-
) {
|
|
864
|
-
return [];
|
|
865
|
-
}
|
|
866
|
-
return [
|
|
867
|
-
{
|
|
868
|
-
stage: reason.stage,
|
|
869
|
-
kind: reason.kind,
|
|
870
|
-
...(typeof reason.artifactId === 'string' && {
|
|
871
|
-
artifactId: reason.artifactId,
|
|
872
|
-
}),
|
|
873
|
-
count: reason.count,
|
|
874
|
-
},
|
|
875
|
-
];
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
function pickAdapterStringFields(value, keys) {
|
|
880
|
-
return Object.fromEntries(
|
|
881
|
-
keys.flatMap((key) =>
|
|
882
|
-
typeof value[key] === 'string' ? [[key, value[key]]] : [],
|
|
883
|
-
),
|
|
884
|
-
);
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
function isAdapterWarningCode(value) {
|
|
888
|
-
return (
|
|
889
|
-
typeof value === 'string' &&
|
|
890
|
-
/^(?:fact-base-freshness-warning|theme-selection-normalized|durability-evidence-required|publish-receipt-evidence-required|(?:expansion|guideline|render|qa)-[a-z0-9-]+|visual-review-required:[a-z0-9-]+|stage-reopened:[a-z0-9-]+:[0-9TZ:.-]+|missing-(?:theme-token|required-anchor):[a-z0-9-]+)$/.test(
|
|
891
|
-
value,
|
|
892
|
-
)
|
|
893
|
-
);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
function isObject(value) {
|
|
897
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
if (
|
|
901
|
-
process.argv[1] &&
|
|
902
|
-
pathToFileURL(process.argv[1]).href === import.meta.url
|
|
903
|
-
) {
|
|
904
|
-
process.exitCode = await runOatExplainerCli();
|
|
905
|
-
}
|