npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ojuri/cli

v1.8.1

Published

Ojuri CLI. Validates the ojuri.yaml deployment manifest and drives the Docker Compose stack.

Readme

@ojuri/cli

The ojuri command. Reads the deployment manifest at ojuri.yaml and, in later phases, renders it into a .env fragment and a Docker Compose overlay and drives the stack.

Eight commands: init, doctor, up, status, down, reset-admin, plus the validate and render they are built on.

Install

npx @ojuri/cli up          # no install at all, in any empty directory
npm install -g @ojuri/cli  # or keep it around as `ojuri`

From a checkout:

cd packages/cli
npm install
npm run build
node dist/index.js validate ../../ojuri.yaml

What an install runs

Everything except FIA. ojuri up in an empty directory writes a manifest whose defaults start Postgres, Redis, Kafka with Zookeeper, one RDA replica behind NGINX, the PAA singleton, the Sentinel dashboard, MLA, and Prometheus with Grafana.

FIA is the one service off by default, because its language model is a 7.6 GB download on first start and it wants 16 GB of RAM. Set services.fia.enabled: true and run ojuri up again.

Three fields differ from what a bare docker compose up produces, because Compose cannot switch a profile on by itself and its own RDA_REPLICAS default is 3:

| Field | Manifest default | Bare compose | |---|---|---| | services.rda.replicas | 1 | 3 | | services.mla.enabled | true | off (mla profile) | | services.sentinel.enabled | true | off (sentinel profile) |

Everything else in the manifest holds the compose file's own value, and CI pins that: a manifest carrying the compose defaults has to render to an empty overlay and a docker compose config byte-identical to the quick start's.

ojuri validate [path]

Checks a manifest and says what is wrong with it. With no path it reads ./ojuri.yaml.

ojuri validate                  # human-readable
ojuri validate --json           # machine-readable, for CI

Exit code is 0 when the manifest is usable and 1 when it is not. Warnings are printed but never change the exit code, so a fresh checkout, which has three of them, still passes.

Two layers run in order. The JSON Schema in schema/ojuri.v1.json decides whether the file is well-formed: unknown fields, bad types, out-of-range replica counts, connection fields on a bundled datastore. If that passes, the semantic rules decide whether the manifest describes a stack that will actually work.

What the rules catch

| Finding | Severity | Why | |---|---|---| | paa-replicas | error | PAA holds the transaction graph in process memory. A second replica takes half the Kafka partitions and builds its graph from half the traffic, so rings spanning both stop being visible. Nothing fails loudly. | | mla-replicas | error | MLA keeps its retrain cooldown in process memory and holds no leader lease, so two copies can retrain at once and write over each other in the shared models/ mount. | | fia-replicas | warning | Safe, because each replica owns whole partitions and report writes are idempotent. The cost is roughly 16 GiB of RAM per copy. Rendering drops FIA's fixed host port when there is more than one. | | fia-enabled | warning | Roughly 10 GB of disk, 16 GB of RAM, and a 7.6 GB download on first start. | | predict-unauthenticated | warning | require_api_key: false leaves POST /v1/predict open to anything that can reach the port. | | prod-api-key, prod-jwt-secret, prod-cors | error in production, otherwise warning | Mirrors RDA's own warnIfUnsafeDefaults(). RDA refuses to boot with NODE_ENV=production while any of these hold, unless ALLOW_UNSAFE_PROD_DEFAULTS=true. Catching it here means finding out before the containers start rather than from a crash loop. | | unresolved-reference | error | An external datastore whose ${VAR} never resolved would render a compose file pointing at the literal text. | | unresolved-reference-optional | warning | Same, for a field the stack can start without, such as an external Redis password. |

Resolving ${VAR}

Any string in the manifest may hold ${VAR} references. They resolve from the process environment first and then from the .env file beside the manifest, which is the order Compose itself uses, so exporting a variable in your shell overrides the file for both.

An unresolved reference is left in place as its literal text so the document still checks against the schema, and reported separately.

Development

npm run build     # tsc into dist/
npm test          # jest
npm run lint      # eslint

