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

loxberry-client-library

v1.2.0

Published

TypeScript client for LoxBerry 3.x: automate plugin zip upload, install, and uninstall (same flows as the stock admin UI), JSON-RPC, CLI, optional MCP server.

Readme

loxberry-client-library

CI Release npm License: MIT Conventional Commits semantic-release Beerware

Releases to npm and the changelog are driven by semantic-release on main using Conventional Commits — see RELEASING.md and CONTRIBUTING.md. Beerware is an informal thank-you on top of MIT (below).

Monorepo

This repository is an npm workspaces monorepo: the root package loxberry-client-library (library + CLI) and packages/loxberry-client-mcp (loxberry-client-mcp on npm — MCP server). Each package has its own package.json, version line, and release notes; CI and Release workflows run from the repo root. See RELEASING.md for how the two packages are published.

TypeScript client for LoxBerry 3.x (3.x-first): JSON-RPC (/admin/system/jsonrpc.php), HTTP Basic on /admin (same as stock htmlauth/.htaccess), and helpers aligned with plugininstall.cgi (plugin list, upload with SecurePIN, uninstall). Includes a small CLI and an optional MCP server package.

Purpose

When you develop a LoxBerry plugin, validating each build usually means manual work: open the appliance in a browser, go to Plugin management, upload your .zip, wait for plugininstall.pl to finish, test, then uninstall — often logging in again for every iteration.

This project automates those same stock flows (multipart upload, install-log polling, uninstall confirmation) over HTTPS from Node.js, so you can wire repeatable pipelines on Windows, macOS, or Linux:

  • package.json scripts — e.g. build → zip → upload → wait → test → uninstall without touching the UI.
  • CI — run install/uninstall checks on a dev appliance from GitHub Actions or another runner.
  • AI-assisted workflows — the optional loxberry-client-mcp server exposes LoxBerry operations as MCP tools so assistants (Cursor, VS Code, …) can drive the same steps with your approval.

Nothing replaces reading LoxBerry’s own docs for plugin packaging; this library only automates HTTP interaction with the existing admin and JSON-RPC endpoints.

Table of contents

Ways to use this project

You can integrate with LoxBerry in three complementary ways — often library + CLI for plugin development, or MCP when you want an AI assistant to drive the same operations.

| How | npm package | What it is for | |-----|-------------|----------------| | Client library | loxberry-client-library | Automate JSON-RPC, plugin list / upload / uninstall, and install-log polling from Node.js or TypeScript — ideal for tests, CI, and plugin tooling (build a zip, upload, verify, tear down). | | CLI | same package (binary loxberry-client) | Shell-friendly commands (plugins list, plugins deploy, plugins upload, plugins uninstall, jsonrpc call, …) without writing code. | | MCP server | loxberry-client-mcp | Model Context Protocol stdio server for editors such as Cursor / VS Code — exposes LoxBerry operations as tools for assistants. |

Plugin development and testing: the library matches stock plugininstall.cgi behavior (multipart upload with SecurePIN, uninstall confirm flow, temp install log polling). That lets you repeat upload → install → uninstall cycles from scripts or tests instead of only manual clicks — see Development → live tests (test:live:full) for an end-to-end example.

Install locally (in another project)

Add the core package as a dependency so versions are pinned in package-lock.json:

npm install loxberry-client-library

Use npx loxberry-client … from that project for CLI commands, or import { … } from "loxberry-client-library" in code (Quick usage).

CLI: npx vs local vs global

  • npx loxberry-client (after a local npm install loxberry-client-library) — runs the CLI from your project’s node_modules without a global install. Good for CI and team repos.

  • Global CLI — one install, loxberry-client on your PATH everywhere:

    npm install -g loxberry-client-library
    loxberry-client --help

    Use when you want the same commands in any directory without a package.json.

MCP server install

The MCP package is separate on npm (same GitHub monorepo). Install it like any other tool:

npm install loxberry-client-mcp
npx loxberry-client-mcp

or, globally: npm install -g loxberry-client-mcp then loxberry-client-mcp. Configuration (env vars, IDE mcp.json) is covered in MCP server and packages/loxberry-client-mcp/README.md.

