Plugin development
Scaffold a plugin package, run it against the real host, understand sandbox config, publish, and consume it from an app.
A plugin is an npm package that exports a default Codemation plugin object from codemation.plugin.ts (built to dist/). It registers nodes, credential types, and optional sandbox defaults so local development matches how a consumer app loads the same package from node_modules.
Use this page for the end-to-end plugin workflow. For reusable nodes and credentials themselves, follow the same guides as app-local extensions—see Nodes and credentials below.
Getting started
Scaffold the plugin template, install, and start the plugin dev server:
pnpm create codemation my-plugin --template plugin
cd my-plugin
pnpm install
pnpm devpnpm dev runs codemation dev:plugin, which boots the real host and UI against a synthetic codemation.config.ts generated next to your plugin (see Lifecycle with the host). Open the URL the CLI prints, create credentials as needed, and run the sample workflow.
To see all template ids (including plugin):
pnpm create codemation --list-templatesFramework package versions (0.0.x)
Scaffolded default and plugin projects list @codemation/* dependencies as 0.0.x. That semver range matches any 0.0.* patch release on npm, so you can run pnpm upgrade (or pnpm update) to move to newer 0.0.z builds without hand-editing versions each time. Pin stricter ranges once you are past pre-1.0 churn or need reproducible installs.
The plugin config file (codemation.plugin.ts)
The default export is built with definePlugin from @codemation/host. Typical shape:
import { createWorkflowBuilder, ManualTrigger } from "@codemation/core-nodes";
import { definePlugin, type CodemationConfig } from "@codemation/host";
const sandbox: CodemationConfig = {
app: {
auth: {
kind: "local",
allowUnauthenticatedInDevelopment: true,
},
database: {
kind: "pglite",
pgliteDataDir: ".codemation/pglite",
},
scheduler: {
kind: "inline",
},
whitelabel: {
productName: "Plugin sandbox",
},
},
workflows: [
createWorkflowBuilder({ id: "wf.plugin.hello", name: "Plugin Hello" })
.trigger(new ManualTrigger("Start", [{ json: { message: "hello plugin" } }]))
.then(new MyNodeConfig("Run node"))
.build(),
],
};
export default definePlugin({
credentialTypes: [
/* optional declarative credential types */
],
register(context) {
context.registerNode(MyNode);
},
sandbox,
});Sandbox (plugin.sandbox)
plugin.sandbox is an optional CodemationConfig fragment.
codemation dev:plugin uses it to synthesize a temporary consumer config for local development, so plugin authors can run the real host and UI against:
- demo workflows
- local auth defaults
- embedded database (PGlite)
- lightweight branding for the plugin sandbox
Plugin surface (definePlugin)
credentialTypes— optional list registered declaratively (alongside anything you register inregister).register— imperative registration: e.g.context.registerNode(...), container bindings, and other host hooks.sandbox— optional; used by thedev:pluginflow so the generated config can merge your sandbox config and append the plugin automatically.
Lifecycle with the host
-
Local plugin development —
codemation dev:plugin(usually viapnpm devin the template) usesPluginDevConfigFactoryto write.codemation/plugin-dev/codemation.config.tsbeside your package. That file:- imports your default plugin export;
- reads
plugin.sandbox ?? {}; - merges that sandbox config into the root
CodemationConfig; - appends your plugin to
plugins.
So you develop against the same host and runtime as a normal consumer, without publishing first.
-
Consumer apps — When the CLI loads a consumer
codemation.config.ts, it merges plugins from your config with plugins discovered undernode_modules(see below). The host then runs registration, workflows, and the UI as usual.
Nodes and credentials
Implementing nodes and credential types in a plugin follows the same patterns as in an application: config classes, @node, CredentialType, constructor injection, and tests without heavy mocking.
One extra rule matters for custom nodes: defining the config class is not enough by itself. The plugin still needs to register the runtime node class in register(context):
export default definePlugin({
register(context) {
context.registerNode(MyNode);
},
});Start here:
- Custom nodes — patterns and engine contract
- Create custom node — step-by-step
- Create custom credential — typed credentials and sessions
- Use a node as an agent tool — wrap a runnable node for
AIAgent
Optional deeper reading:
- Concepts — workflows, nodes, credentials, runs
- Plugin developers — packaging nodes for AI agent tools (
AgentToolFactory.asTool(...)) and stability notes for published surfaces
Publishing
-
Build the package so
codemation.plugin.tscompiles to the entry the manifest points at (the template usestsdown):pnpm build -
Declare the plugin entry in
package.jsonso the host can discover the package after install:{ "name": "@acme/codemation-plugin-hello", "codemation": { "plugin": "./dist/codemation.plugin.js" } } -
Publish to npm (or your private registry) as you would any TypeScript library. Ensure
files/exportsincludedist/and that the plugin entry path is correct for consumers.
Using a plugin in a project
-
Add the dependency to your Codemation consumer app:
pnpm add @acme/codemation-plugin-hello -
Discovery — The host scans
node_modulesfor packages whosepackage.jsonincludescodemation.pluginas a string path and loads that module. You do not have to duplicate that import incodemation.config.tsunless you want an explicit reference. -
Explicit
plugins— You can still add plugins tocodemation.config.ts(for example a local file path while developing). Configured plugins are merged with discovered plugins; when the same published package appears both ways, the host dedupes by package identity. -
Run the app —
pnpm dev/codemation devas usual. Your workflows can reference node types registered by the plugin.
See also
- Getting started — full consumer app path
- Plugin developers — AI tools and export surface for reusable packages