@farmslot/recipe-cli 0.5.0 → 0.7.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +11 -11
  3. package/dist/harness/acceptance-ledger.d.ts +18 -0
  4. package/dist/harness/acceptance-ledger.d.ts.map +1 -0
  5. package/dist/harness/acceptance-ledger.js +119 -0
  6. package/dist/harness/acceptance-ledger.js.map +1 -0
  7. package/dist/harness/adapter-plugins.d.ts +105 -0
  8. package/dist/harness/adapter-plugins.d.ts.map +1 -0
  9. package/dist/harness/adapter-plugins.js +388 -0
  10. package/dist/harness/adapter-plugins.js.map +1 -0
  11. package/dist/harness/command-contract.d.ts +69 -0
  12. package/dist/harness/command-contract.d.ts.map +1 -0
  13. package/dist/harness/command-contract.js +243 -0
  14. package/dist/harness/command-contract.js.map +1 -0
  15. package/dist/harness/commands/call.d.ts.map +1 -1
  16. package/dist/harness/commands/call.js +131 -127
  17. package/dist/harness/commands/call.js.map +1 -1
  18. package/dist/harness/commands/run.d.ts.map +1 -1
  19. package/dist/harness/commands/run.js +158 -125
  20. package/dist/harness/commands/run.js.map +1 -1
  21. package/dist/harness/harness-cli.d.ts +82 -0
  22. package/dist/harness/harness-cli.d.ts.map +1 -0
  23. package/dist/harness/harness-cli.js +319 -0
  24. package/dist/harness/harness-cli.js.map +1 -0
  25. package/dist/harness/index.d.ts +6 -3
  26. package/dist/harness/index.d.ts.map +1 -1
  27. package/dist/harness/index.js +5 -2
  28. package/dist/harness/index.js.map +1 -1
  29. package/dist/harness/live-adapter-contract.d.ts +9 -0
  30. package/dist/harness/live-adapter-contract.d.ts.map +1 -1
  31. package/dist/harness/live-adapter-contract.js +38 -7
  32. package/dist/harness/live-adapter-contract.js.map +1 -1
  33. package/dist/harness/network-observation.d.ts +1 -4
  34. package/dist/harness/network-observation.d.ts.map +1 -1
  35. package/dist/harness/network-observation.js +3 -8
  36. package/dist/harness/network-observation.js.map +1 -1
  37. package/dist/harness/plugin-imports.d.ts +23 -0
  38. package/dist/harness/plugin-imports.d.ts.map +1 -0
  39. package/dist/harness/plugin-imports.js +126 -0
  40. package/dist/harness/plugin-imports.js.map +1 -0
  41. package/dist/harness/recipe-validation.d.ts +2 -0
  42. package/dist/harness/recipe-validation.d.ts.map +1 -1
  43. package/dist/harness/recipe-validation.js +42 -0
  44. package/dist/harness/recipe-validation.js.map +1 -1
  45. package/dist/harness/run-engine.d.ts +27 -8
  46. package/dist/harness/run-engine.d.ts.map +1 -1
  47. package/dist/harness/run-engine.js +86 -86
  48. package/dist/harness/run-engine.js.map +1 -1
  49. package/dist/harness/run-observers.d.ts +1 -4
  50. package/dist/harness/run-observers.d.ts.map +1 -1
  51. package/dist/harness/run-observers.js +6 -3
  52. package/dist/harness/run-observers.js.map +1 -1
  53. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -6,6 +6,38 @@ All notable changes to `@farmslot/recipe-cli` are tracked here.
6
6
 
7
7
  - Active-development baseline; add user-facing changes here before release or package publication.
8
8
 
9
+ ## 0.7.0 - 2026-10-06
10
+
11
+ - Add `createHarnessCli({ host, adapters, libraries, commands, help, beforeDispatch, catalog, adopt, configuredLibraries, replacedOptions })` to `@farmslot/recipe-cli/harness`, the generic front door a product harness presets. `main(argv)` returns `{ exitCode, exit }` and `run()` exits with it.
12
+ - It provides the grouped help (the host's intro, groups and footer, the `<PREFIX>_BIN` dev override line and the prepared-slot line), per-command `--help`, `-v/--version`, which prints the host's package version as one line (a host `package.json` without a version is a startup error), and `--version --verbose`, which adds `@farmslot/recipe-cli <version>` (one line when the host is recipe-cli itself).
13
+ - Each public command carries its `contract`, so the command list and the grammar can't drift apart. Hidden commands keep their private grammar.
14
+ - Order per invocation: `beforeDispatch` (unless the command sets `nudge: false`); help for no arguments; `raw` commands; the public grammar; `libraries.hydrate(--target)`; `ensureAdapterLoaded` for the adapter the command selects (`selectedAdapterId`), with the `--library` entries, the host's `configuredLibraries()` and `adopt`, and the environment the operator started the command with (libraries that `libraries.hydrate` adds never load plugins) (a refused plugin, `AdapterPluginError` or the import fence's `RECIPE_SOURCE_INVALID`, prints its code and next step, or the NDJSON `error` and `complete` events under `--json-stream`, exit 2); `--help` after `--` handed to the command; `call <action> --help` through the `catalog`; commander.
15
+ - One error mapper: a command that throws exits with the error's `exitCode` (a `CliError`), else 1. A public command sets `process.exitCode` by default and a hidden one exits at once; `exit` overrides either.
16
+ - Add the public command contract to `@farmslot/recipe-cli/harness`: `validatePublicInvocation(argv, commands, { replacedOptions })` returns a stable `CLI_*` usage error naming the host, or null.
17
+ - Option builders: `booleanOption`, `valueOption`, `optionalValueOption`, `contractOptions`. `OptionSpec.choices` can be a function of the tokens after the command, read at validation time (for example `(tokens) => adapterChoices(optionValues(tokens, '--library'))`). `optionValues(tokens, option)` returns every value an option takes before `--`.
18
+ - Command hooks: `bypass` (a sub-grammar another tool checks), `refine` (command rules, given `ContractFailures`) and `missingPositionalAction`.
19
+ - `publicCommandTokens(commands)` lists every name and alias.
20
+ - Publish with adapter-sdk 0.5.1, agent-runtime 0.17.0, protocol 0.34.0 and recipe-runner 0.26.0.
21
+
22
+ ## 0.6.0 - 2026-10-06
23
+
24
+ - **BREAKING:** a run plans with a runner that has no trusted mutation. `createRunner` no longer receives the mutation `trustedMutation.load` returned for the runner that computes the plan; only the mutation `authorize` returns reaches a runner. A host whose runner reads `options.trustedMutation` during construction or preflight now sees `undefined` there; move that check to `authorize`, which receives the loaded mutation and the plan.
25
+ - `RecipeEngine.trustedMutation` hooks receive the adapter the command resolved (`--adapter`, a `--platform` target, or the detected one): `load` gets an `adapter` field, and `authorize` a third argument `{ adapter }`. Both input types are exported from `@farmslot/recipe-cli/harness` as `TrustedMutationLoadInput` and `TrustedMutationAuthorizeContext`, so a host can gate a mutation on the platform without resolving the adapter again.
26
+ - `authorize`'s third argument is required in the type: a host that calls `engine.trustedMutation.authorize(base, plan)` itself must pass `{ adapter }`. An implementation that declares two parameters still type-checks.
27
+ - `authorize` may return `undefined`: nothing to bind for this plan. The run then keeps the runner it planned with and executes with no trusted mutation, instead of building a second runner.
28
+ - `run` with a task dir (`RECIPE_TASK_DIR` / `FARMSLOT_TASK_DIR`) writes the task's acceptance ledger, `artifacts/acceptance-status.json` (ADR-060), from the recipe's `proofTargets`, through agent-runtime's ledger module, the one `farmslot-agent ac` uses. A target is `proven` when every node proving it passed and `missing` when one failed or did not run; `AC1` maps to the handoff's `AC-1`. Evidence is the run's `trace.json` plus the proving nodes' artifacts inside the task dir. `AC1` and `AC-1` targets count as one criterion. The run replaces the verdicts of the criteria it proves and leaves the others as they were. A target the handoff doesn't list is refused with a warning on stderr; the run's result and JSON output are unchanged.
29
+ - Static validation (`run --plan`, `validateRunRecipeStatic`) warns on two `command` node patterns whose exit code cannot prove the claim: output piped into `tail` or `head` (`recipe.command_pipe_masks_exit`; the shell reports tail's exit code, so a failing command passes), and a test command with no `assert_output` on it (`recipe.test_command_exit_code_only`; `jest -t` exits 0 when no test matches). `validateCommandNodes` is exported from `@farmslot/recipe-cli/harness`.
30
+ - Add adapter plugins to `@farmslot/recipe-cli/harness`: `ensureAdapterLoaded(id, { libraries, adopt })` loads an adapter a recipe library declares in `recipe-library.json` `adapters` when a command selects it, composes it on the adapter it `extends` (`composeAdapter`: the child's members replace the parent's; manifests, in-code action implementations and doctor checks append) and registers it with the host's adapters. `adapterChoices(libraries)` lists every id `--adapter` accepts without importing anything. A refused plugin throws `AdapterPluginError` with `ADAPTER_SDK_UNSUPPORTED`, `ADAPTER_PLUGIN_INVALID`, `ADAPTER_PLUGIN_LOAD_FAILED`, `ADAPTER_ID_CONFLICT` or `ADAPTER_EXTENDS_UNKNOWN`; `adapterSelectionFailureOut` prints it, or the library reader's `RECIPE_SOURCE_INVALID`, as the `--json` envelope.
31
+ - Plugins load only from the operator's libraries: `--library` entries, then the `RECIPE_LIBRARY_PATH` entries they don't rename (else the personal library), then the libraries the host passes as `configured`; never from a task-local library. A host that extends `RECIPE_LIBRARY_PATH` with libraries it discovers passes the operator's environment as `env` to both `adapterChoices` and `ensureAdapterLoaded`.
32
+ - A plugin may import from disk only the files its digest covers (its module's directory and the library's `actions/`): a `module.registerHooks` resolve hook refuses any other file with `RECIPE_SOURCE_INVALID`, while the plugin loads and while it runs. Plugin code is the module's directory plus the `actions/` files it reaches; an `actions/` file the host imports by itself isn't fenced. `node:` builtins are allowed, and a bare package resolves from the host's install (`HarnessHost.packageRoot`), never from the library's location; a package the host doesn't install, or one in a `node_modules` beside or above the library that isn't on the host's own lookup chain, is refused (a library in the host's project imports the host's packages). Loading a plugin needs Node.js 22.15 or later (`ADAPTER_PLUGIN_LOAD_FAILED` otherwise).
33
+ - `adapterPlugin(id)` and `loadedAdapterPlugins()` describe each loaded plugin (`LoadedAdapterPlugin`: library, root, module, `extends`, and a `digest` over its library, the files under its module's directory and its parent plugin's digest), so a host can bind the actions the plugin ships in code to what a recipe approves. `adapterPluginChecks()` gives `doctor` one check per loaded plugin naming its library and digest.
34
+ - Live action scripts follow the adapter's `extends` chain: every file of an adapter that extends another (`<actions>/<child>/<family>/<name>.mjs`, its short name, its `<family>.mjs` dispatcher) comes before any of its parent's within each actions root, and the adapter at the top of the chain keeps a built-in's order with `shared/`. Built-in adapters, which extend nothing, try exactly the files they did before, in the same order. Recipe variants don't follow `extends`.
35
+ - `selectedAdapterId(tokens)` is the adapter a command selects, the way commands resolve it: the last `--adapter`, else the adapter of the last `--platform`. `declaredAdapterIds(sources)` reads the ids libraries declare without importing anything.
36
+ - Export `resolveTsxBin(tsxCandidates, projectRoot)` from `@farmslot/recipe-cli/harness`, the tsx lookup live adapter scripts run under (`TSX_BIN`, the checkout's tsx, the host package's tsx, the caller's candidates), so a host's readiness check can ask the same question.
37
+ - **BREAKING:** `runRecipe` and `preflightRecipe` no longer activate the run environment themselves; call them inside an `activateRecipeRuntimeEnvironment` scope.
38
+ - `run` and `call` open one `activateRecipeRuntimeEnvironment` scope around preflight, the platform's `runtimeCheck`, the observers and the execution. All of them see the same ports (the slot's, then `--cdp-port`/`--watcher-port`) and the platform's run environment, and the scope restores the environment on every exit. Network and performance observers keep a copy of that environment taken as they start.
39
+ - Publish with adapter-sdk 0.5.0, agent-runtime 0.17.0, protocol 0.34.0 and recipe-runner 0.25.0.
40
+
9
41
  ## 0.5.0 - 2026-10-05
10
42
 
11
43
  - Add `run` and `call` to `@farmslot/recipe-cli/harness`, moved from mm-harness: `handleRun(argv, options)`, `handleCall(argv, options)` and `handleCallHelp(argv, genericHelp, { catalog })`. The host passes a `RecipeEngine` (`RecipeCatalog`: its bundled library, action manifest resolver and validator, and bundled action risk; plus `createRunner`, an optional `trustedMutation` loaded from the command line and bound to the run's plan, the `console` classifier and `runnerIncludes`), and per command `targetDevice`, the `run --plan` host steps and launch wording, and `exampleAction`. `run --list`, `run <recipe> --describe` and `call --list` read the catalog themselves. Messages name the host (`<name> call`, `<product>-checkout`).
package/README.md CHANGED
@@ -29,17 +29,17 @@ Docs: https://farmslot.io/docs/reference/recipe-discovery
29
29
 
30
30
  ## Source layout
31
31
 
32
- | path | owns |
33
- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
34
- | `src/cli.ts` | the `farmslot-recipe` program: the runner's `run`/`validate` plus discovery |
35
- | `src/commands.ts` | discovery command registration, JSON envelopes and errors |
36
- | `src/libraries.ts` | library resolution: precedence, overrides, digests, `requires` checks |
37
- | `src/discovery-index.ts` | the recipe and action index for one platform view |
38
- | `src/composition.ts` | composition, callers and `explain` |
39
- | `src/search.ts`, `src/template.ts`, `src/render.ts` | search ranking, skeletons, text output |
40
- | `src/types.ts` | the documented `--json` schema |
41
- | `src/harness/` | generic harness support (`@farmslot/recipe-cli/harness`): host identity, adapter registry, runtime paths, journal, checkout lock, JSON stream, colour, option parsing, leaf spawning, bounded healing, run evidence (execution provenance, framed recording, run diagnostics, run report, live adapter scripts, run options, trust input, behavioral proof), and the run engine (`RecipeEngine`, recipe library, static validation, action catalog, network observation) |
42
- | `src/harness/commands/` | commands a host CLI dispatches to: `run`, `call`, the lifecycle (`launch`, `reload`, `stop`, `last`), and discovery (`discover.ts`: `actions`, `run --list`, `run --describe`, `call --list`); `overlay.ts` is `install`/`verify`/`cleanup` |
32
+ | path | owns |
33
+ | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | `src/cli.ts` | the `farmslot-recipe` program: the runner's `run`/`validate` plus discovery |
35
+ | `src/commands.ts` | discovery command registration, JSON envelopes and errors |
36
+ | `src/libraries.ts` | library resolution: precedence, overrides, digests, `requires` checks |
37
+ | `src/discovery-index.ts` | the recipe and action index for one platform view |
38
+ | `src/composition.ts` | composition, callers and `explain` |
39
+ | `src/search.ts`, `src/template.ts`, `src/render.ts` | search ranking, skeletons, text output |
40
+ | `src/types.ts` | the documented `--json` schema |
41
+ | `src/harness/` | generic harness support (`@farmslot/recipe-cli/harness`): host identity, adapter registry and library adapter plugins, runtime paths, journal, checkout lock, JSON stream, colour, option parsing, leaf spawning, bounded healing, run evidence (execution provenance, framed recording, run diagnostics, run report, live adapter scripts, run options, trust input, behavioral proof), and the run engine (`RecipeEngine`, recipe library, static validation, action catalog, network observation) |
42
+ | `src/harness/commands/` | commands a host CLI dispatches to: `run`, `call`, the lifecycle (`launch`, `reload`, `stop`, `last`), and discovery (`discover.ts`: `actions`, `run --list`, `run --describe`, `call --list`); `overlay.ts` is `install`/`verify`/`cleanup` |
43
43
 
44
44
  ## Maintenance rules
45
45
 
@@ -0,0 +1,18 @@
1
+ export interface RecipeAcceptanceVerdict {
2
+ id: string;
3
+ verdict: 'proven' | 'missing';
4
+ recipeNodes: string[];
5
+ evidence: string[];
6
+ }
7
+ export interface RecipeAcceptanceRecord {
8
+ recorded: RecipeAcceptanceVerdict[];
9
+ /** Targets the ledger refused (e.g. no such criterion in the handoff), with why. */
10
+ refused: string[];
11
+ }
12
+ /** `AC1` and `AC-1` both name the handoff's first criterion; anything else is not an AC. */
13
+ export declare function acceptanceIdForProofTarget(target: string): string | null;
14
+ export declare function recordRecipeAcceptance(taskDir: string, target: string, result: {
15
+ recipePath: string;
16
+ tracePath: string;
17
+ }): RecipeAcceptanceRecord;
18
+ //# sourceMappingURL=acceptance-ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acceptance-ledger.d.ts","sourceRoot":"","sources":["../../src/harness/acceptance-ledger.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC9B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,oFAAoF;IACpF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,4FAA4F;AAC5F,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGxE;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAChD,sBAAsB,CA2DxB"}
@@ -0,0 +1,119 @@
1
+ // After a run with a task dir, the recipe's proof targets become verdicts in the
2
+ // task's acceptance ledger (artifacts/acceptance-status.json, ADR-060), written by
3
+ // the same code `farmslot-agent ac` uses, so a Farmslot run and a skill run record
4
+ // the same file. A target is `proven` when every node proving it passed, `missing`
5
+ // when one failed or did not run. A target no node proves, and a criterion no
6
+ // target names, stay unrecorded: the run says nothing about them.
7
+ import fs from 'node:fs';
8
+ import { createRequire } from 'node:module';
9
+ import path from 'node:path';
10
+ import { isRecord } from './parse-args.js';
11
+ const require = createRequire(import.meta.url);
12
+ const ledger = require('@farmslot/agent-runtime/scripts/acceptance-ledger.cjs');
13
+ /** `AC1` and `AC-1` both name the handoff's first criterion; anything else is not an AC. */
14
+ export function acceptanceIdForProofTarget(target) {
15
+ const match = /^AC-?([1-9][0-9]*)$/iu.exec(target.trim());
16
+ return match ? `AC-${match[1]}` : null;
17
+ }
18
+ export function recordRecipeAcceptance(taskDir, target, result) {
19
+ const record = { recorded: [], refused: [] };
20
+ if (ledger.handoffAcceptanceCriteria(taskDir).length === 0)
21
+ return record;
22
+ const recipe = readJson(result.recipePath);
23
+ const trace = readJson(result.tracePath);
24
+ // The protocol allows a bare array of entries or `{ entries }`.
25
+ const entries = Array.isArray(trace)
26
+ ? trace
27
+ : isRecord(trace) && Array.isArray(trace.entries)
28
+ ? trace.entries
29
+ : [];
30
+ // The last entry per node is its outcome (a retried node may appear twice).
31
+ const outcome = new Map();
32
+ for (const entry of entries) {
33
+ if (isRecord(entry) && typeof entry.nodeId === 'string')
34
+ outcome.set(entry.nodeId, entry);
35
+ }
36
+ const workflow = isRecord(recipe) && isRecord(recipe.workflow) ? recipe.workflow : {};
37
+ const nodes = isRecord(workflow.nodes) ? workflow.nodes : {};
38
+ // AC1 and AC-1 name the same criterion: its provers are the nodes proving either.
39
+ const provers = new Map();
40
+ for (const proofTarget of proofTargetIds(recipe)) {
41
+ const id = acceptanceIdForProofTarget(proofTarget);
42
+ if (!id)
43
+ continue;
44
+ const ids = new Set(provers.get(id));
45
+ for (const [nodeId, node] of Object.entries(nodes)) {
46
+ if (isRecord(node) && Array.isArray(node.proves) && node.proves.includes(proofTarget)) {
47
+ ids.add(nodeId);
48
+ }
49
+ }
50
+ provers.set(id, [...ids]);
51
+ }
52
+ const artifactsDir = path.dirname(result.tracePath);
53
+ const realTaskDir = fs.realpathSync(taskDir);
54
+ for (const [id, recipeNodes] of provers) {
55
+ if (recipeNodes.length === 0)
56
+ continue;
57
+ const verdict = recipeNodes.every((nodeId) => outcome.get(nodeId)?.ok === true)
58
+ ? 'proven'
59
+ : 'missing';
60
+ const evidence = [
61
+ ...new Set([
62
+ result.tracePath,
63
+ ...recipeNodes.flatMap((nodeId) => nodeArtifactPaths(outcome.get(nodeId), artifactsDir, target)),
64
+ ]
65
+ .map((file) => taskRelative(realTaskDir, file))
66
+ .filter((file) => file !== null)),
67
+ ];
68
+ try {
69
+ ledger.setAcceptanceVerdict(taskDir, { id, verdict, evidence, recipeNodes });
70
+ record.recorded.push({ id, verdict, evidence, recipeNodes });
71
+ }
72
+ catch (error) {
73
+ if (!(error instanceof ledger.AcceptanceRefusal))
74
+ throw error;
75
+ record.refused.push(`${id}: ${error.message}`);
76
+ }
77
+ }
78
+ return record;
79
+ }
80
+ // Proof targets are `{ id, claim }` objects (the recipe contract rejects any other shape).
81
+ function proofTargetIds(recipe) {
82
+ const targets = isRecord(recipe) && Array.isArray(recipe.proofTargets) ? recipe.proofTargets : [];
83
+ return targets
84
+ .map((target) => (isRecord(target) ? target.id : null))
85
+ .filter((id) => typeof id === 'string');
86
+ }
87
+ // A node's artifact paths are relative to the run's artifacts dir or to the checkout;
88
+ // the first that exists is the file.
89
+ function nodeArtifactPaths(entry, artifactsDir, target) {
90
+ const artifacts = Array.isArray(entry?.artifacts) ? entry.artifacts : [];
91
+ return artifacts
92
+ .map((artifact) => isRecord(artifact) && typeof artifact.path === 'string' ? artifact.path : null)
93
+ .filter((file) => file !== null)
94
+ .map((file) => path.isAbsolute(file)
95
+ ? file
96
+ : [path.resolve(artifactsDir, file), path.resolve(target, file)].find((candidate) => fs.existsSync(candidate)))
97
+ .filter((file) => typeof file === 'string');
98
+ }
99
+ // The ledger keeps task-dir relative paths only. Both sides are resolved first, so a
100
+ // symlink can't carry evidence from outside the task dir in, nor a symlinked checkout
101
+ // path drop evidence that is inside.
102
+ function taskRelative(realTaskDir, file) {
103
+ // A file the run didn't leave isn't evidence; any other error reaches the run's warning.
104
+ if (!fs.existsSync(file))
105
+ return null;
106
+ const real = fs.realpathSync(file);
107
+ const relative = path.relative(realTaskDir, real);
108
+ if (!relative ||
109
+ relative === '..' ||
110
+ relative.startsWith(`..${path.sep}`) ||
111
+ path.isAbsolute(relative)) {
112
+ return null;
113
+ }
114
+ return relative.split(path.sep).join('/');
115
+ }
116
+ function readJson(file) {
117
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
118
+ }
119
+ //# sourceMappingURL=acceptance-ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acceptance-ledger.js","sourceRoot":"","sources":["../../src/harness/acceptance-ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import { type AdapterDoctorCheck, type PlatformAdapter } from '@farmslot/adapter-sdk';
2
+ import { type RecipeLibraryAdapterDeclaration, type RecipeLibrarySource } from '@farmslot/recipe-runner';
3
+ import { CliError } from './parse-args.js';
4
+ export type AdapterPluginErrorCode = 'ADAPTER_SDK_UNSUPPORTED' | 'ADAPTER_PLUGIN_INVALID' | 'ADAPTER_PLUGIN_LOAD_FAILED' | 'ADAPTER_ID_CONFLICT' | 'ADAPTER_EXTENDS_UNKNOWN';
5
+ /** A declared adapter that cannot be selected. */
6
+ export declare class AdapterPluginError extends CliError {
7
+ readonly code: AdapterPluginErrorCode;
8
+ readonly userAction: string;
9
+ constructor(code: AdapterPluginErrorCode, message: string, userAction: string);
10
+ }
11
+ /** One `adapters` entry and the library that declares it. */
12
+ export interface DeclaredAdapter extends RecipeLibraryAdapterDeclaration {
13
+ id: string;
14
+ library: string;
15
+ root: string;
16
+ }
17
+ /** A plugin the loader registered: where it came from and the digest of its code. */
18
+ export interface LoadedAdapterPlugin {
19
+ id: string;
20
+ library: string;
21
+ root: string;
22
+ module: string;
23
+ extends?: string;
24
+ /**
25
+ * sha256 over the declaring library's name and real root, the plugin's files
26
+ * (`digestLibraryAdapter`: the module's directory) and, with `extends`, the
27
+ * parent plugin's digest. Taken before the module is imported.
28
+ */
29
+ digest: string;
30
+ }
31
+ export interface AdapterLibraryOptions {
32
+ /** The command's `--library` entries; RECIPE_LIBRARY_PATH and the personal library follow. */
33
+ libraries?: readonly string[];
34
+ /**
35
+ * Libraries the host configures (e.g. a config file), after those. A source
36
+ * whose name or root is already present is skipped, so an earlier one wins.
37
+ */
38
+ configured?: readonly RecipeLibrarySource[];
39
+ /**
40
+ * The environment the operator started the command with (RECIPE_LIBRARY_PATH,
41
+ * the personal library's home), before the host's own library discovery
42
+ * extended it: plugins load only from the operator's libraries. Defaults to
43
+ * process.env; pass the same value to `adapterChoices` and `ensureAdapterLoaded`.
44
+ */
45
+ env?: NodeJS.ProcessEnv;
46
+ }
47
+ export interface AdapterLoadOptions extends AdapterLibraryOptions {
48
+ /**
49
+ * Turns a loaded adapter into the host's adapter, e.g. defaults for members
50
+ * the host's registry requires beyond the SDK. Called before registration.
51
+ */
52
+ adopt?(adapter: PlatformAdapter): PlatformAdapter;
53
+ }
54
+ /**
55
+ * Every adapter id `--adapter` accepts: the registered adapters, then the ids
56
+ * the libraries declare (`declaredAdapterIds`). Reads recipe-library.json only;
57
+ * imports nothing. The libraries are resolved as recipes resolve them: `libraries`
58
+ * (--library), then RECIPE_LIBRARY_PATH entries they don't rename, else the
59
+ * personal library; then `configured`.
60
+ */
61
+ export declare function adapterChoices(libraries?: readonly string[], options?: Pick<AdapterLibraryOptions, 'configured' | 'env'>): string[];
62
+ /**
63
+ * The adapter ids these libraries declare in recipe-library.json `adapters`,
64
+ * read without importing anything. A library without a readable manifest
65
+ * declares nothing here; selecting one of its adapters reports the error.
66
+ */
67
+ export declare function declaredAdapterIds(sources: readonly RecipeLibrarySource[]): string[];
68
+ /**
69
+ * The adapter a command's tokens select, the way commands resolve it: the last
70
+ * `--adapter`, else the adapter of the last `--platform`. Tokens after `--` are
71
+ * passthrough and select nothing.
72
+ */
73
+ export declare function selectedAdapterId(tokens: readonly string[]): string | undefined;
74
+ /** The plugin the loader registered under `id` in the host's registry, if any. */
75
+ export declare function adapterPlugin(id: string): LoadedAdapterPlugin | undefined;
76
+ /** Every plugin the loader registered in the host's registry, in load order. */
77
+ export declare function loadedAdapterPlugins(): LoadedAdapterPlugin[];
78
+ /**
79
+ * One passing, non-required doctor check per loaded plugin, naming its library,
80
+ * module and digest, so the report shows which plugin code this process runs.
81
+ */
82
+ export declare function adapterPluginChecks(): AdapterDoctorCheck[];
83
+ /**
84
+ * Make `id` resolvable: a no-op for an absent id or a registered one, else load
85
+ * the one declaration for it (and what it extends) and register it. A built-in
86
+ * id that a library also declares is a conflict.
87
+ */
88
+ export declare function ensureAdapterLoaded(id: string | undefined, options?: AdapterLoadOptions): Promise<void>;
89
+ /**
90
+ * `child` on top of `parent`: the child's members replace the parent's, except
91
+ * three that append, parent first: the action manifests (`actions.manifestPaths`),
92
+ * the action implementations (`actions.adapters`) and the doctor checks (`doctor`).
93
+ */
94
+ export declare function composeAdapter(parent: PlatformAdapter, child: PlatformAdapter): PlatformAdapter;
95
+ /**
96
+ * Print a refused adapter selection: the `--json` envelope or the human line.
97
+ * Takes any error with a `code` and `userAction` (the loader's, and the library
98
+ * reader's RECIPE_SOURCE_INVALID); returns the exit code.
99
+ */
100
+ export declare function adapterSelectionFailureOut(json: boolean, command: string, error: {
101
+ code: string;
102
+ message: string;
103
+ userAction: string;
104
+ }): number;
105
+ //# sourceMappingURL=adapter-plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-plugins.d.ts","sourceRoot":"","sources":["../../src/harness/adapter-plugins.ts"],"names":[],"mappings":"AAkBA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAOL,KAAK,+BAA+B,EACpC,KAAK,mBAAmB,EAGzB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAE,QAAQ,EAAY,MAAM,iBAAiB,CAAC;AAIrD,MAAM,MAAM,sBAAsB,GAC9B,yBAAyB,GACzB,wBAAwB,GACxB,4BAA4B,GAC5B,qBAAqB,GACrB,yBAAyB,CAAC;AAE9B,kDAAkD;AAClD,qBAAa,kBAAmB,SAAQ,QAAQ;IAC9C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAChB,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAM9E;AAED,6DAA6D;AAC7D,MAAM,WAAW,eAAgB,SAAQ,+BAA+B;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qFAAqF;AACrF,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,8FAA8F;IAC9F,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC5C;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB;IAC/D;;;OAGG;IACH,KAAK,CAAC,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,CAAC;CACnD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,SAAS,GAAE,SAAS,MAAM,EAAO,EACjC,OAAO,GAAE,IAAI,CAAC,qBAAqB,EAAE,YAAY,GAAG,KAAK,CAAM,GAC9D,MAAM,EAAE,CAGV;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,GAAG,MAAM,EAAE,CAEpF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAK/E;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAEzE;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,IAAI,mBAAmB,EAAE,CAE5D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,EAAE,CAQ1D;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAcf;AA2JD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,GAAG,eAAe,CA6B/F;AAuJD;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,MAAM,EACf,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC3D,MAAM,CAqBR"}