# `host` _(no submodules)_ - conflict — Resolves the per-op `conflict` policy on… - [apply](./apply/apply.md) — _(no submodules)_- conflict — Resolves the per-op `conflict` policy on… - [conflict](./apply/conflict.md) — Resolves the per-op `conflict` policy on a target that already exists. Five policies are supported: `error` (raise `APP… - [json-merge](./apply/json-merge.md) — Implements the deep-merge semantics used by `patch-json` ops with `conflict: "merge"`. Match the conventions of `@suave… - [text-patch](./apply/text-patch.md) — Applies an ordered sequence of `TextEdit`s to a string. Used by `patch-text` ops to make targeted insertions/deletions/… - [detect](./detect.md) — Runs a plugin's optional `detect` stage inside an OpenTelemetry span. The plugin reports what is already present in the… - [host](./host.md) — _(no submodules)_- conflict — Resolves the per-op `conflict` policy on… - [plan](./plan.md) — Invokes `plugin.plan(answers, ctx)` and validates the returned `FileOp[]` against `FileOpsSchema` before letting them f… - [post-install](./post-install.md) — Runs the plugin's optional `postInstall` stage with abort propagation. This is where plugins do follow-up work that doe… - [prompts](./prompts.md) — Runs the plugin's optional `prompts` stage and validates the answers against the schema the plugin returns. This is whe… - conflict — Resolves the per-op `conflict` policy on… - [apply](./apply/apply.md) — _(no submodules)_- conflict — Resolves the per-op `conflict` policy on… - [conflict](./apply/conflict.md) — Resolves the per-op `conflict` policy on a target that already exists. Five policies are supported: `error` (raise `APP… - [json-merge](./apply/json-merge.md) — Implements the deep-merge semantics used by `patch-json` ops with `conflict: "merge"`. Match the conventions of `@suave… - [text-patch](./apply/text-patch.md) — Applies an ordered sequence of `TextEdit`s to a string. Used by `patch-text` ops to make targeted insertions/deletions/… - [detect](./detect.md) — Runs a plugin's optional `detect` stage inside an OpenTelemetry span. The plugin reports what is already present in the… - [host](./host.md) — _(no submodules)_- conflict — Resolves the per-op `conflict` policy on… - [plan](./plan.md) — Invokes `plugin.plan(answers, ctx)` and validates the returned `FileOp[]` against `FileOpsSchema` before letting them f… - [post-install](./post-install.md) — Runs the plugin's optional `postInstall` stage with abort propagation. This is where plugins do follow-up work that doe… - [prompts](./prompts.md) — Runs the plugin's optional `prompts` stage and validates the answers against the schema the plugin returns. This is whe… - conflict — Resolves the per-op `conflict` policy on… - [apply](./apply/apply.md) — _(no submodules)_- conflict — Resolves the per-op `conflict` policy on… - [conflict](./apply/conflict.md) — Resolves the per-op `conflict` policy on a target that already exists. Five policies are supported: `error` (raise `APP… - [json-merge](./apply/json-merge.md) — Implements the deep-merge semantics used by `patch-json` ops with `conflict: "merge"`. Match the conventions of `@suave… - [text-patch](./apply/text-patch.md) — Applies an ordered sequence of `TextEdit`s to a string. Used by `patch-text` ops to make targeted insertions/deletions/… - [detect](./detect.md) — Runs a plugin's optional `detect` stage inside an OpenTelemetry span. The plugin reports what is already present in the… - [host](./host.md) — _(no submodules)_- conflict — Resolves the per-op `conflict` policy on… - [plan](./plan.md) — Invokes `plugin.plan(answers, ctx)` and validates the returned `FileOp[]` against `FileOpsSchema` before letting them f… - [post-install](./post-install.md) — Runs the plugin's optional `postInstall` stage with abort propagation. This is where plugins do follow-up work that doe… - [prompts](./prompts.md) — Runs the plugin's optional `prompts` stage and validates the answers against the schema the plugin returns. This is whe… - conflict — Resolves the per-op `conflict` policy on… - [apply](./apply/apply.md) — _(no submodules)_- conflict — Resolves the per-op `conflict` policy on… - [conflict](./apply/conflict.md) — Resolves the per-op `conflict` policy on a target that already exists. Five policies are supported: `error` (raise `APP… - [json-merge](./apply/json-merge.md) — Implements the deep-merge semantics used by `patch-json` ops with `conflict: "merge"`. Match the conventions of `@suave… - [text-patch](./apply/text-patch.md) — Applies an ordered sequence of `TextEdit`s to a string. Used by `patch-text` ops to make targeted insertions/deletions/… - [detect](./detect.md) — Runs a plugin's optional `detect` stage inside an OpenTelemetry span. The plugin reports what is already present in the… - [host](./host.md) — _(no submodules)_- apply — _(no submodules)_… - [plan](./plan.md) — Invokes `plugin.plan(answers, ctx)` and validates the returned `FileOp[]` against `FileOpsSchema` before letting them f… - [post-install](./post-install.md) — Runs the plugin's optional `postInstall` stage with abort propagation. This is where plugins do follow-up work that doe… - [prompts](./prompts.md) — Runs the plugin's optional `prompts` stage and validates the answers against the schema the plugin returns. This is whe… - [apply](./apply/apply.md) — _(no submodules)_- [conflict](./apply/conflict.md) — Resolves the per-op `conflict` policy on a target that already exists. Five policies are supported: `error` (raise `APP… - [json-merge](./apply/json-merge.md) — Implements the deep-merge semantics used by `patch-json` ops with `conflict: "merge"`. Match the conventions of `@suave… - [text-patch](./apply/text-patch.md) — Applies an ordered sequence of `TextEdit`s to a string. Used by `patch-text` ops to make targeted insertions/deletions/… - [detect](./detect.md) — Runs a plugin's optional `detect` stage inside an OpenTelemetry span. The plugin reports what is already present in the… - [host](./host.md) — _(no submodules)_- [plan](./plan.md) — Invokes `plugin.plan(answers, ctx)` and validates the returned `FileOp[]` against `FileOpsSchema` before letting them f… - [post-install](./post-install.md) — Runs the plugin's optional `postInstall` stage with abort propagation. This is where plugins do follow-up work that doe… - [prompts](./prompts.md) — Runs the plugin's optional `prompts` stage and validates the answers against the schema the plugin returns. This is whe… ## Purpose The plugin host. `Host.run(plugin, options)` executes the strict pipeline `detect → prompts → plan → apply → postInstall`, with abort propagation, `--dry-run` short-circuiting, and `only`/`exclude` filtering. Each stage is wrapped in an OpenTelemetry span (`installer.`) carrying `plugin.id`, `plugin.version`, and `dry_run` attributes — that instrumentation is what lets operators correlate apply failures with the specific plugin and version that produced them. ## Features - Strict ordering — optional stages (`detect`, `prompts`, `postInstall`) are skipped without affecting the order of mandatory stages (`plan`, `apply`). - Abort checks at every stage boundary — if the signal aborts mid-run, the host raises `INSTALLER.ABORTED` and apply rolls back any partial work. - `--dry-run` — when set, the host returns the plan ops without invoking apply or postInstall. - `only`/`exclude` — substring filters applied to the op target path (`op.to` for renames, `op.path` otherwise) before apply runs. - `answersFromFile` channel — used by the bin's `--answers ` flag to feed pre-recorded answers into prompt validation without going interactive. ## Basic Example ```ts import { Host, MemoryFs } from "@suaveplan/installer"; const fs = new MemoryFs(); const host = new Host( { cwd: "/", fs, cli: { confirm: async () => false, prompt: async () => "", write: () => undefined }, signal: new AbortController().signal, logger: { debug: () => undefined, info: () => undefined, warn: () => undefined, error: () => undefined, }, templates: { render: (s) => s }, dryRun: false, force: false, noInteractive: true, }, fs ); const result = await host.run(myPlugin); console.log(result.summary); ``` ## Advanced Example ```ts import { Host } from "@suaveplan/installer"; const ac = new AbortController(); const host = new Host({ ...ctx, signal: ac.signal, dryRun: true }, fs); setTimeout(() => ac.abort(), 5_000); const dryResult = await host.run(myPlugin, { only: ["crypto", "events"], exclude: ["legacy"], }); console.log(`would apply ${dryResult.ops.length} ops`); ``` ## API Reference - `class Host` — constructed with `(ctx: InstallerContext, fs: MutableInstallerFs)`. - `Host.run(plugin, options): Promise>` - `interface HostRunOptions extends RunOptions` — adds `answersFromFile?: unknown` for the bin. - `interface HostRunResult` — `{ detect, ops, summary, handle, dryRun }`. ## Implementation Notes `filterOps` runs _after_ plan but _before_ apply, so the conformance guarantee that "every emitted op respects its conflict policy on re-run" applies to the post-filter set. `--dry-run` returns a synthetic summary (`skipped = ops.length`) so dashboards see a coherent counter even on hypothetical runs. Abort checks are placed at every awaitable boundary (after detect, after prompts, before apply) so a long-running prompt schema can be cancelled cleanly. `applyOps` itself owns the rollback journal — the host's only responsibility post-apply is to wire up `postInstall` with the resulting handle.