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

@m6d/cortex-cli

v0.21.0

Published

Scaffold Cortex projects and manage a Cortex deployment

Readme

@m6d/cortex-cli

The cortex command line: scaffold a Cortex project, and manage a Cortex deployment from the terminal.

bunx @m6d/cortex-cli new my-app   # from nowhere — scaffolds a project
bunx cortex agents list           # against the Cortex you logged in to
bunx cortex apply                 # make it match ./cortex.manifest.ts

Commands are noun-first, cortex <resource> <procedure>, except new, which acts on no existing resource, setup and apply, which act on a whole deployment, and login, logout and status, which act on your profiles. Run cortex --help, then cortex <resource> --help.

bun add -d @m6d/cortex-cli

Requires Bun. The binary re-execs itself under Bun when Node started it, and tells you how to install Bun when it is genuinely absent.

cortex new

Scaffolds ./<name>: a backend for Cortex agents, and Cortex itself to run them on.

  • server.ts is a Cortex server built with defineServer from @m6d/cortex: one variable (userName) and one native tool (rollDice), mounted at /cortex by index.ts.
  • cortex.manifest.ts declares a helper agent on that server, for cortex apply.
  • docker-compose.yml runs Cortex's app, worker and engine on the one syscodeia/cortex image, with Postgres, Redis and MinIO. Only the app and the engine publish host ports. They and the backend's port are picked at scaffold time from what was free.
  • .env holds every setting: Cortex's secrets, the backend's key and name, the model provider, and the CORTEX_URL and CORTEX_KEY the CLI reaches this checkout's Cortex with. It is not committed: setup.ts writes it from .env.example on every bun install, generating each secret left blank, so a fresh clone gets its own secrets and a stack that never shares the original's volumes. .dockerignore keeps both out of images.
  • bun run dev starts Cortex (docker compose up), the backend and bun cortex setup --wait --yes in an mprocs dashboard. bun run dev:agent runs the same through concurrently, one labelled log for coding agents and CI.
  • oxlint, prettier, cspell, fallow and tsconfig are inlined, so bun run verify works standalone.

It then runs bun install, and git init unless the target is already inside a work tree. An existing non-empty directory is an error. So is a name like cortex or cortex., since the manifest takes its id from the name and Cortex keeps the id cortex for its own.

bunx @m6d/cortex-cli new shop --datasets

On a terminal it asks only what the flags left open: the name, whether to set up the helper agent on the first bun run dev, and if so the model provider (name, base URL, API key, model). It checks the provider with one chat message to the model, and reuses it for embeddings when it serves the stack's embedding model. With no terminal (stdin and stderr) to ask on, it never prompts and behaves as --yes, so a missing name is a hard error rather than a hang.

| Flag | Default | Writes | | ------------------------ | ------- | ------------------------------------------------------------------------------------------- | | --datasets | off | datasets.ts, an in-memory orders table served as a dataset host, and its manifest entry | | --setup / --no-setup | on | cortex setup among the processes bun run dev starts | | --yes | | Accept the defaults instead of asking; setup then runs with no model provider |

The image runs at the Cortex release that was current when this CLI was published (CORTEX_VERSION in the shell picks another); the backend depends on the @m6d/cortex release of that time. The server is registered under the project name as the manifest id takes it: lowercase words joined by hyphens, cut to Cortex's 64 characters.

cortex setup

Registers a cortex new project's backend in its Cortex, then applies its manifest. It reads the backend's name, URL and key (CORTEX_SERVER_*) and the model provider (MODEL_PROVIDER_*) from .env, and goes in the one order Cortex accepts: the provider's key and the provider, the server key's secret and the server on the provider's model, a sync of the server, then the apply. Each step creates what is missing and keeps what exists, so a second run changes nothing. A key's secret is named for its value (openai-api-key-1a2b3c4d), so a changed key gets a new secret the provider or server moves to, and whatever else presents the old one keeps it. A provider or server under .env's name that calls another URL belongs to something else: setup leaves it alone and stops. So does a MODEL_PROVIDER_* set only in part, or a provider URL on this machine or a private network that EGRESS_ALLOWED_HOSTS in .env does not admit. A server with no model yet gets the provider's.

bun cortex setup              # asks for a model provider when .env has none
bun cortex setup --wait --yes # what `bun run dev` runs

--wait waits up to 10 minutes for Cortex to answer, long enough for a first start to pull the image. Without -p, setup sets up the Cortex .env names: a different CORTEX_URL exported in the shell, which Bun lets win over .env, ends the run. The retried sync, a minute while Cortex cannot reach the backend, gives up at once on any other refusal. With no model provider and --yes, it registers the server without a model and still applies, since an agent may name its own; when Cortex refuses an agent for want of one (chat_model_required), nothing is applied and setup says how to add a provider. The apply deletes what the manifest created and no longer lists, so like cortex apply it asks on a terminal before it writes anything, and needs --yes without one. A field the manifest changes that was also edited live is asked about on a terminal. Under --yes, as bun run dev runs it, such a conflict stops every run until bun cortex apply --yes --prefer live (or manifest) settles it. The run that first creates something says how to sign in, with the password only on a terminal.

Management commands

Manage a Cortex deployment from the terminal. The commands come from Cortex's own management API (/api/management/openapi.json), fetched on each run and cached, so the tree always matches the deployment you point at and a new procedure needs no CLI release.

cortex login                             # asks for the URL and a management key
cortex status
cortex agents list
cortex agents create --name support --displayName '{"en":"Support"}' --serverId <uuid>
cortex tools create --json tool.json --publish
cortex tools list -o json | jq '.items[].name'
cortex logout

Profiles live in ~/.cortex/profiles.json (owner-only). -p, --profile <name>, a global option, picks one for any command, and names the one login saves and logout forgets (default otherwise). CORTEX_URL and CORTEX_KEY, both set, win over the current profile, for CI and for a project's .env; a profile -p names wins over them. CORTEX_HOME relocates the directory.

The grammar is cortex <router> <procedure> [id] [flags], with routers as the API names them (datasetHosts, modelProviders). Each top-level input field is a flag typed from the API's schema; nested values take JSON, and --json <file|-> supplies a whole body that flags then override. A required field left out, id included, is asked for on a terminal and named as the missing flag or positional otherwise. Listings take --pageIndex, --pageSize, --q, and --all to walk every page. -o json prints the raw result for pipes.

Writes to agents, tools, services, skills, triggers, MCP servers, and dataset hosts save drafts. --publish on a write the API marks publishable (update, and a create whose result is complete enough) runs that entity's publish procedure right after; without it the command says so and names the command. Fields the API marks live (x-live-fields) take effect without a publish, from agents' next turn, and the command names them: a trigger's credentialName, fireCredentialName and model, say, or a dataset host's url, headers and timeoutMs. cortex datasetHosts sync <id> reads a host's manifest, and a draft host publishes only after datasetHosts test reached it once.

A procedure the API marks destructive (x-destructive: delete, publish, rotate, toggle and the like) asks before anything is sent, and so does a --publish that runs one. --yes skips the question. With no terminal to ask on, the command fails unless --yes is given, so scripts and CI pass it. Questions render on stderr, so -o json output stays parseable.

cortex apply

Makes Cortex match a manifest, in one transaction: what is new is created, what changed is updated and republished, and what the manifest created and does not list is deleted. Rows it did not create are never touched, and a name one of them holds fails the apply.

cortex apply                             # ./cortex.manifest.ts
cortex apply deploy/manifest.json --dry-run
cortex apply -p prod --yes               # no question, as CI needs
cortex apply --prefer live               # keep every live edit the manifest also changed

An entry's overridable lists the fields people may change in Cortex, such as an agent's prompt. An apply keeps those edits. When the manifest changes a field that was also edited live, cortex apply asks which to keep for each one. --prefer live or --prefer manifest answers them all. --yes never asks: under it, as without a terminal, an apply with conflicts and no --prefer changes nothing and lists them. --dry-run marks each with !.

The path defaults to exactly ./cortex.manifest.ts in the directory you run in. TypeScript and JSON load the same way: the file's default export is the manifest. defineManifest from @m6d/cortex types one as you write it:

import { defineManifest } from "@m6d/cortex";

