@ni-kismet/sl-webapp-nipkg
v2.0.2
Published
Build tool for packaging web applications into SystemLink WebApp .nipkg format. Supports Node.js, Python, .NET Blazor, and static sites.
Readme
SL WebApp NIPKG
A flexible tool for packaging web applications into SystemLink WebApp .nipkg format for National Instruments Package Manager. Works with any web application framework including Node.js (React, Angular, Vue), Python (Pyodide), .NET (Blazor), and static HTML sites.
Features
- 🚀 Easy Integration: Works seamlessly with any web application
- 📦 Automated Packaging: Builds and packages your app in one command
- 🏪 Plugin Manager Submission Ready: Generate Plugin Manager metadata for display name, icon, category, license, and screenshot submission files without embedding
screenshotFilesin the control file - 🚢 SystemLink Deployment: Create/update WebApps and upload
.nipkgcontent from CLI - 🔐 Secure Authentication:
login/logoutcommands store credentials per-host — no secrets in config files - ⚙️ Flexible Configuration: Optional config files - use CLI flags or JSON config
- 🎯 TypeScript Support: Written in TypeScript with full type definitions
- 🌈 Beautiful CLI: Colorful, informative command-line interface
- 🔧 CI/CD Ready: Easy integration with build pipelines
- 🌍 Cross-Platform: Works on Windows, macOS, and Linux without external dependencies
- 🔌 Framework Agnostic: Works with React, Angular, Vue, Blazor, Pyodide, static HTML, and more
- 🎨 Zero Config: No config files required - just point to your build directory
Prerequisites
- Node.js 20 or higher (only to run the packaging tool)
Installation
Global Installation (Recommended)
npm install -g @ni-kismet/sl-webapp-nipkgProject-specific Installation
# As a dev dependency in your project
npm install --save-dev @ni-kismet/sl-webapp-nipkgQuick Start
Option 1: Minimal Usage (No Config Files)
# Package any web application - just point to the build directory
sl-webapp-nipkg build --build-dir ./dist --name my-app
# With full metadata
sl-webapp-nipkg build \
--build-dir ./dist \
--name my-webapp \
--version "1.0.0" \
--maintainer "Your Name <[email protected]>"Option 2: With Configuration File (Recommended for Node.js projects)
Navigate to your project:
cd my-webapp-projectInitialize configuration (optional):
sl-webapp-nipkg initEdit the generated
nipkg.config.json(all fields optional):{ "package": "my-webapp", "maintainer": "John Doe <[email protected]>", "displayName": "My WebApp", "section": "Dashboard", "license": "MIT", "xbPlugin": "webapp", "slPluginManagerTags": "dashboard,monitoring", "iconFile": "icon.svg", "buildDir": "dist", "buildCommand": "npm run build" }Note: The
package,version,description,homepage, andlicenseare auto-populated from yourpackage.jsonwhen usinginit. Other fields must be specified explicitly innipkg.config.jsonif needed.Build and package:
sl-webapp-nipkg build --build # Build, package, and also emit a schema v2 manifest.json sl-webapp-nipkg build --build --manifest
CLI Commands
sl-webapp-nipkg build
Build and package your web application.
Options
Core Options:
--build-dir <path>- Build output directory to package (required if not in config)--name <name>- Package identifier in^[a-z0-9][a-z0-9._-]*$format (auto-detected from package.json or directory name)--version <version>- Package version (auto-detected from package.json or defaults to 1.0.0)--description <description>- Package description (optional)--maintainer <maintainer>- Maintainer info (defaults to 'Unknown')--output-dir <path>- Output directory for nipkg files (default: dist/nipkg)
Build Options:
-b, --build [command]- Run build command before packaging (optionally specify custom command to override config)-v, --verbose- Enable verbose output--skip-cleanup- Skip cleanup of existing packages--build-suffix <suffix>- Add a suffix to the package name (e.g., build ID for CI/CD)--config <path>- Custom config file path (default: 'nipkg.config.json')--manifest- Generatemanifest.jsonfrom the built.nipkg--manifest-output <path>- Output path for the generated manifest file (default:manifest.json)--source-repo <owner/name>- Optional source repository for generated manifest provenance--release-tag <tag>- Optional release tag for generated manifest provenance; must be used together with--source-repo--source-commit <sha>- Optional source commit SHA for generated manifest provenance
Note: CLI options override config file values, which override package.json values.
Examples
Node.js Projects:
# Build and package (runs buildCommand from config)
sl-webapp-nipkg build --build
# Build with custom command (overrides config)
sl-webapp-nipkg build --build "npm run build:production"
# Use existing build output (no build step)
sl-webapp-nipkg build
# Build with build ID suffix for CI/CD
sl-webapp-nipkg build --build --build-suffix "${BUILD_ID}"
# Build, package, and generate manifest.json in one step
sl-webapp-nipkg build --build --manifest
# Build, package, and write manifest to a custom path
sl-webapp-nipkg build --build --manifest --manifest-output submissions/manifest.json
# Verbose output with custom config
sl-webapp-nipkg build --build --verbose --config my-nipkg.config.jsonNon-Node.js Projects (Python, Blazor, Static Sites):
# Python Pyodide webapp
sl-webapp-nipkg build --build-dir ./public --name my-pyodide-app
# .NET Blazor webapp
sl-webapp-nipkg build \
--build-dir ./bin/Release/net8.0/publish \
--name my-blazor-app \
--version "2.0.0"
# Static HTML site
sl-webapp-nipkg build --build-dir ./dist --name my-static-site --version 1.0.0
# With full metadata
sl-webapp-nipkg build \
--build-dir ./output \
--name my-webapp \
--version "1.5.0" \
--description "My awesome webapp" \
--maintainer "Team <[email protected]>"sl-webapp-nipkg init
Initialize a nipkg.config.json file in the current directory. Auto-populates package, maintainer, homepage, and license from package.json, and sets section: "Dashboard" and xbPlugin: "webapp" as starter defaults.
sl-webapp-nipkg generate-manifest
Generate a schema v2 manifest.json for a reviewed .nipkg artifact. The command hashes the artifact bytes and writes the thin submission-manifest shape defined by app-manifest.schema.json.
Options
--output <path>- Output path for manifest file (default:manifest.json)--nipkg-file <path>- Path to the.nipkgartifact to reference and hash--source-repo <owner/name>- Optional source repository inowner/nameformat--release-tag <tag>- Optional release tag for the reviewed artifact; must be used together with--source-repo--source-commit <sha>- Optional 40-character lowercase commit SHA for the build that produced the artifact
The generated manifest always contains schemaVersion: 2, the .nipkg basename as nipkgFile, and a sha256 hash of the exact .nipkg bytes.
Example
# Generate manifest after build
sl-webapp-nipkg build
sl-webapp-nipkg generate-manifest \
--nipkg-file dist/nipkg/my-app_1.0.0_all.nipkg \
--source-repo ni/sl-webapp-nipkg \
--release-tag v1.0.0sl-webapp-nipkg login
Authenticate with a SystemLink host and store credentials locally so you don't need to pass --api-key on every command.
# Interactive — prompts for host and API key
sl-webapp-nipkg login
# Non-interactive — prompts only for API key
sl-webapp-nipkg login --host https://systemlink.example.comCredentials are verified against the SystemLink API before being stored. A 401 response means the key was wrong and nothing is saved.
- Credentials are stored in
~/.sl-webapp-nipkg/credentials.json(permissions set to0600on Unix) - Multiple hosts can be stored side by side
- The
deployanddeletecommands automatically use the stored credential when no explicit key is provided
Login Options
--host <url>- SystemLink host URL (prompted interactively if omitted)--timeout <ms>- Verification request timeout in milliseconds (default30000)
sl-webapp-nipkg logout
Remove the stored credential for a host.
# Interactive — prompts for host
sl-webapp-nipkg logout
# Non-interactive
sl-webapp-nipkg logout --host https://systemlink.example.comLogout Options
--host <url>- SystemLink host URL (prompted interactively if omitted)
sl-webapp-nipkg deploy
Deploy a .nipkg to SystemLink WebApps.
Create mode performs two API calls:
POST {host}/niapp/v1/webapps(creates WebApp and returnsid)PUT {host}/niapp/v1/webapps/{id}/content(uploads.nipkg)
Update mode performs three API calls:
GET {host}/niapp/v1/webapps/{id}(fetches current data to preserve unchanged fields)PUT {host}/niapp/v1/webapps/{id}(updates only provided metadata fields)PUT {host}/niapp/v1/webapps/{id}/content(uploads.nipkg)
The tool can automatically discover and select .nipkg files from your build directory. For updates, you only need to provide the --app-id and any fields you want to change; current values are fetched from SystemLink.
Deploy Options
--nipkg <path>- Path to a.nipkgfile or a directory to search for.nipkgfiles (optional - see auto-discovery below)-b, --build [command]- Run build command before deploying (optionally specify custom command)--latest- Skip interactive selection and auto-pick the newest.nipkg(useful for CI/CD)--build-suffix <suffix>- Suffix appended to nipkg filename when--buildis used (e.g., CI build ID)--skip-cleanup- Skip cleanup of existing nipkg files when--buildis used--host <url>- SystemLink host URL--api-key <key>- API key used asx-ni-api-key--name <name>- WebApp name (optional for update; falls back to top-leveldisplayNamefrom config)--workspace-id <id>- Workspace UUID (required for create, optional for update)--policy-ids <ids>- Comma-separated policy IDs (optional)--properties <json>- JSON properties object (optional, includeversionfield if desired)--update- Update existing WebApp instead of creating new--app-id <id>- Existing WebApp ID (required with--update)--timeout <ms>- Request timeout in milliseconds (default30000)--config <path>- Config path (defaultnipkg.config.json)-v, --verbose- Verbose output
Auto-discovery of nipkg Files
If --nipkg is not provided, the tool will search for .nipkg files in:
- The output directory from the
--buildstep (if--buildwas used) - The
outputDirfromnipkg.config.json(defaults todist/nipkg)
When --nipkg points to a directory, the newest .nipkg inside it is selected automatically.
Default behavior (interactive selection):
sl-webapp-nipkg deploy --update --app-id <id>
# If multiple .nipkg files are found, prompts you to pick oneAuto-select latest (non-interactive, for CI/CD):
sl-webapp-nipkg deploy --update --app-id <id> --latest
# Always picks the newest .nipkg without promptingBuild Before Deploy
Build and deploy in a single command:
# Build with default build command from config, then deploy
sl-webapp-nipkg deploy \
--update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
--build \
--latest
# Build with custom command, then deploy
sl-webapp-nipkg deploy \
--update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
--build "npm run build:prod" \
--latestSmart Updates (Fetch Current Data)
For update operations, the tool fetches current WebApp data from SystemLink. You only need to provide the fields you want to change:
# Update only the properties, keep name/workspace/policyIds unchanged
sl-webapp-nipkg deploy \
--update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
--nipkg ./dist/nipkg/my-app_1.0.1_all.nipkg \
--host "$SYSTEMLINK_HOST" \
--api-key "$SYSTEMLINK_API_KEY" \
--properties '{"version":"1.0.1","description":"Updated version"}'
# Update only the policy IDs, keep other metadata
sl-webapp-nipkg deploy \
--update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
--nipkg ./dist/nipkg/my-app_1.0.1_all.nipkg \
--host "$SYSTEMLINK_HOST" \
--api-key "$SYSTEMLINK_API_KEY" \
--policy-ids "policy-1,policy-2"Deploy Examples
Create new WebApp:
sl-webapp-nipkg deploy \
--nipkg ./dist/nipkg/my-app_1.0.0_all.nipkg \
--host https://test.lifecyclesolutions.ni.com \
--api-key "$SYSTEMLINK_API_KEY" \
--name "My WebApp" \
--workspace-id "45bc27cf-85c6-485c-945d-a51e664f511d" \
--policy-ids "policy-1,policy-2" \
--properties '{"description":"my app","version":"1.0.0"}'Update existing WebApp (minimal - only app-id required):
sl-webapp-nipkg deploy \
--nipkg ./dist/nipkg/my-app_1.0.1_all.nipkg \
--host https://test-api.lifecyclesolutions.ni.com \
--api-key "$SYSTEMLINK_API_KEY" \
--update \
--app-id "35075240-68a1-431b-afe1-9ce76da46de3"Build and deploy in single command:
sl-webapp-nipkg deploy \
--build \
--update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
--latest \
--host https://test-api.lifecyclesolutions.ni.com \
--api-key "$SYSTEMLINK_API_KEY" \
--properties '{"version":"1.0.1"}'Environment variable fallback:
SYSTEMLINK_HOSTSYSTEMLINK_API_KEY
Precedence: CLI flags > environment variables > stored login credential (sl-webapp-nipkg login) > defaults (where applicable).
Security note: The API key is intentionally not a
nipkg.config.jsonfield. The recommended workflow is to runsl-webapp-nipkg loginonce per host — credentials are stored in~/.sl-webapp-nipkg/credentials.jsonwith restricted permissions. For CI/CD pipelines, use theSYSTEMLINK_API_KEYenvironment variable or the--api-keyflag.
Configuration
Configuration File (nipkg.config.json)
All fields are optional. CLI options override config file values. Config file values override package.json auto-detection.
Legacy appStore* fields are still accepted as input during the transition, but generated manifests and control files always use the new Plugin Manager field names.
| Property | Type | Required | Description |
| --------------------------------- | ---------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| package | string | ❌ | Unique package identifier — lowercase with hyphens/dots/underscores (auto-detected from package.json) |
| name | string | ❌ | Deprecated. Use package instead |
| version | string | ❌ | Package version (auto-detected from package.json, defaults to 1.0.0) |
| displayName | string | ❌ | Human-readable display name shown in the catalog card |
| description | string | ❌ | Short description (auto-detected from package.json) |
| section | string | ❌ | Fine-grained catalog section (e.g. 'Dashboard', 'Administration', 'Monitoring') |
| maintainer | string | ❌ | Maintainer information (defaults to 'Unknown') |
| homepage | string | ❌ | Homepage / repository URL (auto-detected from package.json) |
| license | string | ❌ | SPDX license identifier — required for Plugin Manager submissions (auto-detected from package.json) |
| xbPlugin | string | ❌ | Top-level plugin type: 'webapp', 'notebook', 'dashboard', 'routine', or 'bundle' |
| slPluginManagerTags | string | ❌ | Comma-separated Plugin Manager tags (e.g. 'assets,calibration') |
| slPluginManagerMinServerVersion | string | ❌ | Minimum SystemLink server version (e.g. '2024 Q4') |
| iconFile | string | ❌ | Path to the primary app icon (SVG or PNG, max 128×128 px); included in generated manifests and embedded as XB-SlPluginManagerIcon |
| screenshotFiles | string[] | ❌ | Paths to up to 3 screenshot files (PNG, max 800×600 px) preserved in config for higher-level tooling |
| tags | string | ❌ | Standard nipkg Tags: field (falls back to slPluginManagerTags, then appStoreTags if omitted) |
| extraControlFields | object | ❌ | Arbitrary extra control file fields written verbatim as Key: value lines |
| architecture | string | ❌ | Target architecture (default: 'windows_all' for Plugin Manager submissions) |
| userVisible | boolean | 'yes' | 'no' | ❌ | Whether the package appears in the Plugin Manager catalog (default: 'yes') |
| buildDir | string | ❌* | Build output directory (e.g., 'dist', 'build') — *Required if not provided via CLI |
| buildCommand | string | ❌ | Custom build command (default: 'npm run build') |
| outputDir | string | ❌ | Custom nipkg output directory (default: 'dist/nipkg') |
| buildSuffix | string | ❌ | Optional suffix for package filename (e.g., build ID for CI/CD) |
| depends | string[] | ❌ | Package dependencies |
| deployment | object | ❌ | SystemLink deployment defaults used by deploy command |
Example Configuration
{
"package": "my-webapp",
"version": "1.2.3",
"description": "A SystemLink WebApp for National Instruments",
"displayName": "My WebApp",
"section": "Dashboard",
"maintainer": "John Doe <[email protected]>",
"homepage": "https://github.com/org/my-webapp",
"license": "MIT",
"xbPlugin": "webapp",
"slPluginManagerTags": "assets,calibration",
"slPluginManagerMinServerVersion": "2024 Q4",
"iconFile": "assets/icon.svg",
"architecture": "all",
"userVisible": "yes",
"buildDir": "dist",
"buildCommand": "npm run build",
"depends": ["ni-systemlink-server >= 2023.1"],
"outputDir": "packages",
"deployment": {
"host": "https://test.lifecyclesolutions.ni.com",
"name": "my-webapp",
"workspaceId": "45bc27cf-85c6-485c-945d-a51e664f511d",
"policyIds": [],
"properties": {
"description": "my webapp",
"version": "1.2.3"
},
"timeout": 30000
}
}Note: The outputDir is used by the deploy command for automatic .nipkg file discovery.
Framework Examples
Non-Node.js Projects
Python Pyodide:
# No config file needed
sl-webapp-nipkg build \
--build-dir ./public \
--name my-pyodide-app \
--version 1.0.0 \
--maintainer "Python Team <[email protected]>".NET Blazor:
# Package Blazor WebAssembly output
sl-webapp-nipkg build \
--build-dir ./bin/Release/net8.0/publish/wwwroot \
--name my-blazor-app \
--version 2.0.0Static HTML:
# Package any static web content
sl-webapp-nipkg build --build-dir ./dist --name my-static-siteNode.js Projects
React:
{
"maintainer": "Your Name <[email protected]>",
"buildDir": "build",
"buildCommand": "npm run build"
}Vue
{
"maintainer": "Your Name <[email protected]>",
"buildDir": "dist",
"buildCommand": "npm run build"
}Angular
{
"maintainer": "Your Name <[email protected]>",
"buildDir": "dist/my-app/browser",
"buildCommand": "npm run build"
}Note: For Angular projects, specify the build configuration in buildCommand: "buildCommand": "ng build --configuration production" or use an npm script like "build": "ng build --configuration production" and let the default npm run build work.
Next.js
{
"maintainer": "Your Name <[email protected]>",
"buildDir": "out",
"buildCommand": "npm run build && npm run export"
}Integration with Node.js Projects
Add to package.json Scripts
{
"scripts": {
"start": "npm run dev",
"dev": "vite",
"build": "vite build",
"build:nipkg": "sl-webapp-nipkg build --build",
"package:nipkg": "sl-webapp-nipkg build"
}
}Use in npm Scripts
# Build and package for production
npm run build:nipkg
# Package existing build
npm run package:nipkgProgrammatic Usage
import {
SystemLinkNipkgBuilder,
NipkgConfig,
BuildOptions,
} from "@ni-kismet/sl-webapp-nipkg";
const config: NipkgConfig = {
package: "my-app",
version: "1.0.0",
description: "My SystemLink WebApp",
displayName: "My App",
section: "Dashboard",
maintainer: "John Doe <[email protected]>",
license: "MIT",
xbPlugin: "webapp",
buildDir: "dist",
};
const options: BuildOptions = {
build: true,
verbose: true,
};
const builder = new SystemLinkNipkgBuilder(config, options);
await builder.build();CI/CD Integration
The --build-suffix option allows you to create unique package names for PR/branch builds while keeping clean names for production releases.
Output examples:
- PR builds:
my-app_1.0.0.12345_all.nipkg(includes build ID) - Main/production:
my-app_1.0.0_all.nipkg(standard naming)
GitHub Actions
name: Build and Package
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build and Package (PR)
if: github.event_name == 'pull_request'
run: sl-webapp-nipkg build --build --build-suffix "${{ github.run_number }}"
- name: Build and Package (Main)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: sl-webapp-nipkg build --build
- name: Upload Package
uses: actions/upload-artifact@v3
with:
name: nipkg-package
path: dist/nipkg/*.nipkgAzure DevOps
trigger:
- main
pool:
vmImage: "windows-latest"
steps:
- task: NodeTool@0
inputs:
versionSpec: "20.x"
- script: npm ci
displayName: "Install dependencies"
- script: |
if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
npm run build
sl-webapp-nipkg build
else
npm run build
sl-webapp-nipkg build --build-suffix "$(Build.BuildId)"
fi
displayName: "Build and package"
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: "dist/nipkg"
ArtifactName: "nipkg-package"Package Structure
After packaging, your project will have this structure:
your-webapp-project/
├── dist/ # Your build output
│ ├── index.html
│ ├── assets/
│ └── ...
├── dist/nipkg/ # NIPKG packaging
│ ├── your-app_1.0.0_all.nipkg # Final package
│ └── temp-source/ # Temporary (auto-cleaned)
├── nipkg.config.json # Package configuration
└── package.json # NPM scriptsRequirements
- Node.js 20+
Development
Building from Source
git clone https://github.com/ni/sl-webapp-nipkg.git
cd sl-webapp-nipkg
npm install
npm run buildLocal Development
# Link globally for testing
npm link
# Use in any Node.js project
cd /path/to/your/project
sl-webapp-nipkg --helpTroubleshooting
Common Issues
"API key is required"
- Run
sl-webapp-nipkg login --host <url>to store credentials for this host, or - Pass
--api-key "$SYSTEMLINK_API_KEY"on the command line, or - Set the
SYSTEMLINK_API_KEYenvironment variable
"Build directory not found"
- Provide build directory via CLI:
--build-dir ./dist - Or run with
--buildflag to build before packaging - Check that your build command runs successfully
- Verify
buildDirin nipkg.config.json points to the correct directory
"buildDir is required"
- Provide via CLI:
sl-webapp-nipkg build --build-dir ./dist - Or add to nipkg.config.json:
"buildDir": "dist"
Package dependencies missing
- Add required NI runtime dependencies to
dependsarray in config - Example:
"depends": ["ni-systemlink-server >= 2023.1"]
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Submit a pull request
License
MIT © National Instruments
Support
Made with ❤️ for the National Instruments community
