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

@wahlu/cli

v0.1.1

Published

Wahlu CLI — manage your social media from the terminal

Readme

Wahlu CLI

Manage your social media from the terminal. Works for humans, AI agents, and CI/CD pipelines.

Install

npm install -g @wahlu/cli

Authentication

Generate an API key at wahlu.com under Settings > API Keys.

# Option 1: Save to config
wahlu auth login wahlu_live_abc123...

# Option 2: Environment variable
export WAHLU_API_KEY=wahlu_live_abc123...

Authentication priority:

  1. WAHLU_API_KEY environment variable (highest)
  2. Saved key in ~/.config/wahlu/config.json

Quick start

# List your brands
wahlu brand list

# Set a default brand so you don't need --brand every time
wahlu brand switch <brand-id>

# List content items (via post alias)
wahlu post list

# Create a content item
wahlu post create --name "Monday motivation" \
  --copy-mode single \
  --single-copy '{"caption":"Rise and grind","hashtags":[]}' \
  --instagram '{"post_type":"GRID_POST"}'

# Schedule it
wahlu schedule create <content-item-id> \
  --at 2026-03-15T14:00:00Z \
  --integrations <integration-id>

# Upload media
wahlu media upload ./photo.jpg

# List what's been published
wahlu publication list

Commands

Auth

| Command | Description | |---------|-------------| | wahlu auth login <key> | Save API key to ~/.config/wahlu/config.json | | wahlu auth logout | Remove saved API key | | wahlu auth status | Show current auth method, masked key, and config |

Brands

Brands represent social media profiles. All content items, media, publish runs, and queues belong to a brand.

| Command | Description | |---------|-------------| | wahlu brand list | List all brands | | wahlu brand get <id> | Get full brand details | | wahlu brand switch <id> | Set default brand for all commands |

Response fields:

| Field | Type | Description | |-------|------|-------------| | id | string | Brand ID | | name | string | Brand name | | description | string|null | Brand description | | logo_url | string|null | Logo URL | | timezone | string|null | IANA timezone (e.g. Australia/Sydney) | | website | string|null | Brand website URL | | business_category | string|null | Business category | | brand_kit | object|null | Brand kit (fonts, colours, voice) | | content_preferences | object|null | CTA, logo frequency settings | | image_posting | object|null | Image posting preferences | | video_posting | object|null | Video posting preferences | | created_at | string | ISO 8601 timestamp | | updated_at | string | ISO 8601 timestamp |

Content items (post command)

Content items are the core content unit. Captions/hashtags are canonical via copy_mode + single_copy/platform_copy, while platform settings control media/post options.

| Command | Description | |---------|-------------| | wahlu post list | List content items (paginated) | | wahlu post get <id> | Get full content item details | | wahlu post create [options] | Create a new content item | | wahlu post update <id> [options] | Update a content item (partial update) | | wahlu post delete <id> | Permanently delete a content item |

Create/update options:

| Option | Description | |--------|-------------| | --name <name> | Content item name (max 500 chars) | | --copy-mode <mode> | Canonical caption mode: single or per_platform | | --single-copy <json> | Canonical shared copy JSON: {"caption":"...","hashtags":["..."],"title":"..."} | | --platform-copy <json> | Canonical per-platform copy JSON map | | --instagram <json> | Instagram settings as JSON string | | --tiktok <json> | TikTok settings as JSON string | | --facebook <json> | Facebook settings as JSON string | | --youtube <json> | YouTube settings as JSON string | | --linkedin <json> | LinkedIn settings as JSON string | | --labels <ids...> | Label IDs to attach (max 50) |

Response fields:

| Field | Type | Description | |-------|------|-------------| | id | string | Content item ID | | name | string|null | Content item name | | brand_id | string | Brand ID | | label_ids | string[] | Attached label IDs | | created_by | string|null | Creator user ID | | thumbnail_timestamp | number | Thumbnail timestamp (seconds) | | copy_mode | string|null | single | per_platform | | single_copy | object|null | Canonical shared caption + hashtags | | platform_copy | object|null | Canonical per-platform caption map | | instagram_settings | object|null | Instagram configuration | | tiktok_settings | object|null | TikTok configuration | | facebook_settings | object|null | Facebook configuration | | youtube_settings | object|null | YouTube configuration | | linkedin_settings | object|null | LinkedIn configuration | | created_at | string | ISO 8601 timestamp | | updated_at | string | ISO 8601 timestamp |

Platform settings reference

Captions and hashtags come from canonical copy fields:

  • --copy-mode single --single-copy '{"caption":"...","hashtags":["..."]}'
  • --copy-mode per_platform --platform-copy '{"instagram":{"caption":"...","hashtags":[]}}'

Instagram (--instagram):

| Field | Type | Values | |-------|------|--------| | post_type | string | GRID_POST | REEL | STORY | | media_ids | string[] | Media IDs to attach | | trial_reel | boolean | Post as trial reel (non-followers first) | | graduation_strategy | string | MANUAL | SS_PERFORMANCE |

TikTok (--tiktok):

| Field | Type | Values | |-------|------|--------| | post_type | string | VIDEO | IMAGE | CAROUSEL | | media_ids | string[] | Media IDs to attach | | privacy_level | string | PUBLIC_TO_EVERYONE | MUTUAL_FOLLOW_FRIENDS | FOLLOWER_OF_CREATOR | SELF_ONLY | | allow_comment | boolean | Allow comments (default: true) | | allow_duet | boolean | Allow duets (default: true, video only) | | allow_stitch | boolean | Allow stitches (default: true, video only) | | auto_add_music | boolean | Auto-add music (photo/carousel only) | | is_aigc | boolean | Disclose as AI-generated content | | is_commercial_content | boolean | Mark as commercial/branded content |

Facebook (--facebook):

| Field | Type | Values | |-------|------|--------| | post_type | string | FB_POST | FB_STORY | FB_REEL | FB_TEXT | | media_ids | string[] | Media IDs to attach |

YouTube (--youtube):

| Field | Type | Values | |-------|------|--------| | title | string | Video title | | description | string | Video description | | post_type | string | YT_SHORT | YT_VIDEO | | media_ids | string[] | Media IDs to attach | | privacy_level | string | PUBLIC | UNLISTED | PRIVATE | | notify_subscribers | boolean | Notify subscribers on publish |

LinkedIn (--linkedin):

| Field | Type | Values | |-------|------|--------| | post_type | string | LI_TEXT | LI_IMAGE | LI_VIDEO | LI_ARTICLE | | media_ids | string[] | Media IDs to attach | | visibility | string | PUBLIC | CONNECTIONS | | title | string | Article title (LI_ARTICLE only) | | original_url | string | Article URL (LI_ARTICLE only) |

Examples:

# Instagram grid post
wahlu post create --name "Photo post" \
  --copy-mode single \
  --single-copy '{"caption":"Hello!","hashtags":[]}' \
  --instagram '{"post_type":"GRID_POST","media_ids":["mid-123"]}'

# Cross-platform video
wahlu post create --name "Video" \
  --copy-mode per_platform \
  --platform-copy '{"tiktok":{"caption":"Check this out","hashtags":["video"]},"instagram":{"caption":"Check this out","hashtags":[]}}' \
  --tiktok '{"post_type":"VIDEO","media_ids":["mid-123"]}' \
  --instagram '{"post_type":"REEL","media_ids":["mid-123"]}'

# LinkedIn article
wahlu post create --name "Article share" \
  --copy-mode single \
  --single-copy '{"caption":"Read our latest","hashtags":[]}' \
  --linkedin '{"post_type":"LI_ARTICLE","original_url":"https://example.com","title":"Our Post"}'

Publish runs (schedule command)

