/** * @fileoverview Static manifest reader + the single admission gate * (release 2.8.0, identity & compatibility — Phase 2). * * `loadToolManifest` reads a tool's static front matter **before** * importing its runtime `Tool` module: * * - **bundled** / **installed** — `package.json#opensipTools` in the tool's * own package dir (one read for both; the discovery walker already * touches these package.json files). * - **project-local** — a JSON sidecar (`opensip-tool.manifest.json`) in * the tool's directory, since a project-local tool is authored content, * not an installed npm package with a package.json marker. * * It validates the identity subset (`kind: 'tool'`, `id`, `commands`), * derives `name`/`version` from the package.json's own top-level fields * (the manifest block does NOT redeclare them — single source of truth), * and returns `undefined` (with a structured logger diagnostic) on a * malformed or missing manifest. It NEVER imports the tool module. * * `admitTool` is the single gate the bundled and external paths share: it * records `ToolProvenance` (incl. a `manifestHash` over the canonical * manifest JSON) and runs `checkCompatibility(manifest.apiVersion)`: * - compatible → `admit` * - incompatible + not requested → `skip` (with diagnostic) * - incompatible + explicitly asked → `fail-closed` (with diagnostic) * It emits exactly one structured logger evt per decision. * * Both functions are pure over real filesystem reads of package.json / * the sidecar — no tool-module import, no module singletons. They stay in * core (the kernel) and import nothing from contracts/cli/tools-runtime. */ import { type CompatibilityVerdict } from '../tools/compatibility.js'; import type { ToolPluginManifest, ToolProvenance, ToolSource } from '../tools/manifest.js'; /** * Filename of the project-local manifest sidecar. A project-local tool is * authored content under `/opensip-tools/…`, not an installed npm * package, so it has no `package.json#opensipTools` marker — it declares * its identity via this JSON sidecar (deliberately NOT an executable * `.mjs`, so the host reads identity without running tool code). */ export declare const PROJECT_LOCAL_MANIFEST_FILE = "opensip-tool.manifest.json"; /** * Read + validate a tool's static `ToolPluginManifest` for the given * source, WITHOUT importing the tool's runtime module. * * - `bundled` / `installed` — reads `/package.json`, taking identity * from the `opensipTools` block and `name`/`version` from the * package.json's own top-level fields. * - `project-local` — reads `/opensip-tool.manifest.json` (the JSON * sidecar), which carries the full identity inline. * * @param source Where the tool came from — selects the manifest location. * @param dir The tool's package/authoring directory. * @returns The validated manifest, or `undefined` (with a structured * `plugin.manifest.read_failed` diagnostic) when the file is missing, * unparseable, or fails identity validation. */ export declare function loadToolManifest(source: ToolSource, dir: string): ToolPluginManifest | undefined; /** * The outcome of the single admission gate — the decision plus the * `ToolProvenance` recorded regardless of verdict (so an incompatible * tool can still be surfaced) and the underlying `CompatibilityVerdict`. * * - `admit` — compatible; the host may import + register the tool. * - `skip` — incompatible but not explicitly requested: dropped * silently from the user's perspective (diagnostic only). * - `fail-closed` — incompatible AND explicitly requested: the host must * fail with the Phase-0 incompatible exit code. */ export interface AdmissionResult { readonly decision: 'admit' | 'skip' | 'fail-closed'; readonly provenance: ToolProvenance; readonly verdict: CompatibilityVerdict; readonly diagnostic?: string; } /** * Run the single compatibility gate over a manifest and produce an * {@link AdmissionResult}. Records `ToolProvenance` (source + identity + * `manifestHash`) regardless of verdict, then maps the * `checkCompatibility` outcome to a decision: * * - compatible → `admit` * - incompatible + `!explicitlyRequested` → `skip` * - incompatible + `explicitlyRequested` → `fail-closed` * * Emits exactly one structured logger evt per decision * (`plugin.manifest.loaded` / `plugin.incompatible.skipped` / * `plugin.incompatible.failed`). Never throws, never imports the tool. * * @param args.manifest The validated manifest (from {@link loadToolManifest}). * @param args.source Where the tool came from. * @param args.dir The tool's resolved directory (recorded as `resolvedPath`). * @param args.packageName npm package name, when known (bundled/installed). * @param args.explicitlyRequested Whether the user named this tool directly * (e.g. via a plugin pin) — promotes an incompatible `skip` to `fail-closed`. */ export declare function admitTool(args: { readonly manifest: ToolPluginManifest; readonly source: ToolSource; readonly dir: string; readonly packageName?: string; readonly explicitlyRequested: boolean; }): AdmissionResult; //# sourceMappingURL=manifest-loader.d.ts.map