Install

Core library + CLI (this repository’s main package):

npm install loxberry-client-library

MCP server (optional; separate package):

npm install loxberry-client-mcp

See Ways to use this project for when to use each and local vs global CLI.

Quick usage

import { LoxBerryClient, SessionAuth } from "loxberry-client-library";

const baseUrl = "https://loxberry.local";
const session = new SessionAuth();
// Default strategy "basic": same user/password as the browser admin (Apache Basic on /admin)
await session.login(baseUrl, "admin", "secret");

const client = new LoxBerryClient({ baseUrl, session });

const ms = await client.call("LBSystem::get_miniservers", []);
const plugins = await client.plugins.listInstalledPlugins();

From the shell, plugins deploy --project . is the usual plugin-author loop: it picks the newest dist/loxberry-plugin-*.zip, reads FOLDER= from plugin.cfg, uploads, waits for install + list row, and tolerates a known “md5 changed though something threw” quirk. Pair with plugins uninstall --name <folder> (folder or md5 — the CLI resolves folder → pid when needed).

MQTT broker hints (optional, JSON-RPC only)

fetchMqttConnectionDetails is exported from loxberry-client-library/mqtt. It calls LoxBerry JSON-RPC (mqtt_connectiondetails with fallbacks) and returns host/port/user/pass hints. This repo does not ship or depend on an MQTT client — connect with whatever library you use (mqtt, WebSocket, etc.).

import { fetchMqttConnectionDetails } from "loxberry-client-library/mqtt";

const hints = await fetchMqttConnectionDetails(client);
// e.g. build an URL from hints?.brokerhost, hints?.brokerport, …

Architecture (overview)

Stock LoxBerry 3.x serves the admin UI and APIs under /admin behind HTTP Basic (Apache htmlauth). This library sends the same credentials on each request and talks to jsonrpc.php, plugininstall.cgi, and related endpoints.

flowchart LR
  subgraph dev["Your machine"]
    CLI["loxberry-client CLI"]
    LIB["LoxBerryClient"]
    MCP["loxberry-client-mcp"]
    CLI --> LIB
    MCP --> LIB
  end
  subgraph lb["LoxBerry appliance"]
    ADM["/admin htmlauth"]
    RPC["jsonrpc.php"]
    PLG["plugininstall.cgi"]
    LOG["logfile.cgi / install log"]
    ADM --> RPC
    ADM --> PLG
    ADM --> LOG
  end
  LIB -->|"HTTPS + Basic / session"| ADM

After plugins upload, the stock UI polls a temp install log URL; the library exposes followPluginInstallTempLog() for the same behavior before waitForPluginFolder().

CLI

The CLI reads process.env only (no bundled .env loader). After build, or via npx once published, run loxberry-client --help.

To load a file locally, use Node 20.6+ --env-file, for example:

node --env-file=.env ./node_modules/loxberry-client-library/dist/cli.cjs plugins list

(or set LOXBERRY_* in your shell / CI secrets).

Auto-generated from src/cli-reference.ts — run npm run docs:sync-cli after changing commands.

Global flags

| Flag | Description | |------|-------------| | --help / -h | Print help and exit. | | --baseUrl <url> | LoxBerry base URL (overrides LOXBERRY_BASE_URL). | | --user <name> | Admin user (overrides LOXBERRY_USERNAME). | | --password <secret> | Password (overrides LOXBERRY_PASSWORD). | | --file <path> | Path to a .zipplugins upload (required), or plugins deploy (optional: newest dist/loxberry-plugin-*.zip if omitted). | | --project <dir> | Plugin project root (contains plugin.cfg + dist/). Default: current directory. Used by plugins deploy. | | --name <id> | Used by plugins uninstall — 32-char md5 (pid) or the FOLDER= name; the latter is resolved via plugins list. | | --securePin <pin> | Used by plugins upload — overrides LOXBERRY_SECURE_PIN. | | --wait-install | Used by plugins upload — after POST, follow the temp logfile.cgi install log, then (with --plugin-folder) wait until that folder appears in plugins list (same as stock UI). | | --plugin-folder <name> | With --wait-install: poll the installed-plugins list until this FOLDER from plugin.cfg exists. | | --follow | Used by logs install — poll generic install log until completion. | | --params '<json>' | Used by jsonrpc call — JSON-RPC params (default []). |

Commands

| Command | Description | |---------|-------------| | loxberry-client plugins list | Print installed plugins (JSON) from plugin admin list URL. | | loxberry-client plugins upload --file ./plugin.zip [--wait-install] [--plugin-folder myplugin] | POST multipart upload to stock plugininstall.cgi (set LOXBERRY_SECURE_PIN for install). Add --wait-install to poll the temp install log and (recommended) --plugin-folder to wait until the plugin row exists. | | loxberry-client plugins deploy [--project .] [--file ./dist/....zip] [--plugin-folder myplugin] | Plugin developer shortcut: from --project, read FOLDER= in plugin.cfg, pick the newest dist/loxberry-plugin-*.zip, then upload with --wait-install and wait for the folder; if the main flow throws but the installed md5 changes, still exits 0 (LoxBerry quirk). | | loxberry-client plugins uninstall --name <md5-or-folder> | Two-step GET uninstall (confirm + answer=1), same as the web UI. --name can be the plugin folder (from plugin.cfg); the CLI resolves the pid (md5) from plugins list when the value is not 32 hex chars. | | loxberry-client logs install | Read getInstallLog() (generic path; not the per-upload tempfile).Add --follow to poll until a completion phrase appears. | | loxberry-client jsonrpc call <method> [--params '[]'] | Call /admin/system/jsonrpc.php with session/Basic headers. |

Environment

| Variable | Purpose | |----------|---------| | LOXBERRY_BASE_URL | e.g. https://loxberry.local | | LOXBERRY_USERNAME / LOXBERRY_PASSWORD | Web admin; sent as HTTP Basic on /admin (stock) | | LOXBERRY_HTTP_BASIC_* | Optional separate Basic layer (see .env.example) | | LOXBERRY_AUTH_STRATEGY | basic (default) or form | | LOXBERRY_LOGIN_PATH | Form-login path if form | | LOXBERRY_SECURE_PIN | Required for plugin install via upload API / MCP |

Examples

npx loxberry-client plugins list --baseUrl https://loxberry.local --user admin --password "$LOX_PASS"
npx loxberry-client plugins upload --file ./dist/myplugin.zip --wait-install --plugin-folder myplugin
npx loxberry-client plugins deploy --project .
npx loxberry-client plugins uninstall --name <md5-or-folder>
npx loxberry-client logs install --follow
npx loxberry-client jsonrpc call LBSystem::get_miniservers --params '[]'

Environment file

  • .env.example — committed template; copy to .env and fill in values. .env is gitignored and is never committed.
  • test/live/loxberry-live.test.ts loads .env from the repo root with a tiny test helper (no dotenv package) when you run npm run test:live*.
  • Live runs are gated by LOXBERRY_LIVE_TESTS=1, which only the test:live* npm scripts set. Keeping LOXBERRY_BASE_URL in .env for the CLI will not make npm test call your LoxBerry.

npm lifecycle scripts

.npmrc sets ignore-scripts=true so dependency postinstall (and similar) scripts do not run during npm install. This library has no runtime npm dependencies (only devDependencies for building and testing). To run install scripts once (e.g. debugging a native addon), use npm install --ignore-scripts=false or temporarily remove that line.

Browser and global builds

  • ESM / CJS: dist/index.js and dist/index.cjs (see package.json exports).
  • IIFE (global): dist/loxberry-client.browser.global.js exposes the default export as window.LoxBerryClient (namespace object).

Calling a real LoxBerry from the browser usually hits CORS; prefer Node for automation or proxy requests through your dev server.

Plugin admin (stock LoxBerry)

Defaults match upstream: list/upload/uninstall go to /admin/system/plugininstall.cgi. Upload requires your SecurePIN (same as the web UI):

await client.plugins.uploadPluginZip(buf, "plugin.zip", { securePin: "1234" });
// Install runs in the background on the appliance; poll until the plugin row exists:
await client.plugins.waitForPluginFolder("myplugin", { title: "My Plugin", timeoutMs: 120_000 });