Schedule content items for future publishing to specific integrations.

| Command | Description | |---------|-------------| | wahlu schedule list | List publish runs (paginated) | | wahlu schedule create <content-item-id> | Schedule a content item | | wahlu schedule update <publish-run-id> | Update a publish run (reschedule/retarget) | | wahlu schedule delete <id> | Remove a publish run (does not delete the content item) |

Create options:

| Option | Required | Description | |--------|----------|-------------| | --at <datetime> | Yes | ISO 8601 datetime (e.g. 2026-03-15T14:00:00Z) | | --integrations <ids...> | Yes | Integration IDs to publish to (max 20) |

Response fields:

| Field | Type | Description | |-------|------|-------------| | id | string | Publish run ID | | content_item_id | string | Referenced content item ID | | scheduled_at | string | ISO 8601 datetime | | integration_ids | string[] | Integration IDs | | status | string | e.g. ready_for_processing, published, failed | | approval_status | string|null | Approval status | | source | string|null | api for API-created entries | | failure_reason | string|null | Failure reason | | created_at | string | ISO 8601 timestamp | | updated_at | string | ISO 8601 timestamp |

Example:

wahlu schedule create content-abc \
  --at 2026-03-15T14:00:00Z \
  --integrations int-123 int-456

Queues

Queues define recurring time slots for automatic publishing.

| Command | Description | |---------|-------------| | wahlu queue list | List all queues | | wahlu queue add <queue-id> <content-item-id> | Append a content item to a queue |

Response fields:

| Field | Type | Description | |-------|------|-------------| | id | string | Queue ID | | name | string | Queue name | | active | boolean | Whether the queue is active | | mode | string | Queue mode | | times_of_day | string[] | Scheduled times (e.g. ["09:00","17:00"]) | | timezone | string|null | IANA timezone | | next_run_at | string|null | Next scheduled publishing time | | loop | boolean | Whether to loop through posts | | content_item_ids | string[] | Ordered content item IDs in the queue | | integration_ids | string[] | Integration IDs | | created_at | string | ISO 8601 timestamp | | updated_at | string | ISO 8601 timestamp |

Media

Upload images and videos to your media library.

| Command | Description | |---------|-------------| | wahlu media list | List media files (paginated) | | wahlu media upload <file> | Upload a local file | | wahlu media delete <id> | Permanently delete a media file |

Supported formats: .png, .jpg, .jpeg, .gif, .webp, .mp4, .mov, .webm

Response fields:

| Field | Type | Description | |-------|------|-------------| | id | string | Media ID (use in media_ids arrays) | | file_name | string | Original filename | | content_type | string | MIME type (e.g. image/jpeg) | | size | number | File size in bytes | | duration | number|null | Duration in seconds (video only) | | status | string | ready_for_processing | processing | completed | failed | | download_url | string|null | Signed download URL | | thumbnail_large_url | string|null | Large thumbnail | | thumbnail_small_url | string|null | Small thumbnail | | source | string|null | upload | generated | stock | scan | | description | string|null | Media description | | created_at | string | ISO 8601 timestamp | | updated_at | string | ISO 8601 timestamp |

Upload workflow:

# Upload returns a media ID
wahlu media upload ./photo.jpg
# Uploaded photo.jpg — media ID: mid-abc123

# Use the media ID in a content item
wahlu post create --name "Photo post" \
  --copy-mode single \
  --single-copy '{"caption":"Nice!","hashtags":[]}' \
  --instagram '{"post_type":"GRID_POST","media_ids":["mid-abc123"]}'

Ideas

Save content ideas for later development into full content items.

| Command | Description | |---------|-------------| | wahlu idea list | List ideas (paginated) | | wahlu idea create <name> | Save a new idea | | wahlu idea delete <id> | Delete an idea |

Create options:

| Option | Description | |--------|-------------| | --description <text> | Detailed description (max 10000 chars) | | --type <type> | Idea type (max 50 chars) |

