Skip to content

Repository files navigation

NHPProxy CLI & Library

This package provides a CLI and an API Library to integrate with NHP New Zealand. You can search products, check pricing, check stock availability, manage your cart, and pull order/invoice history.

Note

This tool is currently configured for NHP New Zealand (nhpnz.co.nz). However, it should also work on the Australian version of the site (nhp.com.au) with some minor domain and configuration tweaks.

CLI Usage

Requirements

  • Deno 2.x. Install it with the official one-liner, then check deno --version:

    irm https://deno.land/install.ps1 | iex            # Windows (PowerShell)
    curl -fsSL https://deno.land/install.sh | sh       # macOS / Linux
  • Git, to clone this repository.

  • A login for the NHP New Zealand web portal (nhpnz.co.nz).

Install

Clone the repository and run the CLI from inside it:

git clone https://github.com/Bedz01/nhp-cli.git
cd nhp-cli
deno run -A nhp_cli.js help

-A grants the network and file access the tool needs: the portal, and its state directory (see Setup). deno task cli <args> is a shorthand for the same command. Deno fetches the dependencies on the first run, so that run takes a moment longer.

To run it as nhp from any directory, install it globally:

deno install -g -A -f -n nhp nhp_cli.js

Deno puts the nhp shim in its bin directory (~/.deno/bin, or %USERPROFILE%\.deno\bin on Windows) and tells you if that directory is not on your PATH yet. All dependencies are referenced with explicit versioned specifiers, so the installed command works from any directory without extra flags. Re-run the install command after pulling changes.

Setup

Log in once:

nhp login

It asks for your portal username and password (the password is not echoed), logs in, and remembers both so later commands - and the frequent re-logins the portal's short sessions force - happen silently. Any command run before that will ask the same questions itself if it is running in a terminal. nhp login --reset asks again (to change accounts); nhp logout forgets the session and the saved credentials.

Where things live. All state goes in a per-user directory, never in the checkout, so it survives re-clones and git never sees it:

Platform Directory
Windows %APPDATA%\nhp
Linux / macOS $XDG_CONFIG_HOME/nhp, else ~/.config/nhp
Override NHP_CONFIG_DIR
config.json       { "sellMarginMultiplier": 1.25 }   optional; enables the Sell: line
credentials.json  saved by `nhp login`
cookies.json      the portal session

How the password is stored. On Windows it is encrypted with DPAPI in the current-user scope, so the file can only be decrypted by your Windows account on that machine; copying it elsewhere yields nothing. The CLI does this through a short PowerShell script (ProtectedData, no cmdlets or modules involved), tried in Windows PowerShell 5.1 and then pwsh, and only when it actually needs to log in. If neither PowerShell works, the password is stored as plain text and nhp login says so. On Linux and macOS the file is plain text with mode 600.

Scripts and CI. Environment variables override the stored file and never prompt:

  • NHP_USERNAME, NHP_PASSWORD
  • NHP_SELL_MARGIN (optional)
  • NHP_CONFIG_DIR (optional)

With --json or --tsv, or with no terminal, a missing login is an error (Run 'nhp login') rather than a prompt, so stdout stays clean.

Upgrading from 1.3 or earlier. The old credentials.json and cookies.json next to nhp_cli.js are still read as a fallback. Run nhp login once: it moves the credentials (and the margin) into the state directory, after which the old files are no longer read and can be deleted.

Commands

Run nhp help (or nhp --help) for the full built-in reference.

