Skip to content

Plugin structure

Under the hood, a .lia file is a ZIP archive with a fixed structure. Liatir validates the bundle signature before reading the manifest or executing any runtime payload.

Runtime variants

There are three supported runtimes: Node, Python, and WASM.

Node plugins should not maintain a hand-written .lia-manifest.json. liatir build generates the manifest from the exported definePlugin({...}) contract.

WASM plugins do use .lia-manifest.json, because Rust/WASM cannot export the JavaScript plugin contract at build time.

Python plugins also use .lia-manifest.json. The manifest declares the same Liatir I/O schema plus the Python entry point and optional Python dependency requirements.

Generated Node manifest

For Node plugins, the manifest is generated by liatir build after bundling and validating the default export.

Manifest example:

json
{
  "name": "my-plugin",
  "version": "1.0.0",
  "description": "What this plugin does",
  "runtime": "node",
  "category": "Utilities",
  "tags": ["text"],
  "inputSchema": {
    "text": {
      "type": "string",
      "label": "Text",
      "description": "Field description.",
      "required": true,
      "default": "hello from Liatir"
    }
  },
  "outputSchema": {
    "length": {
      "type": "number",
      "label": "Length",
      "description": "Field description.",
      "format": "integer"
    }
  }
}

Node entry point (index.ts or .js)

Keep the entry point clean and do not alter its structure, or Liatir will reject the plugin. Organize your source files in the project's src/ folder and keep the entry point as a thin wrapper that exports the plugin logic.

ts
import { definePlugin, field, type PluginContext } from "@liatir/api";

const liatirPlugin = definePlugin({
  inputs: {
    text: field.string({
      label: "Text",
      description: "Text to analyze.",
      required: true,
      default: "hello from Liatir",
    }),
  },
  outputs: {
    length: field.number({
      label: "Length",
      description: "Number of characters in the input text.",
      format: "integer",
    }),
  },
});

export default liatirPlugin.main(async ({ input, Liatir }: PluginContext<typeof liatirPlugin>) => {

  // Your logic

  return {
    length: input.text.length,
  };
});

liatir build rejects Node plugins that export an invalid contract.

Field schemas

Input fields can be of type:

  • string
  • number
  • boolean
  • file

Output fields can be of type:

  • string
  • number
  • boolean
  • file
  • stats
  • json

Shared field properties include:

PropertyDescription
labelHuman-readable label shown in the UI.
descriptionShort explanation shown near the field.
requiredWhether the field must be set before running.
defaultDefault value applied by the UI and by liatir dev.
acceptAccepted file extensions for file inputs.
extExpected file extensions for file outputs.
formatNumeric output display hint: integer, decimal, percent, or bytes.

File outputs

If an output field has type: "file", the returned value can either reference an existing file path or provide inline content for Liatir to persist.

ts
return {
  report: {
    content: "sample,score\nA,0.92\n",
    fileName: "report.csv",
  },
};

Liatir registers saved file outputs under the workspace's Results folder so they can be opened later or connected to downstream pipeline steps.

WASM manifest

WASM plugins keep their schema in .lia-manifest.json:

json
{
  "name": "wasm-length",
  "version": "1.0.0",
  "description": "Count characters in text.",
  "runtime": "wasm",
  "inputSchema": {
    "text": { "type": "string", "label": "Text", "required": true }
  },
  "outputSchema": {
    "length": { "type": "number", "label": "Length", "format": "integer" }
  }
}

The WASM binary reads JSON input from stdin and writes JSON output to stdout. Liatir rejects WASM plugins that do not follow the I/O contract.

Python manifest

Python plugins keep their schema and runtime dependency metadata in .lia-manifest.json:

json
{
  "name": "python-length",
  "version": "1.0.0",
  "description": "Count characters with Python.",
  "runtime": "python",
  "inputSchema": {
    "text": {
      "type": "string",
      "label": "Text",
      "required": true,
      "default": "hello from Liatir"
    }
  },
  "outputSchema": {
    "length": {
      "type": "number",
      "label": "Length",
      "format": "integer"
    }
  },
  "python": {
    "entry": "src/main.py",
    "pythonRequirement": {
      "minVersion": "3.10",
      "maxVersionExclusive": "3.13",
      "label": "Python >=3.10,<3.13"
    },
    "packages": [],
    "requirements": []
  }
}

The Python entry point must define a callable main(input) function. Liatir passes the input object as JSON-compatible data and expects main to return a JSON-compatible object whose keys match the output schema.

python
def main(input):
    text = str(input.get("text", ""))
    return {
        "length": len(text),
    }

When the packaged .lia runs, Liatir creates an isolated managed Python runtime for that plugin bundle and installs the declared packages or requirements there.