Documentation / Développement / Référence / meridian-plugin CLI

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.yamlgenerated/{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) if dir/package.json exists.
  • Creates src/behaviors/ and workflows/ 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/validatemeridian-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 dir plus its transitive dependsOn closure via standard Node resolution (require.resolve("<dep>/plugin.yaml") from the plugin folder; loadPluginClosure in libs/plugin-cli/src/discover.ts). A dependsOn package that is not installed is skipped silently.

  • Emits, into dir/generated/:

    File Emitted when Contents
    types.ts the plugin contributes ≥1 type one export interface per contributed type; import type lines for types owned by dependencies (<dep>/types).
    nodes.ts ≥1 node has a behavior PluginNodeIO (per-node inputs/outputs shapes) and the BehaviorFor<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/Durationstring; Number/Decimal/Integernumber; Booleanboolean); quantity{ value: number; unit: string; system?: string }; ref{ id: string }; anyunknown; listT[]; 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 --plugin directory 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 runs validateSpec, 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 --locale throws usage: 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 exits 0.
  • For each bundle (sorted by filename):
    • The source locale (bundle name equal to manifest.locale) is exempt from manifest keys; only its runtime section 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.
  • 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:

  1. Collects every module: referenced by the manifest — node behaviors, port adapters, event sources — and resolves each to its source (./x.ts, or ./x.js with source under src/; resolveEntry). An unresolvable module throws <root> : module du manifeste introuvable : <module> ….

  2. Removes and recreates dist/.

  3. If there are modules, runs esbuild with these settings:

    esbuild option Value
    bundle true
    splitting true (shared code → chunks/[name]-[hash])
    format esm
    platform node
    target node20
    external ["node:*", "@meridian/*"] — Node builtins and the SDK stay external (provided by the host)
    banner.js ESM-compat shim defining require, __filename, __dirname
    logLevel warning

    outbase is src/ when all entries live under src/ (scaffold layout), else the plugin root. A non-empty esbuild error list throws esbuild : <n> erreur(s).

  4. If a tsconfig.build.json or tsconfig.json exists, runs npx tsc -p <tsconfig> --emitDeclarationOnly --noCheck for the ./types export consumed by dependent plugins. A non-zero status throws tsc --emitDeclarationOnly a échoué (<tsconfig>). Plugins without a tsconfig emit no declarations.

  5. Copies plugin.yaml into dist/, rewriting every module: *.ts to *.js.

  6. Copies i18n/*.yaml and i18n/*.yml into dist/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.yaml exists, publishes <dir>/dist; otherwise publishes <dir> directly. A directory without plugin.yaml throws <dir> : pas de plugin.yaml — publiez le dossier COMPILÉ (dist/), pas la source.
  • Reads name and version from 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>/…, writing plugin.yaml last 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.yaml marker exists (interrupted publishes are invisible), sorted in ascending semver order via the semver package; 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.

75 documents13 sectionssource : /docs · généré au build