Specs live under test/, with manifests under test/fixtures/. The build tsconfig compiles src/ only; tsconfig.test.json adds the specs back for ts-jest.

test/default-manifest.spec.ts pins the committed ojuri.yaml at the repo root against the default.yaml fixture and against the values in docker-compose.yml. If you change the default stack, that spec is where it will complain.

ojuri render [path]

Turns the manifest into two files under .ojuri/, and prints the exact Compose command that uses them.

ojuri render                    # write .ojuri/
ojuri render --print-command    # print the command, write nothing
ojuri render --build            # command for building from source
ojuri render --out-dir build/   # somewhere other than .ojuri/

It validates first and refuses to render a manifest with errors: a rendered stack built on a broken manifest is worse than no stack, because it looks like it worked. Warnings do not block.

What it writes

.env.rendered holds the variables Compose substitutes that the manifest controls: OJURI_VERSION, RDA_REPLICAS, RDA_REQUIRE_API_KEY, SENTINEL_CORS_ORIGINS, and MLA_HEALTH_URL when MLA is enabled. Your own .env is not touched. Compose reads repeated --env-file in order with the last winning, so the printed command passes yours first and this one second.

docker-compose.override.ojuri.yml carries only what the manifest changes:

  • External datastores. The bundled service is removed with !reset, and every dependant's depends_on is rebuilt with !override, which replaces the map rather than merging into it. Without that second step Compose rejects the project: service "rda" depends on undefined service "postgres". The connection details are then written per service, because docker-compose.yml hardcodes postgres, redis and kafka:29092 as literals that nothing in .env can redirect.
  • Observability off. Prometheus and Grafana carry no Compose profile, so they are removed rather than withheld.
  • Replicas. RDA scales through RDA_REPLICAS and nothing else. The others get deploy.replicas, and a service with a fixed host port loses it above one replica, since 9094 cannot be published twice.
  • Sentinel. The shipped nginx.conf routes / to RDA, so enabling the profile alone starts a container nothing reaches. The nginx config bind mount is repointed at nginx/nginx.sentinel.conf, which is the same file with / going to Sentinel and an explicit /v1/ carrying the API routes that used to fall through.

The no-op property

A manifest holding the compose defaults renders an empty overlay and an .env.rendered whose values match .env.example exactly. The resolved Compose project is then byte-identical to the README quick start's.

That is the whole point of the manifest layer, so it is enforced rather than asserted: .github/workflows/ci.yml renders test/fixtures/bare-compose.yaml and diffs docker compose config both ways. The same job then renders the committed ojuri.yaml and fails if it changes anything beyond the three fields above. docker compose config needs no Docker daemon, so the job runs in seconds.

One function, two commands

SENTINEL_CORS_ORIGINS is derived by derivedCorsOrigins() in src/manifest/cors.ts. validate checks that value against RDA's production guard and render writes it into the stack. If the two ever computed it differently, validate would pass a manifest that renders to a stack RDA refuses to boot. test/render.spec.ts pins them together.

ojuri init

Writes ojuri.yaml and a .env, and refuses to overwrite either.

The .env is a copy of .env.example with the development defaults replaced by generated secrets: AUTH_JWT_SECRET, POSTGRES_PASSWORD and ADMIN_SEED_PASSWORD. Pass --keep-dev-defaults to copy it verbatim instead.

Two things about that are worth knowing.

POSTGRES_PASSWORD, DB_PASSWORD and the password inside DB_URL all move together. The container takes the first; host-side tooling reads the other two. Changing one alone leaves a checkout where the stack starts and npm run db:migrate cannot authenticate against it.

ADMIN_SEED_PASSWORD only takes effect on a fresh database. The admin user is created inside a migration, so on a database where that migration has already run the value is inert and the existing admin is untouched. npm run reset:admin is the way in there.

MLA_SERVICE_TOKEN is generated too. RDA accepts it as a bearer credential for models:register and models:set_status, and the development default in .env.example is a published string long enough to clear RDA's 32-character floor, so leaving it would ship a working model-registry credential identical on every install.

ojuri up [path]

Validates, renders, starts the stack, waits for db-migrate to exit cleanly and for RDA to answer /ready through NGINX, then prints every URL it started with what each is for, a runnable curl with a fresh UUID, the admin credentials situation, and, when FIA is off, the two steps that turn it on.

Images are pulled as their own streamed step rather than left to up -d, which on a first run is a silent multi-gigabyte download that reads as a hang.

Switching MLA, FIA, Sentinel or observability off in the manifest and running this again removes their containers. A bundled datastore you have pointed at your own is named rather than removed: its data outlives the container either way, but deleting one is not this command's call. Withholding the Compose profile is not enough: up -d does not mention the container and it keeps running, and --remove-orphans leaves it as well, because a service in an inactive profile is still a defined service. Volumes are kept, so FIA's 7.6 GB model cache survives being switched off.

ojuri up              # pull the published images
ojuri up --build      # build from source
ojuri up --yes        # skip the confirmations

It stops before starting anything if postgres.mode is external, unless given --yes. db-migrate runs seed:run on every boot, and against your own database that is a write to data this command does not own.

What it can tell you about the admin password

The admin user is created by a migration, which prints a banner only when it generated the password itself. Three cases, three answers:

| Migration logs | What up says | |---|---| | Carries the seed banner | The generated password, quoted from the logs. It is not recoverable later. | | Knex reports "Already up to date" | The database already existed; the admin is unchanged. | | Migrations ran, ADMIN_SEED_PASSWORD set | The password is that value, if this run created the database. |

Guessing wrong here sends someone hunting for a password that was never printed, so the fourth case says plainly that it cannot tell.

Why it does not issue the first API key

POST /v1/admin/api-keys sits behind denyIfPasswordRotation, and the seeded admin carries mustChangePassword=true, so the bootstrap credential gets a 423 from every admin endpoint until the password is rotated. Rotating it here would mean this command inventing a password and consuming a deliberate security gate, so when auth.require_api_key is true it prints the two steps instead.

ojuri reset-admin [path]

Issues a new password for the seeded admin and prints it once.

ojuri reset-admin                                   # generate one
ojuri reset-admin --password 'my-chosen-secret'     # or pick it
ojuri reset-admin --username alice --tenant acme    # defaults: admin / default

mustChangePassword is set, so the next login forces a rotation.

It runs inside the RDA container, which carries bcrypt, knex and the connection details, so the stack has to be up. The repository's npm run reset:admin does the same thing from a checkout; this exists because an adopter who installed with npx @ojuri/cli up has no checkout, and no docker compose invocation of their own either, since the compose files live under .ojuri/stack and need the project name and five --env-file / -f flags to address.

ojuri status [path]

Container state from docker compose ps, then a readiness probe per enabled service.

Probes go through NGINX first, /ready for RDA and the proxied /fia/ and /mla/ prefixes for those, falling back to the direct host ports. That is the same both-ways approach scripts/demo-traffic.mjs already takes, and it finds a service run natively alongside a Compose stack.

RDA_HEALTH_URL and friends override the list, but only when actually set. They are commented out in .env.example and describe host-side runs, so treating a comment as configuration would point every probe at a port nothing is listening on.

ojuri down [path]

Stops the stack. --volumes also deletes its data, which needs --yes: that is the Postgres data, the Redis snapshot, the Kafka log, the Grafana dashboards, and the FIA model cache, a 7.6 GB download to rebuild.

ojuri doctor [path]

Read-only host check, with no side effects beyond briefly binding the ports it tests.

  • Docker 20.10+ and Compose 2.24+. The Compose floor is real: the rendered overlay uses !reset and !override.
  • Every host port the manifest's stack will publish, and only those. An external datastore needs none, a scaled FIA gives its port up, and observability off frees 9090 and 3001.
  • External datastores actually accept a connection.
  • With FIA enabled, whether the host has the RAM and disk, scaled by replica count. Always a warning, never an error: FIA_DISABLE_LLM exists and the operator may know something the check does not.