@farmslot/recipe-cli 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +11 -11
- package/dist/harness/catalog.d.ts +71 -0
- package/dist/harness/catalog.d.ts.map +1 -0
- package/dist/harness/catalog.js +280 -0
- package/dist/harness/catalog.js.map +1 -0
- package/dist/harness/commands/call.d.ts +18 -0
- package/dist/harness/commands/call.d.ts.map +1 -0
- package/dist/harness/commands/call.js +663 -0
- package/dist/harness/commands/call.js.map +1 -0
- package/dist/harness/commands/discover.d.ts +29 -0
- package/dist/harness/commands/discover.d.ts.map +1 -0
- package/dist/harness/commands/discover.js +563 -0
- package/dist/harness/commands/discover.js.map +1 -0
- package/dist/harness/commands/run.d.ts +40 -0
- package/dist/harness/commands/run.d.ts.map +1 -0
- package/dist/harness/commands/run.js +727 -0
- package/dist/harness/commands/run.js.map +1 -0
- package/dist/harness/heal-bounds.d.ts +8 -0
- package/dist/harness/heal-bounds.d.ts.map +1 -1
- package/dist/harness/heal-bounds.js +10 -0
- package/dist/harness/heal-bounds.js.map +1 -1
- package/dist/harness/host.d.ts +5 -0
- package/dist/harness/host.d.ts.map +1 -1
- package/dist/harness/host.js +7 -0
- package/dist/harness/host.js.map +1 -1
- package/dist/harness/index.d.ts +8 -0
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +8 -0
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/network-observation.d.ts +19 -0
- package/dist/harness/network-observation.d.ts.map +1 -0
- package/dist/harness/network-observation.js +202 -0
- package/dist/harness/network-observation.js.map +1 -0
- package/dist/harness/recipe-library.d.ts +65 -0
- package/dist/harness/recipe-library.d.ts.map +1 -0
- package/dist/harness/recipe-library.js +182 -0
- package/dist/harness/recipe-library.js.map +1 -0
- package/dist/harness/recipe-validation.d.ts +27 -0
- package/dist/harness/recipe-validation.d.ts.map +1 -0
- package/dist/harness/recipe-validation.js +249 -0
- package/dist/harness/recipe-validation.js.map +1 -0
- package/dist/harness/run-engine.d.ts +79 -0
- package/dist/harness/run-engine.d.ts.map +1 -0
- package/dist/harness/run-engine.js +632 -0
- package/dist/harness/run-engine.js.map +1 -0
- package/dist/harness/run-observers.d.ts +13 -0
- package/dist/harness/run-observers.d.ts.map +1 -0
- package/dist/harness/run-observers.js +21 -0
- package/dist/harness/run-observers.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// The recipes `run` accepts: library sources (the host's bundled library
|
|
2
|
+
// always among them), recipe-argument resolution, runnable listing and
|
|
3
|
+
// descriptions with declared parameters.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { loadRecipeLibraries, resolveRecipeLibrarySources, } from '@farmslot/recipe-runner';
|
|
7
|
+
import { recipeComposition } from '../composition.js';
|
|
8
|
+
import { assessRecipe } from '../discovery-index.js';
|
|
9
|
+
import { harnessHost } from './host.js';
|
|
10
|
+
import { gitLibraryProvenance } from './library-provenance.js';
|
|
11
|
+
import { isRecord, optionString, optionStrings, usageError, } from './parse-args.js';
|
|
12
|
+
/**
|
|
13
|
+
* The configured library sources (`--library`, the recipe's own library, the
|
|
14
|
+
* environment) plus the bundled library, each with its provenance. The bundled
|
|
15
|
+
* source name may only point at the bundled root.
|
|
16
|
+
*/
|
|
17
|
+
export async function resolveLibrarySources(catalog, libraryEntry, recipePath) {
|
|
18
|
+
const bundled = catalog.bundledLibrary;
|
|
19
|
+
const cliEntries = typeof libraryEntry === 'string' ? [libraryEntry] : libraryEntry ? [...libraryEntry] : [];
|
|
20
|
+
const sources = await resolveRecipeLibrarySources({
|
|
21
|
+
...(cliEntries.length > 0 ? { cliEntries } : {}),
|
|
22
|
+
...(recipePath ? { recipePath } : {}),
|
|
23
|
+
});
|
|
24
|
+
const canonicalRoot = path.resolve(bundled.root);
|
|
25
|
+
const configuredCanonical = sources.find((source) => source.name === bundled.name);
|
|
26
|
+
if (configuredCanonical && path.resolve(configuredCanonical.root) !== canonicalRoot) {
|
|
27
|
+
throw usageError(`Recipe library source ${bundled.name} must resolve to ${canonicalRoot}; got ${path.resolve(configuredCanonical.root)}.`);
|
|
28
|
+
}
|
|
29
|
+
if (!sources.some((source) => path.resolve(source.root) === canonicalRoot)) {
|
|
30
|
+
sources.push({ name: bundled.name, root: canonicalRoot });
|
|
31
|
+
}
|
|
32
|
+
return Promise.all(sources.map(async (source) => {
|
|
33
|
+
const isBundled = source.name === bundled.name;
|
|
34
|
+
const detected = isBundled ? {} : await gitLibraryProvenance(source.root);
|
|
35
|
+
return {
|
|
36
|
+
...source,
|
|
37
|
+
provenance: {
|
|
38
|
+
kind: isBundled ? 'bundled' : 'library',
|
|
39
|
+
trust: isBundled ? 'trusted' : 'unknown',
|
|
40
|
+
name: source.name ?? path.basename(source.root),
|
|
41
|
+
...detected,
|
|
42
|
+
...source.provenance,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The library sources a command's `--library` names (unless the caller already
|
|
49
|
+
* resolved them), and the adapter's action manifest over them (or its
|
|
50
|
+
* `--action-manifest`).
|
|
51
|
+
*/
|
|
52
|
+
export async function resolveCommandManifest(catalog, adapter, options, resolvedSources) {
|
|
53
|
+
const librarySources = resolvedSources ?? (await resolveLibrarySources(catalog, optionStrings(options, 'library')));
|
|
54
|
+
const resolution = await catalog.resolveActionManifest(adapter, optionString(options, 'actionManifest'), librarySources);
|
|
55
|
+
return { ...resolution, librarySources };
|
|
56
|
+
}
|
|
57
|
+
function isRecipeFile(p) {
|
|
58
|
+
try {
|
|
59
|
+
return fs.statSync(p).isFile();
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// ENOENT/EACCES alike mean "not a usable file"; the caller's fall-through
|
|
63
|
+
// to the library probe / RECIPE_NOT_FOUND teaching IS the recovery.
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** A recipe file path, or a library recipe name the sources resolve for the adapter. */
|
|
68
|
+
export async function resolveRunRecipeArg(catalog, recipeArg, adapter, librarySources) {
|
|
69
|
+
const direct = path.resolve(recipeArg);
|
|
70
|
+
if (isRecipeFile(direct))
|
|
71
|
+
return { recipeFile: direct };
|
|
72
|
+
const sources = effectiveLibrarySources(catalog, librarySources);
|
|
73
|
+
const resolution = await loadRecipeLibraries(sources, { adapter });
|
|
74
|
+
if (!recipeArg.includes('/') && !recipeArg.includes(path.sep)) {
|
|
75
|
+
const selected = resolution.recipes.get(recipeArg);
|
|
76
|
+
if (selected)
|
|
77
|
+
return { recipeFile: selected.path, ref: selected.ref };
|
|
78
|
+
}
|
|
79
|
+
const available = [...resolution.recipes.keys()].sort();
|
|
80
|
+
const nonCanonical = sources.filter((source) => source.name !== catalog.bundledLibrary.name);
|
|
81
|
+
const notFoundCore = nonCanonical.length > 0
|
|
82
|
+
? `recipe not found: ${recipeArg} — not a file, and no recipe matched in library sources [${sources.map((source) => source.name ?? path.basename(source.root)).join(', ')}].`
|
|
83
|
+
: `recipe not found: ${recipeArg} — not a file, and no packaged library recipe matched.`;
|
|
84
|
+
const suffix = available.length > 0
|
|
85
|
+
? ` Library recipes for ${adapter}: ${available.join(', ')} (${harnessHost().name} run <name>).`
|
|
86
|
+
: ` The packaged library has no recipes for ${adapter}.`;
|
|
87
|
+
return { notFound: notFoundCore + suffix };
|
|
88
|
+
}
|
|
89
|
+
function effectiveLibrarySources(catalog, librarySources) {
|
|
90
|
+
return librarySources && librarySources.length > 0
|
|
91
|
+
? librarySources
|
|
92
|
+
: [{ name: catalog.bundledLibrary.name, root: path.resolve(catalog.bundledLibrary.root) }];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Library recipes `run` accepts for this adapter: every precedence winner and
|
|
96
|
+
* qualified alias whose document, and every recipe it calls, validates against
|
|
97
|
+
* the adapter's resolved manifest, so discovery and execution agree.
|
|
98
|
+
*/
|
|
99
|
+
export async function runnableLibraryRecipes(catalog, adapter, sources) {
|
|
100
|
+
const resolution = await loadRecipeLibraries(sources, { adapter });
|
|
101
|
+
const { manifest } = await catalog.resolveActionManifest(adapter, undefined, sources);
|
|
102
|
+
return [...resolution.recipes.values()].filter((recipe) => assessRecipe({ resolution, manifest }, recipe).length === 0);
|
|
103
|
+
}
|
|
104
|
+
export async function listRunnableRecipes(catalog, adapter, librarySources) {
|
|
105
|
+
return (await runnableLibraryRecipes(catalog, adapter, effectiveLibrarySources(catalog, librarySources)))
|
|
106
|
+
.map((recipe) => recipeSummary(recipe.ref, adapter, recipe.document, recipe))
|
|
107
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
108
|
+
}
|
|
109
|
+
export async function describeRunnableRecipe(catalog, recipeArg, adapter, librarySources) {
|
|
110
|
+
const resolved = await resolveRunRecipeArg(catalog, recipeArg, adapter, librarySources);
|
|
111
|
+
if ('notFound' in resolved)
|
|
112
|
+
return resolved;
|
|
113
|
+
let document;
|
|
114
|
+
try {
|
|
115
|
+
document = JSON.parse(fs.readFileSync(resolved.recipeFile, 'utf8'));
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
return {
|
|
119
|
+
unreadable: `could not read recipe ${resolved.recipeFile}: ${error instanceof Error ? error.message : String(error)}`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (!isRecord(document)) {
|
|
123
|
+
return {
|
|
124
|
+
unreadable: `recipe ${resolved.recipeFile} must contain a JSON object.`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const libraryResolution = await loadRecipeLibraries(effectiveLibrarySources(catalog, librarySources), { adapter });
|
|
128
|
+
const selected = resolved.ref ? libraryResolution.recipes.get(resolved.ref) : undefined;
|
|
129
|
+
const composition = recipeComposition(document, libraryResolution.recipes);
|
|
130
|
+
const summary = recipeSummary(resolved.ref ?? recipeArg, adapter, document, selected);
|
|
131
|
+
return {
|
|
132
|
+
recipe: {
|
|
133
|
+
...summary,
|
|
134
|
+
source: selected?.source ?? 'file',
|
|
135
|
+
file: selected?.file ?? resolved.recipeFile,
|
|
136
|
+
path: resolved.recipeFile,
|
|
137
|
+
...(typeof document.$schema === 'string' ? { $schema: document.$schema } : {}),
|
|
138
|
+
actions: composition.actions,
|
|
139
|
+
nestedRecipes: composition.nestedRecipes,
|
|
140
|
+
unresolvedRecipes: composition.unresolvedRecipes,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function recipeSummary(ref, adapter, document, selected) {
|
|
145
|
+
return {
|
|
146
|
+
name: ref,
|
|
147
|
+
source: selected?.source ?? 'file',
|
|
148
|
+
file: selected?.file ?? '',
|
|
149
|
+
shadows: selected?.shadows ?? [],
|
|
150
|
+
adapter,
|
|
151
|
+
variant: selected?.adapter ?? null,
|
|
152
|
+
parameters: recipeParameters(document),
|
|
153
|
+
...(typeof document.title === 'string' && document.title.trim()
|
|
154
|
+
? { title: document.title.trim() }
|
|
155
|
+
: {}),
|
|
156
|
+
...(typeof document.description === 'string' && document.description.trim()
|
|
157
|
+
? { description: document.description.trim() }
|
|
158
|
+
: {}),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function recipeParameters(document) {
|
|
162
|
+
if (!isRecord(document.paramsSchema))
|
|
163
|
+
return [];
|
|
164
|
+
const schema = document.paramsSchema;
|
|
165
|
+
const required = new Set(Array.isArray(schema.required)
|
|
166
|
+
? schema.required.filter((entry) => typeof entry === 'string')
|
|
167
|
+
: []);
|
|
168
|
+
if (!isRecord(schema.properties))
|
|
169
|
+
return [];
|
|
170
|
+
return Object.entries(schema.properties).map(([name, raw]) => {
|
|
171
|
+
const property = isRecord(raw) ? raw : {};
|
|
172
|
+
return {
|
|
173
|
+
name,
|
|
174
|
+
...(typeof property.type === 'string' ? { type: property.type } : {}),
|
|
175
|
+
required: required.has(name),
|
|
176
|
+
...(Object.hasOwn(property, 'default') ? { default: property.default } : {}),
|
|
177
|
+
...(typeof property.description === 'string' ? { description: property.description } : {}),
|
|
178
|
+
...(Array.isArray(property.enum) ? { enum: property.enum } : {}),
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=recipe-library.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe-library.js","sourceRoot":"","sources":["../../src/harness/recipe-library.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type RecipeActionManifestDocument, type RecipeValidationFinding, type RecipeValidationResult } from '@farmslot/protocol';
|
|
2
|
+
import { type RecipeLibrarySource } from '@farmslot/recipe-runner';
|
|
3
|
+
import type { RecipeCatalog } from './catalog.js';
|
|
4
|
+
import { type CliOptions } from './parse-args.js';
|
|
5
|
+
/** `value` with every exact `{{params.<path>}}` string replaced by the parameter it names, when present. */
|
|
6
|
+
export declare function resolveRecipeParamValue(value: unknown, params: Record<string, unknown>): unknown;
|
|
7
|
+
/** The adapter's `actions.inputFindings` on every workflow node, with parameters resolved. */
|
|
8
|
+
export declare function validateActionInputs(recipe: unknown, adapter: string, params?: Record<string, unknown>): RecipeValidationFinding[];
|
|
9
|
+
export declare function validateRecipeAdapterAware(adapter: string, recipe: unknown, manifest: RecipeActionManifestDocument, librarySources?: RecipeLibrarySource[], rootRef?: string, params?: Record<string, unknown>, proof?: boolean): Promise<RecipeValidationResult>;
|
|
10
|
+
export interface RunRecipeStaticValidation {
|
|
11
|
+
recipe: unknown;
|
|
12
|
+
recipeFile: string;
|
|
13
|
+
findings: RecipeValidationFinding[];
|
|
14
|
+
errorCount: number;
|
|
15
|
+
manifestOk: boolean;
|
|
16
|
+
schemaValid: boolean;
|
|
17
|
+
effectiveParams: Record<string, unknown>;
|
|
18
|
+
/** The library sources this validation resolved — returned so callers (run's
|
|
19
|
+
* runtime options) reuse them instead of re-resolving. */
|
|
20
|
+
librarySources?: RecipeLibrarySource[];
|
|
21
|
+
usageError?: {
|
|
22
|
+
code: string;
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare function validateRunRecipeStatic(catalog: RecipeCatalog, recipeArg: string, adapter: string, options: CliOptions, params?: Record<string, unknown>): Promise<RunRecipeStaticValidation>;
|
|
27
|
+
//# sourceMappingURL=recipe-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe-validation.d.ts","sourceRoot":"","sources":["../../src/harness/recipe-validation.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAG5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,mBAAmB,EAIzB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAEL,KAAK,UAAU,EAOhB,MAAM,iBAAiB,CAAC;AAIzB,4GAA4G;AAC5G,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAgBhG;AAED,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,uBAAuB,EAAE,CAa3B;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,4BAA4B,EACtC,cAAc,CAAC,EAAE,mBAAmB,EAAE,EACtC,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,KAAK,UAAQ,GACZ,OAAO,CAAC,sBAAsB,CAAC,CA8GjC;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC;8DAC0D;IAC1D,cAAc,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACvC,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,UAAU,EACnB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,yBAAyB,CAAC,CAoGpC"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// Static recipe validation for `run` and `call`: the manifest, the declared
|
|
2
|
+
// parameters, the platform's input checks on every node, called library
|
|
3
|
+
// recipes, and behavioral proof bindings.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { applyRecipeParamDefaults, digestRecipeDocument, getRecipeActionManifestActionNames, normalizeRecipeRef, validateRecipeParams, validateRecipeWithManifest, } from '@farmslot/protocol';
|
|
7
|
+
import { loadRecipeLibraries, RecipeResolutionError, resolveRecipeDependencies, validateRecipeDependencyParams, } from '@farmslot/recipe-runner';
|
|
8
|
+
import { harnessAdapters } from './adapters.js';
|
|
9
|
+
import { harnessHost } from './host.js';
|
|
10
|
+
import { actionManifestPathOption, isRecord, optionFlag, optionString, optionStrings, shellQuoteArg, targetPath, } from './parse-args.js';
|
|
11
|
+
import { resolveLibrarySources, resolveRunRecipeArg } from './recipe-library.js';
|
|
12
|
+
import { validateRuntimeProofPlan } from './runtime-proof.js';
|
|
13
|
+
/** `value` with every exact `{{params.<path>}}` string replaced by the parameter it names, when present. */
|
|
14
|
+
export function resolveRecipeParamValue(value, params) {
|
|
15
|
+
if (typeof value === 'string') {
|
|
16
|
+
const exact = /^\{\{params\.([A-Za-z0-9_.-]+)\}\}$/u.exec(value);
|
|
17
|
+
if (!exact)
|
|
18
|
+
return value;
|
|
19
|
+
let current = params;
|
|
20
|
+
for (const segment of exact[1].split('.')) {
|
|
21
|
+
if (!isRecord(current) || !Object.hasOwn(current, segment))
|
|
22
|
+
return value;
|
|
23
|
+
current = current[segment];
|
|
24
|
+
}
|
|
25
|
+
return current;
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(value))
|
|
28
|
+
return value.map((entry) => resolveRecipeParamValue(entry, params));
|
|
29
|
+
if (!isRecord(value))
|
|
30
|
+
return value;
|
|
31
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, resolveRecipeParamValue(entry, params)]));
|
|
32
|
+
}
|
|
33
|
+
/** The adapter's `actions.inputFindings` on every workflow node, with parameters resolved. */
|
|
34
|
+
export function validateActionInputs(recipe, adapter, params) {
|
|
35
|
+
if (!isRecord(recipe) || !isRecord(recipe.workflow))
|
|
36
|
+
return [];
|
|
37
|
+
const nodes = isRecord(recipe.workflow.nodes) ? recipe.workflow.nodes : undefined;
|
|
38
|
+
if (!nodes)
|
|
39
|
+
return [];
|
|
40
|
+
const registry = harnessAdapters();
|
|
41
|
+
const actions = registry.has(adapter) ? registry.get(adapter).actions : undefined;
|
|
42
|
+
const findings = [];
|
|
43
|
+
for (const [nodeId, rawNode] of Object.entries(nodes)) {
|
|
44
|
+
const node = params ? resolveRecipeParamValue(rawNode, params) : rawNode;
|
|
45
|
+
if (!isRecord(node))
|
|
46
|
+
continue;
|
|
47
|
+
findings.push(...(actions?.inputFindings?.(nodeId, node) ?? []));
|
|
48
|
+
}
|
|
49
|
+
return findings;
|
|
50
|
+
}
|
|
51
|
+
export async function validateRecipeAdapterAware(adapter, recipe, manifest, librarySources, rootRef, params, proof = false) {
|
|
52
|
+
let externalRecipeIds;
|
|
53
|
+
let libraryResolution;
|
|
54
|
+
if (librarySources && librarySources.length > 0) {
|
|
55
|
+
libraryResolution = await loadRecipeLibraries(librarySources, {
|
|
56
|
+
adapter,
|
|
57
|
+
});
|
|
58
|
+
externalRecipeIds = new Set(libraryResolution.recipes.keys());
|
|
59
|
+
}
|
|
60
|
+
const validationOptions = externalRecipeIds !== undefined ? { externalRecipeIds } : undefined;
|
|
61
|
+
const withManifest = validateRecipeWithManifest(recipe, manifest, validationOptions);
|
|
62
|
+
const findings = [
|
|
63
|
+
...withManifest.findings,
|
|
64
|
+
...(withManifest.status === 'valid' && params
|
|
65
|
+
? validateRecipeWithManifest(resolveRecipeParamValue(recipe, params), manifest, validationOptions).findings
|
|
66
|
+
: []),
|
|
67
|
+
...validateActionInputs(recipe, adapter, params),
|
|
68
|
+
];
|
|
69
|
+
if (withManifest.status === 'valid' && isRecord(recipe) && libraryResolution) {
|
|
70
|
+
try {
|
|
71
|
+
const digest = digestRecipeDocument(recipe);
|
|
72
|
+
const dependencies = resolveRecipeDependencies({
|
|
73
|
+
rootRef: rootRef ?? `$root:${digest}`,
|
|
74
|
+
root: recipe,
|
|
75
|
+
rootSource: {
|
|
76
|
+
kind: 'recipe-file',
|
|
77
|
+
trust: 'unknown',
|
|
78
|
+
name: rootRef ?? 'recipe file',
|
|
79
|
+
digest,
|
|
80
|
+
},
|
|
81
|
+
recipes: libraryResolution.recipes,
|
|
82
|
+
});
|
|
83
|
+
if (params) {
|
|
84
|
+
validateRecipeDependencyParams({
|
|
85
|
+
root: recipe,
|
|
86
|
+
params,
|
|
87
|
+
recipes: dependencies.recipes,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
for (const dependency of dependencies.recipes.values()) {
|
|
91
|
+
findings.push(...validateRecipeWithManifest(dependency.document, manifest, validationOptions).findings);
|
|
92
|
+
}
|
|
93
|
+
const validateDependencyInstances = (document, parentParams) => {
|
|
94
|
+
if (!isRecord(document) ||
|
|
95
|
+
!isRecord(document.workflow) ||
|
|
96
|
+
!isRecord(document.workflow.nodes))
|
|
97
|
+
return;
|
|
98
|
+
for (const rawNode of Object.values(document.workflow.nodes)) {
|
|
99
|
+
const node = resolveRecipeParamValue(rawNode, parentParams);
|
|
100
|
+
if (!isRecord(node) || node.action !== 'call' || typeof node.ref !== 'string')
|
|
101
|
+
continue;
|
|
102
|
+
const dependency = dependencies.recipes.get(normalizeRecipeRef(node.ref));
|
|
103
|
+
if (!dependency)
|
|
104
|
+
continue;
|
|
105
|
+
const supplied = isRecord(node.params) ? node.params : {};
|
|
106
|
+
const childParams = applyRecipeParamDefaults(supplied, isRecord(dependency.document) ? dependency.document.paramsSchema : undefined);
|
|
107
|
+
findings.push(...validateRecipeWithManifest(resolveRecipeParamValue(dependency.document, childParams), manifest, validationOptions).findings);
|
|
108
|
+
findings.push(...validateActionInputs(dependency.document, adapter, childParams));
|
|
109
|
+
validateDependencyInstances(dependency.document, childParams);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
validateDependencyInstances(recipe, params ?? {});
|
|
113
|
+
if (proof && !findings.some((finding) => finding.severity === 'error')) {
|
|
114
|
+
findings.push(...validateRuntimeProofPlan(recipe, new Map([...dependencies.recipes].map(([ref, dependency]) => [
|
|
115
|
+
ref,
|
|
116
|
+
dependency.document,
|
|
117
|
+
]))));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (!(error instanceof RecipeResolutionError))
|
|
122
|
+
throw error;
|
|
123
|
+
findings.push({
|
|
124
|
+
severity: 'error',
|
|
125
|
+
code: error.code,
|
|
126
|
+
path: 'workflow',
|
|
127
|
+
message: `${error.message} Next: ${error.userAction}`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const errors = findings.filter((finding) => finding.severity === 'error').length;
|
|
132
|
+
const warnings = findings.length - errors;
|
|
133
|
+
return {
|
|
134
|
+
status: errors > 0 ? 'invalid' : 'valid',
|
|
135
|
+
findings,
|
|
136
|
+
summary: { errors, warnings },
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
export async function validateRunRecipeStatic(catalog, recipeArg, adapter, options, params = {}) {
|
|
140
|
+
// Resolve library sources first so resolveRunRecipeArg can probe personal and team
|
|
141
|
+
// recipe dirs with correct precedence before the canonical packaged library.
|
|
142
|
+
let librarySources = await resolveLibrarySources(catalog, optionStrings(options, 'library'));
|
|
143
|
+
const resolved = await resolveRunRecipeArg(catalog, recipeArg, adapter, librarySources);
|
|
144
|
+
const recipeFile = 'recipeFile' in resolved ? resolved.recipeFile : path.resolve(recipeArg);
|
|
145
|
+
if ('recipeFile' in resolved && !resolved.ref) {
|
|
146
|
+
librarySources = await resolveLibrarySources(catalog, optionStrings(options, 'library'), recipeFile);
|
|
147
|
+
}
|
|
148
|
+
const empty = {
|
|
149
|
+
recipe: undefined,
|
|
150
|
+
recipeFile,
|
|
151
|
+
findings: [],
|
|
152
|
+
errorCount: 0,
|
|
153
|
+
manifestOk: false,
|
|
154
|
+
schemaValid: false,
|
|
155
|
+
effectiveParams: params,
|
|
156
|
+
librarySources,
|
|
157
|
+
};
|
|
158
|
+
if ('notFound' in resolved) {
|
|
159
|
+
return {
|
|
160
|
+
...empty,
|
|
161
|
+
usageError: {
|
|
162
|
+
code: 'RECIPE_NOT_FOUND',
|
|
163
|
+
message: resolved.notFound +
|
|
164
|
+
(await actionInsteadOfRecipeHint(catalog, recipeArg, adapter, options, librarySources)),
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
let recipe;
|
|
169
|
+
try {
|
|
170
|
+
recipe = JSON.parse(fs.readFileSync(recipeFile, 'utf8'));
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
return {
|
|
174
|
+
...empty,
|
|
175
|
+
usageError: {
|
|
176
|
+
code: 'RECIPE_UNPARSEABLE',
|
|
177
|
+
message: `recipe is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const findings = [];
|
|
182
|
+
const { manifest } = await catalog.resolveActionManifest(adapter, optionString(options, 'actionManifest'), librarySources);
|
|
183
|
+
let manifestOk = true;
|
|
184
|
+
try {
|
|
185
|
+
await catalog.validateManifest(manifest);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
manifestOk = false;
|
|
189
|
+
findings.push({
|
|
190
|
+
severity: 'error',
|
|
191
|
+
code: 'manifest.invalid',
|
|
192
|
+
path: actionManifestPathOption(options, adapter),
|
|
193
|
+
message: error instanceof Error ? error.message : String(error),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
let effectiveParams = params;
|
|
197
|
+
if (isRecord(recipe)) {
|
|
198
|
+
effectiveParams = applyRecipeParamDefaults(params, recipe.paramsSchema);
|
|
199
|
+
findings.push(...validateRecipeParams(effectiveParams, recipe.paramsSchema).findings);
|
|
200
|
+
}
|
|
201
|
+
// Reuse librarySources already resolved above — no second resolution needed.
|
|
202
|
+
const validation = manifestOk
|
|
203
|
+
? await validateRecipeAdapterAware(adapter, recipe, manifest, librarySources, resolved.ref, effectiveParams, optionFlag(options, 'proof'))
|
|
204
|
+
: {
|
|
205
|
+
status: 'invalid',
|
|
206
|
+
findings: [],
|
|
207
|
+
summary: { errors: 1, warnings: 0 },
|
|
208
|
+
};
|
|
209
|
+
findings.push(...validation.findings);
|
|
210
|
+
const errorCount = findings.filter((finding) => finding.severity === 'error').length;
|
|
211
|
+
return {
|
|
212
|
+
recipe,
|
|
213
|
+
recipeFile,
|
|
214
|
+
findings,
|
|
215
|
+
errorCount,
|
|
216
|
+
manifestOk,
|
|
217
|
+
schemaValid: validation.status === 'valid' && errorCount === 0,
|
|
218
|
+
effectiveParams,
|
|
219
|
+
librarySources,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
// A bare name that is one action, not a recipe: the `call` that runs it,
|
|
223
|
+
// judged against the library sources the recipe lookup already resolved.
|
|
224
|
+
async function actionInsteadOfRecipeHint(catalog, recipeArg, adapter, options, librarySources) {
|
|
225
|
+
if (recipeArg.includes('/') || recipeArg.includes(path.sep))
|
|
226
|
+
return '';
|
|
227
|
+
try {
|
|
228
|
+
const { manifest } = await catalog.resolveActionManifest(adapter, optionString(options, 'actionManifest'), librarySources);
|
|
229
|
+
const actions = getRecipeActionManifestActionNames(manifest);
|
|
230
|
+
const matches = actions.includes(recipeArg)
|
|
231
|
+
? [recipeArg]
|
|
232
|
+
: actions.filter((name) => name.split('.').pop() === recipeArg);
|
|
233
|
+
if (matches.length !== 1)
|
|
234
|
+
return '';
|
|
235
|
+
const device = optionString(options, 'device');
|
|
236
|
+
const actionManifest = optionString(options, 'actionManifest');
|
|
237
|
+
const parts = [harnessHost().name, 'call', matches[0]];
|
|
238
|
+
parts.push('--adapter', adapter, '--target', shellQuoteArg(targetPath(options)));
|
|
239
|
+
if (device)
|
|
240
|
+
parts.push('--device', shellQuoteArg(device));
|
|
241
|
+
if (actionManifest)
|
|
242
|
+
parts.push('--action-manifest', shellQuoteArg(actionManifest));
|
|
243
|
+
return ` This is an action, not a recipe. Use: ${parts.join(' ')}.`;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return '';
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=recipe-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe-validation.js","sourceRoot":"","sources":["../../src/harness/recipe-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { CommandOptions, HealBoundViolation, HealPolicy, HealState, RecipeNodeEvent, RecipeRunOptions } from '@farmslot/adapter-sdk';
|
|
2
|
+
import type { RecipeActionManifestDocument, RecipeExecutionPlan } from '@farmslot/protocol';
|
|
3
|
+
import { type RecipeRunner, type RecipeRunRequest, type RecipeRunResult } from '@farmslot/recipe-runner';
|
|
4
|
+
import type { ActionCapabilitySource, RecipeCatalog } from './catalog.js';
|
|
5
|
+
import { type ExecutionProvenanceInput, type ExecutionProvenanceSnapshot } from './execution-provenance.js';
|
|
6
|
+
import { type CliOptions } from './parse-args.js';
|
|
7
|
+
import { type ConsoleAllowlist, type ConsoleClassifier, type RecipeRunEvidence } from './run-diagnostics.js';
|
|
8
|
+
export interface RecipeRunnerOptions<TMutation> {
|
|
9
|
+
quietStdout?: boolean;
|
|
10
|
+
suppressLibraryResolutionLogs?: boolean;
|
|
11
|
+
autoHud?: boolean;
|
|
12
|
+
actionSources: ReadonlyMap<string, ActionCapabilitySource>;
|
|
13
|
+
trustTaskActions: boolean;
|
|
14
|
+
trustedMutation?: TMutation;
|
|
15
|
+
onActionEvent?(event: RecipeNodeEvent): void;
|
|
16
|
+
}
|
|
17
|
+
/** What a host executes recipes with, on top of its catalog. */
|
|
18
|
+
export interface RecipeEngine<TMutation = unknown, TAllowlist extends ConsoleAllowlist = ConsoleAllowlist> extends RecipeCatalog {
|
|
19
|
+
createRunner(adapter: string, manifest: RecipeActionManifestDocument, options: RecipeRunnerOptions<TMutation>): Promise<RecipeRunner>;
|
|
20
|
+
/**
|
|
21
|
+
* Funded external mutations: the context loaded from the run's command line,
|
|
22
|
+
* then bound to the run's execution plan. Absent: no run binds one.
|
|
23
|
+
*/
|
|
24
|
+
trustedMutation?: {
|
|
25
|
+
load(input: {
|
|
26
|
+
cli: CommandOptions;
|
|
27
|
+
artifactsDir: string;
|
|
28
|
+
projectRoot: string;
|
|
29
|
+
rootRecipe: unknown;
|
|
30
|
+
}): Promise<TMutation | undefined>;
|
|
31
|
+
authorize(base: TMutation, plan: RecipeExecutionPlan): Promise<TMutation>;
|
|
32
|
+
};
|
|
33
|
+
/** The console rules run diagnostics classify with. */
|
|
34
|
+
console: ConsoleClassifier<TAllowlist>;
|
|
35
|
+
/** The host package paths a run's provenance binds. */
|
|
36
|
+
runnerIncludes: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
/** A run's options: the generic ones (with the platform's own) and the command line it came from. */
|
|
39
|
+
export type RecipeEngineRunOptions = RecipeRunOptions & {
|
|
40
|
+
cli?: CommandOptions;
|
|
41
|
+
};
|
|
42
|
+
export interface PreparedRecipeExecution {
|
|
43
|
+
runner: RecipeRunner;
|
|
44
|
+
runRequest: RecipeRunRequest;
|
|
45
|
+
absoluteArtifactsDir: string;
|
|
46
|
+
useFramedRecording: boolean;
|
|
47
|
+
provenanceInput: ExecutionProvenanceInput;
|
|
48
|
+
provenanceSnapshots: ExecutionProvenanceSnapshot[];
|
|
49
|
+
}
|
|
50
|
+
export declare function runRecipe<TMutation, TAllowlist extends ConsoleAllowlist>(engine: RecipeEngine<TMutation, TAllowlist>, adapter: string, recipe: string | object, artifactsDir: string, projectRoot: string, actionManifestPath?: string, runtimeOptions?: RecipeEngineRunOptions, preflightedExecution?: PreparedRecipeExecution, effectsState?: HealState): Promise<RecipeRunEvidence>;
|
|
51
|
+
export declare function preflightRecipe<TMutation, TAllowlist extends ConsoleAllowlist>(engine: RecipeEngine<TMutation, TAllowlist>, adapter: string, recipe: string | object, artifactsDir: string, projectRoot: string, actionManifestPath?: string, runtimeOptions?: RecipeEngineRunOptions): Promise<PreparedRecipeExecution>;
|
|
52
|
+
/** Record the run's recoveries and mutations in its summary and artifact manifest. */
|
|
53
|
+
export declare function persistRunEffects(summaryPath: string, artifactManifestPath: string, state: HealState): void;
|
|
54
|
+
export declare function activateRecipeRuntimeEnvironment(adapter: string, projectRoot: string, runtimeOptions: RecipeRunOptions): () => void;
|
|
55
|
+
export declare function prepareRuntimeIfNeeded(adapter: string, projectRoot: string, runtimeOptions: RecipeRunOptions): Promise<void>;
|
|
56
|
+
/** A one-node recipe that runs `action` with `args` through the real engine path. */
|
|
57
|
+
export declare function synthesizeOneNodeRecipe(action: string, args: Record<string, unknown>): Record<string, unknown>;
|
|
58
|
+
interface PreparedHeal {
|
|
59
|
+
state: HealState;
|
|
60
|
+
heal: HealPolicy;
|
|
61
|
+
}
|
|
62
|
+
interface PrepareHealOptions {
|
|
63
|
+
appRestartAuthored?: boolean;
|
|
64
|
+
onPhase?: (phase: 'install' | 'healthcheck' | 'recover', fields?: Record<string, unknown>) => void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Refuse a concurrent recipe, parse --heal, ensure the runtime overlay, and run
|
|
68
|
+
* the platform's runtime check: the heal state to run with, or the exit code.
|
|
69
|
+
*/
|
|
70
|
+
export declare function prepareHeal(adapter: string, target: string, options: CliOptions, json: boolean, prepareOptions?: PrepareHealOptions): Promise<PreparedHeal | number>;
|
|
71
|
+
/** Run once; a failed run is classified against the recovery bounds. */
|
|
72
|
+
export declare function executeWithHealBounds<T extends RecipeRunResult>(exec: () => Promise<T>, target: string, state: HealState): Promise<{
|
|
73
|
+
result: T;
|
|
74
|
+
violation: HealBoundViolation | null;
|
|
75
|
+
}>;
|
|
76
|
+
export declare function emitHealViolation(json: boolean, command: 'run' | 'call', result: Pick<RecipeRunResult, 'summaryPath' | 'tracePath' | 'artifactManifestPath'>, violation: HealBoundViolation, state: HealState, adapter?: string): number;
|
|
77
|
+
export declare function countRecipeNodes(recipe: unknown): number | undefined;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=run-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-engine.d.ts","sourceRoot":"","sources":["../../src/harness/run-engine.ts"],"names":[],"mappings":"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"}
|