@farmslot/agent-runtime 0.3.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 +11 -0
- package/bin/farmslot-agent.mjs +1 -1
- package/dist/execution-template/create.d.ts.map +1 -1
- package/dist/execution-template/create.js +9 -1
- package/dist/execution-template/create.js.map +1 -1
- package/dist/execution-template/execution-template.test.js +309 -1
- package/dist/execution-template/execution-template.test.js.map +1 -1
- package/dist/execution-template/frontmatter.d.ts.map +1 -1
- package/dist/execution-template/frontmatter.js +19 -4
- package/dist/execution-template/frontmatter.js.map +1 -1
- package/dist/execution-template/index.d.ts +8 -1
- package/dist/execution-template/index.d.ts.map +1 -1
- package/dist/execution-template/index.js +5 -0
- package/dist/execution-template/index.js.map +1 -1
- package/dist/execution-template/infer.d.ts.map +1 -1
- package/dist/execution-template/infer.js +14 -1
- package/dist/execution-template/infer.js.map +1 -1
- package/dist/execution-template/lint.d.ts.map +1 -1
- package/dist/execution-template/lint.js +8 -0
- package/dist/execution-template/lint.js.map +1 -1
- package/dist/execution-template/project-worker.d.ts +19 -0
- package/dist/execution-template/project-worker.d.ts.map +1 -0
- package/dist/execution-template/project-worker.js +143 -0
- package/dist/execution-template/project-worker.js.map +1 -0
- package/dist/execution-template/resolve.d.ts +1 -1
- package/dist/execution-template/resolve.d.ts.map +1 -1
- package/dist/execution-template/resolve.js +12 -2
- package/dist/execution-template/resolve.js.map +1 -1
- package/dist/execution-template/select.d.ts +8 -0
- package/dist/execution-template/select.d.ts.map +1 -0
- package/dist/execution-template/select.js +80 -0
- package/dist/execution-template/select.js.map +1 -0
- package/dist/execution-template/snapshot.d.ts +13 -0
- package/dist/execution-template/snapshot.d.ts.map +1 -0
- package/dist/execution-template/snapshot.js +46 -0
- package/dist/execution-template/snapshot.js.map +1 -0
- package/dist/execution-template/source-config.d.ts +19 -0
- package/dist/execution-template/source-config.d.ts.map +1 -0
- package/dist/execution-template/source-config.js +90 -0
- package/dist/execution-template/source-config.js.map +1 -0
- package/dist/execution-template/types.d.ts +38 -1
- package/dist/execution-template/types.d.ts.map +1 -1
- package/dist/execution-template/types.js +7 -2
- package/dist/execution-template/types.js.map +1 -1
- package/package.json +4 -3
- package/scripts/check-task-artifact-contract.mjs +112 -33
- package/scripts/checklist-target.cjs +13 -0
- package/scripts/execution-template-cli.mjs +150 -19
- package/scripts/mark-checklist-step.cjs +16 -6
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Standalone execution-template CLI for ADR-049.
|
|
4
4
|
* Used by `farmslot-agent execution-template` and thin Consensys wrappers.
|
|
5
5
|
*/
|
|
6
|
-
import { existsSync, statSync } from 'node:fs';
|
|
6
|
+
import { existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { dirname, resolve } from 'node:path';
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
9
|
|
|
@@ -12,11 +12,13 @@ const distEntry = resolve(packageRoot, 'dist', 'index.js');
|
|
|
12
12
|
|
|
13
13
|
function usage(exitCode = 0) {
|
|
14
14
|
const text = [
|
|
15
|
-
'Usage: execution-template <list|lint|new> [options]',
|
|
15
|
+
'Usage: execution-template <list|materialize|lint|new> [options]',
|
|
16
16
|
'',
|
|
17
|
-
'list --dir <path> --project-worker <path> --package-templates <path>',
|
|
17
|
+
'list --dir <path> --domain-dir <domain=path> --project-worker <path> --package-templates <path>',
|
|
18
18
|
' [--project-name name] [--package-id id] [--flow f] [--run-mode m]',
|
|
19
|
-
' [--platform p] [--no-include-shadowed] [--json]',
|
|
19
|
+
' [--platform p] [--domain d] [--no-include-shadowed] [--json]',
|
|
20
|
+
'materialize <output> --flow f --run-mode m --platform p [--domain d] [--id id]',
|
|
21
|
+
' [the same source options as list] [--provenance path] [--json]',
|
|
20
22
|
'lint <file-or-dir> [--json]',
|
|
21
23
|
'new <path> [--flow f] [--run-mode m] [--platform p] [--title t] [--force] [--json]',
|
|
22
24
|
].join('\n');
|
|
@@ -44,9 +46,22 @@ function parseRunMode(value) {
|
|
|
44
46
|
throw new Error(`--run-mode must be autonomous|interactive|validation (got ${value})`);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
function parseDomainDir(value) {
|
|
50
|
+
const separator = value.indexOf('=');
|
|
51
|
+
if (separator <= 0 || separator === value.length - 1) {
|
|
52
|
+
throw new Error('--domain-dir must use domain=path');
|
|
53
|
+
}
|
|
54
|
+
const domain = value.slice(0, separator);
|
|
55
|
+
if (!/^[a-z][a-z0-9-]*$/.test(domain)) {
|
|
56
|
+
throw new Error(`--domain-dir domain must be a lowercase slug (got ${domain})`);
|
|
57
|
+
}
|
|
58
|
+
return { domain, root: value.slice(separator + 1) };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function parseCatalogArgs(args, { materialize = false } = {}) {
|
|
48
62
|
const opts = {
|
|
49
63
|
dirs: [],
|
|
64
|
+
domainDirs: [],
|
|
50
65
|
projectWorker: null,
|
|
51
66
|
projectName: 'project',
|
|
52
67
|
packageTemplates: null,
|
|
@@ -54,12 +69,18 @@ async function cmdList(args, runtime) {
|
|
|
54
69
|
flow: undefined,
|
|
55
70
|
runMode: undefined,
|
|
56
71
|
platform: undefined,
|
|
72
|
+
domain: undefined,
|
|
73
|
+
id: undefined,
|
|
57
74
|
includeShadowed: true,
|
|
75
|
+
output: null,
|
|
76
|
+
provenance: null,
|
|
58
77
|
json: false,
|
|
59
78
|
};
|
|
60
79
|
for (let i = 0; i < args.length; i += 1) {
|
|
61
80
|
const arg = args[i];
|
|
62
81
|
if (arg === '--dir') opts.dirs.push(takeValue(args, i++, arg));
|
|
82
|
+
else if (arg === '--domain-dir')
|
|
83
|
+
opts.domainDirs.push(parseDomainDir(takeValue(args, i++, arg)));
|
|
63
84
|
else if (arg === '--project-worker') opts.projectWorker = takeValue(args, i++, arg);
|
|
64
85
|
else if (arg === '--project-name') opts.projectName = takeValue(args, i++, arg);
|
|
65
86
|
else if (arg === '--package-templates') opts.packageTemplates = takeValue(args, i++, arg);
|
|
@@ -67,20 +88,51 @@ async function cmdList(args, runtime) {
|
|
|
67
88
|
else if (arg === '--flow') opts.flow = takeValue(args, i++, arg);
|
|
68
89
|
else if (arg === '--run-mode') opts.runMode = parseRunMode(takeValue(args, i++, arg));
|
|
69
90
|
else if (arg === '--platform') opts.platform = takeValue(args, i++, arg);
|
|
91
|
+
else if (arg === '--domain') opts.domain = takeValue(args, i++, arg);
|
|
92
|
+
else if (arg === '--id') opts.id = takeValue(args, i++, arg);
|
|
93
|
+
else if (arg === '--provenance' && materialize) opts.provenance = takeValue(args, i++, arg);
|
|
70
94
|
else if (arg === '--include-shadowed') opts.includeShadowed = true;
|
|
71
95
|
else if (arg === '--no-include-shadowed') opts.includeShadowed = false;
|
|
72
96
|
else if (arg === '--json') opts.json = true;
|
|
73
97
|
else if (arg === '-h' || arg === '--help') usage(0);
|
|
98
|
+
else if (materialize && !arg.startsWith('-') && !opts.output) opts.output = arg;
|
|
74
99
|
else throw new Error(`unknown option ${arg}`);
|
|
75
100
|
}
|
|
101
|
+
return opts;
|
|
102
|
+
}
|
|
76
103
|
|
|
104
|
+
function buildSources(opts, runtime) {
|
|
77
105
|
const sources = [];
|
|
106
|
+
const domainSourceIds = new Set();
|
|
78
107
|
for (const [index, dir] of opts.dirs.entries()) {
|
|
79
108
|
const root = resolve(dir);
|
|
80
109
|
if (!existsSync(root) || !statSync(root).isDirectory()) {
|
|
81
110
|
throw new Error(`--dir is not a directory: ${root}`);
|
|
82
111
|
}
|
|
83
|
-
|
|
112
|
+
const source = runtime.customTemplateSource(`dir-${index + 1}`, root, 'flow-tree');
|
|
113
|
+
source.sourceRevision = runtime.executionTemplateSourceRevision(root);
|
|
114
|
+
source.sourceDirty = runtime.executionTemplateSourceDirty(root);
|
|
115
|
+
sources.push(source);
|
|
116
|
+
}
|
|
117
|
+
for (const item of opts.domainDirs) {
|
|
118
|
+
const root = resolve(item.root);
|
|
119
|
+
if (!existsSync(root) || !statSync(root).isDirectory()) {
|
|
120
|
+
throw new Error(`--domain-dir is not a directory: ${root}`);
|
|
121
|
+
}
|
|
122
|
+
const sourceId = `team:${item.domain}`;
|
|
123
|
+
if (domainSourceIds.has(sourceId)) {
|
|
124
|
+
throw new Error(`--domain-dir repeats domain ${item.domain}`);
|
|
125
|
+
}
|
|
126
|
+
domainSourceIds.add(sourceId);
|
|
127
|
+
sources.push({
|
|
128
|
+
id: sourceId,
|
|
129
|
+
kind: 'workspace',
|
|
130
|
+
root,
|
|
131
|
+
layout: 'flow-tree',
|
|
132
|
+
domains: [item.domain],
|
|
133
|
+
sourceRevision: runtime.executionTemplateSourceRevision(root),
|
|
134
|
+
sourceDirty: runtime.executionTemplateSourceDirty(root),
|
|
135
|
+
});
|
|
84
136
|
}
|
|
85
137
|
if (opts.projectWorker) {
|
|
86
138
|
const input = resolve(opts.projectWorker);
|
|
@@ -92,34 +144,112 @@ async function cmdList(args, runtime) {
|
|
|
92
144
|
if (!existsSync(workerRoot) || !statSync(workerRoot).isDirectory()) {
|
|
93
145
|
throw new Error(`--project-worker is not a directory: ${workerRoot}`);
|
|
94
146
|
}
|
|
95
|
-
sources.push(
|
|
147
|
+
sources.push(
|
|
148
|
+
runtime.projectWorkerTemplateSource(
|
|
149
|
+
opts.projectName,
|
|
150
|
+
projectTemplatesDir,
|
|
151
|
+
runtime.executionTemplateSourceRevision(projectTemplatesDir),
|
|
152
|
+
runtime.executionTemplateSourceDirty(projectTemplatesDir),
|
|
153
|
+
),
|
|
154
|
+
);
|
|
96
155
|
}
|
|
97
156
|
if (opts.packageTemplates) {
|
|
98
157
|
const root = resolve(opts.packageTemplates);
|
|
99
158
|
if (!existsSync(root) || !statSync(root).isDirectory()) {
|
|
100
159
|
throw new Error(`--package-templates is not a directory: ${root}`);
|
|
101
160
|
}
|
|
102
|
-
|
|
161
|
+
const source = runtime.packageFlowTreeTemplateSource(opts.packageId, root);
|
|
162
|
+
source.sourceRevision = runtime.executionTemplateSourceRevision(root);
|
|
163
|
+
source.sourceDirty = runtime.executionTemplateSourceDirty(root);
|
|
164
|
+
sources.push(source);
|
|
103
165
|
}
|
|
104
166
|
if (sources.length === 0) {
|
|
105
|
-
throw new Error('provide --dir, --project-worker, and/or --package-templates');
|
|
167
|
+
throw new Error('provide --dir, --domain-dir, --project-worker, and/or --package-templates');
|
|
106
168
|
}
|
|
169
|
+
return sources;
|
|
170
|
+
}
|
|
107
171
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
172
|
+
async function cmdList(args, runtime) {
|
|
173
|
+
const opts = parseCatalogArgs(args);
|
|
174
|
+
if (opts.id) throw new Error('--id is only valid with materialize');
|
|
175
|
+
const sources = buildSources(opts, runtime);
|
|
176
|
+
const templates =
|
|
177
|
+
opts.flow && opts.platform && opts.runMode
|
|
178
|
+
? runtime.listCompatibleExecutionTemplates({
|
|
179
|
+
sources,
|
|
180
|
+
flow: opts.flow,
|
|
181
|
+
platform: opts.platform,
|
|
182
|
+
runMode: opts.runMode,
|
|
183
|
+
...(opts.domain ? { domain: opts.domain } : {}),
|
|
184
|
+
})
|
|
185
|
+
: runtime.listExecutionTemplates({
|
|
186
|
+
sources,
|
|
187
|
+
flow: opts.flow,
|
|
188
|
+
runMode: opts.runMode,
|
|
189
|
+
platform: opts.platform,
|
|
190
|
+
domain: opts.domain,
|
|
191
|
+
includeShadowed: opts.includeShadowed,
|
|
192
|
+
});
|
|
193
|
+
const catalog = templates.map((entry) => ({
|
|
194
|
+
...runtime.executionTemplateReference(entry),
|
|
195
|
+
title: entry.title,
|
|
196
|
+
sourceKind: entry.sourceKind,
|
|
197
|
+
...(entry.shadowedBy ? { shadowedBy: entry.shadowedBy } : {}),
|
|
198
|
+
}));
|
|
115
199
|
if (opts.json) {
|
|
116
|
-
process.stdout.write(`${JSON.stringify({ templates }, null, 2)}\n`);
|
|
200
|
+
process.stdout.write(`${JSON.stringify({ templates: catalog }, null, 2)}\n`);
|
|
117
201
|
return;
|
|
118
202
|
}
|
|
119
|
-
for (const entry of
|
|
203
|
+
for (const entry of catalog) {
|
|
120
204
|
const shadow = entry.shadowedBy ? ` (shadowed by ${entry.shadowedBy})` : '';
|
|
121
205
|
process.stdout.write(
|
|
122
|
-
`${entry.id}\t${entry.flow}\t${entry.runMode ?? '-'}\t${entry.platforms.join(',')}\t${entry.sourceId}
|
|
206
|
+
`${entry.id}\t${entry.flow}\t${entry.runMode ?? '-'}\t${entry.platforms.join(',')}\t${entry.sourceId}${shadow}\n`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function cmdMaterialize(args, runtime) {
|
|
212
|
+
const opts = parseCatalogArgs(args, { materialize: true });
|
|
213
|
+
if (!opts.output) throw new Error('materialize requires <output>');
|
|
214
|
+
if (!opts.flow) throw new Error('materialize requires --flow');
|
|
215
|
+
if (!opts.runMode) throw new Error('materialize requires --run-mode');
|
|
216
|
+
if (!opts.platform) throw new Error('materialize requires --platform');
|
|
217
|
+
const output = resolve(opts.output);
|
|
218
|
+
if (existsSync(output)) {
|
|
219
|
+
throw new Error(`Execution-template destination already exists: ${output}`);
|
|
220
|
+
}
|
|
221
|
+
if (opts.provenance && existsSync(resolve(opts.provenance))) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Execution-template provenance destination already exists: ${resolve(opts.provenance)}`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
const selected = runtime.selectExecutionTemplate({
|
|
227
|
+
sources: buildSources(opts, runtime),
|
|
228
|
+
flow: opts.flow,
|
|
229
|
+
platform: opts.platform,
|
|
230
|
+
runMode: opts.runMode,
|
|
231
|
+
...(opts.domain ? { domain: opts.domain } : {}),
|
|
232
|
+
...(opts.id ? { explicitId: opts.id } : {}),
|
|
233
|
+
});
|
|
234
|
+
const materialized = runtime.materializeExecutionTemplate(selected.entry, output);
|
|
235
|
+
const provenance = {
|
|
236
|
+
schemaVersion: 1,
|
|
237
|
+
selectionReason: selected.reason,
|
|
238
|
+
executionTemplate: materialized.reference,
|
|
239
|
+
};
|
|
240
|
+
if (opts.provenance) {
|
|
241
|
+
const destination = resolve(opts.provenance);
|
|
242
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
243
|
+
writeFileSync(destination, `${JSON.stringify(provenance, null, 2)}\n`, {
|
|
244
|
+
encoding: 'utf8',
|
|
245
|
+
flag: 'wx',
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
const result = { ...materialized, provenance };
|
|
249
|
+
if (opts.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
250
|
+
else {
|
|
251
|
+
process.stdout.write(
|
|
252
|
+
`materialized ${selected.entry.id} from ${selected.entry.sourceId} -> ${materialized.path}\n`,
|
|
123
253
|
);
|
|
124
254
|
}
|
|
125
255
|
}
|
|
@@ -192,6 +322,7 @@ async function main() {
|
|
|
192
322
|
if (!command || command === '-h' || command === '--help') usage(0);
|
|
193
323
|
const runtime = await loadRuntime();
|
|
194
324
|
if (command === 'list') await cmdList(rest, runtime);
|
|
325
|
+
else if (command === 'materialize') await cmdMaterialize(rest, runtime);
|
|
195
326
|
else if (command === 'lint') await cmdLint(rest, runtime);
|
|
196
327
|
else if (command === 'new') await cmdNew(rest, runtime);
|
|
197
328
|
else usage(2);
|
|
@@ -6,7 +6,10 @@ const {
|
|
|
6
6
|
expandedArtifactsForCommand,
|
|
7
7
|
resolveWorkerTerminalContract,
|
|
8
8
|
} = require('./worker-terminal-contract.cjs');
|
|
9
|
-
const {
|
|
9
|
+
const {
|
|
10
|
+
parseTaskDirMarkArgs,
|
|
11
|
+
terminalContractInputForChecklist,
|
|
12
|
+
} = require('./checklist-target.cjs');
|
|
10
13
|
|
|
11
14
|
const START_COMMANDS = new Set(['start']);
|
|
12
15
|
const TERMINAL_COMMANDS = new Set(['complete', 'no-change', 'blocked']);
|
|
@@ -161,8 +164,14 @@ function resolveTerminalPreset(command) {
|
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
|
|
167
|
+
function terminalContractPath(taskDir, taskPath) {
|
|
168
|
+
const scopedPath = path.join(taskDir, terminalContractInputForChecklist(path.basename(taskPath)));
|
|
169
|
+
if (fs.existsSync(scopedPath)) return scopedPath;
|
|
170
|
+
return path.join(taskDir, 'inputs', 'worker-terminal-contract.json');
|
|
171
|
+
}
|
|
172
|
+
|
|
164
173
|
function loadTerminalContract(taskDir, taskPath) {
|
|
165
|
-
const contractPath =
|
|
174
|
+
const contractPath = terminalContractPath(taskDir, taskPath);
|
|
166
175
|
if (fs.existsSync(contractPath)) {
|
|
167
176
|
return JSON.parse(fs.readFileSync(contractPath, 'utf8'));
|
|
168
177
|
}
|
|
@@ -290,12 +299,12 @@ function assertChecklistComplete(taskPath, { allowOneUnchecked = false } = {}) {
|
|
|
290
299
|
process.exit(1);
|
|
291
300
|
}
|
|
292
301
|
|
|
293
|
-
function assertArtifactContract(taskDir, contract, terminalCommand) {
|
|
302
|
+
function assertArtifactContract(taskDir, taskPath, contract, terminalCommand) {
|
|
294
303
|
if (!fs.existsSync(ARTIFACT_CONTRACT_SCRIPT)) {
|
|
295
304
|
console.error(`missing artifact contract script: ${ARTIFACT_CONTRACT_SCRIPT}`);
|
|
296
305
|
process.exit(1);
|
|
297
306
|
}
|
|
298
|
-
const contractPath =
|
|
307
|
+
const contractPath = terminalContractPath(taskDir, taskPath);
|
|
299
308
|
const args = [ARTIFACT_CONTRACT_SCRIPT, taskDir];
|
|
300
309
|
if (fs.existsSync(contractPath)) {
|
|
301
310
|
args.push('--contract', contractPath);
|
|
@@ -329,7 +338,7 @@ function assertTerminalPackagedEvidence(taskPath, taskDir, terminalCommand) {
|
|
|
329
338
|
const contract = loadTerminalContract(taskDir, taskPath);
|
|
330
339
|
if (contract) {
|
|
331
340
|
assertRequiredArtifacts(taskDir, terminalCommand, contract);
|
|
332
|
-
assertArtifactContract(taskDir, contract, terminalCommand);
|
|
341
|
+
assertArtifactContract(taskDir, taskPath, contract, terminalCommand);
|
|
333
342
|
return;
|
|
334
343
|
}
|
|
335
344
|
|
|
@@ -344,7 +353,7 @@ function assertTerminalPackagedEvidence(taskPath, taskDir, terminalCommand) {
|
|
|
344
353
|
}
|
|
345
354
|
|
|
346
355
|
if (terminalCommand === 'complete' || terminalCommand === 'no-change') {
|
|
347
|
-
assertArtifactContract(taskDir);
|
|
356
|
+
assertArtifactContract(taskDir, taskPath);
|
|
348
357
|
}
|
|
349
358
|
}
|
|
350
359
|
|
|
@@ -374,6 +383,7 @@ function buildSignalUpdate(signal, terminal, target, timing, events, now, taskPa
|
|
|
374
383
|
...(terminal.command === 'complete' && opts['no-self-review']
|
|
375
384
|
? { needsSelfReview: false }
|
|
376
385
|
: {}),
|
|
386
|
+
...(opts['skip-learnings'] ? { artifactWaivers: { learnings: true } } : {}),
|
|
377
387
|
};
|
|
378
388
|
|
|
379
389
|
const contract = terminal ? loadTerminalContract(_taskDir, taskPath) : null;
|