plugin dev events¶
meridian plugin dev --json writes a stream of JSON objects to stdout, one per line, while it watches a plugin's directory. It is for scripts and AI agents following a live plugin. meridian plugin events --json reports the same events on demand. Progress and errors go to stderr, never to stdout.
For the command itself, see meridian plugin dev. For what a development deployment is, see Development deployments.
mkdir -p .meridian
meridian plugin dev --instance my-plugin --yes --json > .meridian/dev.jsonl 2> .meridian/dev.err
Write the stream under .meridian/
plugin dev sends every file in the plugin's directory that changes, except .meridian/ and what .dockerignore names. Output written anywhere else in the directory would be sent to the plugin as a change, again and again.
Revisions¶
Every change plugin dev sends is given a revision, a whole number. Revisions count up from 1, and the first run, before anything is sent, is revision 0. Every event carries the revision it is about, so you can tell which save it concerns.
Where events come from¶
| Source | Events | How to recognise it |
|---|---|---|
The meridian command line, on your machine |
sent |
Only on the plugin dev --json stream. It has no at field. |
| The plugin's sidecar | synced, refused |
"by": "sidecar" |
The dev runner in the plugin's container (meridian-dev run, from the Python SDK) |
seeded, restarted, crashed, exited |
No by field. |
| The Python SDK, inside the plugin's process | ready |
No by field. |
The runner's and the sidecar's events are merged and sorted by at. plugin dev reports each one once, however many times a poll returns it.
Events¶
sent¶
This process sent a change to the deployment, and the deployment gave it a revision.
| Field | Type | Meaning |
|---|---|---|
event |
string | "sent" |
revision |
integer | The revision the change was given. |
files |
integer | How many files were sent, each whole. |
deleted |
integer | How many files were sent as deleted. |
synced¶
The sidecar wrote the change into the plugin's live folder. It writes every file first and the revision last, so a change half-written never runs.
| Field | Type | Meaning |
|---|---|---|
event |
string | "synced" |
revision |
integer | The revision written. |
files |
integer | Files written. |
deleted |
integer | Files deleted. |
at |
number | When, in seconds since the Unix epoch. |
by |
string | "sidecar" |
restarted¶
The plugin's process was started on this revision. The runner starts the plugin when the container starts, and again on each new revision, stopping the running process first. It sends SIGTERM and waits 5 seconds before killing the process.
| Field | Type | Meaning |
|---|---|---|
event |
string | "restarted" |
revision |
integer | The revision it started on. |
pid |
integer | The new process's id. |
at |
number | When, in seconds since the Unix epoch. |
ready¶
The plugin registered with its sidecar again: this revision is running. The SDK writes this from inside meridian.connect() once registration is admitted, so ready means running, not merely started.
| Field | Type | Meaning |
|---|---|---|
event |
string | "ready" |
revision |
integer | The revision now running. |
at |
number | When, in seconds since the Unix epoch. |
crashed¶
The plugin's process stopped with an error. It is not started again until the next revision, so a plugin that can't start does not restart as fast as it fails.
| Field | Type | Meaning |
|---|---|---|
event |
string | "crashed" |
revision |
integer | The revision that crashed. |
exit |
integer or null |
The process's exit code. null when no process was started, because pyproject.toml names no usable [project.scripts] entry point. |
traceback |
string | The last 40 lines the process printed, or why it could not be started. |
at |
number | When, in seconds since the Unix epoch. |
exited¶
The plugin's process stopped by itself, without an error. Like crashed, it is not started again until the next revision.
| Field | Type | Meaning |
|---|---|---|
event |
string | "exited" |
revision |
integer | The revision that exited. |
exit |
integer | Always 0. |
at |
number | When, in seconds since the Unix epoch. |
refused¶
The sidecar refused the plugin something it tried to do, for example a typed operation none of its roles grants, or a command for an account outside its write scope. This is the plugin's grants working, not a bug to code around. Changing roles takes a new version, which a person approves. See Plugins, roles and grants.
| Field | Type | Meaning |
|---|---|---|
event |
string | "refused" |
revision |
integer | The revision running when it was refused. |
reason |
string | What was refused, and why, in the sidecar's words. |
at |
number | When, in seconds since the Unix epoch. |
by |
string | "sidecar" |
seeded¶
The runner found a new, empty live folder and filled it from the plugin's image, before starting the plugin for the first time.
| Field | Type | Meaning |
|---|---|---|
event |
string | "seeded" |
revision |
integer | The revision the folder was on, normally 0. |
at |
number | When, in seconds since the Unix epoch. |
Note
seeded is written by the runner and returned like any other event, but the event table in the plugin template's AGENTS.md doesn't list it. Treat it as informational.
Following a change¶
- Save a file. There is nothing to run: the save is the deploy.
- Find the
sentline after your save, and itsrevision, R. Several saves close together can land as one revision, so read the newestsent. - Wait for
readyorcrashedat R. Nothing about R is known before then. - On
crashed, read itstraceback, fix the cause, and save again. The next revision replaces it.
To check what happened since your change from another terminal, pass the revision before it:
meridian plugin logs --instance my-plugin --since <R-1>
meridian plugin events --instance my-plugin --since <R-1> --json
--since N returns only what belongs to revisions after N.
plugin events --json¶
Without --follow, meridian plugin events --json prints one object holding the current revision and every event kept:
{
"revision": 2,
"events": [
{"revision": 1, "event": "synced", "files": 9, "deleted": 0, "at": 1790000000.1, "by": "sidecar"},
{"revision": 1, "event": "restarted", "pid": 42, "at": 1790000000.4},
{"revision": 1, "event": "ready", "at": 1790000001.2}
]
}
With --follow, it prints one event object per line, as they happen, like plugin dev --json without sent.
The runner and the sidecar each keep their latest 2,000 events.
Text form¶
Without --json, each event is one line with its revision first. A crashed event's traceback follows on indented lines:
| Event | Line |
|---|---|
sent |
r2 sent (1 files, 0 deleted) |
synced |
r2 synced (1 sent, 0 deleted) |
refused |
r2 refused: <reason> |
crashed |
r2 crashed, exit 1 then the traceback, indented four spaces |
exited |
r2 exited, exit 0 |
| any other | r2 <event> |
Events the SDK's documentation mentions but the sidecar does not emit
The docstring of the SDK's dev runner (meridian/dev.py) says the sidecar's own events include what the instance published and received. The sidecar in this release records only synced and refused.