@farmslot/recipe-cli 0.4.1 → 0.6.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 +32 -0
- package/README.md +11 -11
- package/dist/harness/acceptance-ledger.d.ts +18 -0
- package/dist/harness/acceptance-ledger.d.ts.map +1 -0
- package/dist/harness/acceptance-ledger.js +119 -0
- package/dist/harness/acceptance-ledger.js.map +1 -0
- package/dist/harness/adapter-plugins.d.ts +105 -0
- package/dist/harness/adapter-plugins.d.ts.map +1 -0
- package/dist/harness/adapter-plugins.js +388 -0
- package/dist/harness/adapter-plugins.js.map +1 -0
- 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 +667 -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 +760 -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 +10 -1
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +10 -1
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/live-adapter-contract.d.ts +9 -0
- package/dist/harness/live-adapter-contract.d.ts.map +1 -1
- package/dist/harness/live-adapter-contract.js +38 -7
- package/dist/harness/live-adapter-contract.js.map +1 -1
- package/dist/harness/network-observation.d.ts +16 -0
- package/dist/harness/network-observation.d.ts.map +1 -0
- package/dist/harness/network-observation.js +197 -0
- package/dist/harness/network-observation.js.map +1 -0
- package/dist/harness/plugin-imports.d.ts +23 -0
- package/dist/harness/plugin-imports.d.ts.map +1 -0
- package/dist/harness/plugin-imports.js +126 -0
- package/dist/harness/plugin-imports.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 +29 -0
- package/dist/harness/recipe-validation.d.ts.map +1 -0
- package/dist/harness/recipe-validation.js +291 -0
- package/dist/harness/recipe-validation.js.map +1 -0
- package/dist/harness/run-engine.d.ts +98 -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 +10 -0
- package/dist/harness/run-observers.d.ts.map +1 -0
- package/dist/harness/run-observers.js +24 -0
- package/dist/harness/run-observers.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Network observation around a run: the platform's capture backend
|
|
2
|
+
// (`observation.network`), an automatic whole-run capture summarized per node,
|
|
3
|
+
// and the `app.network_capture` windows recipes open on the same session.
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { harnessAdapter } from './adapters.js';
|
|
6
|
+
import { indexArtifactManifest, writeContainedArtifact } from './artifact-files.js';
|
|
7
|
+
import { hostEnvName } from './host.js';
|
|
8
|
+
const AUTO_CAPTURE_ID = 'run-network';
|
|
9
|
+
const AUTO_ARTIFACT_PATH = 'network/run-summary.json';
|
|
10
|
+
const sessions = new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Open the run's network session on the platform's backend and, unless the
|
|
13
|
+
* host's `AUTO_NETWORK_CAPTURE` is `0`, capture the whole run. Undefined when
|
|
14
|
+
* the platform observes no network.
|
|
15
|
+
*/
|
|
16
|
+
export async function startRunNetworkObservation(adapter, target, artifactsDir, env) {
|
|
17
|
+
const network = harnessAdapter(adapter).observation?.network;
|
|
18
|
+
if (!network)
|
|
19
|
+
return undefined;
|
|
20
|
+
const autoCapture = env[hostEnvName('AUTO_NETWORK_CAPTURE')] !== '0';
|
|
21
|
+
const key = path.resolve(artifactsDir);
|
|
22
|
+
const session = {
|
|
23
|
+
adapter,
|
|
24
|
+
artifactsDir: key,
|
|
25
|
+
autoStarted: false,
|
|
26
|
+
autoStartedAt: Date.now(),
|
|
27
|
+
nodeEvents: [],
|
|
28
|
+
};
|
|
29
|
+
sessions.set(key, session);
|
|
30
|
+
try {
|
|
31
|
+
session.backend = await network.backend(target, env, key);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
session.setupError = boundedError(error);
|
|
35
|
+
}
|
|
36
|
+
if (autoCapture) {
|
|
37
|
+
session.autoStartedAt = Date.now();
|
|
38
|
+
if (session.backend) {
|
|
39
|
+
try {
|
|
40
|
+
await session.backend.start({
|
|
41
|
+
id: AUTO_CAPTURE_ID,
|
|
42
|
+
bodyJsonFields: ['type'],
|
|
43
|
+
maxDurationMs: 60 * 60 * 1000,
|
|
44
|
+
maxRequests: 10_000,
|
|
45
|
+
methods: [],
|
|
46
|
+
urlIncludes: [],
|
|
47
|
+
});
|
|
48
|
+
session.autoStarted = true;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
session.setupError = boundedError(error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
onActionEvent(event) {
|
|
57
|
+
session.nodeEvents.push({ ...event, epochMs: Date.now() });
|
|
58
|
+
},
|
|
59
|
+
async finalize(artifactManifestPath) {
|
|
60
|
+
try {
|
|
61
|
+
if (autoCapture) {
|
|
62
|
+
const summary = await automaticSummary(session);
|
|
63
|
+
await writeSummary(key, AUTO_ARTIFACT_PATH, summary);
|
|
64
|
+
if (artifactManifestPath) {
|
|
65
|
+
await indexArtifactManifest(artifactManifestPath, [
|
|
66
|
+
{
|
|
67
|
+
path: AUTO_ARTIFACT_PATH,
|
|
68
|
+
type: 'report',
|
|
69
|
+
label: 'Automatic network observation',
|
|
70
|
+
category: 'diagnostic',
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
sessions.delete(key);
|
|
78
|
+
await session.backend?.close();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `app.network_capture phase=start|end id=<id>` on the run's session: start a
|
|
85
|
+
* filtered capture window, or end it and write its summary artifact. `adapter`
|
|
86
|
+
* is the platform dispatching the action; it names the observation when the
|
|
87
|
+
* run opened no session.
|
|
88
|
+
*/
|
|
89
|
+
export async function runNetworkCaptureAction(adapter, node, context) {
|
|
90
|
+
const action = 'app.network_capture';
|
|
91
|
+
const phase = String(node.phase ?? '').toLowerCase();
|
|
92
|
+
const id = String(node.id ?? '').trim();
|
|
93
|
+
if (!['start', 'end'].includes(phase) || !id) {
|
|
94
|
+
throw new Error('app.network_capture requires phase=start|end and a non-empty id.');
|
|
95
|
+
}
|
|
96
|
+
const session = sessions.get(path.resolve(context.artifactsDir));
|
|
97
|
+
if (!session?.backend) {
|
|
98
|
+
throw new Error(`${capitalize(session?.adapter ?? adapter)} network observation is unavailable: ${session?.setupError ?? 'run observer was not started'}.`);
|
|
99
|
+
}
|
|
100
|
+
if (phase === 'start') {
|
|
101
|
+
const result = await session.backend.start(captureParams(id, node));
|
|
102
|
+
return {
|
|
103
|
+
output: {
|
|
104
|
+
action,
|
|
105
|
+
phase,
|
|
106
|
+
...asRecord(result),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const artifactPath = String(node.artifact_path ?? `network/${id}-summary.json`);
|
|
111
|
+
const summary = await session.backend.end(id);
|
|
112
|
+
await writeSummary(context.artifactsDir, artifactPath, summary);
|
|
113
|
+
return {
|
|
114
|
+
output: {
|
|
115
|
+
action,
|
|
116
|
+
phase,
|
|
117
|
+
...summary,
|
|
118
|
+
},
|
|
119
|
+
artifacts: [
|
|
120
|
+
{
|
|
121
|
+
path: artifactPath,
|
|
122
|
+
type: 'report',
|
|
123
|
+
nodeId: context.nodeId,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async function automaticSummary(session) {
|
|
129
|
+
let summary;
|
|
130
|
+
if (session.autoStarted && session.backend) {
|
|
131
|
+
try {
|
|
132
|
+
summary = await session.backend.end(AUTO_CAPTURE_ID);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
summary = unavailableSummary(session.autoStartedAt, boundedError(error));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
summary = unavailableSummary(session.autoStartedAt, session.setupError ?? 'Network observer did not start.');
|
|
140
|
+
}
|
|
141
|
+
const startedAt = Number(summary.startedAtEpochMs ?? session.autoStartedAt);
|
|
142
|
+
return {
|
|
143
|
+
...summary,
|
|
144
|
+
nodeEvents: session.nodeEvents.map((event) => ({
|
|
145
|
+
action: event.action,
|
|
146
|
+
elapsedMs: Math.max(0, event.epochMs - startedAt),
|
|
147
|
+
nodeId: event.nodeId,
|
|
148
|
+
status: event.status,
|
|
149
|
+
})),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function unavailableSummary(startedAtEpochMs, reason) {
|
|
153
|
+
return {
|
|
154
|
+
schemaVersion: 1,
|
|
155
|
+
id: AUTO_CAPTURE_ID,
|
|
156
|
+
status: 'unavailable',
|
|
157
|
+
startedAtEpochMs,
|
|
158
|
+
endedAtEpochMs: Date.now(),
|
|
159
|
+
maxDurationMs: 60 * 60 * 1000,
|
|
160
|
+
reconnects: 0,
|
|
161
|
+
droppedRequests: 0,
|
|
162
|
+
unavailableReasons: [reason],
|
|
163
|
+
coverageGapReasons: [],
|
|
164
|
+
uninspectableBodyRequests: 0,
|
|
165
|
+
projectedBodyFields: ['type'],
|
|
166
|
+
totalRequests: 0,
|
|
167
|
+
requestsByMethod: {},
|
|
168
|
+
requestsByHost: {},
|
|
169
|
+
requestsByType: {},
|
|
170
|
+
requests: [],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function captureParams(id, node) {
|
|
174
|
+
return {
|
|
175
|
+
id,
|
|
176
|
+
urlIncludes: node.url_includes ?? [],
|
|
177
|
+
methods: node.methods ?? [],
|
|
178
|
+
bodyJsonFields: node.body_json_fields ?? [],
|
|
179
|
+
maxRequests: node.max_requests,
|
|
180
|
+
maxDurationMs: node.max_duration_ms,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async function writeSummary(artifactsDir, relativePath, summary) {
|
|
184
|
+
await writeContainedArtifact(artifactsDir, relativePath, `${JSON.stringify(summary, null, 2)}\n`, 'Network artifact');
|
|
185
|
+
}
|
|
186
|
+
function capitalize(value) {
|
|
187
|
+
return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
188
|
+
}
|
|
189
|
+
function boundedError(error) {
|
|
190
|
+
return String(error instanceof Error ? error.message : error).slice(0, 256);
|
|
191
|
+
}
|
|
192
|
+
function asRecord(value) {
|
|
193
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
194
|
+
? value
|
|
195
|
+
: {};
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=network-observation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-observation.js","sourceRoot":"","sources":["../../src/harness/network-observation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** What one loaded plugin may import: the real paths of its digested files. */
|
|
2
|
+
export interface PluginImportScope {
|
|
3
|
+
id: string;
|
|
4
|
+
library: string;
|
|
5
|
+
/** Real path of the library root. */
|
|
6
|
+
root: string;
|
|
7
|
+
/** Real paths of the files under the module's directory (or the module alone at the root). */
|
|
8
|
+
moduleFiles: ReadonlySet<string>;
|
|
9
|
+
/** Real paths of every file its digest covers: `moduleFiles` plus the library's actions/. */
|
|
10
|
+
files: ReadonlySet<string>;
|
|
11
|
+
/** A file URL inside the host's install that bare package specifiers resolve from. */
|
|
12
|
+
hostURL: string;
|
|
13
|
+
/** Real path of the host's package root: the node_modules on its lookup chain are its install. */
|
|
14
|
+
hostRoot: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Whether this Node.js can fence plugin imports: `module.registerHooks`
|
|
18
|
+
* (Node.js 22.15 / 23.5 and later). Without it a host must not load plugins.
|
|
19
|
+
*/
|
|
20
|
+
export declare function canFencePluginImports(): boolean;
|
|
21
|
+
/** Fence the imports `scope`'s code makes; installs the hook on first use. */
|
|
22
|
+
export declare function fencePluginImports(scope: PluginImportScope): void;
|
|
23
|
+
//# sourceMappingURL=plugin-imports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-imports.d.ts","sourceRoot":"","sources":["../../src/harness/plugin-imports.ts"],"names":[],"mappings":"AAgBA,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,8FAA8F;IAC9F,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,6FAA6F;IAC7F,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CA8CjE"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// The import fence for adapter plugins. A plugin's digest covers the files
|
|
2
|
+
// `libraryAdapterFiles` lists (its module's directory and the library's
|
|
3
|
+
// actions/), so a plugin may import only those from disk: a module resolve hook
|
|
4
|
+
// refuses any import from plugin code that resolves, after symlinks, to another
|
|
5
|
+
// file. Plugin code is the module's directory plus the actions/ files it reaches
|
|
6
|
+
// through its own imports; an actions/ file the host imports by itself is not.
|
|
7
|
+
// A bare package specifier resolves from the host's install, never from the
|
|
8
|
+
// library's location, and `node:` builtins stay allowed. The hook stays
|
|
9
|
+
// installed, so imports the plugin makes later, while it runs, are fenced too.
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import nodeModule from 'node:module';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { RecipeTrustError } from '@farmslot/recipe-runner';
|
|
15
|
+
const scopes = [];
|
|
16
|
+
/** actions/ files plugin code imported, by real path, and the plugins that reached them. */
|
|
17
|
+
const reached = new Map();
|
|
18
|
+
let installed = false;
|
|
19
|
+
/**
|
|
20
|
+
* Whether this Node.js can fence plugin imports: `module.registerHooks`
|
|
21
|
+
* (Node.js 22.15 / 23.5 and later). Without it a host must not load plugins.
|
|
22
|
+
*/
|
|
23
|
+
export function canFencePluginImports() {
|
|
24
|
+
return typeof nodeModule.registerHooks === 'function';
|
|
25
|
+
}
|
|
26
|
+
/** Fence the imports `scope`'s code makes; installs the hook on first use. */
|
|
27
|
+
export function fencePluginImports(scope) {
|
|
28
|
+
scopes.push(scope);
|
|
29
|
+
if (installed)
|
|
30
|
+
return;
|
|
31
|
+
nodeModule.registerHooks({
|
|
32
|
+
resolve(specifier, context, nextResolve) {
|
|
33
|
+
const owners = ownersOf(context.parentURL);
|
|
34
|
+
if (owners.length === 0 || nodeModule.isBuiltin(specifier)) {
|
|
35
|
+
return nextResolve(specifier, context);
|
|
36
|
+
}
|
|
37
|
+
const owner = owners[0];
|
|
38
|
+
if (isBareSpecifier(specifier)) {
|
|
39
|
+
// A package comes from the host's install, not whatever node_modules sits
|
|
40
|
+
// beside or above the library, which no digest covers.
|
|
41
|
+
let result;
|
|
42
|
+
try {
|
|
43
|
+
result = nextResolve(specifier, { ...context, parentURL: owner.hostURL });
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw refusal(owner, specifier, "which the host's install does not provide");
|
|
47
|
+
}
|
|
48
|
+
const target = filePath(result.url);
|
|
49
|
+
if (target !== undefined && owners.some((scope) => inOrAboveLibrary(scope, target)))
|
|
50
|
+
throw refusal(owner, specifier, `which resolves to ${target}, beside or above the library`);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
const result = nextResolve(specifier, context);
|
|
54
|
+
const target = filePath(result.url);
|
|
55
|
+
// A file that doesn't exist fails to import on its own.
|
|
56
|
+
if (target === undefined)
|
|
57
|
+
return result;
|
|
58
|
+
const covering = owners.filter((scope) => scope.files.has(target));
|
|
59
|
+
if (covering.length === 0)
|
|
60
|
+
throw refusal(owner, specifier, `which resolves to ${target}: outside the files its digest covers (its module's directory and the library's actions/)`);
|
|
61
|
+
for (const scope of covering) {
|
|
62
|
+
if (!scope.moduleFiles.has(target))
|
|
63
|
+
markReached(target, scope);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
installed = true;
|
|
69
|
+
}
|
|
70
|
+
/** The plugins whose code `parentURL` is: their module directory, or an actions/ file they reached. */
|
|
71
|
+
function ownersOf(parentURL) {
|
|
72
|
+
const parent = parentURL === undefined ? undefined : filePath(parentURL);
|
|
73
|
+
if (parent === undefined)
|
|
74
|
+
return [];
|
|
75
|
+
const viaActions = reached.get(parent);
|
|
76
|
+
return scopes.filter((scope) => scope.moduleFiles.has(parent) || viaActions?.has(scope));
|
|
77
|
+
}
|
|
78
|
+
function markReached(file, scope) {
|
|
79
|
+
let owners = reached.get(file);
|
|
80
|
+
if (!owners)
|
|
81
|
+
reached.set(file, (owners = new Set()));
|
|
82
|
+
owners.add(scope);
|
|
83
|
+
}
|
|
84
|
+
// Inside the library, or in a node_modules whose parent directory holds the
|
|
85
|
+
// library but not the host. A node_modules on the host's own lookup chain (its
|
|
86
|
+
// package directory or any directory above it) is the host's install, even when
|
|
87
|
+
// the library sits under the same directory (a library folder in the product
|
|
88
|
+
// repo, with the host in <repo>/node_modules).
|
|
89
|
+
function inOrAboveLibrary(scope, file) {
|
|
90
|
+
if (isWithin(scope.root, file))
|
|
91
|
+
return true;
|
|
92
|
+
const parts = file.split(path.sep);
|
|
93
|
+
return parts.some((part, index) => {
|
|
94
|
+
if (part !== 'node_modules')
|
|
95
|
+
return false;
|
|
96
|
+
const parent = parts.slice(0, index).join(path.sep) || path.sep;
|
|
97
|
+
return isWithin(parent, scope.root) && !isWithin(parent, scope.hostRoot);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function refusal(scope, specifier, reason) {
|
|
101
|
+
return new RecipeTrustError({
|
|
102
|
+
code: 'RECIPE_SOURCE_INVALID',
|
|
103
|
+
message: `adapter '${scope.id}' (library ${scope.library}) imports ${specifier}, ${reason}.`,
|
|
104
|
+
userAction: "keep the plugin's code under its module's directory or the library's actions/, or import a package the host installs",
|
|
105
|
+
reason: 'invalid-source',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function isBareSpecifier(specifier) {
|
|
109
|
+
return !/^(?:\.{1,2}\/|\/|file:|#|data:)/u.test(specifier) && !path.isAbsolute(specifier);
|
|
110
|
+
}
|
|
111
|
+
function isWithin(root, file) {
|
|
112
|
+
return file === root || file.startsWith(root.endsWith(path.sep) ? root : `${root}${path.sep}`);
|
|
113
|
+
}
|
|
114
|
+
// The real path behind a file: URL; undefined for another scheme or a missing file.
|
|
115
|
+
function filePath(url) {
|
|
116
|
+
if (!url.startsWith('file:'))
|
|
117
|
+
return undefined;
|
|
118
|
+
try {
|
|
119
|
+
return fs.realpathSync(fileURLToPath(url));
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// A missing file: resolution or the import reports it.
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=plugin-imports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-imports.js","sourceRoot":"","sources":["../../src/harness/plugin-imports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type RecipeLibrarySource, type ResolvedLibraryRecipe } from '@farmslot/recipe-runner';
|
|
2
|
+
import type { RecipeCatalog, ResolvedActionManifest } from './catalog.js';
|
|
3
|
+
import { type CliOptions } from './parse-args.js';
|
|
4
|
+
export interface RecipeParameterSummary {
|
|
5
|
+
name: string;
|
|
6
|
+
type?: string;
|
|
7
|
+
required: boolean;
|
|
8
|
+
default?: unknown;
|
|
9
|
+
description?: string;
|
|
10
|
+
enum?: unknown[];
|
|
11
|
+
}
|
|
12
|
+
export interface RunnableRecipe {
|
|
13
|
+
name: string;
|
|
14
|
+
source: string;
|
|
15
|
+
file: string;
|
|
16
|
+
shadows: string[];
|
|
17
|
+
adapter: string;
|
|
18
|
+
variant: string | null;
|
|
19
|
+
parameters: RecipeParameterSummary[];
|
|
20
|
+
title?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface RunnableRecipeDetail extends RunnableRecipe {
|
|
24
|
+
path: string;
|
|
25
|
+
$schema?: string;
|
|
26
|
+
actions: string[];
|
|
27
|
+
nestedRecipes: string[];
|
|
28
|
+
unresolvedRecipes: string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The configured library sources (`--library`, the recipe's own library, the
|
|
32
|
+
* environment) plus the bundled library, each with its provenance. The bundled
|
|
33
|
+
* source name may only point at the bundled root.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveLibrarySources(catalog: RecipeCatalog, libraryEntry: string | readonly string[] | undefined, recipePath?: string): Promise<RecipeLibrarySource[]>;
|
|
36
|
+
/**
|
|
37
|
+
* The library sources a command's `--library` names (unless the caller already
|
|
38
|
+
* resolved them), and the adapter's action manifest over them (or its
|
|
39
|
+
* `--action-manifest`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveCommandManifest(catalog: RecipeCatalog, adapter: string, options: CliOptions, resolvedSources?: RecipeLibrarySource[]): Promise<ResolvedActionManifest & {
|
|
42
|
+
librarySources: RecipeLibrarySource[];
|
|
43
|
+
}>;
|
|
44
|
+
/** A recipe file path, or a library recipe name the sources resolve for the adapter. */
|
|
45
|
+
export declare function resolveRunRecipeArg(catalog: RecipeCatalog, recipeArg: string, adapter: string, librarySources?: RecipeLibrarySource[]): Promise<{
|
|
46
|
+
recipeFile: string;
|
|
47
|
+
ref?: string;
|
|
48
|
+
} | {
|
|
49
|
+
notFound: string;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Library recipes `run` accepts for this adapter: every precedence winner and
|
|
53
|
+
* qualified alias whose document, and every recipe it calls, validates against
|
|
54
|
+
* the adapter's resolved manifest, so discovery and execution agree.
|
|
55
|
+
*/
|
|
56
|
+
export declare function runnableLibraryRecipes(catalog: RecipeCatalog, adapter: string, sources: RecipeLibrarySource[]): Promise<ResolvedLibraryRecipe[]>;
|
|
57
|
+
export declare function listRunnableRecipes(catalog: RecipeCatalog, adapter: string, librarySources?: RecipeLibrarySource[]): Promise<RunnableRecipe[]>;
|
|
58
|
+
export declare function describeRunnableRecipe(catalog: RecipeCatalog, recipeArg: string, adapter: string, librarySources?: RecipeLibrarySource[]): Promise<{
|
|
59
|
+
recipe: RunnableRecipeDetail;
|
|
60
|
+
} | {
|
|
61
|
+
notFound: string;
|
|
62
|
+
} | {
|
|
63
|
+
unreadable: string;
|
|
64
|
+
}>;
|
|
65
|
+
//# sourceMappingURL=recipe-library.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe-library.d.ts","sourceRoot":"","sources":["../../src/harness/recipe-library.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAE3B,MAAM,yBAAyB,CAAC;AAKjC,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAG1E,OAAO,EACL,KAAK,UAAU,EAKhB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,sBAAsB,EAAE,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,cAAc;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,aAAa,EACtB,YAAY,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,EACpD,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAkChC;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,UAAU,EACnB,eAAe,CAAC,EAAE,mBAAmB,EAAE,GACtC,OAAO,CAAC,sBAAsB,GAAG;IAAE,cAAc,EAAE,mBAAmB,EAAE,CAAA;CAAE,CAAC,CAS7E;AAYD,wFAAwF;AACxF,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,mBAAmB,EAAE,GACrC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBtE;AAWD;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,mBAAmB,EAAE,GAC7B,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAMlC;AAED,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,mBAAmB,EAAE,GACrC,OAAO,CAAC,cAAc,EAAE,CAAC,CAM3B;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,mBAAmB,EAAE,GACrC,OAAO,CAAC;IAAE,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAqC3F"}
|
|
@@ -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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"}
|