Products & Pricing

  • nhp search <query> - Search for products matching a query.
  • nhp price <part>[:qty]... [--full|--tsv] - Price and stock for one or more part numbers as a ledger, one line per part: PART DESCRIPTION QTY BUY [SELL] STOCK (description truncated to fit, SELL only when a margin is configured, stock as glyph + NZ on-hand quantity + state; an unknown part keeps its line with the portal's reason and ✗ NOT FOUND). Quantities go on the part as K144:2 06850863:10; --qty <n> is the default for parts without one. A bare number is always a part number. Add --full for the record view with discount, AU stock and the rest.
  • nhp csv <csvFile> [--full|--tsv] - The same for part numbers listed in a CSV file. Columns: partNumber[,qty] - a single-column file of part numbers also works (qty defaults to 1), and a header row is skipped automatically.

Orders & Invoices

  • nhp orders [offset] [--full|--tsv] - Get order history as a compact ledger (ORDER PO STATUS DATE, one line per order). Add --full for the record view with totals. Optional search flags: --dateFrom, --dateTo, --purchaseNumber, --documentNumber, --orderNumber, --customerReference.
  • nhp invoices [offset] [--brief|--tsv] - Get invoice history. Accepts the same optional search flags as orders. Add --brief for the ledger view.
  • nhp order <orderId...> [--full|--tsv] - Line items for one or more orders as a ledger, one line per item: PART DESCRIPTION DLV/ORD STATUS (description truncated to fit, delivered/ordered quantity coloured green/yellow/red). Several order IDs are fetched one at a time (each is a full page scrape) and print one ledger each, separated by a blank line and led by Order | PO | Ref | Date; an order that can't be fetched is reported on stderr without stopping the others, and the command still exits non-zero. With several IDs, --json emits an array with one entry per argument (a failure becoming { orderId, error }); a single ID keeps the bare scrape result. Add --full for the record view with header, addresses, prices and totals.
  • nhp invoice <id> [--brief|--tsv] - Get detailed line items for a specific invoice. --brief prints PART DESCRIPTION QTY.
  • nhp po <query> - Search order history by PO Number. A single match auto-expands like nhp order (ledger, or record view with --full).

Cart Management

  • nhp cart add <partNumber> [qty] - Add an item to the cart. The two-argument form treats the second argument as a quantity only when it is 1-9999 with no leading zero; anything else (e.g. the numeric part number 06850863) is treated as a second part number. For explicit quantities - including large ones - use the part:qty form: nhp cart add K144:2 06850863:10.
  • nhp cart list - View current items in the cart.
  • nhp cart remove <partNumber|line#> - Remove an item, by part number or by the line number shown in cart list. Part numbers take priority when both interpretations are possible.
  • nhp cart update <partNumber|line#> <qty> - Update the quantity of a cart item.
  • nhp cart clear - Empty the entire cart.
  • nhp cart upload <csvFilePath> - Upload a CSV of part numbers to the cart (same format as nhp csv). The tool verifies every part actually landed in the cart and reports any that were rejected.

Authentication

  • nhp login [--reset] - Log in and save the session. Asks for the username and password the first time (or with --reset) and remembers them; otherwise re-logs in with the saved ones. --json reports the username, where the credentials came from, and the file paths.
  • nhp logout - Forget the saved session and credentials.

Spreadsheet Export (--tsv)

price, csv, orders, order, invoices, invoice and po take --tsv: one header row and one tab-separated row per record (per line item for order/invoice, across every order given), with the order- or invoice-level fields repeated on each row so the result is a flat table. Plain text only - no colour, glyphs, truncation, or progress line; money and quantities are bare numbers parsed out of the portal's $1,234.56 strings - so it pastes straight into columns:

nhp order SOR1000001 SOR1000002 --tsv | Set-Clipboard        # then paste into Excel/Sheets
nhp invoices --dateFrom 2026-07-01 --tsv | Set-Clipboard
nhp price K144 06850863 --tsv | ConvertFrom-Csv -Delimiter "`t"   # PowerShell objects

Columns (blank where NHP has no such value):

price/csv   PART  DESCRIPTION  QTY  BUY  SELL  LIST  CURRENCY  STOCK  STOCK STATUS  ERROR
orders      ORDER  PO  STATUS  DATE  TOTAL
order       ORDER  PO  ORDER STATUS  DATE  LINE  PART  DESCRIPTION  DELIVERED  ORDERED  LINE STATUS  UNIT PRICE  TOTAL
invoices    INVOICE  PO  REF  STATUS  DATE  TOTAL  OUTSTANDING
invoice     INVOICE  PO  REF  DATE  LINE  PART  DESCRIPTION  QTY  UNIT PRICE  TOTAL

STOCK is the NZ on-hand quantity and STOCK STATUS the badge text; LIST is always blank (the portal has no list price) and CURRENCY always NZD; SELL is filled only when sellMarginMultiplier is configured; LINE is the 1-based position (the portal has no line numbers). Dates are dd/mm/yyyy as in every other view. Failures behave as in the human view (stderr, non-zero exit) and contribute no rows. --tsv wins over --full/--brief, --json wins over both.

JSON Output & Exit Codes

You can append --json to any command to receive the raw JSON response on stdout instead of the formatted terminal output. Progress and error messages go to stderr, so stdout stays valid JSON for piping:

nhp orders 0 --purchaseNumber PO-12345 --json > orders.json

Failed operations (unknown part numbers on cart add, items not found, API errors) exit with a non-zero status code, so the CLI is safe to script against.

Portal Quirks

  • Header labels differ between the order and invoice pages, including capitalisation. The order page says Order Created on and Customer Reference no; the invoice page says Invoice date (lower-case d) and the same Customer Reference no. The ledger summary line and the --tsv exports look the labels up through one shared fallback list in formatters.js; add new spellings there, not at the call sites.
  • Portal dates are unpadded d/M/yyyy (3/09/2026, day first). Every printed date goes through formatDate, which zero-pads to dd/mm/yyyy.

Library API Usage

You can also integrate this package directly into your own JavaScript applications.

Import the Library

In Deno (or standard ES module environments), simply import the entry point mod.js.

import { NHPClient } from "./mod.js";

Initialization

Create a new instance of the client. By default it resolves credentials the way the CLI does (environment variables, then the file nhp login saved) and keeps the session in the same state directory (see Setup).

const client = new NHPClient({
  // Optional configuration overrides:
  cookiePath: "./data/custom_cookies.json",
  credentials: {
    username: "your_email@example.com",
    password: "your_password",
  },
  timeoutMs: 120000, // Per-request timeout (default 120s - the NHP portal is slow)
  silent: true, // Set to true to suppress internal console log messages
});

// Always call ensureLogin() before making API calls
await client.ensureLogin();

credentials may also be an async function; it is called only when a login is actually needed, so an expensive lookup (a keychain, a prompt) does not run on every command. onLogin(creds) is called after each successful login. The store export has the pieces the CLI uses: resolveCredentials, saveCredentials, dpapiProtect/dpapiUnprotect, configDir.

All requests carry a timeout and automatically re-authenticate once if the saved session has expired.

API Methods

searchProducts(query)

Searches for products matching a keyword.

const results = await client.searchProducts("battery");
console.log(results.widgets[0].content); // Array of products

getPriceAndStock(products)

Fetches the current pricing and stock availability (including local NZ and AU stock). Expects an array of objects containing itemId (the part number) and qty.

const items = [
  { itemId: "TPHS25R5GM", qty: 1 },
  { itemId: "1756BA1", qty: 5 },
];
const pricingData = await client.getPriceAndStock(items);

// Example response mapping:
for (const prod of pricingData.ChildProducts) {
  if (prod.HasError || prod.ProductExist === false) {
    // Unknown part - the reason is in prod.ErrorMessages
    continue;
  }
  console.log(`Buy Price: ${prod.AdjustedPriceWithCurrency}`);
  console.log(`NZ Stock: ${prod.OnHandQty}`);
}

getOrders(pageSize, offset, options)

Fetches the user's order history. The options object can include any combination of the following search filters: documentNumber, orderNumber, purchaseNumber, customerReference, dateFrom, dateTo.

// Get first 20 orders
const orders = await client.getOrders(20, 0);

// Search for a specific Purchase Order number and Date Range
const poOrders = await client.getOrders(20, 0, {
  purchaseNumber: "PO-12345",
  dateFrom: "01/12/2025",
  dateTo: "06/12/2025",
});

getInvoices(pageSize, offset, options)

Fetches the user's invoice history. The options object accepts the same search filters as getOrders.

const invoices = await client.getInvoices(20, 0, { dateFrom: "01/01/2026" });

getOrderDetails(orderId)

Fetches specific line items and shipping statuses for a given order ID.

const details = await client.getOrderDetails("ORDER_ID_HERE");
console.log(details);

getInvoiceDetails(invoiceId)

Fetches specific line items and pricing for a given invoice ID (Document Number).

const items = await client.getInvoiceDetails("SIN987654321");
console.log(items);

Cart Management

The library provides complete functionality to manage the user's shopping cart:

await client.addToCart("115797", 2);
const cart = await client.getCart();
await client.updateCartLineQuantity(cart.Lines[0].ExternalCartLineId, 5);
await client.removeCartLine(cart.Lines[0].ExternalCartLineId);
await client.clearCart();

// CSV uploads verify the result against the cart, since the NHP upload
// endpoint does not report failures in its response:
const result = await client.uploadCartCsv("./bulk_order.csv");
// -> { success: boolean, requested: string[], missing: string[] }

Development

deno task check   # lint + type-check + tests
deno task test    # offline unit tests only

Offline unit tests live in tests/ and run in CI on every push.

About

NHP New Zealand CLI and an API Library

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages