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

imgscribe

v0.1.1

Published

AI-powered product image processor that generates SEO metadata and outputs structured JSON

Readme

imgscribe

AI-powered product image processor that generates SEO metadata and outputs structured JSON.

Drop product images into a folder — imgscribe analyses each one with a vision AI model, generates titles, descriptions, keywords, alt text, and SEO metadata, converts to WebP, and returns structured JSON.

Install

npm install imgscribe

Quick Start (CLI)

Create a config file:

// imgscribe.config.js
module.exports = {
  aiApiKey: process.env.NVIDIA_API_KEY,
  skuPrefix: 'NXT',
  storageDir: './storage/products',
};

Run:

npx imgscribe --config ./imgscribe.config.js

The CLI watches ./products for new images, processes them one by one in test mode (pauses after each), and outputs JSON for each processed image.

Quick Start (Programmatic)

const imgscribe = require('imgscribe');

imgscribe.configure({
  aiApiKey: 'nvapi-your-key',
  skuPrefix: 'NXT',
  categories: ['Lighting', 'Furniture', 'Decor'],
  allowNewCategories: false,
});

const result = await imgscribe.process('./products/lamp.jpg');
console.log(result);

API

configure(config)

Merges your config into the defaults. Call before process() or start().

process(imagePath)

Processes a single image. Returns a promise resolving to the output object, or null if the image was flagged or failed.

start()

Starts the interactive CLI watcher mode.

Config Reference

| Key | Type | Default | Description | |-----|------|---------|-------------| | watchDir | string | './products' | Directory to watch for new images | | storageDir | string | './storage/products' | Base output directory for WebP files | | isolation | boolean | true | Group outputs into run folders | | isolationMode | string | 'new' | 'new', 'merge', or 'increment' | | skuPrefix | string | 'IMG' | Prefix for generated SKU numbers | | aiBaseUrl | string | 'https://integrate.api.nvidia.com/v1' | OpenAI-compatible API base URL | | aiApiKey | string | '' | API key for the vision model | | aiModel | string | 'meta/llama-4-maverick-17b-128e-instruct' | Model identifier | | targetMarket | string | '' | Market context injected into AI prompt | | siteKeywords | string[] | [] | SEO keywords for AI context (used only if relevant) | | categories | string[] | [] | Constrain AI to these category names | | allowNewCategories | boolean | true | Let AI create categories outside the list | | categoryMode | string | 'path,ai' | Category resolution priority chain |

Output

Each successfully processed image returns:

{
  "sku": "NXT0005",
  "slug": "elegant-black-crystal-chandelier",
  "title": "Elegant Black Crystal Chandelier",
  "category": "Lighting",
  "altText": "Black crystal chandelier with elegant curved arms",
  "metaTitle": "Elegant Black Crystal Chandelier | Premium Lighting",
  "metaDescription": "Premium black crystal chandelier featuring curved arms and refined design. Ideal for modern and classic interiors.",
  "description": "This elegant black crystal chandelier combines modern aesthetics with classic craftsmanship...",
  "keywords": ["chandelier", "crystal", "lighting", "black", "elegant"],
  "clarity": 9,
  "webpPath": "storage/products/run_2026-06-25_001/lighting/elegant-black-crystal-chandelier-NXT0005.webp"
}

| Field | Type | Description | |-------|------|-------------| | sku | string | Generated SKU (prefix + 4-digit number) | | slug | string | URL-friendly slug derived from title | | title | string | AI-generated product title (4-6 words) | | category | string | Resolved category (see Category System) | | altText | string | Descriptive alt text for accessibility | | metaTitle | string | SEO meta title (50-60 chars) | | metaDescription | string | SEO meta description (140-155 chars) | | description | string | Product description (2-3 sentences) | | keywords | string[] | SEO keywords array | | clarity | number | Image clarity score (1-10) | | webpPath | string | Path to the converted WebP file |

imgscribe returns structured JSON — persist it to any database, CMS, or file using your own application code.

Category System

Categories are resolved using a priority chain configured by categoryMode:

| Mode | Behavior | |------|----------| | path,ai | Folder name first, falls back to AI if no subfolder | | ai,path | AI response first, falls back to folder name | | ai | AI only — folder structure ignored | | path | Folder only — AI category ignored |

Folder-based (path): Derived from subfolder name in the watch directory. products/lighting/lamp.jpg resolves to lighting. Images in the root resolve to uncategorised.

AI-based (ai): The AI assigns a category. Constrain it by passing a categories array:

imgscribe.configure({
  categories: ['Lighting', 'Furniture', 'Textiles', 'Decor'],
  allowNewCategories: false, // forces 'Other' if nothing fits
  categoryMode: 'ai',
});

AI Provider

imgscribe uses the OpenAI SDK, so any OpenAI-compatible API works. Swap providers by changing aiBaseUrl, aiApiKey, and aiModel.

NVIDIA NIM (default):

imgscribe.configure({
  aiBaseUrl: 'https://integrate.api.nvidia.com/v1',
  aiApiKey: 'nvapi-...',
  aiModel: 'meta/llama-4-maverick-17b-128e-instruct',
});

Groq:

imgscribe.configure({
  aiBaseUrl: 'https://api.groq.com/openai/v1',
  aiApiKey: 'gsk_...',
  aiModel: 'llama-3.2-90b-vision-preview',
});

OpenRouter:

imgscribe.configure({
  aiBaseUrl: 'https://openrouter.ai/api/v1',
  aiApiKey: 'sk-or-...',
  aiModel: 'meta-llama/llama-4-maverick:free',
});

Ollama (local):

imgscribe.configure({
  aiBaseUrl: 'http://localhost:11434/v1',
  aiApiKey: 'ollama',
  aiModel: 'llava',
});

Laravel Integration

Call the CLI from PHP and decode the JSON output:

$config = base_path('imgscribe.config.js');
$image = storage_path('app/uploads/product.jpg');

$output = shell_exec("npx imgscribe --config {$config} --once {$image} 2>/dev/null");

// Extract JSON block from output
preg_match('/\{[\s\S]*\}/', $output, $matches);
$result = json_decode($matches[0] ?? '{}', true);

echo $result['title'];     // "Elegant Black Crystal Chandelier"
echo $result['sku'];        // "NXT0005"
echo $result['webpPath'];  // path to converted WebP

Or use the programmatic API via a Node.js microservice and call it over HTTP from Laravel.

SKU Persistence

SKU counters are stored in .imgscribe-state.json in the project root. Each prefix tracks its own counter independently. Changing the prefix starts a new sequence.

Run Isolation

When isolation: true, each run creates a timestamped subfolder:

storage/products/
  run_2026-06-25_001/
    lighting/
      elegant-chandelier-NXT0001.webp
    furniture/
      oak-dining-table-NXT0002.webp
  run_2026-06-25_002/
    ...

Modes:

  • new — Creates a fresh run folder each startup
  • merge — Reuses the most recent run folder
  • increment — Same as merge (SKU counter continues globally)

Set isolation: false to skip run folders entirely.

Requirements

  • Node.js 18+
  • sharp (installed automatically)
  • A vision-capable AI model API key (NVIDIA NIM, Groq, OpenRouter, Ollama, etc.)

License

MIT