export default defineManifest({
    id: "shop",
    tools: [
        {
            type: "http",
            name: "findOrder",
            description: "Find one order by id.",
            url: "https://shop.example.com/orders/{orderId}",
            method: "GET",
            readOnly: true,
            inputSchema: {
                type: "object",
                properties: { orderId: { type: "string" } },
                required: ["orderId"],
            },
            audience: { agents: ["support"] },
        },
    ],
});

An openapi source makes every operation of an OpenAPI document a tool the manifest owns, so a backend with many endpoints needs no entry per tool:

openapi: [
    {
        name: "shop",
        url: "https://shop.example.com/openapi.json",
        headers: [{ header: "Authorization", prefix: "Bearer ", from: { kind: "runToken" } }],
        // For an API that wraps every answer: where each one keeps its result.
        // resultPath: "/data",
        pinned: ["findOrder"],
        // A GET runs at once; any other method waits for the end user's approval.
        approval: { never: ["addOrderNote"], always: ["exportOrders"] },
        audience: { agents: ["support"] },
    },
],

Every apply reads the document first, without the source's headers. An operation it adds is created, a changed one updated, and a dropped one deleted. A source whose headers carry a credential or the run token is read only over https, or from a host the deployment allows, since the document says where they go. A source needs the tools write permission even when nothing changes. Each tool is named after its operationId, made an identifier of at most 64 characters; an operation Cortex cannot import is left out. Two operations that come out with one name are told apart by a number (getOrders, getOrders2) in the order of their paths. The apply fails when pinned or approval names a tool no operation has (unknown_tool) or any name such a collision decided (ambiguous_tool), since a path the document adds or drops could move it to another endpoint. It also fails when an operation's name is already a listed tool's or another source's.

A collection lists its documents. Each is inline text, a file path relative to the manifest file, or a url Cortex reads on every apply, with no credentials:

collections: [
    {
        name: "Policies",
        audience: { agents: ["support"] },
        documents: [
            { name: "faq.md", text: "# FAQ\n\nReturns take 30 days." },
            { path: "./docs/returns.pdf" },
            { url: "https://shop.example.com/policies/shipping.html" },
        ],
        // overridable: ["documents"],
    },
],

A document's name sets its type: .md, .txt, .html, .htm, .pdf or .docx. A path or URL document is named after its file unless name says otherwise. Each is at most 10 MB. cortex apply reads paths and sends their bytes, so a request carries every inline and path document again; Cortex stores only what changed. A document with new bytes replaces the old one, which keeps answering until the new one is processed, and one the manifest drops is retired.

Listing documents makes the manifest own them: an apply retires a document uploaded in the console, the first apply after upgrading included, unless the collection offers documents in overridable. Offered, a document uploaded or removed in Cortex stays so until the manifest changes that same document, which cortex apply then asks about.

The manifest is checked against the contract before anything is sent, then sent as written, so Cortex fills its own defaults. It goes to the management API (manifests/sync, or manifests/preview with --dry-run, which changes nothing) under the same profile the management commands use, so the key needs manifests:apply and the write permission of every kind the apply changes. The output lists each changed entry under its kind: + created, ~ updated, - deleted. A trigger the apply creates, or whose fireCredential it drops, gets a fire secret, printed once below the summary and never again.

Since a sync deletes, cortex apply asks before sending it. --yes skips the question, and is required with no terminal to ask on. --dry-run never asks.

References between entries (mentions, service scopes, which role a credential or secret plays) are judged on the state the apply leaves, so the order the manifest lists them in does not matter. A dataset host's headers are the exception: its catalog is read with them before the apply, so they are judged on the deployment as it stands. A credential a trigger fires with cannot go to a host in the same apply that drops it; drop it first, then apply again.

Global options

| Option | Description | | ---------------------- | -------------------------------------------- | | -v, --verbose | Print full errors and the commands being run | | -p, --profile <name> | Which saved profile to use | | -V, --version | Output the version number | | -h, --help | Display help for command |

Exit codes

| Command | 0 | 1 | | ------- | ---------------------------------------------------------- | --------------------------------------------------------- | | new | Files written — even if bun install failed | Bad target, missing name, cancelled | | setup | Set up, or the server registered with no model to apply on | Not a project, Cortex refused or unreachable, or declined | | apply | Applied, or previewed with --dry-run | Invalid manifest, refused, or declined | | others | The procedure ran | It failed, or couldn't run |