How Extension Manifests Define Model and Process Extensions in Modly

Extension manifests in Modly are JSON configuration files that declare whether an extension provides AI models or custom processing logic, defining entry points, node behaviors, and metadata that the application parses at startup to populate the extensions store and workflow engine.

In the lightningpixel/modly repository, every plugin is distributed as a folder containing a mandatory manifest.json file. This manifest serves as the single source of truth that tells Modly whether the extension contributes AI model capabilities or custom processing workflows. By parsing these manifests at runtime, Modly builds a unified registry that powers both the extension manager UI and the visual workflow generator.

How Modly Scans and Parses Extension Manifests

When Modly initializes or when a user triggers the extensions list command, the application executes a systematic discovery process across both user-installed and built-in extension directories.

Scanning Extension Directories

The main process begins by scanning two locations simultaneously using readExtensionsFromDir() in electron/main/ipc-handlers.ts. The function checks both the user-supplied extensions folder and the built-in extensions directory:

// electron/main/ipc-handlers.ts → readExtensionsFromDir()
// (lines ≈ 53-71)
const [userExts, builtinExts] = await Promise.all([
    readExtensionsFromDir(extensionsDir, false),
    readExtensionsFromDir(builtinDir,    true),
]);

For each candidate folder, Modly looks for either manifest.json or package.json. If found, the raw JSON is fed into the parsing pipeline.

Validating and Normalizing Manifest Data

The parseExtensionManifest() function (lines ≈ 11-44 in the same file) transforms the raw JSON into normalized ModelExtension or ProcessExtension objects. This function handles the type discrimination logic:

// electron/main/ipc-handlers.ts → parseExtensionManifest()
function parseExtensionManifest(parsed, fallbackId, trustedRepos, builtin = false) {
    const common = { … };
    const nodes = (parsed.nodes ?? []).map(…);
    if (parsed.type === 'process') {
        return { …common, type: 'process', entry: parsed.entry ?? 'processor.js', nodes };
    }
    return { …common, type: 'model', nodes };
}

The resulting objects are stored in the extensions store (src/shared/stores/extensionsStore.ts), which exposes modelExtensions and processExtensions arrays consumed by the React UI via the useExtensionsStore hook.

Model Extensions vs. Process Extensions

Modly distinguishes between two extension types based on the type field in the manifest. While both share the same node schema (ExtensionNode defined in src/shared/types/electron.d.ts), they differ in execution semantics and required fields.

Property Model Extension Process Extension
type 'model' 'process'
entry None—models are collections of nodes that produce assets Path to the JavaScript or Python entry point (defaults to processor.js)
nodes Describe input-to-output behavior for AI inference (e.g., text-to-image) Describe processing steps for custom operations (e.g., mesh optimization)
Required fields id, name, type='model', nodes Same as model, plus entry and type='process'

Structure of a Model Extension Manifest

A minimal model manifest defines nodes that map inputs to outputs without an execution entry point:

{
  "id": "my-diffusion",
  "name": "My Diffusion Model",
  "type": "model",
  "nodes": [
    {
      "id": "txt2img",
      "name": "Text-to-Image",
      "input": "text",
      "output": "image",
      "params_schema": [
        { "id": "prompt", "label": "Prompt", "type": "string", "default": "" }
      ]
    }
  ]
}

Structure of a Process Extension Manifest

Process extensions require an entry field specifying the script to invoke when the workflow runs:

{
  "id": "mesh-opt",
  "name": "Mesh Optimizer",
  "type": "process",
  "entry": "processor.js",
  "nodes": [
    {
      "id": "optimize",
      "name": "Optimize Mesh",
      "input": "mesh",
      "output": "mesh",
      "params_schema": [
        { "id": "target_faces", "label": "Target Faces", "type": "int", "default": 5000 }
      ]
    }
  ]
}

Consuming Extension Data in the UI and Workflow Engine

Once parsed, extension manifests power both the extension management interface and the visual workflow builder.

Accessing Extensions via the Zustand Store

The UI retrieves loaded extensions through the extensions store. Components call loadExtensions() to trigger a refresh and access the partitioned arrays:

import { useExtensionsStore } from '@shared/stores/extensionsStore';

function ExtensionList() {
  const { modelExtensions, processExtensions, loadExtensions } = useExtensionsStore();
  useEffect(() => { loadExtensions(); }, []);
  // Render badges based on `type` field
}

Building Workflow-Ready Nodes

The workflow generator consumes both extension types through buildAllWorkflowExtensions() in src/areas/workflows/mockExtensions.ts. This function iterates over modelExtensions and processExtensions, transforming each node definition into a WorkflowExtension object that the canvas UI can render:

import { buildAllWorkflowExtensions } from '@areas/workflows/mockExtensions';

const workflowExts = buildAllWorkflowExtensions(modelExtensions, processExtensions);
// workflowExts contains unified objects with type: 'model' | 'process'

Handling Manifest Errors and Validation

During installation, Modly validates extension manifests to prevent corrupted plugins from crashing the application. If a manifest is missing, unparsable, or the installation never completed, Modly records a manifestError property on the extension object. This field accepts one of three literal values: 'missing', 'invalid', or 'incomplete'. The UI checks this property to display warning badges and disable broken extensions.

Summary

  • Extension manifests (manifest.json) are mandatory configuration files that declare an extension's identity, type, and node definitions in the lightningpixel/modly architecture.
  • Model extensions define AI inference nodes without execution entry points, while process extensions specify a script entry point for custom processing logic.
  • The parseExtensionManifest() function in electron/main/ipc-handlers.ts normalizes raw JSON into typed ModelExtension or ProcessExtension objects.
  • Parsed extensions populate a Zustand store (src/shared/stores/extensionsStore.ts) that provides reactive data to the React UI and workflow engine.
  • Error states (manifestError) track corrupted installations, allowing the UI to gracefully handle missing or invalid manifests.

Frequently Asked Questions

What fields are required in a Modly extension manifest?

Every manifest must include id, name, type (either 'model' or 'process'), and a nodes array defining the extension's interface. Process extensions additionally require an entry field specifying the path to the processor script, which defaults to processor.js if omitted.

How does Modly distinguish between model and process extensions?

Modly checks the type field during the parseExtensionManifest() execution in electron/main/ipc-handlers.ts. When type equals 'process', the parser includes an entry property in the resulting object; otherwise, it returns a ModelExtension without execution logic. Both types share the same ExtensionNode schema for defining inputs, outputs, and parameters.

Where does Modly store parsed extension data?

Parsed extension objects are stored in the extensions store located at src/shared/stores/extensionsStore.ts. This Zustand-based store maintains separate arrays for modelExtensions and processExtensions, which components access via the useExtensionsStore() hook. The store persists the unified registry across the application lifecycle.

What happens if an extension manifest is invalid?

If readExtensionsFromDir() encounters a missing, unparsable, or incomplete manifest, it flags the extension with a manifestError property set to 'missing', 'invalid', or 'incomplete'. The UI consumes this metadata to display error states and prevent users from activating corrupted extensions in workflows.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →