Skip to content

serve (stdio transport) ​

i18nprune serve runs a long-lived process that speaks JSON-RPC 2.0 over stdio, one JSON object per line. It lets a host — an editor extension, a desktop app, or a scripted session — drive i18nprune without re-implementing it and without opening a port across a WSL or SSH boundary.

serve is not a normal command. Its stdout carries the protocol stream, not human output, and the global --json flag does not apply to it.

Invocations ​

ShapeCommand
Pinned to one project (sandbox)i18nprune serve --project /path/to/app
Many projects, root per request (hub)i18nprune serve
Windows desktop → project in WSLwsl.exe -d <distro> -- node <cli> serve --project <path>
Desktop → remote hostssh <host> i18nprune serve --project <path>

Both remote shapes are stdio pass-through: no port forwarding, no inbound port on the remote machine.

Flags ​

FlagRole
--project <path>Pin the session to one project root (sandbox mode). Omit for hub mode, where each request names its own root.
--allow-writesOpt in to mutating operations. Default: read-only.
--idle-timeout <seconds>Exit after this much inactivity. Default 900; 0 disables the backstop. ping resets it.

Root modes ​

The two modes are mutually exclusive. In sandbox mode the root is pinned and confined: a request naming a different root is an error, never a silent switch. In hub mode, root is required per request.

Read-only by default ​

Without --allow-writes, a session refuses every mutating method with a SERVE_WRITES_DISABLED error, and the project cache is forced read-only — so even read operations write no cache file or directory. Read-only means no disk writes at all, not merely "no project mutation". Add --allow-writes to enable op.sync, op.generate, op.cleanup, and op.locales.delete.

Protocol ​

Stdio carries newline-delimited JSON: one request, response, or notification object per line. Diagnostics (serve: ready, cancel notices, internal errors) go to stderr.

Handshake ​

Send initialize before anything else and fail closed on an unsupported protocolVersion:

json
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"1"}}

The result reports the server and what it can do:

json
{"jsonrpc":"2.0","id":1,"result":{
  "protocolVersion":"1",
  "serverInfo":{"name":"i18nprune","version":"1.1.1","node":"v22.0.0"},
  "apiVersion":"1",
  "rootMode":"sandbox",
  "projectRoot":"/path/to/app",
  "capabilities":{
    "ops":["config","doctor","validate","languages","providers","sync","generate","cleanup","locales.delete"],
    "progress":true,
    "progressOps":["validate","sync","generate"],
    "writeOps":["sync","generate","cleanup","locales.delete"],
    "cancelScope":"running",
    "writes":false
  }
}}

Feature-detect against capabilities — a method the server cannot honour never appears in ops.

FieldMeaning
opsMethods this server implements.
writesWhether mutating ops are enabled (set by --allow-writes).
writeOpsThe mutating methods, each refused unless writes is true.
progress / progressOpsprogress is true when any op streams $/progress; progressOps names exactly which do. Do not expect progress from an op that is not listed.
cancelScopeCancellation tier: 'running' can abort an op mid-flight; 'queued' only refuses a request cancelled before it starts; 'none' is reserved.

Lifecycle methods ​

MethodKindNotes
initializerequestRequired first. Params { protocolVersion }.
pingrequestReturns {}; resets the idle timer.
shutdownrequestMarks the session as shutting down; awaits exit.
exitnotificationEnds the process. Closing stdin also ends it.

Operations ​

Every operation is op.<name>. Params are { projectRoot?, options? } — projectRoot is required in hub mode and rejected when it differs from the pinned root in sandbox mode.

Results reuse the --json envelope (ok / kind / data / issues / meta), so a client learns one result shape for both the CLI and the server.

Read ops ​

MethodReturns
op.configResolved config snapshot
op.doctorEnvironment + config checks
op.validateKey observations, missing keys, suggestions
op.languagesLocale catalog (no project config needed)
op.providersTranslation backend discovery

Write ops (--allow-writes) ​

MethodReturns
op.syncMerge/prune summary; writes changed locale files
op.generateProgress + translate outcome; writes target locales
op.cleanupRemovable keys; writes the removals
op.locales.deleteDeletes locale segment files

Write ops run best-effort patching and cache invalidation exactly as their CLI counterparts do, and drop that root's cached context afterwards so the next read sees the new files. They are non-interactive: a case the CLI would prompt for (missing --target, resume selection) comes back as an ok: false envelope with issues, never a hang.

Progress ​

Ops whose core path emits run events stream $/progress notifications keyed by request id:

json
{"jsonrpc":"2.0","method":"$/progress","params":{"id":2,"event":{"type":"run.progress.validate","phase":"scan"}}}

Progress is a side channel: it never alters the final result envelope. Only the ops in capabilities.progressOps stream it.

Cancellation ​

Send the $/cancelRequest notification with the target request id:

json
{"jsonrpc":"2.0","method":"$/cancelRequest","params":{"id":2}}

Cancellation works in two windows, and capabilities.cancelScope is 'running':

  • Before dispatch — a request cancelled while queued is refused with SERVE_REQUEST_CANCELLED.
  • Mid-flight — the long translate-pool op (op.generate) accepts a cooperative AbortSignal and checks it at its await boundaries. A cancel unwinds the running op; the partial result is discarded and the request returns SERVE_REQUEST_CANCELLED instead of a result. No partial locale is written.

Short local ops (op.sync, op.cleanup, op.locales.delete) do not need a mid-flight abort and are not cancellation targets — they finish in one dispatch turn.

A cancelled request therefore always answers with an error, never a result the client walked away from.

Errors ​

Errors use the standard JSON-RPC error object, with an i18nprune issue code under error.data.code (e.g. SERVE_WRITES_DISABLED, SERVE_METHOD_NOT_FOUND, SERVE_REQUEST_CANCELLED). See CLI issues for the code catalog.

Example session ​

text
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"1"}}
← {"jsonrpc":"2.0","id":1,"result":{ ... }}
→ {"jsonrpc":"2.0","id":2,"method":"op.validate","params":{"options":{}}}
← {"jsonrpc":"2.0","method":"$/progress","params":{"id":2,"event":{ ... }}}
← {"jsonrpc":"2.0","id":2,"result":{"ok":true,"kind":"validate","data":{ ... },"issues":[],"meta":{ ... }}}
→ {"jsonrpc":"2.0","id":3,"method":"shutdown"}
← {"jsonrpc":"2.0","id":3,"result":{}}
→ {"jsonrpc":"2.0","method":"exit"}