/** * Resolving what the caller pointed at. * * Runs before anything is parsed. Its whole job is to answer "what am I validating, and how deep * can I go against it" — and, when it cannot, to say so in terms of what is actually on disk rather * than what was expected to be. * * That last part is why these failures are worth care despite being the least interesting ones: they * are the first thing an agent hits, and a refusal that cannot name the real alternatives turns into * guessing. So the resolver reads the directory before it complains about it. * * ## Filenames are conventions, not contract * * Nothing in the runtime requires a recipe to be called `runtime.yaml`, or a scanner directory to be * called `scanners/` — install accepts any path, and the scanner location is a field inside the * recipe. Those names are used here only to break ties when a caller points at a directory, and the * resolver refuses rather than guesses when they do not. * * ## The manifest wins over the filesystem * * When there IS a `strategy.yaml` around the target, it — not the directory listing — says which * recipe belongs to the package and what its instances are called. Two package models on one * directory disagreed in both harmful directions: a leftover root `runtime.yaml` was validated, and * proven, against a file deploy would never install; and instance names came from directory names, * so the re-check commands printed here named things `close.py --instance` cannot resolve. The * filesystem walk below survives for the one case the manifest cannot serve — a directory with a * recipe and no package around it yet, which is where an author lives while writing one. * * A manifest that is PRESENT and will not load is neither of those states, and collapsing it into * "no package here" reinstates the very bug above: the root falls through to the conventional-name * tie-break, resolves the leftover recipe, and reports a green PASS. Present-versus-absent is * decidable with one `existsSync` and no reading of the file, so the two are kept apart — at a * package root the resolver refuses, and anywhere the target resolves on its own merits it carries * {@link ResolvedTarget.packageUnreadable} so no consumer has to guess which state it is in. */ import { type DeployPackage } from "../deploy/package.js"; import type { Depth, Finding } from "./types.js"; /** What the caller pointed at. A union so "a path and inline content" is not representable. */ export type TargetInput = { kind: "path"; path?: string; cwd: string; } | { kind: "content"; content: string; dir?: string; cwd: string; }; export type TargetKind = /** A recipe, named directly or found inside a directory. Every depth is reachable. */ "recipe" /** A directory of scanner code with no recipe. Code can be checked; nothing can be executed. */ | "scanner-dir" /** A single entry file. Same limits as a scanner directory. */ | "entrypoint" /** Recipe content supplied inline. Reaches execution only when a directory is supplied too. */ | "inline"; export interface ResolvedTarget { kind: TargetKind; /** Absolute path to the recipe, when one was found. */ recipePath?: string; /** Recipe content, when supplied inline instead of on disk. */ recipeContent?: string; /** Absolute directory that relative paths inside the recipe resolve against. */ baseDir?: string; /** Absolute scanner directory, for the code-only forms. */ scannerRoot?: string; /** Entry filename within {@link scannerRoot}, for the single-file form. */ entrypoint?: string; /** Depths this target can reach. */ allowedDepths: readonly Depth[]; /** * The strategy package this recipe belongs to, when a `strategy.yaml` declares it. Absent for the * mid-authoring case — a recipe with no package around it yet — so every reader must handle its * absence rather than assume a package is always there. * * An absent `pkg` on its own does NOT mean there is no package: check * {@link packageUnreadable} before concluding that. A check that skips when it is set is a check * that goes quiet exactly where the package is broken. */ pkg?: DeployPackage; /** * A `strategy.yaml` was found near this target and could not be loaded — so a package exists and * this run cannot say what is in it. * * Set only where the target resolved on its own merits; at the package root the resolver refuses * instead. It carries the loader's own message so a consumer can quote the reason rather than * re-derive it. Consumers that key off {@link pkg} must fail CLOSED on this rather than treat it * as "no package": "I could not read it" is never "there is nothing there". */ packageUnreadable?: { manifestPath: string; error: string; }; } export type TargetResolution = { ok: true; target: ResolvedTarget; } | { ok: false; finding: Finding; }; /** * Resolve what to validate, or say why it could not be resolved. * * `requestedDepth` is checked against what the target can reach, so "a live run against a bare * scanner directory" fails here — before a process is spawned — rather than deep inside a launch * that was never going to be buildable. */ export declare function resolveTarget(input: TargetInput, requestedDepth: Depth): TargetResolution; //# sourceMappingURL=target.d.ts.map