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

@modeldesk/cli

v0.6.0

Published

Command-line interface (CLI) for the ModelDesk platform

Readme

ModelDesk CLI

The ModelDesk command-line interface pulls a model's generated documentation content bundle into your repository, so your own documentation pipeline (for example ReSpec with GitHub Actions) can render and publish it. ModelDesk generates the content and hands it off; it does not render, host, or publish documents.

Install

The CLI runs on Node.js (version 22 or newer), on Linux, macOS, and Windows. It is plain JavaScript with no native dependencies, so the Node version is the only requirement. Run it on demand with npx, or install it globally.

npx @modeldesk/cli@latest bundle pull --model <modelId> --stack respec --template conceptueel-toc
# or install it once
npm install -g @modeldesk/cli

On Windows, a global install is invoked through the modeldesk.ps1 shim npm generates. If your PowerShell execution policy blocks scripts ("running scripts is disabled on this system"), call modeldesk.cmd, use npx @modeldesk/cli@latest instead, or, if your organization permits it, allow signed scripts for your own account with Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.

Commands

modeldesk bundle pull       Materialize a model's content bundle into the repository
modeldesk bundle templates  List a model's document templates (its stack/template pairs)
modeldesk note add          Create an empty note file for an element and add it to the repo's notes.json
modeldesk auth login        Log in through the browser (OAuth authorization code + PKCE)
modeldesk auth logout       Remove the stored credential for the active host
modeldesk auth status       Show the active host, identity, and token validity

Global options: --help (-h), --version (-v). Commands take named flags; note add also takes the element's name as a positional argument.

Log in

modeldesk auth login opens your browser to the ModelDesk sign-in page. You authenticate there (the CLI never sees your password), and after a successful login the CLI stores a refresh token in ~/.modeldesk/credentials.json (on Windows, %USERPROFILE%\.modeldesk\credentials.json). Later commands run unattended: the CLI refreshes silently and only re-opens the browser when the stored credential lapses.

The token file is readable only by you: on Linux and macOS the CLI sets file mode 0600, and on Windows it inherits the default private permissions of your user profile.

modeldesk auth login --host modeldesk.io
modeldesk auth status    # shows who you are logged in as, and whether the credential is valid
modeldesk auth logout    # removes the stored credential for the host

Authenticate in CI

In a pipeline the CLI authenticates with a service account (org settings → Service accounts), in one of two ways. A machine run refuses to take the host from the committed .modeldesk/config.json, so a PR-editable file can never decide where a credential is sent: against modeldesk.io set nothing (it is the default), against any other instance pass --host or set MODELDESK_HOST, and leave host out of the committed config in a repository that pulls from CI. Model, stack, template, and version still come from the committed config.

GitHub Actions (federated — no stored secrets). Add a trust rule to the service account, grant the workflow id-token: write, and run the CLI; it exchanges the workflow's own OIDC token itself:

permissions:
  contents: read # any permissions block drops the defaults, so restate what the job needs
  id-token: write # lets the workflow request an OIDC token; the CLI does the rest
steps:
  - uses: actions/checkout@v7
  - name: Pull the bundle
    run: npx @modeldesk/cli@latest bundle pull --version 1.0.0

Any other CI (issued secret). Store an issued secret in the pipeline's secret manager and set MODELDESK_CLIENT_ID (the account's UUID) and MODELDESK_CLIENT_SECRET (the mdsk_… value). The CLI mints short-lived tokens itself; nothing is written to disk.

Selection is deterministic (secret vars > GitHub federation > interactive) and a machine run never falls back to browser login — a CI runner without machine credentials fails immediately with guidance. modeldesk auth status verifies the credential with a real mint (exit non-zero on rejection); use it sparingly, since each probe counts as credential use. Never set NODE_DEBUG=http or DEBUG=* in CI: Node's HTTP diagnostics print the Authorization header.

Configuration

The model, stack, template, host, and version each resolve by precedence: a command-line flag, then a MODELDESK_* environment variable, then a committed .modeldesk/config.json, then (for the host only) a built-in default. A configured repository therefore runs modeldesk bundle pull with no arguments, while CI can override any value per build with an environment variable.

The CLI operates on the current working directory: run it from the directory that holds .modeldesk/config.json (typically the repository root). Config in parent directories is not searched, and a pull writes its content and manifest under the directory you run it from.

// .modeldesk/config.json (committed to your repository)
{
  "model": "3f2b…-model-uuid",
  "stack": "respec",
  "template": "conceptueel-toc",
  "host": "modeldesk.io",
  "version": "1.0.0", // optional: pin a released model version
}