Uninstall uses the same flow as the UI: GET do=uninstall&pid=<md5> then confirm with answer=1.

Override paths only if your image differs:

Plugin admin paths

Override via LoxBerryClient options when needed:

new LoxBerryClient({
  baseUrl,
  session,
  pluginPaths: {
    list: "/your/path/plugins.php",
    upload: "/your/path/upload.php",
    uninstall: "/your/path/uninstall.php",
    installLog: "/your/path/log.php",
  },
});

Capture real URLs from your browser devtools and add fixtures under test/fixtures/ to lock behavior (TDD).

MCP server

See packages/loxberry-client-mcp/README.md. Build with npm run build:all.

Testing the MCP server

  1. Build: npm run build:all (or npm run build:mcp if the core library is already built).
  2. Automated smoke (CI + local): after a build (npm run build:mcp or build:all), from the repo root — MCP SDK client over stdio: tools/list plus tools/callplugins_list against a dead port (expects failure, proves the tool path works without a real LoxBerry):
    npm run test:mcp
  3. Real LoxBerry: set env (see below) and use Inspector or Cursor; or point the server at your unit and invoke tools there.
  4. Env (when actually calling tools): set LOXBERRY_BASE_URL (and credentials) for the server process — same variables as .env.example.
  5. Manual / IDE: @modelcontextprotocol/inspector:
    npx @modelcontextprotocol/inspector node packages/loxberry-client-mcp/dist/server.js
  6. Cursor / Copilot: use a stdio server; pass LOXBERRY_* in env in mcp.json (Cursor does not load your .env for MCP). Examples: local node + path to dist/server.js, or after npm i -g loxberry-client-mcp use command: loxberry-client-mcp (see packages/loxberry-client-mcp/README.md).

Publishing loxberry-client-mcp as its own npm package (alongside loxberry-client-library) is described in RELEASING.md.

Development

npm install
cp .env.example .env   # then edit; PowerShell: Copy-Item .env.example .env
npm test
npm run build

For maintainers: AGENTS.md summarizes architecture and conventions for AI-assisted work. Conventional commits and devDependency rationale: CONTRIBUTING.md. Releases and npm: RELEASING.md.

Before the first push to main (or merge that lands on main): run the full local gate and npm run release:dry-run so CI and semantic-release match your expectations—see the First push checklist in RELEASING.md.

Live tests (real LoxBerry)

Default npm test never registers the live suite (no appliance required). The test:live* scripts set LOXBERRY_LIVE_TESTS=1 so Mocha runs test/live/loxberry-live.test.ts against your unit.

| Script | What it does | |--------|----------------| | npm run test:live | Read-only style checks: login, JSON-RPC LBSystem::get_miniservers, listInstalledPlugins. | | npm run test:live:upload | Same + uploads the tiny fixture under test/fixtures/e2e-plugin/ (zipped in memory). Installs plugin folder loxberryclie2e. | | npm run test:live:full | Same as upload, then uninstalls that plugin — only if title matches E2E Client Lib (safety check). Use only on a dev LoxBerry you control. Verifies list → upload/install → list → uninstall → list. | | npm run test:live:debug | Same as test:live:upload, plus LOXBERRY_LIVE_DEBUG=1: writes tmp/loxberry-live-debug/ (upload-response.html, list-latest.html, trace.log) and stderr traces. Upload only (no uninstall). | | npm run test:live:debug:full | test:live:full + LOXBERRY_LIVE_DEBUG=1: full install and uninstall cycle with the same debug artifacts, plus uninstall-response.html after confirm. Best single command to prove list/upload/uninstall end-to-end. |

How long should upload + install take?

  • HTTP POST (upload zip): usually 1–15 seconds to the Pi.
  • Background plugininstall.pl for the tiny E2E plugin: often ~20–90 seconds on a normal SD card; under load, updates, or slow I/O it can reach 2–4 minutes. The test polls the plugin list until LOXBERRY_LIVE_INSTALL_TIMEOUT_MS (default 120000 = 2 minutes) or Mocha’s slow-suite timeout (install wait + 60s headroom, max 10 minutes). If installs routinely exceed 2 minutes, raise LOXBERRY_LIVE_INSTALL_TIMEOUT_MS (e.g. 300000).

