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

norn-cli

v4.0.1

Published

Version-controlled API and database tests. Author in VS Code, then run the same files from the CLI and CI.

Readme

Norn

Norn keeps API tests and database checks in repeatable, version-controlled files your whole team can trust. Author, inspect, and debug them in VS Code, then run the same files from the CLI and CI.

Simple API Requests

Send requests and run sequences

Chain and Debug API Requests

Debug sequences in VS Code

Why Norn

Most API tools split the work across too many places: one app for sending requests, another for test logic, shell scripts for CI, and a pile of copied values between them. Norn keeps that work in plain text files inside VS Code.

That means you can:

  • send single HTTP requests without leaving the editor
  • build reusable sequences with variables, captured values, assertions, waits, retries, and branching
  • debug those sequences with breakpoints and step-through execution
  • run the exact same files from the CLI for smoke tests, regression suites, and pipelines

What You Get

  • .norn files for requests, sequences, and tests
  • .nornenv files for environments and secrets
  • .nornapi files for reusable endpoint definitions
  • .nornsql files for database queries and commands
  • .nornagent files for contract-checked AI agents and model-directed delegation
  • syntax highlighting, IntelliSense, and diagnostics
  • response inspection, JSON diffing, and click-to-generate assertions
  • tagged and parameterized test execution in VS Code and the CLI

Example

var baseUrl = https://api.example.com

sequence Checkout
    POST {{baseUrl}}/auth/login
    Content-Type: application/json
    {
        "username": "demo",
        "password": "secret"
    }

    var token = $1.body.accessToken

    GET {{baseUrl}}/orders
    Authorization: Bearer {{token}}

    assert $2.status == 200
end sequence

This is the core idea: one file can hold the request, the flow, the captured data, and the assertion. When the flow grows, you still stay in text, version control, and normal code review.

In VS Code

Use Norn to:

  • send a request directly from a .norn file
  • run a whole sequence from the editor
  • debug a sequence with breakpoints
  • run test sequences from the Testing view

In The CLI

The CLI uses the same execution model as the extension, so local runs and CI runs stay aligned.

npm install -g norn-cli
norn ./tests/smoke.norn -e dev

.nornenv Templates And Extends

Use [template:name] sections for reusable environment building blocks, then compose selectable [env:name] sections with extends. Only templates can be extended. Templates are not selectable from the VS Code environment picker or CLI; only [env:...] names can be used with -e.

var timeout = 30000

[template:prod]
var baseUrl = https://api.example.com
secret apiKey = prod-key-789

[template:uk]
var dbHost = db.uk.example.com
var bucket = data-uk

[env:prod_uk extends prod, uk]
var failoverHost = api-failover.uk.example.com

Resolution order is common <- template1 <- template2 <- self, so later templates win on collisions and the env section itself wins over everything. The VS Code editor also shows Activate CodeLens actions, inherited-variable peeks, hover resolution chains, and inlay hints for {{name}} / {{$env.name}} references.

Inlay Hints Across Every File Type

Every {{...}} reference in any Norn file shows its resolved value as a gray inline hint under the active env, with the same masking-for-secrets rules as .nornenv. The three scopes available in .norn resolve in precedence order:

  1. Sequence-local var declarations inside the current test sequence ... end sequence block
  2. File-level var declarations at the top of the file (above any sequence)
  3. Active env effective vars (common ← ancestor templates ← self)

{{$env.name}} always reads from scope 3, skipping local/file vars. Runtime values ($1.body.id, request captures, run X() returns) render no hint inline; hover narrates them with source line. .nornapi and .nornsql use env scope only; .nornsql additionally shows the resolved connection string after connection NAME.

Deterministic MCP Tools

Norn can call MCP tools from sequences without leaving the .norn runtime. MCP sessions are deterministic and shared across the full sequence run by default, so nested sequences reuse the same connection for the same resolved server alias.

A server can be declared in two places, and an alias resolves through them in this order:

  1. an mcp <Alias> ... end mcp block in a .nornagent sidecar the file imports — see Contract-Checked Agent Graphs, and prefer this when agents use the server too, because the file then says what it connects to;
  2. mcp.servers in the nearest norn.config.json.

Create a norn.config.json in the root of your project:

{
    "version": 1,
    "mcp": {
        "servers": {
            "localTools": {
                "transport": "stdio",
                "command": ["node", "./tools/mcp-server.js"]
            },
            "remoteTools": {
                "transport": "http",
                "url": "https://mcp.example.com/mcp",
                "headers": {
                    "Authorization": "Bearer {{$env.mcpToken}}"
                },
                "timeoutMs": 5000
            }
        }
    }
}

Use MCP tools directly inside sequences:

sequence ToolFlow
    var tools = run mcp list localTools
    var result = run mcp call localTools summarize_text(text: "hello world", format: "short")

    assert tools[0].name exists
    assert result.structuredContent.summary exists
end sequence

Behavior:

  • run mcp list <alias> returns the full tool list and drains paginated nextCursor responses automatically.
  • run mcp call <alias> <tool>(...) supports named arguments or positional arguments bound in tool-schema order, and returns a deterministic result envelope with content, structuredContent, isError, text, server, and tool.
  • Tool structuredContent is validated against the MCP tool's advertised outputSchema when present.
  • A run-scoped session is closed automatically when the outermost sequence finishes or fails. A sidecar-declared server can narrow that with session agent or session call, which close at their own boundary instead.
  • An imported .norn helper sequence resolves aliases through its own imports, not its caller's — the same rule agent scopes follow.

Contract-Checked Agent Graphs

Define agents in an imported .nornagent sidecar. An agent can grant another agent through agents; the callee then appears to the calling model as an ordinary tool. Its accepts schema constrains and validates the generated tool input, and its returns schema validates the result before it travels back up the graph.

A model block declares the provider, the provider's model, and where the API key comes from. Nothing is implied by convention: apiKey points at a .nornenv variable, so the file says exactly which value it needs and the secret itself never leaves the environment.

model Workbench
    provider openai          # openai, anthropic, google, or local
    name gpt-4o
    apiKey {{$env.OPENAI_API_KEY}}
    # baseUrl {{$env.OPENAI_BASE_URL}}   # optional; required for local
end model

agent DomainExpert
    model Workbench
    describe "Call for domain questions and include the ticket context."
    accepts contracts/domain-question.schema.json
    returns contracts/domain-answer.schema.json
    system "Answer only the supplied domain question."
end agent

agent TicketRouter
    model Workbench
    agents DomainExpert
    system "Consult the domain expert when the ticket needs it."
end agent

Giving agents hands: declared MCP servers

An agent reaches the outside world through MCP servers, and those are declared in the same file, the same way — every input the server needs written down, credentials as .nornenv references:

mcp Browser
    transport stdio
    command npx playwright-mcp --headless --isolated
    session agent            # run (default) | agent | call
end mcp

mcp House
    transport http
    url {{$env.HOUSE_MCP_URL}}                          # the MCP endpoint, path included
    header Authorization: Bearer {{$env.HOUSE_TOKEN}}
    timeout 60000
end mcp

agent FrontendTester
    model Workbench
    mcp Browser                                    # every tool this server advertises
    tools House.getFixtureUser, House.resetTenant  # only these, from this one
    system file prompts/frontend-tester.md
end agent
  • transport stdio spawns the server per run; transport http only dials one. Norn never starts an http server, so it must already be listening — a refused connection fails before any model spend, naming the URL it tried.
  • mcp <Alias> and tools <Alias>.<tool> are deliberately different statements. A granted tool executes when the model asks for it, so the authored grant is the permission boundary: dropping four characters must not silently widen access from one tool to twenty.
  • header writes an HTTP header the way you would write it anywhere else. header Name: value, and the value is the rest of the line taken verbatim — so Bearer {{token}} needs no quotes, and quotes you add become part of the token. The colon is optional but recommended: it is what the request side of Norn uses, and IntelliSense inserts it for you.
  • session decides what state is shared. The default, run, gives every agent in a sequence one session — deliberate for an orchestration, and a trap when a backend agent inherits a frontend agent's login and passes a test that should have failed. session agent is how an agent asks for its own.
  • An alias with no block still resolves through norn.config.json mcp.servers, and a block in the sidecar wins over a config entry of the same name.

Deterministic run mcp steps resolve the same aliases, so a server can be proved reachable with no model and no cost:

import "./agents.nornagent"

test sequence ServersUp
    var tools = run mcp list House
    assert tools.length > 0
end sequence

The runnable demos/mcp-ticket-testing example is the whole shape end to end: a browser agent, a backend agent narrowed on the same custom server, a reporter, and a judge over the expected test cases.

