pnpm-airgap
v2.2.0
Published
Transfer pnpm project dependencies between online and offline environments
Maintainers
Readme
pnpm-airgap
The complete solution for transferring pnpm dependencies to air-gapped environments.
The Problem
Getting pnpm projects into secure, offline, or air-gapped environments is challenging:
- No tooling for pnpm lockfiles - Existing airgap tools only work with npm's package-lock.json
- Complex dependency trees - pnpm's advanced resolution (peer deps, optionals, workspaces) makes manual approaches nearly impossible
- Registry population gap - No automated way to populate offline registries with pnpm project dependencies
The Solution
pnpm-airgap is a standalone tool that:
- Reads
pnpm-lock.yamland downloads ALL dependencies - Publishes packages to any npm-compatible registry (Verdaccio, Nexus, Artifactory)
- Works as a single file - no
npm installrequired in airgap - Supports pnpm lockfile versions 5.x, 6.x, and 9.x
Quick Start
1. Download the standalone CLI
The CLI is a single file (~1.1MB) that runs with just Node.js:
# From npm (online)
npm pack pnpm-airgap
tar -xzf pnpm-airgap-*.tgz
# Use: node package/dist/cli.cjs
# Or build from source
pnpm install && pnpm build
# Use: node dist/cli.cjs2. Fetch dependencies (online)
node cli.cjs fetch -l ./pnpm-lock.yaml -o ./packages3. Transfer to airgap
Copy the packages folder and cli.cjs to your air-gapped environment.
4. Publish to local registry (airgap)
# Start your registry (e.g., Verdaccio)
verdaccio &
# Login
npm login --registry http://localhost:4873
# Publish all packages
node cli.cjs publish -p ./packages -r http://localhost:48735. Install your project
pnpm install --registry http://localhost:4873Interactive Mode
Run without arguments for a guided wizard:
node cli.cjs┌───────────────────────────────────────┐
│ pnpm-airgap v2.0.0 │
│ Transfer dependencies to air-gapped │
│ environments with ease │
└───────────────────────────────────────┘
? What would you like to do?
❯ 📦 Fetch dependencies from lockfile
📤 Publish packages to registry
🔄 Sync registries
📊 Export registry state
📖 Quick start guide
✖ ExitCommands
fetch - Download packages from lockfile
node cli.cjs fetch [options]
Options:
-l, --lockfile <path> Path to pnpm-lock.yaml (default: ./pnpm-lock.yaml)
-o, --output <path> Output directory (default: ./airgap-packages)
-r, --registry <url> Source registry (default: https://registry.npmjs.org)
--registry-state <path> Registry state file for incremental fetching
--skip-optional Skip optional dependencies
--concurrency <number> Parallel downloads (default: 5)
--debug Enable debug outputpublish - Publish packages to registry
node cli.cjs publish [options]
Options:
-p, --packages <path> Packages directory (default: ./airgap-packages)
-r, --registry <url> Target registry (default: http://localhost:4873)
--concurrency <number> Parallel publishes (default: 3)
--no-skip-existing Publish all packages even if they exist
--dry-run Preview without publishing
--debug Enable debug outputsync - Sync between registries
node cli.cjs sync [options]
Options:
-s, --source <url> Source registry URL
-d, --dest <url> Destination registry URL
-o, --output <path> Output directory
--scope <scope> Only sync packages in this scope
--download-only Only download, don't publish
--publish-only Only publish existing packages
--dry-run Preview without changesregistry-state export - Export for incremental sync
Export all packages from a registry to enable incremental fetching:
node cli.cjs registry-state export -r http://localhost:4873 -o registry-state.json
# Then use with fetch to skip existing packages
node cli.cjs fetch -l pnpm-lock.yaml --registry-state registry-state.jsoninfo - Show bundle information
node cli.cjs info ./packagesinit - Create config file
node cli.cjs initConfiguration
Create pnpm-airgap.config.json:
{
"fetch": {
"lockfilePath": "./pnpm-lock.yaml",
"outputDir": "./airgap-packages",
"concurrency": 5,
"registryUrl": "https://registry.npmjs.org",
"skipOptional": false
},
"publish": {
"packagesDir": "./airgap-packages",
"registryUrl": "http://localhost:4873",
"concurrency": 3,
"skipExisting": true
},
"sync": {
"sourceRegistry": "",
"destRegistry": "http://localhost:4873",
"outputDir": "./sync-packages",
"skipExisting": true
}
}Features
| Feature | Description | | --------------------- | ----------------------------------------------------------------- | | Standalone Binary | Single 1.1MB file, runs with just Node.js - no npm install needed | | Interactive Mode | Guided wizard for all commands | | Auto-detection | Finds lockfiles and package directories automatically | | Incremental Sync | Export registry state to skip already-synced packages | | Smart Tagging | Auto-detects prerelease tags, handles version conflicts | | Safety Blocks | Prevents accidental publish to public registries (npmjs.org) | | Rate Limiting | Automatic backoff for 429 errors | | Robust Parsing | Handles scoped packages, aliases, patches, peer deps |
Workflow Examples
Complete Airgap Transfer
Online Machine:
# Fetch all dependencies
node cli.cjs fetch -l pnpm-lock.yaml -o ./packages
# Create transfer archive
tar -czf transfer.tar.gz packages/ cli.cjsOffline Machine:
# Extract
tar -xzf transfer.tar.gz
# Start registry and login
verdaccio &
npm login --registry http://localhost:4873
# Publish
node cli.cjs publish -p ./packages -r http://localhost:4873
# Install your project
echo "registry=http://localhost:4873" > .npmrc
pnpm installIncremental Updates
Avoid re-downloading packages that already exist:
# Export state from airgap registry
node cli.cjs registry-state export -r http://verdaccio:4873 -o state.json
# Transfer state.json to online machine
# Fetch only missing packages
node cli.cjs fetch -l pnpm-lock.yaml --registry-state state.json -o ./packages
# Result: If lockfile needs 500 packages but 450 exist, only 50 are downloadedProgrammatic API
import { fetchDependencies, publishPackages } from 'pnpm-airgap';
// Fetch
await fetchDependencies({
lockfilePath: './pnpm-lock.yaml',
outputDir: './packages',
registryUrl: 'https://registry.npmjs.org',
concurrency: 5,
});
// Publish
await publishPackages({
packagesDir: './packages',
registryUrl: 'http://localhost:4873',
concurrency: 3,
skipExisting: true,
});Compatibility
| Component | Supported Versions | | ----------------- | ------------------------------------------------- | | Node.js | 18.0.0 or higher | | pnpm lockfile | v5, v6, v9 | | Registries | Verdaccio, Nexus, Artifactory, any npm-compatible | | Platforms | Windows, Linux, macOS |
Reports
Both fetch and publish commands generate JSON reports:
metadata.json- Package list and metadatabundle-info.json- Download statisticspublish-report.json- Publishing results
Troubleshooting
Authentication Issues
# Verify you're logged in
npm whoami --registry http://localhost:4873
# Re-login if needed
npm login --registry http://localhost:4873Missing Packages
Check bundle-info.json for download failures and ensure lockfile is current.
Publishing Conflicts
The tool automatically handles:
- Version conflicts (uses version-specific tags)
- Prerelease versions (applies correct tags)
- Already-existing packages (skips by default)
Development
# Install dependencies
pnpm install
# Build
pnpm build
# Run tests
pnpm test
# Lint
pnpm lintLicense
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