The listInstalledPlugins live test is quick (~1s) and fails fast if the HTML parser finds zero plugins (misconfigured base URL or non-stock markup). The upload / uninstall tests live in a nested describe with a long Mocha timeout so only that block waits for plugininstall.pl on the Pi.

Debugging (logs + artifacts)

  1. Run npm run test:live:debug (upload + artifacts) or npm run test:live:debug:full (adds uninstall + uninstall-response.html). Open tmp/loxberry-live-debug/upload-response.html — if it is not the install log viewer, the POST did not start an install (PIN, lock, validation).
  2. Open list-latest.html — raw last plugin list GET body; after debug:full, refresh mentally: row should appear after install, disappear after uninstall.
  3. Open uninstall-response.html ( debug:full only ) — HTML from the confirmed uninstall GET.
  4. Read trace.log (and stderr): install log polls, list polls, uninstall steps.
  5. For any app using LoxBerryClient (CLI, scripts), set LOXBERRY_CLIENT_DEBUG=1 to print the same plugin-step lines to stderr without writing files.

Upload POST matches the stock browser form: saveformdata, empty archiveurl, uploadfile as application/zip, securepin, btnsubmit. The client checks that the response looks like the install log progress page (not an error/lock HTML).

After upload, the stock UI polls logfile.cgi?logfile=<random>.log until plugininstall.pl finishes. The library does the same via followPluginInstallTempLog() (tempfile from extractInstallLogTempfileFromHtml() on the POST body) before waiting for the new plugin row — polling only the plugin list is not enough.

Manual install check (same zip as the live test)

Stock plugininstall.pl always runs cp …/icons/*. A zip without icons/icon_64.png (and siblings) makes that step fail; the plugin may never show in the UI. The fixture now includes minimal PNGs.

Build a zip on disk and upload it in the browser (Plugin management → Install):

npm run build:e2e-zip

Default output: tmp/loxberryclie2e-manual-upload.zip (override: npx tsx scripts/build-e2e-plugin-zip.ts path/to/out.zip). If that works in the UI but the automated test does not, compare SecurePIN and credentials; if the zip fails in the UI too, open the installation log on the LoxBerry page.

You can put LOXBERRY_LIVE_UPLOAD / LOXBERRY_LIVE_UNINSTALL in .env if you like; LOXBERRY_LIVE_TESTS should come only from the npm scripts. Uninstall is gated by LOXBERRY_LIVE_UNINSTALL=1 (test:live:full sets it).

Upload tests need LOXBERRY_SECURE_PIN in .env (the same SecurePIN you enter on the plugin install page in the browser). Without it, test:live:upload / test:live:full fails with a clear error.

If your LoxBerry uses different plugin admin URLs than the library defaults, live tests will fail until you align pluginPaths (extend the live test client options if needed).

Auth: basic vs form

Stock LoxBerry does not use /admin/index.php for login — the whole htmlauth tree is AuthType Basic (.htaccess). The client default strategy: "basic" sends Authorization: Basic … with your web UI user/password on every request (and probes /admin/system/index.cgi).

If you use a custom HTML form login instead, set LOXBERRY_AUTH_STRATEGY=form and LOXBERRY_LOGIN_PATH as needed.

Troubleshooting

  • 401: Wrong username/password for HTTP Basic (same as first browser prompt on /admin).
  • 404 on “login”: You were on the old form-login path; with defaults, auth probes /admin/system/index.cgi. Fix LOXBERRY_BASE_URL (scheme/host/port).
  • JSON-RPC uses the same Authorization (and cookies, if any) as other /admin requests — nothing extra to enable (wiki — JsonRpc).
  • Optional: LOXBERRY_HTTP_BASIC_* if the Basic user/password differ from LOXBERRY_USERNAME / LOXBERRY_PASSWORD.

License

MIT — see LICENSE.

Beerware: If we meet someday and you found this useful, you can buy me a beer. (This is a social request, not a legal condition of the MIT license.)

Funding

package.json includes an npm funding field; run npm fund in this repo to see the URL.