Response fields:

| Field | Type | Description | |-------|------|-------------| | id | string | Idea ID | | name | string|null | Idea name/title | | description | string|null | Detailed description | | type | string|null | Idea type | | status | string | Status | | labels | string[] | Text labels | | created_at | string | ISO 8601 timestamp | | updated_at | string | ISO 8601 timestamp |

Labels

Labels categorise and organise content items and media.

| Command | Description | |---------|-------------| | wahlu label list | List all labels | | wahlu label create <name> | Create a label | | wahlu label delete <id> | Delete a label |

Create options:

| Option | Description | |--------|-------------| | --color <hex> | Colour hex code (e.g. #ff5500) |

Response fields:

| Field | Type | Description | |-------|------|-------------| | id | string | Label ID | | name | string | Label name | | color | string|null | Colour hex code | | created_at | string | ISO 8601 timestamp | | updated_at | string | ISO 8601 timestamp |

Integrations (read-only)

Connected social media accounts. You need integration IDs when scheduling content items.

| Command | Description | |---------|-------------| | wahlu integration list | List connected integrations |

Response fields:

| Field | Type | Description | |-------|------|-------------| | id | string | Integration ID (use in --integrations when scheduling) | | platform | string | instagram | tiktok | facebook | youtube | linkedin | | status | string | Connection status | | display_name | string|null | Display name on the platform | | username | string|null | Platform username/handle | | avatar_url | string|null | Profile avatar URL | | permissions | object|null | Granted permissions | | created_at | string | ISO 8601 timestamp | | updated_at | string | ISO 8601 timestamp |

Publications (read-only)

Records of content items published to social media platforms.

| Command | Description | |---------|-------------| | wahlu publication list | List published content items (paginated) |

Response fields:

| Field | Type | Description | |-------|------|-------------| | id | string | Publication ID | | platform | string | instagram | tiktok | facebook | youtube | linkedin | | post_id | string | Source content item ID | | post_name | string|null | Content item name | | post_type | string|null | Post type | | status | string | processing | published | failed | | source | string|null | calendar (from schedule) or queue (from queue) | | failure_reason | string|null | Failure reason | | integration_id | string | Integration used | | publish_id | string|null | Platform content ID | | published_at | string | When published (ISO 8601) | | created_at | string | ISO 8601 timestamp | | updated_at | string | ISO 8601 timestamp |

Global options

| Flag | Description | |------|-------------| | --brand <id> | Use a specific brand (overrides default) | | --json | Output as JSON (available on all list/get/create/update commands) | | --help | Show help for any command | | --version | Show CLI version |

Pagination

All list commands support pagination:

| Flag | Default | Max | Description | |------|---------|-----|-------------| | --page <n> | 1 | - | Page number | | --limit <n> | 50 | 100 | Items per page |

Configuration

Config is stored at ~/.config/wahlu/config.json:

{
  "api_key": "wahlu_live_...",
  "api_url": "https://api.wahlu.com",
  "default_brand_id": "abc123"
}

Environment variables take priority over config file:

| Variable | Description | |----------|-------------| | WAHLU_API_KEY | API key | | WAHLU_API_URL | API base URL (default: https://api.wahlu.com) | | WAHLU_BRAND_ID | Default brand ID |

For AI agents

Every command supports --json for structured output:

# Get content item IDs
wahlu post list --json | jq '.[].id'

# Get integration IDs for scheduling
wahlu integration list --json | jq '.[] | {id, platform, username}'

# Find failed publications
wahlu publication list --json | jq '.[] | select(.status == "failed")'

# Create and capture the ID
CONTENT_ITEM_ID=$(wahlu post create --name "Auto post" --json | jq -r '.id')
wahlu schedule create $CONTENT_ITEM_ID --at 2026-03-15T14:00:00Z --integrations int-123

Documentation

Full documentation: wahlu.com/docs

License

MIT