A prompt that outgrows its sidecar can live in its own file instead. describe and system both accept file <path>, resolved relative to the .nornagent file just like an import:

agent TicketRouter
    model Workbench
    agents DomainExpert
    system file prompts/ticket-router.md
end agent

The file's text is the prompt verbatim — no escaping, so quotes and backslashes stay as written — and {{...}} references in it resolve exactly as they do inline. A missing or empty prompt file is a parse error on the directive line.

Run the graph from an ordinary sequence; the same nested contract and retry trace appears in VS Code and the CLI:

import "./agents.nornagent"

test sequence RouteTicket
    var ticket = run readJson "./ticket.json"
    var verdict = run TicketRouter ticket
    assert verdict.text exists
end sequence

Malformed handoffs are returned to the calling model as field-level tool errors so it can correct them. The default guardrails are depth 5, 25 total agent invocations, and a two-failed-attempt contract cap. Configure them globally, per provider, or on an individual agent (most specific wins):

{
    "version": 1,
    "agents": {
        "max_tokens": 16000,
        "max_depth": 5,
        "max_invocations": 25,
        "contract_retries": 2,
        "recording": { "enabled": true },
        "providers": {
            "local": { "max_tokens": 4096 }
        }
    }
}

max_tokens is an output ceiling, not prepaid usage: raising it does not spend tokens by itself. Depth, invocation, and retry limits can permit additional model calls, so tighten those three when controlling run cost. See the runnable demos/agent-workbench example for delegation and a visible contract correction.

Every sequence run that reaches an agent is recorded under .norn-cache/runs/ by default, with a rolling cap of 20 files. Recordings hold the resolved provider request, response, tool transcript, contracts, conversation state, and ordered hop events. Values declared as secrets in .nornenv are written as stable named placeholders and restored from the selected environment when replay starts; a missing value stops replay before a provider or tool can run. Set agents.recording.enabled to false to disable automatic recording.

Replay a recording without model calls, MCP calls, or other sequence side effects using the local CLI:

node ./dist/cli.js replay .norn-cache/runs/<recording>.json
node ./dist/cli.js replay .norn-cache/runs/<recording>.json --json
node ./dist/cli.js replay .norn-cache/runs/<recording>.json --from CriteriaComparer

Pure replay reproduces the stored trace and exits non-zero for failed hops or contracts, so a deliberately copied recording can be used as a no-key CI fixture. --from accepts a unique agent name or canonical path such as TicketRouter[1]/CriteriaComparer[1]; the prefix stays replayed and that hop onward runs live against the current graph.

Norn: Show Agent Graph, or the CodeLens on any agent block, draws the graph for a .nornagent file: who calls whom, the accepts/returns contract on each boundary, and how each one turned out. Hover an agent or a boundary for the full detail — models, timings, usage, prompts, payloads, contract issues.

The graph's toolbar also lists the project's recorded runs, each labelled with the .norn file it came from. Pick one to see how it finished, or press Play run to watch it unfold hop by hop on the canvas. Playback replays the recording's own events, so nothing re-executes and no model is called; press Stop to jump to the end. A run from a file the open one took no part in is drawn from that recording alone — the toolbar says which file it came from, and This file — current view returns you to the open file, ready for its next live run. Recording is on by default and keeps the last 20 runs under .norn-cache/runs/.

To step a recording in VS Code, use the existing norn debugger and add the artifact to a launch configuration:

{
    "type": "norn",
    "request": "launch",
    "name": "Replay RouteTicket",
    "file": "${workspaceFolder}/route-ticket.norn",
    "sequence": "RouteTicket",
    "recording": "${workspaceFolder}/fixtures/route-ticket-run.json",
    "stopOnEntry": true
}

Agent calls appear as nested frames with Request, Response, Contracts, and Conversation scopes. Normal step controls navigate the recorded hop timeline; Norn Debug: Run to Next Contract Failure stops on retry violations as well as final contract failures. With Agent Graph open, the paused invocation is highlighted and selecting a node moves the replay cursor to that invocation.

Good Fit For

  • backend teams validating APIs during development
  • QA and automation work that needs readable test flows
  • regression and smoke suites that should run the same way locally and in CI
  • projects that want API requests and API tests to live next to the code

Diff preview test line.

ping