| Setting | Flag | Environment variable | Config key | Default | | -------- | ------------ | -------------------- | ---------- | -------------- | | Model | --model | MODELDESK_MODEL | model | (required) | | Stack | --stack | MODELDESK_STACK | stack | (required) | | Template | --template | MODELDESK_TEMPLATE | template | (required) | | Host | --host | MODELDESK_HOST | host | modeldesk.io | | Version | --version | MODELDESK_VERSION | version | live HEAD |

  • model is the model's UUID (copy it from the model's "Command-line access" panel in the app).
  • host is your instance's domain — modeldesk.io resolves to https://api.modeldesk.io and https://auth.modeldesk.io. Use localhost against a local development stack.
  • stack and template together identify the document shape to pull. A template slug is unique only within its stack, so both are required — pass --stack/--template, set the env vars, or commit them. Run modeldesk bundle templates to list the valid stack / template pairs for a model.
  • Machine credentials (MODELDESK_CLIENT_ID, MODELDESK_CLIENT_SECRET) are read from the environment only and are never placed in the committed config. In a machine run the host must also come from --host or MODELDESK_HOST (see Authenticate in CI).

Two more keys serve notes. They are read from the config file only, and each folder is a path relative to the repository root:

  • notesDir (default md-notes/ at the repository root): the notes folder. A pull places every Markdown file in it as a note, by its path, and note add creates note files in it; --dir overrides it for one note. It cannot be the repository root, lie in the content root or hold it, or be a symlink.
  • publishDirs (no default): the folders your document's publish step copies, such as ["data", "media", "js", "css"] for Geonovum's NL-ReSpec-template. A ReSpec snapshot inlines the note Markdown, but an image a note links must sit in a copied folder; with publishDirs set, a pull warns about every image outside them.

bundle pull

A pull is a full sync of the content root (for ReSpec, data/model/). It writes every generated fragment, removes files a previous pull left orphaned, and renders and fetches the per-diagram images, streaming the render progress to your terminal. The whole pull is pinned to a single commit, so a moving HEAD cannot mix fragments. Text is written first: a failure in the slower diagram phase leaves valid text on disk and exits non-zero, and the pull stays re-runnable.

modeldesk bundle pull                 # in a configured repository (model, stack, template from config)
modeldesk bundle pull --model <modelId> --stack respec --template conceptueel-toc --host modeldesk.io

The content root is CLI-managed. Everything under the content root (data/model/) is owned by the CLI: hand edits there are overwritten, and stale files are removed. Keep hand-authored prose outside the content root (your template references the generated files with data-include). Run --dry-run first to preview exactly what a pull would write, overwrite, and remove.

Flags

| Flag | Effect | | ---------------- | --------------------------------------------------------------------------------- | | --model <id> | The model's UUID (required, unless set via env or config). | | --stack <id> | The content stack to render (required; see bundle templates). | | --template <s> | The document shape within the stack (required; see bundle templates). | | --host <host> | Target a specific instance (default: modeldesk.io). | | --version <n> | Pin a released model version for a reproducible pull (default: live HEAD). | | --dry-run | Preview the plan (writes, overwrites, removals, diagram count) and touch nothing. | | --no-diagrams | Write text fragments only; skip the diagram render and fetch (a fast inner loop). | | --adopt | Allow the CLI to prune a content root it did not previously manage. | | --yes | Skip the confirmation for an unusually large removal set. | | --allow-empty | Permit a pull that returns no fragments (a genuinely emptied model). |

--no-diagrams is convenient while iterating on text, but any diagram data-include will 404 at bake time until you run a full pull.

Keep the content root LF. The CLI always writes fragments with LF line endings. On Windows, Git's core.autocrlf=true rewrites them to CRLF on checkout, so every pull would then show a diff on files whose content never changed. Pin the content root in .gitattributes:

data/model/** text eol=lf

Ignore the manifest. Each pull records what it wrote in .modeldesk/<stack>.manifest.json (for ReSpec, .modeldesk/respec.manifest.json). It is regenerated every pull and is used for precise cleanup and rename hints, so add .modeldesk/*.manifest.json to your .gitignore — committing it is unnecessary and a stale committed copy can only mislead a later pull.

Portable file names

Generated paths are safe to check out on any operating system. Element and diagram names that would otherwise produce a name Windows reserves for a device (con, prn, aux, nul, com0-com9, lpt0-lpt9) get a short id suffix automatically, exactly as any other name clash does, so no action is needed. The one case that cannot be resolved automatically is a custom metamodel type whose name slugs to a reserved word: a type name becomes a folder shared by every element of that type, so there is nothing unique to suffix it with. The bundle then fails to build with an error naming the type to rename.

Safe removal

A pull's one destructive step — removing orphaned files — is guarded so a misconfiguration cannot delete files the CLI did not create:

  • A pull that returns no fragments (usually a wrong model or template) writes and deletes nothing and exits with an error; use --allow-empty only for a genuinely emptied model.
  • In a repository the CLI already manages, it removes only files it recorded writing.
  • A non-empty content root that the CLI has not managed before is not pruned until you confirm it (interactively, or with --adopt in CI).
  • An unusually large removal set asks for confirmation (or --yes).
  • All writes and removals stay within the content root; a manifest path that tries to escape it is refused.

The removal list is always printed. In CI, run a --dry-run first as a safety gate.

bundle templates

Lists the document templates available for a model — the shapes you can pass to --template. The output is a table: the content STACK, the TEMPLATE slug (the --template value), and its display NAME. A stack (for example respec) is the render engine and content root a template produces for; a template slug is unique only within a stack, so the stack namespaces it — read a row as stack / template.

modeldesk bundle templates --model <modelId> --host modeldesk.io
STACK   TEMPLATE         NAME
respec  conceptueel      MIM conceptueel
respec  conceptueel-toc  MIM conceptueel (TOC hiërarchisch)
respec  logisch          MIM logisch
respec  logisch-toc      MIM logisch (TOC hiërarchisch)

Take a STACK / TEMPLATE pair from the table straight into a pull. Pass --stack <id> to list only the templates of one stack.

note add

A note is your own Markdown under a generated element, a package, or the model root, kept outside the content root so a pull never removes it. intro sits after the element's heading and diagrams, after at the end of the element's own content (before nested subtypes).

A note file names its element by its path: the element's generated file under data/model/, with the notes folder (notesDir, default md-notes/) in its place, ending in .after.md for the after slot. So md-notes/bestuurlijk-gebied/objecttype/gemeentegebied.md is the intro note of the object type whose file is data/model/bestuurlijk-gebied/objecttype/gemeentegebied.md, and md-notes/bestuurlijk-gebied/index.after.md the after note of that domain. Creating the file is all an editor does, in the GitHub web editor if need be.

Every pull, CI's included, matches new note files against the manifest the server sends and records them in .modeldesk/notes.json (committed: add .modeldesk to the workflow's commit beside data/model), keyed by element id so a rename keeps the note in place. An entry can also name a file elsewhere, written by hand:

{
  "notes": {
    "dafddbaf-9e2a-4eda-88f7-9fae3a47362f": {
      "name": "Gemeentegebied",
      "intro": "md-notes/bestuurlijk-gebied/objecttype/gemeentegebied.md"
    },
    "125cd721-c388-49ef-bc26-d9ee56964357": {
      "name": "Domein Bestuurlijk gebied",
      "intro": "data/model-overzicht.md",
      "after": "md-notes/bestuurlijk-gebied/index.after.md"
    }
  }
}

name is refreshed by every pull and read by nothing. A package's or the model's after note comes under a heading of its own, which becomes a numbered section in the table of contents; the note names it on its first line, <!-- heading: Afsluiting -->, an HTML comment the document does not show.

Note paths are relative to the repository root, outside data/model/, and free of .., %, #, ?, backslashes and leading or trailing spaces. Editing a note's text needs no pull; adding or removing one, or changing a heading line, does. Notes need a server that renders the respec stack at 1.4 or later with a MIM template; a bundle without any note slot fails the pull once, naming its stack version.

modeldesk note add <name> [--slot intro|after] [--heading <text>] [--dir <dir>] [--element <id>] [--stack <id>]

note add creates the note at its path under --dir or notesDir (holding only the heading line for a container's after, empty otherwise) and adds its entry to notes.json. It finds the element in the last pull's manifest, so run bundle pull first; it contacts no server. A name several elements share is asked about on a terminal and refused elsewhere, listing the ids for --element. --heading is required for a package's or the model's after slot and refused elsewhere. It never overwrites a file, replaces a filled slot, or names a file another entry names.

On pull, the CLI checks notes.json and the files it names before contacting the server. Once the bundle has arrived, it stops before writing on a note file that matches no element (naming close paths), a note file for a slot another file already fills, a missing note file, a path inside the content root, a slot the element does not carry, or a container's after note without a heading line. It warns on an element not in the bundle (the entry is kept), a heading line in a note that takes none, a missing image, an image outside publishDirs or without alt text, a heading text two notes share without an explicit {#id}, and one file filling several slots. --dry-run prints an adopt <file>: <title> <slot> line per new note file and a fill <title> <slot>: <path> line per placed note, and writes nothing.


Learn more at modeldesk.io.