CLI tool for Nussknacker Cloud - produce and consume messages with ease.
npm install -g @nussknacker/clinpm install @nussknacker/cli
npx nu-cli --helpcd cli
npm install
npm run build
npm link-
Initialize configuration:
nu-cli init
-
Start consuming messages:
nu-cli consume
Copy the webhook URL to Nu Cloud subscription.
-
Start producing messages:
nu-cli produce
Initialize configuration file interactively.
nu-cli init # Interactive mode
nu-cli init --no-interactive # Use template
nu-cli init -o myconfig.yaml # Custom output pathCreates:
nu-config.yaml- Configuration filemessage-template.yaml- Default message template
Interactive prompts:
- Nu Cloud API URL
- Username
- Password
- Delay between messages
- Multiple profiles support
Send a single message to Nu Cloud (manual mode).
# Using inline JSON/YAML data
nu-cli send --data '{"name": "John", "age": 30}'
# Using a file
nu-cli send --file message.json
# Using a custom template
nu-cli send --template custom-template.yaml
# Dry run
nu-cli send --dry-run
# With specific profile
nu-cli send --profile production --data '{"event": "user_login"}'Priority: --data > --file > --template > config template > default template
Options:
-C, --config <path>- Config file path (default:nu-config.yaml)-p, --profile <name>- Config profile to use-d, --data <json>- Message data as JSON/YAML string-f, --file <path>- Message data from file (JSON/YAML)-t, --template <path>- Template file to use (overrides config)--dry-run- Show what would be sent without sending
Send messages to Nu Cloud continuously.
nu-cli produce # Start continuous production
nu-cli produce -c 10 # Send 10 messages and exit
nu-cli produce -c 1 # Send single message
nu-cli produce --dry-run # Preview without sending
nu-cli produce -C custom.yaml # Use custom config
nu-cli produce --profile production # Use named profile
nu-cli produce --delay 5 # Override delay (seconds)
nu-cli produce --template custom.yaml # Use custom templateOptions:
-C, --config <path>- Config file path (default:nu-config.yaml)-p, --profile <name>- Config profile to use-d, --delay <seconds>- Delay between messages (overrides config)-t, --template <path>- Template file to use (overrides config)-c, --count <number>- Send specified number of messages and exit--dry-run- Show what would be sent without sending
Start webhook consumer with tunnel support.
# With auto-detected tunnel (cloudflared or tailscale)
nu-cli consume
# With specific tunnel provider
nu-cli consume --tunnel cloudflared
nu-cli consume --tunnel tailscale
# With custom webhook path (tailscale)
nu-cli consume --tunnel tailscale --tunnel-path /my-webhook
# Without tunnel (local only)
nu-cli consume --no-tunnel
# Custom port
nu-cli consume --port 8080
# Debug mode
nu-cli consume --debug
# JSON output mode (for piping)
nu-cli consume --json
nu-cli consume --json | jq '.email'
nu-cli consume --json --no-tunnel > messages.jsonlOptions:
-p, --port <number>- Server port (default:6555)--tunnel <provider>- Tunnel provider:cloudflared,tailscale,none(default:auto)--tunnel-path <path>- Webhook path for tunnel (tailscale only, default:/webhook)--no-tunnel- Skip tunnel setup--exact-path- Accept webhooks only on root path/(default: wildcard/*)--debug- Enable debug logging--json- Output raw JSON only (suitable for piping to other tools)
Tunnel Providers:
- cloudflared: Automatic, requires
cloudflaredinstalled - tailscale: Requires
tailscaleinstalled and authenticated - auto: Detects which tool is available (cloudflared first, then tailscale)
- none: Local server only (no public URL)
Webhook Path:
By default, the consumer accepts webhooks on any path (wildcard /*). Each message includes the request path in output:
- Normal mode: Shows path with 📍 emoji before each message
- JSON mode: Includes
pathfield in output object{"path": "/webhook", "data": {...}}
Use --exact-path to accept webhooks only on root path /:
nu-cli consume --exact-pathWith --exact-path:
- Only
/is accepted (other paths return 404) - Path is not shown in output
- JSON mode outputs just the message data
JSON Mode:
The --json flag outputs only raw JSON messages (one per line) to stdout, suppressing all other logs.
- Default (wildcard):
{"path": "/webhook", "data": {...}} - With
--exact-path:{"email": "...", ...}(just message data)
Perfect for piping to other tools:
# Filter emails with jq
nu-cli consume --json | jq '.email'
# Save to JSONL file
nu-cli consume --json --no-tunnel > messages.jsonl
# Process with custom script
nu-cli consume --json | while read -r msg; do
echo "$msg" | jq '.userId' >> user_ids.txt
doneErrors are still sent to stderr in JSON format: {"error": "message"}
Install tunnel tools:
# Cloudflared
brew install cloudflare/cloudflare/cloudflared
# Tailscale
# Visit https://tailscale.com/downloadGenerate Avro schema from message template.
nu-cli schema # Print to stdout
nu-cli schema -o schema.avsc # Save to fileOptions:
-o, --output <path>- Output file (stdout if not specified)
api:
url: "https://your-api.cloud.nussknacker.io/topics/your-topic"
username: "publisher"
password: "" # Leave empty for endpoints without authentication
producer:
delay_seconds: 1
template_path: "./message-template.yaml" # Path to message template (created by init)Optional Authentication:
If your endpoint doesn't require authentication, leave the password empty. If the endpoint requires auth but you provide no password, you'll get a 401 error.
Use profiles to manage different environments (dev/staging/prod):
# Default configuration
api:
url: "https://dev.cloud.nussknacker.io/topics/dev"
username: "publisher"
password: "dev_pass"
producer:
delay_seconds: 1
# Named profiles
profiles:
production:
api:
url: "https://prod.cloud.nussknacker.io/topics/prod"
password: "prod_pass"
staging:
api:
url: "https://staging.cloud.nussknacker.io/topics/staging"
password: "staging_pass"Usage:
nu-cli produce --profile production
nu-cli produce --profile stagingProfiles are merged with the default configuration, so you only need to specify the values that differ.
Templates define the structure of generated messages using faker.js for realistic test data.
The CLI includes a default template with faker.js support:
name: "::person.fullName"
email: "::internet.email"
age: "::number.int({min:18, max:65})"
timestamp: "current_timestamp"Create your own template file:
# my-template.yaml
userId: "::string.uuid"
username: "::internet.userName"
company: "::company.name"
location: "::location.city"
price: "::commerce.price({min:10, max:1000})"
status: '::helpers.arrayElement(["active","pending","suspended"])'Use it:
# In nu-config.yaml
producer:
template_path: "./my-template.yaml"
# Or via CLI flag
nu-cli produce --template ./my-template.yaml
nu-cli send --template ./my-template.yamlFull faker.js API support using :: prefix. Parameters are parsed as JSON and spread as ...args to faker functions.
Simple calls (no parameters):
name: "::person.fullName"
email: "::internet.email"
company: "::company.name"
address: "::location.streetAddress"
uuid: "::string.uuid"With parameters (use JSON syntax, wrap in single quotes in YAML):
# Object parameters
age: "::number.int({min:18, max:65})"
price: "::commerce.price({min:10, max:1000})"
# Single argument (array)
event: '::helpers.arrayElement(["login","logout","purchase"])'
# Multiple arguments (array, options object)
tags: '::helpers.arrayElements(["tech","sports","music"], {min:2, max:4})'
# Multiple arguments (coordinates, distance, isMetric)
location: '::location.nearbyGPSCoordinate([52.52, 13.40], 10, true)'How it works:
::category.methodmaps tofaker.category.method()- Parameters are parsed as JSON:
({min:18,max:65})→fn({min:18, max:65}) - Supports any faker.js function signature exactly as documented
Special values:
current_timestamp→ ISO 8601 timestamp
Complete event template:
# events.yaml
userId: "::string.uuid"
eventType: '::helpers.arrayElement(["login","logout","purchase","click"])'
timestamp: "current_timestamp"
user:
name: "::person.fullName"
email: "::internet.email"
age: "::number.int({min:18, max:65})"
metadata:
ip: "::internet.ip"
device: '::helpers.arrayElement(["mobile","desktop","tablet"])'
browser: '::helpers.arrayElement(["Chrome","Firefox","Safari","Edge"])'
location: "::location.city"
coordinates: '::location.nearbyGPSCoordinate([52.52, 13.40], 10, true)'E-commerce order:
orderId: "::string.uuid"
products: '::helpers.arrayElements(["laptop","phone","tablet","watch","headphones"], {min:1, max:3})'
totalPrice: "::commerce.price({min:50, max:5000})"
status: '::helpers.arrayElement(["pending","processing","shipped","delivered"])'
customer:
id: "::string.uuid"
name: "::person.fullName"
email: "::internet.email"
shippingAddress:
street: "::location.streetAddress"
city: "::location.city"
country: "::location.country"
zipCode: "::location.zipCode"
createdAt: "current_timestamp"nu-cli produce --delay 3nu-cli produce --profile production --oncenu-cli produce --dry-run --once
{
"name": "Alice"
}nu-cli schema -o schema.avscnu-cli consume --port 9000 --no-tunnelcli/
├── src/
│ ├── commands/ # CLI commands (produce, consume, schema, init)
│ ├── lib/
│ │ ├── producer/ # Producer logic
│ │ ├── consumer/ # Consumer logic (Fastify server + tunnel)
│ │ ├── template/ # Template engine & schema generation
│ │ └── config/ # Config loading & validation
│ ├── utils/ # Utilities (logger, errors, process)
│ └── templates/ # Config templates
├── dist/ # Build output
├── package.json
├── tsconfig.json
└── tsup.config.ts
# Development mode (watches for changes)
npm run dev -- produce --dry-run --once
# Type checking
npm run typecheck
# Build for production
npm run build
# Link globally for local testing
npm link
# Test all commands
nu-cli --help
nu-cli init --no-interactive
nu-cli schema
nu-cli produce --dry-run --once
nu-cli consume --no-tunnel- Edit source files in
src/ - Run
npm run buildto compile - Test with
nu-clicommand
Run nu-cli init to create a config file, or specify a custom path with --config.
Edit your nu-config.yaml and set a real password (not your_password).
Install cloudflared or use --no-tunnel flag:
nu-cli consume --no-tunnelGenerate the correct Avro schema for your message template:
nu-cli schemaThen create a topic in Nu Cloud with this schema.
This package uses semantic-release for automated versioning and publishing to npm.
- Merge to
master→ stable version (e.g.,0.2.0) - Published with
latesttag - Install:
npm install @nussknacker/cli
- Merge to
dev→ beta version (e.g.,0.2.0-beta.1) - Published with
betatag - Install:
npm install @nussknacker/cli@beta
Beta workflow:
- Create PR to
devbranch with Conventional Commits - Merge → GitHub Actions publishes beta (
0.2.0-beta.1) - Test beta version
Stable workflow:
- Merge
dev→master(after testing) - GitHub Actions publishes stable (
0.2.0)
feat: add new feature→ MINOR version bump (0.1.0 → 0.2.0)fix: resolve bug→ PATCH version bump (0.1.0 → 0.1.1)feat!: breaking change→ MAJOR version bump (0.1.0 → 1.0.0)docs: update readme→ No releasechore: update deps→ No release
dev: 0.2.0-beta.1 → 0.2.0-beta.2 → 0.2.0-beta.3
↓ merge to master
master: 0.2.0 → 0.2.1
See CONTRIBUTING.md for full workflow details.
# Dry run (no publish)
npm run build
npm publish --dry-run
# Test semantic-release locally
npx semantic-release --dry-run --no-ci
# Test beta from dev branch
git checkout dev
npx semantic-release --dry-run --no-ciApache-2.0