exlab_wizard.plugins.host#
Plugin host: spawns plugin worker subprocesses with strict isolation.
Backend Spec §6.3 (subprocess isolation, IPC envelope, resource limits, failure handling, security model) and §4.4.3 (the host’s place in the creation pipeline).
The host is the only piece of the plugin system that runs in the long-lived FastAPI server process. It owns:
Worker spawning via
asyncio.create_subprocess_exec()(NEVERshell=True).A sanitized environment passed to each worker (only
PATH,HOME,LANG, andEXLAB_*allowlist variables are forwarded).POSIX resource limits applied via
preexec_fn(RLIMIT_AS,RLIMIT_CPU,RLIMIT_NOFILE).A wall-clock timer per worker (
asyncio.wait_for); on expiry the worker is sentSIGTERM, givenWORKER_TIMEOUT_GRACE_SECONDS, and thenSIGKILL-ed.The IPC envelope: a JSON object on the worker’s stdin and a JSON object back on its stdout. The worker’s stderr is a structured-log side-channel that the host parses line-by-line and forwards into the canonical log chain (Backend Spec §16.8); any non-JSON stderr output is captured verbatim into
<central_log_dir>/plugins/<plugin>/<run_id>.stderr.The
PluginInputRequiredsuspend/resume handshake (Backend Spec §6.4): on exit code 2 the host invokes the caller-suppliedon_input_requiredcoroutine, awaits the operator’s response, and re-spawns the worker with the newextra_inputspayload.Forbidden-write enforcement (Backend Spec §6.1.5): the host snapshots the destination tree before each plugin runs, and reverts (and marks
status="policy_violation") any write that touchesREADME.md, the.exlab-wizard/subtree, or.exlab-answers.yml.
Functions
|
Return |
|
Return a tiny in-memory registry over |
Classes
|
Frame surfaced to the controller when a plugin needs more input. |
|
Spawns plugin worker subprocesses with strict isolation. |
|
Aggregate result returned by |
|
One resolved plugin, ready to be spawned. |
|
Read-only registry surface the host depends on. |
- class exlab_wizard.plugins.host.InputRequiredPayload(plugin, fields, reason)[source]#
Bases:
objectFrame surfaced to the controller when a plugin needs more input.
Backend Spec §6.4.1. The controller forwards this to the WebSocket client as the
input_requiredevent; the operator’s response is handed back to the host via theon_input_requiredcallback’s return value.
- class exlab_wizard.plugins.host.PluginHost(registry, *, central_log_dir=None)[source]#
Bases:
objectSpawns plugin worker subprocesses with strict isolation.
Backend Spec §6.3 (subprocess model) / §4.4.3 (host-in-pipeline).
- Parameters:
registry (
PluginRegistryProtocol)
- async run_pass(ctx, file_paths, plugin_order, on_input_required)[source]#
Drive the plugin pass for a single creation session.
Spawns one worker subprocess per plugin in
plugin_order, feeding each its full set of matched files. On exit code 2 (PluginInputRequired) the host invokeson_input_required; if the awaitable resolves to a dict, the worker is re-spawned with that dict asextra_inputs; if it resolves toNone, the session is aborted.- Parameters:
- Return type:
- class exlab_wizard.plugins.host.PluginPassResult(applied=<factory>, aborted=False)[source]#
Bases:
objectAggregate result returned by
PluginHost.run_pass().Backend Spec §6.2.4.
appliedis the per-plugin record list that the controller writes intocreation.json’splugins_appliedblock;abortedis set toTrueonly when the operator cancelled anPluginInputRequiredprompt.
- class exlab_wizard.plugins.host.PluginRecord(name, version, package_path, module_name, timeout_seconds=30, memory_mb=512, supported_extensions=())[source]#
Bases:
objectOne resolved plugin, ready to be spawned. Backend Spec §6.2.1.
- name#
Manifest
namefield; the plugin’s stable identifier.
- version#
Manifest
versionfield; surfaced intocreation.json.
- package_path#
Filesystem path to the plugin package directory (the directory containing
manifest.ymland__init__.py). The host prepends the parent of this path to the worker’ssys.pathsoimport <package_path.name>resolves.
- module_name#
Importable module name – typically
package_path.name.
- timeout_seconds#
Manifest
isolation.timeout_seconds(capped againstPLUGIN_TIMEOUT_MAX_SECONDSby the registry).
- memory_mb#
Manifest
isolation.memory_mb(capped againstPLUGIN_MEMORY_MAX_MBby the registry).
- supported_extensions#
Manifest
supported_extensions– the host uses this to filter file lists when the controller does not pre-filter.
- Parameters:
- class exlab_wizard.plugins.host.PluginRegistryProtocol(*args, **kwargs)[source]#
Bases:
ProtocolRead-only registry surface the host depends on. Backend Spec §6.2.
The concrete implementation lives in
plugins/registry.py(Agent A). The host only consumes a single method:get_record(name)which returns the resolvedPluginRecordfor a registered plugin.
- exlab_wizard.plugins.host.applied_entry_as_json(entry)[source]#
Return
entryas a JSON-friendly dict (deep-copied).
- exlab_wizard.plugins.host.build_test_registry(records)[source]#
Return a tiny in-memory registry over
records. Tests only.- Parameters:
records (
Iterable[PluginRecord])- Return type: