@farmslot/recipe-harness 0.7.0 → 0.9.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 +17 -0
- package/README.md +83 -182
- package/dist/adapters/app-lifecycle.d.ts.map +1 -1
- package/dist/adapters/app-lifecycle.js +5 -0
- package/dist/adapters/app-lifecycle.js.map +1 -1
- package/dist/adapters/core.d.ts.map +1 -1
- package/dist/adapters/core.js +25 -46
- package/dist/adapters/core.js.map +1 -1
- package/dist/adapters/ui.d.ts +0 -2
- package/dist/adapters/ui.d.ts.map +1 -1
- package/dist/adapters/ui.js +5 -2
- package/dist/adapters/ui.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +0 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/run-command.d.ts.map +1 -1
- package/dist/cli/run-command.js +197 -32
- package/dist/cli/run-command.js.map +1 -1
- package/dist/cli/support.d.ts +5 -2
- package/dist/cli/support.d.ts.map +1 -1
- package/dist/cli/support.js +131 -25
- package/dist/cli/support.js.map +1 -1
- package/dist/cli/validate-command.d.ts.map +1 -1
- package/dist/cli/validate-command.js +4 -1
- package/dist/cli/validate-command.js.map +1 -1
- package/dist/core/compose.d.ts +21 -31
- package/dist/core/compose.d.ts.map +1 -1
- package/dist/core/compose.js +93 -125
- package/dist/core/compose.js.map +1 -1
- package/dist/core/execution.d.ts +40 -0
- package/dist/core/execution.d.ts.map +1 -0
- package/dist/core/execution.js +263 -0
- package/dist/core/execution.js.map +1 -0
- package/dist/core/graph.d.ts +4 -5
- package/dist/core/graph.d.ts.map +1 -1
- package/dist/core/graph.js +51 -103
- package/dist/core/graph.js.map +1 -1
- package/dist/core/hud.d.ts.map +1 -1
- package/dist/core/hud.js +2 -6
- package/dist/core/hud.js.map +1 -1
- package/dist/core/library.d.ts +17 -51
- package/dist/core/library.d.ts.map +1 -1
- package/dist/core/library.js +144 -155
- package/dist/core/library.js.map +1 -1
- package/dist/core/parameters.d.ts +5 -0
- package/dist/core/parameters.d.ts.map +1 -0
- package/dist/core/parameters.js +57 -0
- package/dist/core/parameters.js.map +1 -0
- package/dist/core/passive-observations.d.ts +2 -5
- package/dist/core/passive-observations.d.ts.map +1 -1
- package/dist/core/passive-observations.js +6 -40
- package/dist/core/passive-observations.js.map +1 -1
- package/dist/core/path.d.ts +6 -0
- package/dist/core/path.d.ts.map +1 -0
- package/dist/core/path.js +126 -0
- package/dist/core/path.js.map +1 -0
- package/dist/core/recording-cleanup.d.ts +1 -0
- package/dist/core/recording-cleanup.d.ts.map +1 -1
- package/dist/core/recording-cleanup.js +8 -0
- package/dist/core/recording-cleanup.js.map +1 -1
- package/dist/core/resolution-error.d.ts +7 -0
- package/dist/core/resolution-error.d.ts.map +1 -0
- package/dist/core/resolution-error.js +9 -0
- package/dist/core/resolution-error.js.map +1 -0
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +178 -373
- package/dist/core/runner.js.map +1 -1
- package/dist/core/trace.d.ts +3 -4
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/core/trace.js +3 -4
- package/dist/core/trace.js.map +1 -1
- package/dist/core/trust-error.d.ts +9 -0
- package/dist/core/trust-error.d.ts.map +1 -0
- package/dist/core/trust-error.js +18 -0
- package/dist/core/trust-error.js.map +1 -0
- package/dist/core/trust-input.d.ts +20 -0
- package/dist/core/trust-input.d.ts.map +1 -0
- package/dist/core/trust-input.js +68 -0
- package/dist/core/trust-input.js.map +1 -0
- package/dist/core/trust.d.ts +20 -0
- package/dist/core/trust.d.ts.map +1 -0
- package/dist/core/trust.js +231 -0
- package/dist/core/trust.js.map +1 -0
- package/dist/core/types.d.ts +41 -47
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/node/writers.d.ts +3 -8
- package/dist/node/writers.d.ts.map +1 -1
- package/dist/node/writers.js +27 -26
- package/dist/node/writers.js.map +1 -1
- package/dist/recording/cdp-video-recorder.js +2 -3
- package/dist/recording/cdp-video-recorder.js.map +1 -1
- package/dist/runtime/cdp.d.ts.map +1 -1
- package/dist/runtime/cdp.js +3 -7
- package/dist/runtime/cdp.js.map +1 -1
- package/package.json +2 -2
- package/dist/cli/flows-command.d.ts +0 -3
- package/dist/cli/flows-command.d.ts.map +0 -1
- package/dist/cli/flows-command.js +0 -313
- package/dist/cli/flows-command.js.map +0 -1
- package/dist/core/flows.d.ts +0 -45
- package/dist/core/flows.d.ts.map +0 -1
- package/dist/core/flows.js +0 -359
- package/dist/core/flows.js.map +0 -1
- package/dist/core/predicates.d.ts +0 -7
- package/dist/core/predicates.d.ts.map +0 -1
- package/dist/core/predicates.js +0 -102
- package/dist/core/predicates.js.map +0 -1
- package/dist/core/promote.d.ts +0 -41
- package/dist/core/promote.d.ts.map +0 -1
- package/dist/core/promote.js +0 -249
- package/dist/core/promote.js.map +0 -1
package/dist/cli/support.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { readdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { isRecord, mergeRecipeValidationResults, validateArtifactManifestDocument, validateRecipeArtifactPackage, validateRecipeDocument, validateRecipeWithManifest, } from '@farmslot/protocol';
|
|
3
|
+
import { applyRecipeParamDefaults, digestRecipeDocument, isRecord, mergeRecipeValidationResults, resolvedRecipeArtifactPath, validateArtifactManifestDocument, validateRecipeArtifactPackage, validateRecipeDocument, validateRecipeParams, validateRecipeWithManifest, } from '@farmslot/protocol';
|
|
4
|
+
import { resolveRecipeDependencies, rootResolutionRef, validateRecipeDependencyParams, } from '../core/compose.js';
|
|
4
5
|
import { loadRecipeLibraries } from '../core/library.js';
|
|
6
|
+
import { readFileWithinRoot } from '../core/path.js';
|
|
7
|
+
import { RecipeResolutionError } from '../core/resolution-error.js';
|
|
5
8
|
export function recipeCliBaseDir() {
|
|
6
9
|
return process.env.INIT_CWD ?? process.cwd();
|
|
7
10
|
}
|
|
@@ -15,6 +18,36 @@ export function parsePositiveInteger(value) {
|
|
|
15
18
|
}
|
|
16
19
|
return parsed;
|
|
17
20
|
}
|
|
21
|
+
export function parseRecipeParamAssignments(values) {
|
|
22
|
+
const params = {};
|
|
23
|
+
for (const value of values) {
|
|
24
|
+
const separator = value.indexOf('=');
|
|
25
|
+
if (separator <= 0) {
|
|
26
|
+
throw new Error(`Recipe parameter ${JSON.stringify(value)} must use key=value.`);
|
|
27
|
+
}
|
|
28
|
+
const key = value.slice(0, separator).trim();
|
|
29
|
+
if (!key || Object.hasOwn(params, key)) {
|
|
30
|
+
throw new Error(Object.hasOwn(params, key)
|
|
31
|
+
? `Recipe parameter ${key} was provided more than once.`
|
|
32
|
+
: `Recipe parameter ${JSON.stringify(value)} has an empty key.`);
|
|
33
|
+
}
|
|
34
|
+
const raw = value.slice(separator + 1);
|
|
35
|
+
let parsed;
|
|
36
|
+
try {
|
|
37
|
+
parsed = JSON.parse(raw);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
parsed = raw;
|
|
41
|
+
}
|
|
42
|
+
Object.defineProperty(params, key, {
|
|
43
|
+
value: parsed,
|
|
44
|
+
enumerable: true,
|
|
45
|
+
configurable: true,
|
|
46
|
+
writable: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return params;
|
|
50
|
+
}
|
|
18
51
|
export function parseRecordingTarget(options) {
|
|
19
52
|
if (options.recordPid)
|
|
20
53
|
return { kind: 'pid', pid: parsePositiveInteger(options.recordPid) };
|
|
@@ -44,19 +77,26 @@ export async function readRecipeCliJsonFile(filePath, baseDir = recipeCliBaseDir
|
|
|
44
77
|
}
|
|
45
78
|
return parseRecipeCliJsonText(filePath, text);
|
|
46
79
|
}
|
|
47
|
-
async function
|
|
48
|
-
const resolvedPath = resolveRecipeCliPath(filePath, baseDir);
|
|
80
|
+
export async function readRecipeCliJsonFileWithinRoot(root, relativePath) {
|
|
49
81
|
let text;
|
|
50
82
|
try {
|
|
51
|
-
text = await
|
|
83
|
+
text = (await readFileWithinRoot(root, relativePath)).toString('utf-8');
|
|
52
84
|
}
|
|
53
85
|
catch (error) {
|
|
54
|
-
if (error?.code === 'ENOENT')
|
|
55
|
-
return undefined;
|
|
56
86
|
const message = error instanceof Error ? error.message : String(error);
|
|
57
|
-
throw new Error(`Failed to read JSON from ${
|
|
87
|
+
throw new Error(`Failed to read JSON from ${relativePath}: ${message}`, { cause: error });
|
|
88
|
+
}
|
|
89
|
+
return parseRecipeCliJsonText(relativePath, text);
|
|
90
|
+
}
|
|
91
|
+
async function readOptionalRecipeCliJsonFileWithinRoot(root, relativePath) {
|
|
92
|
+
try {
|
|
93
|
+
return await readRecipeCliJsonFileWithinRoot(root, relativePath);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (error?.cause?.code === 'ENOENT')
|
|
97
|
+
return undefined;
|
|
98
|
+
throw error;
|
|
58
99
|
}
|
|
59
|
-
return parseRecipeCliJsonText(filePath, text);
|
|
60
100
|
}
|
|
61
101
|
function parseRecipeCliJsonText(filePath, text) {
|
|
62
102
|
try {
|
|
@@ -67,12 +107,44 @@ function parseRecipeCliJsonText(filePath, text) {
|
|
|
67
107
|
throw new Error(`Failed to parse JSON from ${filePath}: ${message}`);
|
|
68
108
|
}
|
|
69
109
|
}
|
|
70
|
-
export async function validateRecipeCliInput({ recipePath, actionManifestPath, artifactManifestPath, artifactDir, baseDir = recipeCliBaseDir(), librarySources, }) {
|
|
110
|
+
export async function validateRecipeCliInput({ recipePath, actionManifestPath, artifactManifestPath, artifactDir, baseDir = recipeCliBaseDir(), librarySources, params = {}, }) {
|
|
71
111
|
const recipe = await readRecipeCliJsonFile(recipePath, baseDir);
|
|
72
112
|
const results = [];
|
|
73
|
-
const
|
|
74
|
-
?
|
|
113
|
+
const effectiveParams = isRecord(recipe)
|
|
114
|
+
? applyRecipeParamDefaults(params, recipe.paramsSchema)
|
|
115
|
+
: params;
|
|
116
|
+
if (isRecord(recipe))
|
|
117
|
+
results.push(validateRecipeParams(effectiveParams, recipe.paramsSchema));
|
|
118
|
+
const artifactRoot = artifactDir ? resolveRecipeCliPath(artifactDir, baseDir) : undefined;
|
|
119
|
+
const artifactPaths = artifactRoot ? await listRelativeRecipeCliFiles(artifactRoot) : undefined;
|
|
120
|
+
const recipeResolution = artifactRoot
|
|
121
|
+
? await readOptionalRecipeCliJsonFileWithinRoot(artifactRoot, 'recipe-resolution.json')
|
|
75
122
|
: undefined;
|
|
123
|
+
const resolvedRecipes = {};
|
|
124
|
+
if (artifactRoot && isRecord(recipeResolution) && Array.isArray(recipeResolution.dependencies)) {
|
|
125
|
+
for (const dependency of recipeResolution.dependencies) {
|
|
126
|
+
if (!isRecord(dependency))
|
|
127
|
+
continue;
|
|
128
|
+
const artifact = resolvedRecipeArtifactPath(dependency.digest);
|
|
129
|
+
if (!artifact)
|
|
130
|
+
continue;
|
|
131
|
+
const document = await readOptionalRecipeCliJsonFileWithinRoot(artifactRoot, artifact);
|
|
132
|
+
if (document !== undefined)
|
|
133
|
+
resolvedRecipes[String(dependency.digest)] = document;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const libraryResolution = librarySources && librarySources.length > 0
|
|
137
|
+
? await loadRecipeLibraries(librarySources)
|
|
138
|
+
: undefined;
|
|
139
|
+
const externalRecipeIds = new Set(libraryResolution?.recipes.keys() ?? []);
|
|
140
|
+
if (isRecord(recipeResolution) && Array.isArray(recipeResolution.dependencies)) {
|
|
141
|
+
for (const dependency of recipeResolution.dependencies) {
|
|
142
|
+
if (isRecord(dependency) && typeof dependency.ref === 'string') {
|
|
143
|
+
externalRecipeIds.add(dependency.ref);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const validationOptions = externalRecipeIds.size > 0 ? { externalRecipeIds } : undefined;
|
|
76
148
|
if (actionManifestPath) {
|
|
77
149
|
const actionManifest = await readRecipeCliJsonFile(actionManifestPath, baseDir);
|
|
78
150
|
results.push(validateRecipeWithManifest(recipe, actionManifest, validationOptions));
|
|
@@ -80,16 +152,57 @@ export async function validateRecipeCliInput({ recipePath, actionManifestPath, a
|
|
|
80
152
|
else {
|
|
81
153
|
results.push(validateRecipeDocument(recipe, validationOptions));
|
|
82
154
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
155
|
+
if (!artifactDir &&
|
|
156
|
+
libraryResolution &&
|
|
157
|
+
isRecord(recipe) &&
|
|
158
|
+
results.every((result) => result.status === 'valid')) {
|
|
159
|
+
try {
|
|
160
|
+
const rootDigest = digestRecipeDocument(recipe);
|
|
161
|
+
resolveRecipeDependencies({
|
|
162
|
+
rootRef: rootResolutionRef(rootDigest),
|
|
163
|
+
root: recipe,
|
|
164
|
+
rootSource: {
|
|
165
|
+
kind: 'operator',
|
|
166
|
+
trust: 'unknown',
|
|
167
|
+
path: resolveRecipeCliPath(recipePath, baseDir),
|
|
168
|
+
digest: rootDigest,
|
|
169
|
+
},
|
|
170
|
+
recipes: libraryResolution.recipes,
|
|
171
|
+
});
|
|
172
|
+
validateRecipeDependencyParams({
|
|
173
|
+
root: recipe,
|
|
174
|
+
params: effectiveParams,
|
|
175
|
+
recipes: libraryResolution.recipes,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
results.push({
|
|
180
|
+
status: 'invalid',
|
|
181
|
+
findings: [
|
|
182
|
+
{
|
|
183
|
+
severity: 'error',
|
|
184
|
+
code: error instanceof RecipeResolutionError
|
|
185
|
+
? error.code.toLowerCase()
|
|
186
|
+
: 'recipe.unresolved_dependency_graph',
|
|
187
|
+
path: 'workflow',
|
|
188
|
+
message: error instanceof RecipeResolutionError
|
|
189
|
+
? `${error.message} Next: ${error.userAction}.`
|
|
190
|
+
: error instanceof Error
|
|
191
|
+
? error.message
|
|
192
|
+
: String(error),
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
summary: { errors: 1, warnings: 0 },
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
86
199
|
const manifestPath = artifactManifestPath ??
|
|
87
200
|
(artifactDir ? path.join(artifactDir, 'artifact-manifest.json') : undefined);
|
|
88
201
|
let manifest;
|
|
89
202
|
if (manifestPath) {
|
|
90
203
|
manifest = artifactManifestPath
|
|
91
204
|
? await readRecipeCliJsonFile(manifestPath, baseDir)
|
|
92
|
-
: await
|
|
205
|
+
: await readOptionalRecipeCliJsonFileWithinRoot(artifactRoot, 'artifact-manifest.json');
|
|
93
206
|
if (manifest !== undefined && !artifactDir) {
|
|
94
207
|
results.push(validateArtifactManifestDocument(manifest, {
|
|
95
208
|
recipe,
|
|
@@ -98,19 +211,12 @@ export async function validateRecipeCliInput({ recipePath, actionManifestPath, a
|
|
|
98
211
|
}
|
|
99
212
|
}
|
|
100
213
|
if (artifactDir) {
|
|
101
|
-
// Flow-call resolution already ran above via validateRecipeWithManifest with the
|
|
102
|
-
// configured library. Always read resolved-recipe.json so a present-but-malformed
|
|
103
|
-
// file throws (never silently skipped); the validator then checks the composition
|
|
104
|
-
// in full only for a passing run (runPassed) — matching the runner/gateway.
|
|
105
|
-
const resolvedRecipe = await readOptionalRecipeCliJsonFile(path.join(artifactDir, 'resolved-recipe.json'), baseDir);
|
|
106
214
|
results.push(validateRecipeArtifactPackage({
|
|
107
215
|
recipe,
|
|
108
216
|
manifest,
|
|
109
217
|
artifactPaths,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// status still requires the composition to be proven.
|
|
113
|
-
runPassed: !(isRecord(manifest) && manifest.runStatus === 'fail'),
|
|
218
|
+
resolvedRecipes,
|
|
219
|
+
recipeResolution,
|
|
114
220
|
}));
|
|
115
221
|
}
|
|
116
222
|
return mergeRecipeValidationResults(results);
|
|
@@ -124,7 +230,7 @@ async function listRelativeRecipeCliFiles(root) {
|
|
|
124
230
|
if (entry.isDirectory()) {
|
|
125
231
|
await visit(relativePath);
|
|
126
232
|
}
|
|
127
|
-
else if (entry.isFile()) {
|
|
233
|
+
else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
128
234
|
output.push(relativePath.split(path.sep).join('/'));
|
|
129
235
|
}
|
|
130
236
|
}
|
package/dist/cli/support.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"support.js","sourceRoot":"","sources":["../../src/cli/support.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EACL,QAAQ,EACR,4BAA4B,EAE5B,gCAAgC,EAChC,6BAA6B,EAC7B,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"support.js","sourceRoot":"","sources":["../../src/cli/support.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-command.d.ts","sourceRoot":"","sources":["../../src/cli/validate-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAczC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"validate-command.d.ts","sourceRoot":"","sources":["../../src/cli/validate-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAczC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuC9D"}
|
|
@@ -11,7 +11,10 @@ export function registerValidateCommand(program) {
|
|
|
11
11
|
.option('--library <entry>', 'Recipe library source as name=path or path (repeatable; order is precedence, first wins). Defaults to RECIPE_LIBRARY_PATH, then the personal library under the farmslot home — same resolution as run.', collectRepeatable, [])
|
|
12
12
|
.option('--json', 'Print validation result as JSON')
|
|
13
13
|
.action(async (recipePath, options) => {
|
|
14
|
-
const librarySources = await resolveRecipeLibrarySources({
|
|
14
|
+
const librarySources = await resolveRecipeLibrarySources({
|
|
15
|
+
cliEntries: options.library,
|
|
16
|
+
recipePath,
|
|
17
|
+
});
|
|
15
18
|
const result = await validateRecipeCliInput({
|
|
16
19
|
recipePath,
|
|
17
20
|
actionManifestPath: options.actionManifest,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-command.js","sourceRoot":"","sources":["../../src/cli/validate-command.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAUtD,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,oDAAoD,CAAC;SACjE,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;SAC3C,MAAM,CAAC,8BAA8B,EAAE,6BAA6B,CAAC;SACrE,MAAM,CAAC,gCAAgC,EAAE,wBAAwB,CAAC;SAClE,MAAM,CAAC,sBAAsB,EAAE,4BAA4B,CAAC;SAC5D,MAAM,CACL,mBAAmB,EACnB,wMAAwM,EACxM,iBAAiB,EACjB,EAAc,CACf;SACA,MAAM,CAAC,QAAQ,EAAE,iCAAiC,CAAC;SACnD,MAAM,CAAC,KAAK,EAAE,UAAkB,EAAE,OAA+B,EAAE,EAAE;QACpE,MAAM,cAAc,GAAG,MAAM,2BAA2B,CAAC,
|
|
1
|
+
{"version":3,"file":"validate-command.js","sourceRoot":"","sources":["../../src/cli/validate-command.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAUtD,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,oDAAoD,CAAC;SACjE,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;SAC3C,MAAM,CAAC,8BAA8B,EAAE,6BAA6B,CAAC;SACrE,MAAM,CAAC,gCAAgC,EAAE,wBAAwB,CAAC;SAClE,MAAM,CAAC,sBAAsB,EAAE,4BAA4B,CAAC;SAC5D,MAAM,CACL,mBAAmB,EACnB,wMAAwM,EACxM,iBAAiB,EACjB,EAAc,CACf;SACA,MAAM,CAAC,QAAQ,EAAE,iCAAiC,CAAC;SACnD,MAAM,CAAC,KAAK,EAAE,UAAkB,EAAE,OAA+B,EAAE,EAAE;QACpE,MAAM,cAAc,GAAG,MAAM,2BAA2B,CAAC;YACvD,UAAU,EAAE,OAAO,CAAC,OAAO;YAC3B,UAAU;SACX,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC;YAC1C,UAAU;YACV,kBAAkB,EAAE,OAAO,CAAC,cAAc;YAC1C,oBAAoB,EAAE,OAAO,CAAC,gBAAgB;YAC9C,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,GAAG,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzD,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CACT,sBAAsB,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,OAAO,CAAC,MAAM,YAAY,MAAM,CAAC,OAAO,CAAC,QAAQ,YAAY,CAC7G,CAAC;YACF,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACtC,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3F,CAAC;QACH,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa,EAAE,QAAkB;IAC1D,OAAO,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC"}
|
package/dist/core/compose.d.ts
CHANGED
|
@@ -1,33 +1,23 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* flows), and `uses` is dropped so `validateFlowCalls` verifies every ref is
|
|
8
|
-
* present inline rather than short-circuiting on the external catalog. Returns the
|
|
9
|
-
* recipe unchanged (same reference) when nothing external was composed — recipe.json
|
|
10
|
-
* is already the full composition.
|
|
11
|
-
*/
|
|
12
|
-
export declare function buildResolvedRecipe(recipe: unknown, flowCatalog: ReadonlyMap<string, InlineFlow>): unknown;
|
|
13
|
-
export interface ComposeRecipeOptions {
|
|
14
|
-
projectRoot: string;
|
|
15
|
-
recipeDir: string;
|
|
16
|
-
/** Library sources whose flows count as resolvable, mirroring run resolution. */
|
|
17
|
-
librarySources?: RecipeLibrarySource[];
|
|
18
|
-
logger?: RecipeLogger;
|
|
1
|
+
import { type RecipeResolutionDocument, type RecipeResolutionEdge, type RecipeSourceProvenance } from '@farmslot/protocol';
|
|
2
|
+
import type { ResolvedLibraryRecipe } from './library.js';
|
|
3
|
+
export interface ResolvedRecipeDependencies {
|
|
4
|
+
recipes: ReadonlyMap<string, ResolvedLibraryRecipe>;
|
|
5
|
+
edges: RecipeResolutionEdge[];
|
|
6
|
+
document: RecipeResolutionDocument;
|
|
19
7
|
}
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
8
|
+
export declare function rootResolutionRef(digest: string): string;
|
|
9
|
+
/** Validate every static call boundary after parent defaults and parameters are applied. */
|
|
10
|
+
export declare function validateRecipeDependencyParams(options: {
|
|
11
|
+
root: Record<string, unknown>;
|
|
12
|
+
params: Record<string, unknown>;
|
|
13
|
+
recipes: ReadonlyMap<string, ResolvedLibraryRecipe>;
|
|
14
|
+
}): void;
|
|
15
|
+
/** Resolve the exact static recipe call DAG before any side effect. */
|
|
16
|
+
export declare function resolveRecipeDependencies(options: {
|
|
17
|
+
rootRef: string;
|
|
18
|
+
root: Record<string, unknown>;
|
|
19
|
+
rootSource: RecipeSourceProvenance;
|
|
20
|
+
recipes: ReadonlyMap<string, ResolvedLibraryRecipe>;
|
|
21
|
+
maxDepth?: number;
|
|
22
|
+
}): ResolvedRecipeDependencies;
|
|
33
23
|
//# sourceMappingURL=compose.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/core/compose.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/core/compose.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAE5B,MAAM,oBAAoB,CAAC;AAK5B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAI1D,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACpD,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,QAAQ,EAAE,wBAAwB,CAAC;CACpC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,4FAA4F;AAC5F,wBAAgB,8BAA8B,CAAC,OAAO,EAAE;IACtD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;CACrD,GAAG,IAAI,CAkBP;AAED,uEAAuE;AACvE,wBAAgB,yBAAyB,CAAC,OAAO,EAAE;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,UAAU,EAAE,sBAAsB,CAAC;IACnC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,0BAA0B,CAuF7B"}
|
package/dist/core/compose.js
CHANGED
|
@@ -1,136 +1,104 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizeRecipeRef, resolvedRecipeArtifactPath, } from '@farmslot/protocol';
|
|
2
|
+
import { DEFAULT_MAX_RECIPE_CALL_DEPTH } from './execution.js';
|
|
3
|
+
import { extractWorkflowGraph } from './graph.js';
|
|
2
4
|
import { isRecord } from './json.js';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
return undefined;
|
|
8
|
-
const workflow = isRecord(flow.workflow) ? flow.workflow : flow;
|
|
9
|
-
return isRecord(workflow.nodes) ? workflow.nodes : undefined;
|
|
5
|
+
import { resolveRecipeParams, resolveRecipeValue } from './parameters.js';
|
|
6
|
+
import { RecipeResolutionError } from './resolution-error.js';
|
|
7
|
+
export function rootResolutionRef(digest) {
|
|
8
|
+
return `$root:${digest}`;
|
|
10
9
|
}
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
node.
|
|
23
|
-
|
|
24
|
-
node.ref.trim()) {
|
|
25
|
-
refs.push(node.ref);
|
|
10
|
+
/** Validate every static call boundary after parent defaults and parameters are applied. */
|
|
11
|
+
export function validateRecipeDependencyParams(options) {
|
|
12
|
+
const visit = (document, params) => {
|
|
13
|
+
for (const rawNode of Object.values(extractWorkflowGraph(document).nodes)) {
|
|
14
|
+
if (rawNode.action !== 'call' || typeof rawNode.ref !== 'string')
|
|
15
|
+
continue;
|
|
16
|
+
const ref = normalizeRecipeRef(rawNode.ref);
|
|
17
|
+
const dependency = options.recipes.get(ref);
|
|
18
|
+
if (!dependency)
|
|
19
|
+
continue;
|
|
20
|
+
const node = resolveRecipeValue(rawNode, params);
|
|
21
|
+
const childParams = resolveRecipeParams(ref, dependency.document, isRecord(node) && isRecord(node.params) ? node.params : {}, { allowTemplates: true });
|
|
22
|
+
visit(dependency.document, childParams);
|
|
26
23
|
}
|
|
27
|
-
}
|
|
28
|
-
|
|
24
|
+
};
|
|
25
|
+
visit(options.root, options.params);
|
|
29
26
|
}
|
|
30
|
-
/**
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
refs.push(node.ref);
|
|
27
|
+
/** Resolve the exact static recipe call DAG before any side effect. */
|
|
28
|
+
export function resolveRecipeDependencies(options) {
|
|
29
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_RECIPE_CALL_DEPTH;
|
|
30
|
+
const resolved = new Map();
|
|
31
|
+
const edges = [];
|
|
32
|
+
const visiting = [];
|
|
33
|
+
const visited = new Set();
|
|
34
|
+
const visit = (from, document, depth) => {
|
|
35
|
+
if (depth >= maxDepth) {
|
|
36
|
+
throw new RecipeResolutionError('RECIPE_CALL_DEPTH_EXCEEDED', `Recipe call depth exceeded maximum ${maxDepth}.`, `reduce nested recipe calls below ${maxDepth}`);
|
|
41
37
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
seeds.push(...callRefsInLifecycle(workflow.teardown));
|
|
61
|
-
}
|
|
62
|
-
if (isRecord(recipe.startState) &&
|
|
63
|
-
recipe.startState.action === 'call' &&
|
|
64
|
-
typeof recipe.startState.ref === 'string') {
|
|
65
|
-
seeds.push(recipe.startState.ref);
|
|
66
|
-
}
|
|
67
|
-
const required = new Set();
|
|
68
|
-
const queue = [...seeds];
|
|
69
|
-
while (queue.length > 0) {
|
|
70
|
-
const ref = queue.shift();
|
|
71
|
-
if (required.has(ref))
|
|
72
|
-
continue;
|
|
73
|
-
required.add(ref);
|
|
74
|
-
for (const child of callRefsInNodes(flowNodes(flowCatalog.get(ref)))) {
|
|
75
|
-
if (!required.has(child))
|
|
76
|
-
queue.push(child);
|
|
38
|
+
for (const ref of callRefs(document)) {
|
|
39
|
+
edges.push({ from, to: ref });
|
|
40
|
+
const cycleStart = visiting.indexOf(ref);
|
|
41
|
+
if (cycleStart >= 0 || ref === options.rootRef) {
|
|
42
|
+
const cycle = [...visiting.slice(Math.max(cycleStart, 0)), ref];
|
|
43
|
+
throw new RecipeResolutionError('RECIPE_CALL_CYCLE', `Recipe call cycle detected: ${cycle.join(' -> ')}.`, 'remove one call edge so the recipe dependency graph is acyclic');
|
|
44
|
+
}
|
|
45
|
+
const dependency = options.recipes.get(ref);
|
|
46
|
+
if (!dependency) {
|
|
47
|
+
throw new RecipeResolutionError('RECIPE_REFERENCE_NOT_FOUND', `Recipe ${ref} is not available from configured libraries.`, `add ${ref} to a configured recipe library or correct call.ref`);
|
|
48
|
+
}
|
|
49
|
+
resolved.set(ref, dependency);
|
|
50
|
+
if (visited.has(ref))
|
|
51
|
+
continue;
|
|
52
|
+
visiting.push(ref);
|
|
53
|
+
visit(ref, dependency.document, depth + 1);
|
|
54
|
+
visiting.pop();
|
|
55
|
+
visited.add(ref);
|
|
77
56
|
}
|
|
57
|
+
};
|
|
58
|
+
visiting.push(options.rootRef);
|
|
59
|
+
visit(options.rootRef, options.root, 0);
|
|
60
|
+
visiting.pop();
|
|
61
|
+
const rootDigest = options.rootSource.digest;
|
|
62
|
+
if (!rootDigest) {
|
|
63
|
+
throw new RecipeResolutionError('RECIPE_RESOLUTION_DIGEST_MISSING', 'Resolved root recipe source is missing its content digest.', 'reload the root recipe through the runner so its digest is computed');
|
|
78
64
|
}
|
|
79
|
-
|
|
65
|
+
const dependencies = [...resolved.values()]
|
|
66
|
+
.sort((left, right) => compareStrings(left.ref, right.ref))
|
|
67
|
+
.map((recipe) => {
|
|
68
|
+
const digest = recipe.provenance.digest;
|
|
69
|
+
if (!digest) {
|
|
70
|
+
throw new RecipeResolutionError('RECIPE_RESOLUTION_DIGEST_MISSING', `Resolved recipe ${recipe.ref} is missing its content digest.`, `reload ${recipe.ref} through the library resolver so its digest is computed`);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
ref: recipe.ref,
|
|
74
|
+
source: recipe.source,
|
|
75
|
+
file: recipe.file,
|
|
76
|
+
digest,
|
|
77
|
+
artifact: resolvedRecipeArtifactPath(digest),
|
|
78
|
+
...(recipe.adapter ? { adapter: recipe.adapter } : {}),
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
recipes: resolved,
|
|
83
|
+
edges,
|
|
84
|
+
document: {
|
|
85
|
+
schema_version: 1,
|
|
86
|
+
root: { ref: options.rootRef, digest: rootDigest },
|
|
87
|
+
dependencies,
|
|
88
|
+
edges: [...edges].sort((left, right) => compareStrings(`${left.from}:${left.to}`, `${right.from}:${right.to}`)),
|
|
89
|
+
},
|
|
90
|
+
};
|
|
80
91
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
* recipe whose `call.ref`s resolve without the recipe library. Only referenced
|
|
84
|
-
* flows are inlined (not the whole catalog, and not unreferenced authored inline
|
|
85
|
-
* flows), and `uses` is dropped so `validateFlowCalls` verifies every ref is
|
|
86
|
-
* present inline rather than short-circuiting on the external catalog. Returns the
|
|
87
|
-
* recipe unchanged (same reference) when nothing external was composed — recipe.json
|
|
88
|
-
* is already the full composition.
|
|
89
|
-
*/
|
|
90
|
-
export function buildResolvedRecipe(recipe, flowCatalog) {
|
|
91
|
-
if (!isRecord(recipe) || flowCatalog.size === 0)
|
|
92
|
-
return recipe;
|
|
93
|
-
const refs = collectRequiredFlowRefs(recipe, flowCatalog);
|
|
94
|
-
const flows = {};
|
|
95
|
-
for (const ref of refs) {
|
|
96
|
-
const flow = flowCatalog.get(ref);
|
|
97
|
-
if (flow)
|
|
98
|
-
flows[ref] = flow;
|
|
99
|
-
}
|
|
100
|
-
if (Object.keys(flows).length === 0)
|
|
101
|
-
return recipe;
|
|
102
|
-
// Emit only when `uses`/library composition contributed — either a reached flow
|
|
103
|
-
// is not an authored inline flow, or the recipe declared `uses` (now inlined and
|
|
104
|
-
// dropped). Otherwise recipe.json is already self-contained.
|
|
105
|
-
const authoredFlowKeys = new Set(isRecord(recipe.flows) ? Object.keys(recipe.flows) : []);
|
|
106
|
-
const hasUses = Array.isArray(recipe.uses) && recipe.uses.length > 0;
|
|
107
|
-
const composedExternal = Object.keys(flows).some((ref) => !authoredFlowKeys.has(ref));
|
|
108
|
-
if (!composedExternal && !hasUses)
|
|
109
|
-
return recipe;
|
|
110
|
-
const { uses: _uses, ...rest } = recipe;
|
|
111
|
-
void _uses;
|
|
112
|
-
return { ...rest, flows };
|
|
92
|
+
function compareStrings(left, right) {
|
|
93
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
113
94
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
projectRoot: options.projectRoot,
|
|
123
|
-
recipeDir: options.recipeDir,
|
|
124
|
-
});
|
|
125
|
-
const resolution = options.librarySources && options.librarySources.length > 0
|
|
126
|
-
? await loadRecipeLibraries(options.librarySources, options.logger)
|
|
127
|
-
: undefined;
|
|
128
|
-
const usedLibraryFlows = new Map();
|
|
129
|
-
const { catalog } = createEffectiveFlowCatalog(recipeLocalFlows, resolution, usedLibraryFlows, options.logger);
|
|
130
|
-
const resolved = buildResolvedRecipe(recipe, catalog);
|
|
131
|
-
const flowCount = resolved !== recipe && isRecord(resolved) && isRecord(resolved.flows)
|
|
132
|
-
? Object.keys(resolved.flows).length
|
|
133
|
-
: 0;
|
|
134
|
-
return { resolved, flowCount };
|
|
95
|
+
function callRefs(recipe) {
|
|
96
|
+
const refs = [];
|
|
97
|
+
for (const node of Object.values(extractWorkflowGraph(recipe).nodes)) {
|
|
98
|
+
if (node.action === 'call' && typeof node.ref === 'string' && node.ref.trim()) {
|
|
99
|
+
refs.push(normalizeRecipeRef(node.ref));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return refs;
|
|
135
103
|
}
|
|
136
104
|
//# sourceMappingURL=compose.js.map
|