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

sainsburys-groceries-cli

v0.4.0

Published

Sainsbury's Groceries CLI + agent skill: previous orders, slot booking, amending orders. Runtime-agnostic TypeScript (Node & Bun). Impersonated-TLS transport passes Akamai with no browser after one-time auth; remote CDP (--ws, incl. LightPanda) supported.

Readme

sainsburys-groceries

skills.sh installs License: MIT

Runtime-agnostic (Node & Bun) TypeScript CLI + agent skill for automating Sainsbury's Groceries: previous orders, delivery slot booking, basket management, and amending your upcoming order. No browser required after one-time login.

Personal-use tool. Talks to Sainsbury's unofficial internal API with your own account. Nothing here places orders or charges you — checkout is intentionally out of scope.

Install the agent skill (one command)

Works with Claude Code, Codex, Cursor, Copilot, Windsurf, Gemini CLI, Cline, OpenCode, Goose and more:

npx skills add mike-grant/sainsburys-groceries

Prefer manual? Symlink it yourself:

git clone https://github.com/mike-grant/sainsburys-groceries
./sainsburys-groceries/scripts/install-skill.sh   # links into ~/.claude/skills, ~/.agents/skills

Then just ask your agent: "what did I order from Sainsbury's last week?" or "add oat milk and eggs to my Sainsbury's basket" — the SKILL.md teaches it the exact commands and safety semantics.

Install the CLI

npm i -g sainsburys-groceries-cli     # Node >= 20
# or zero-install per command:
npx -y sainsburys-groceries-cli search "oat milk"
# or run straight from a clone:
bun src/index.ts --help               # Bun >= 1.1
node src/index.ts --help              # same file, same features

Authenticate (one-time)

sainsburys login                 # visible local window; complete MFA yourself
sainsburys login --ws ws://host:9222   # same, in a remote CDP browser
SAINSBURYS_CDP_ONLY=1 SAINSBURYS_WS=ws://127.0.0.1:9222 sainsburys whoami # Pi-safe CDP-only mode

# headless / CI — CLI fills the form itself; creds via env only (never argv):
[email protected] SAINSBURYS_PASSWORD='...' sainsburys login
SAINSBURYS_USERNAME=... SAINSBURYS_PASSWORD=... sainsburys login --mfa 123456  # pre-supply OTP
# Agent-friendly MFA handoff: the process prints MFA_REQUIRED, then waits for the file
SAINSBURYS_USERNAME=... SAINSBURYS_PASSWORD=... sainsburys login --ws ws://127.0.0.1:9222 --mfa-file /tmp/sainsburys-mfa

# or, no browser at all:
sainsburys import-cookie-header "WC_AUTHENTICATION_...=...; JSESSIONID=..." -t <wcauthtoken>
# or env: SAINSBURYS_COOKIE / SAINSBURYS_WCAUTHTOKEN

Headless credential mode pauses for the SMS/app OTP if --mfa isn't given and a terminal is attached. For Hermes or another non-interactive agent, pass --mfa-file <path>: it prints MFA_REQUIRED, polls that file, and continues as soon as the user/agent writes the code. On failure it saves a screenshot to ~/.sainsburys/login-debug.png for diagnosis.

Session persists to ~/.sainsburys/session.json (chmod 600). Verify with sainsburys whoami, re-login roughly weekly when it expires.

Commands

| Task | Command | |---|---| | view one order in full | orders view <orderId> or orders view latest | | search order history | orders find "mushy peas" --pages 3 | | orders history | orders list [-n 10] / orders latest | | search | search "oat milk" -n 5 [--json] | | basket | basket view · basket add "milk" -q 2 · basket remove <item_uid> | | amend upcoming order | amend add "<query or uid>" -q 2 | | slots | slots reservation · slots list [-p POSTCODE] · slots book <id\|regex> [--dry-run] · slots cancel | | diagnostics | doctor api · doctor lightpanda [--ws ws://127.0.0.1:9222] |

Global flags: --json -v --ws <url> --cdp-only --headed --browser --http --session <path>.

Amend semantics

With a booked slot, adding items amends the upcoming order (Sainsbury's models pre-checkout amendments this way); without one, items go to the plain basket. The CLI prints which target was used (-> next-delivery / -> basket). Checkout/payment is intentionally out of scope.

How it talks to Sainsbury's

Akamai (their WAF) denies non-browser TLS fingerprints — plain fetch from Bun/Node/curl gets 403 Access Denied at the edge — and also denies HeadlessChrome user agents.

| Transport | Flag | Browser needed | Notes | |---|---|---|---| | koonjs (default) | — | No | Rust/BoringSSL Chrome JA3+H2 impersonation via napi-rs; native under Node & Bun | | remote CDP page | --ws ws://host:9222 | Remote only | In-page fetch() on a warmed groceries SPA page in any CDP browser incl. LightPanda | | local Chromium page | --browser (+--headed) | Yes, local | Same as above with auto-discovered Playwright Chromium | | raw fetch | --http | No | Usually edge-denied; kept for tolerant networks/proxies |

On Raspberry Pi, use --cdp-only with a Chromium-compatible CDP endpoint. This prevents the CLI from trying to load the optional native koonjs module.

Agent MFA handoff

The MFA code is entered into the already-running login process. Do not invoke login a second time after the code arrives, because that can create a new challenge. Start one process with a new empty file, keep it running, and have the agent ask you for the code when it sees MFA_REQUIRED:

MFA_FILE=/tmp/sainsburys-mfa.$$
rm -f "$MFA_FILE"
SAINSBURYS_USERNAME=... SAINSBURYS_PASSWORD=... \
  sainsburys --cdp-only --ws "$SAINSBURYS_WS" login --mfa-file "$MFA_FILE" \
  > /tmp/sainsburys-login.log 2>&1 &
# after the agent receives the MFA code from you:
printf '%s\n' '<code>' > "$MFA_FILE"

The waiting process submits the code in the existing browser page, saves the session, and subsequent CDP commands reuse it. Verify with sainsburys --cdp-only whoami.

In the normal mode only login needs a visible browser; everything else is plain authenticated REST over an impersonated TLS session (~0.8s per command). In --cdp-only mode, all commands intentionally use the CDP browser's session.

Runtimes

One transport, no split — koonjs runs natively under both runtimes:

| Invocation | Notes | |---|---| | node src/index.ts ... / installed bin | in-process (Node >= 20 type-stripping) | | bun src/index.ts ... / bunx --bun sainsburys | in-process |

Containers

No browser in the image; auth comes in via mount or env:

# any node:*-slim base works; koonjs ships napi prebuilds for linux x64/arm64
RUN npm i -g sainsburys-groceries-cli
docker run -v ~/.sainsburys:/home/shopper/.sainsburys:ro ...          # mounted session
docker run -e SAINSBURYS_COOKIE="..." -e SAINSBURYS_WCAUTHTOKEN="..." # or env

Set TZ=Europe/London for sane slot times. Sessions/cookies are gitignored and dockerignored.

LightPanda

LightPanda exposes a CDP server (lightpanda serve, default ws://127.0.0.1:9222); the CLI accepts any such endpoint via --ws. Test acceptance end-to-end:

bunx lightpanda serve &    # or docker run -p 9222:9222 lightpanda/browser:latest
sainsburys doctor lightpanda

Known caveats (see lightpanda-io/browser issues): /json/list may be missing, Playwright-over-Bun WebSocket instability, no screenshots/PDF rendering.

Tests

bun test           # offline unit tests
bun run typecheck  # tsc --noEmit

Disclaimer

Unofficial, undocumented APIs — for personal use with your own account only. Endpoints can change without notice; doctor api helps diagnose. Not affiliated with Sainsbury's Supermarkets Ltd.