meridian-plugin CLI
meridian-plugin authors, projects, validates, bundles, and publishes a Meridian
plugin from any directory — no platform repository or workspace checkout required.
It is the bin of the @meridian/plugin-cli package
(libs/plugin-cli/package.json); the entry point is
libs/plugin-cli/src/cli.ts.
Task-oriented walkthroughs live elsewhere: this page is the exhaustive command surface. See scaffold a plugin, generate types, validate a workflow, translate a plugin, build and bundle, and publish to the registry.
Synopsis
meridian-plugin <command> [args] [flags]
| Command | Purpose |
|---|---|
new <name> [dir] |
Scaffold a standalone plugin package. |
gen-types [dir=.] |
Project plugin.yaml → generated/{types,nodes}.ts. |
validate <workflow.yaml> [--plugin <dir>]… |
Static-check a workflow against plugins. |
i18n extract [dir=.] --locale <locale> |
Write/complete i18n/<locale>.yaml. |
i18n check [dir=.] |
List missing/orphan i18n keys. |
build [dir=.] |
Bundle the plugin to a self-contained dist/. |
publish [dir=.] --registry <url> |
Publish a built plugin (dist/) to a registry. |
versions <@scope/name> --registry <url> |
List a plugin's published versions. |
help, -h, --help, (none) |
Print usage. |
Positional directory arguments are the first token that does not start with -.
Unknown flags are ignored except where a value is explicitly consumed
(--plugin, --locale, --registry).
Dispatch and exit codes
cli.ts dispatches on the first argument (main in
libs/plugin-cli/src/cli.ts). The process exit code is the command's return
value.
| Situation | stdout/stderr | Exit |
|---|---|---|
No command, help, -h, --help |
usage text on stdout | 0 |
| Unknown command | Unknown command: <cmd> + usage on stderr |
1 |
i18n with no/unknown subcommand |
thrown usage error (see below) | 1 |
| Any command throws | ✗ <message> on stderr |
1 |
| Command succeeds | command-specific output | 0 |
Uncaught exceptions are funnelled through a single try/catch around main
that prints ✗ <error message> to stderr and exits 1. validate and
i18n check return their exit code directly (0/1) rather than throwing.
new
meridian-plugin new <name> [dir]
Scaffolds a standalone plugin package that compiles out of the box. Source:
libs/plugin-cli/src/commands/scaffold.ts.
| Argument | Required | Default |
|---|---|---|
<name> |
yes | — (npm package name, e.g. @acme/bmi) |
[dir] |
no | <name> with the @scope/ prefix stripped, resolved to an absolute path |
Behavior:
- Errors (
<dir> already contains a package.json) ifdir/package.jsonexists. - Creates
src/behaviors/andworkflows/directories. - Writes the files below, then prints
Scaffolded <name> in <dir>and a next-step line.
| File | Contents |
|---|---|
package.json |
name, version: 0.1.0, type: module; scripts build/gen-types/validate → meridian-plugin …; dep @meridian/shared; devDeps @meridian/plugin-cli, typescript. |
tsconfig.json |
NodeNext, strict, declaration, outDir: dist, rootDir: src. |
plugin.yaml |
apiVersion: meridian/plugin-v1 manifest with one value set, one type, one trigger node, one compute node. |
src/behaviors/example.ts |
Two NodeBehaviorFactory exports (exampleReceived, exampleScore). |
workflows/example.workflow.yaml |
apiVersion: meridian/v3 workflow wiring the two nodes. |
.gitignore |
node_modules/, dist/. |
README.md |
Install/gen-types/validate/build instructions. |
The scaffolded manifest and workflow conform to the plugin manifest and workflow DSL schemas.
gen-types
meridian-plugin gen-types [dir=.]
Projects a plugin's plugin.yaml into TypeScript. Source:
libs/plugin-cli/src/commands/gen-types.ts; codegen core is
libs/plugin-cli/src/codegen.ts.
| Argument | Required | Default |
|---|---|---|
[dir] |
no | . |
Behavior:
Loads the plugin at
dirplus its transitivedependsOnclosure via standard Node resolution (require.resolve("<dep>/plugin.yaml")from the plugin folder;loadPluginClosureinlibs/plugin-cli/src/discover.ts). AdependsOnpackage that is not installed is skipped silently.Emits, into
dir/generated/:File Emitted when Contents types.tsthe plugin contributes ≥1 type one export interfaceper contributed type;import typelines for types owned by dependencies (<dep>/types).nodes.ts≥1 node has a behaviorPluginNodeIO(per-nodeinputs/outputsshapes) and theBehaviorFor<Id>alias.If neither file would be produced, prints
<name>: nothing to generate (no contributed types, no behavior nodes).and writes nothing.On success, prints
<name> → generated/<files>(comma-separated).
Type mapping (mapRef in codegen.ts): primitive → its TS scalar
(String/Code/Uri/Date/DateTime/Time/Duration → string;
Number/Decimal/Integer → number; Boolean → boolean); quantity →
{ value: number; unit: string; system?: string }; ref → { id: string };
any → unknown; list → T[]; coded → a union of literal codes when the
value set is local, else string; object → the referenced interface name.
A referenced object type absent from the dependsOn closure throws
[<plugin>] type "<name>" not found in the dependsOn closure …. See the
TypeRef reference and
codegen internals.
validate
meridian-plugin validate <workflow.yaml> [--plugin <dir>]…
Statically checks a workflow against the SDK primitives plus the given plugins
and their dependsOn closures. No engine or infrastructure is started. Source:
libs/plugin-cli/src/commands/validate.ts.
| Argument / flag | Required | Default |
|---|---|---|
<workflow.yaml> |
yes | — (first non-flag token) |
--plugin <dir> (repeatable) |
no | ["."] |
Behavior:
- Missing workflow argument throws
usage: meridian-plugin validate <workflow.yaml> [--plugin <dir>]…. - Loads each
--plugindirectory and its closure (deduped by name), registers each manifest's metadata only — value sets, dimensions, types, node metas; no behavior modules are imported — in dependency order (dependency before dependent). - Parses the workflow (
WorkflowSpec.parse) and runsvalidateSpec, which checks that ports exist, connection types are compatible, context is satisfied, and required inputs are wired.
| Outcome | Output | Exit |
|---|---|---|
| No errors | ✓ <name>: valid (ports exist, connection types compatible, context, required inputs). on stdout |
0 |
| Errors | ✗ <name>: <n> error(s): then one - <error> per line on stderr |
1 |
Agent (agent.*) nodes are not projected by the CLI and are reported as unknown
node types. See validate a workflow and the
static validation engine.
i18n extract
meridian-plugin i18n extract [dir=.] --locale <locale>
Writes or completes the translation overlay i18n/<locale>.yaml. Keys are
derived from the manifest, never invented. Source:
libs/plugin-cli/src/commands/i18n.ts (key schema:
@meridian/shared/plugin/i18n).
| Argument / flag | Required | Default |
|---|---|---|
[dir] |
no | . |
--locale <locale> |
yes | — |
Behavior:
- Missing
--localethrowsusage: meridian-plugin i18n extract [dir=.] --locale <locale>. - Reads the manifest, derives i18n entries, and merges them with any existing
i18n/<locale>.yaml:- Manifest-derived keys are laid out in manifest order.
- Existing translated values are preserved.
- Newly-derived keys with no value get an empty placeholder preceded by a
# source: <text>comment. - An existing
runtime:section is preserved verbatim at the end.
- Creates
i18n/if needed and writes the file. - Prints
<name> → i18n/<locale>.yaml (<total> clé(s) au total, <added> ajoutée(s)). - Keys present in the bundle but absent from the manifest are reported as orphans
(kept as-is):
⚠ <n> clé(s) orpheline(s) …followed by one- <key>per line.
Keys follow the <section>.<id>.<path> structure. See
translate a plugin.
i18n check
meridian-plugin i18n check [dir=.]
Lists missing and orphan i18n keys across all i18n/*.yaml bundles. Source:
libs/plugin-cli/src/commands/i18n.ts.
| Argument | Required | Default |
|---|---|---|
[dir] |
no | . |
Behavior:
- Reads the manifest and derives the known key set.
- If
i18n/is absent or contains no*.yaml/*.yml, prints a "nothing to check" line and exits0. - For each bundle (sorted by filename):
- The source locale (bundle name equal to
manifest.locale) is exempt from manifest keys; only itsruntimesection matters. Reported✓ <locale>: locale source (runtime uniquement, clés du manifeste non requises). - Other locales: any manifest key without a value is missing; any bundle key outside the manifest is an orphan.
- The source locale (bundle name equal to
- Output per non-clean locale:
<locale>:, then✗ <n> manquante(s) :/⚠ <n> orpheline(s) (hors manifeste) :with one- <key>per line. Clean locales print✓ <locale>: à jour (<n> clé(s)).
| Outcome | Exit |
|---|---|
| No missing keys anywhere (orphans allowed) | 0 |
| At least one missing key | 1 |
build
meridian-plugin build [dir=.]
Builds the deliverable dist/ of a plugin: an esbuild bundle with runtime
dependencies inlined so the artifact is self-contained. Source:
libs/plugin-cli/src/commands/build.ts. There is no build-manifest.mjs; the
CLI does the bundling.
| Argument | Required | Default |
|---|---|---|
[dir] |
no | . |
Behavior:
Collects every
module:referenced by the manifest — node behaviors, port adapters, event sources — and resolves each to its source (./x.ts, or./x.jswith source undersrc/;resolveEntry). An unresolvable module throws<root> : module du manifeste introuvable : <module> ….Removes and recreates
dist/.If there are modules, runs esbuild with these settings:
esbuild option Value bundletruesplittingtrue(shared code →chunks/[name]-[hash])formatesmplatformnodetargetnode20external["node:*", "@meridian/*"]— Node builtins and the SDK stay external (provided by the host)banner.jsESM-compat shim defining require,__filename,__dirnamelogLevelwarningoutbaseissrc/when all entries live undersrc/(scaffold layout), else the plugin root. A non-empty esbuild error list throwsesbuild : <n> erreur(s).If a
tsconfig.build.jsonortsconfig.jsonexists, runsnpx tsc -p <tsconfig> --emitDeclarationOnly --noCheckfor the./typesexport consumed by dependent plugins. A non-zero status throwstsc --emitDeclarationOnly a échoué (<tsconfig>). Plugins without a tsconfig emit no declarations.Copies
plugin.yamlintodist/, rewriting everymodule: *.tsto*.js.Copies
i18n/*.yamlandi18n/*.ymlintodist/i18n/.
Prints ✓ <name>@<version> → dist/ (<n> module(s) bundlé(s), deps runtime incluses).
See build and bundle and bundling and distribution.
publish
meridian-plugin publish [dir=.] --registry <url>
Publishes a built plugin's dist/ to a plugin registry. Source:
libs/plugin-cli/src/commands/publish.ts; registry semantics in
libs/plugin-cli/src/registry/registry.ts, storage backends in
libs/plugin-cli/src/registry/store.ts.
| Argument / flag | Required | Default |
|---|---|---|
[dir] |
no | . |
--registry <url> |
yes unless MERIDIAN_REGISTRY is set |
$MERIDIAN_REGISTRY |
Registry URL resolution: --registry <url> if present, else the
MERIDIAN_REGISTRY environment variable; neither present throws
registre non configuré : passez --registry <url> ou définissez MERIDIAN_REGISTRY.
See environment variables.
Behavior:
- If
<dir>/dist/plugin.yamlexists, publishes<dir>/dist; otherwise publishes<dir>directly. A directory withoutplugin.yamlthrows<dir> : pas de plugin.yaml — publiez le dossier COMPILÉ (dist/), pas la source. - Reads
nameandversionfrom the manifest; a missing either throws<path> : nom ou version manquant. - Refuses to overwrite an already-published version (versions are immutable):
<name>@<version> est déjà publié sur <url> — les versions sont immuables, incrémentez. - Uploads every
dist/file under@scope/name/<version>/…, writingplugin.yamllast as the completeness marker. - Prints
✓ <name>@<version> publié sur <url> (<n> fichier(s)).
Registry backends
openStore (registry/store.ts) selects a backend from the URL:
| URL form | Backend |
|---|---|
/abs/path, ./rel/path, file:///… |
Local directory (dev, tests, NFS). |
gs://bucket[/prefix] |
Google Cloud Storage via REST JSON. |
The GCS backend authenticates with Application Default Credentials
(google-auth-library, scope devstorage.read_write, imported lazily) and uses
the ifGenerationMatch=0 precondition to enforce immutability
(HTTP 412 → clé déjà présente). See
set up a plugin registry
and declare and install plugins.
versions
meridian-plugin versions <@scope/name> --registry <url>
Lists the complete published versions of a plugin. Source:
libs/plugin-cli/src/commands/publish.ts (versionsCommand).
| Argument / flag | Required | Default |
|---|---|---|
<@scope/name> |
yes | — (first non-flag token that is not the --registry value) |
--registry <url> |
yes unless MERIDIAN_REGISTRY is set |
$MERIDIAN_REGISTRY |
Behavior:
- Missing name throws
usage: meridian-plugin versions <@scope/nom> --registry <url>. - Lists only versions whose
plugin.yamlmarker exists (interrupted publishes are invisible), sorted in ascending semver order via thesemverpackage; non-semver directory names sort to the end. - No versions: prints
<name> : aucune version publiée sur <url>. - Otherwise prints one version per line.
Environment variables
| Variable | Used by | Effect |
|---|---|---|
MERIDIAN_REGISTRY |
publish, versions |
Default registry URL when --registry is omitted. |
Google Cloud Storage registries additionally read the ambient ADC credentials
resolved by google-auth-library. Full instance-side variables are documented in
environment variables.
Related references
- Plugin manifest — the
plugin.yamlschema every command reads. - TypeRef — the type expressions codegen projects.
- SDK surface — the
@meridian/*imports left external at build time. - Behavior contract — the
NodeBehaviorFactorysignaturesgen-typestargets. - Workflow DSL — the workflow
validatechecks.