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

ethos-cli

v0.24.0

Published

Commander-based CLI for Cluster table operations

Readme

Cluster CLI

Commander-based CLI for Cluster agent and table operations.

Native human calling

Cluster Dialer can use the computer's microphone and speakers without an open dashboard. Install the Cluster Audio companion on the computer with the microphone, then authenticate the CLI as the calling human and workspace. Run ethos dialer audio-devices to select devices and ethos dialer microphone --consent to connect them. Keep that terminal open; use its returned session ID with ethos dialer call in another terminal, or let a connected agent place the call through Cluster MCP. Ctrl+C disconnects audio and requests hangup. OS microphone permission is required. For a remote development stack, run capture on the human's computer and point its CLI at the test backend; running capture on the server cannot access a laptop mic.

Install

npm install -g ethos-cli

Auth

The CLI can connect through your existing browser session:

ethos auth login
ethos auth status
ethos auth logout

Use --app-url if your frontend is not on the default local/prod URL:

ethos --api-url http://127.0.0.1:8001 auth login --app-url http://localhost:3001

When running from Codex, pass the current thread UUID to return to that thread after browser approval:

ethos auth login --codex-thread-id "$CODEX_THREAD_ID"

You can also configure auth with flags or environment variables:

export ETHOS_API_URL="https://api.ethos.hello-cluster.com"
export ETHOS_API_KEY="..."
export ETHOS_ORG_ID="..."

Every command also accepts --api-url, --api-key, --access-token, --org-id, and --json.

Connected inboxes

ethos accounts email --json

Lists all email accounts connected to the active workspace, with their email addresses, account IDs, connection statuses, and email settings. Includes inboxes not yet assigned to campaigns.

Exclusions

ethos exclusions list --kind domain --search acme
ethos exclusions add --kind email [email protected] [email protected]
ethos exclusions add --kind linkedin --file profiles.txt
ethos exclusions remove <exclusion-id> <exclusion-id>

Manages the workspace exclusion list: email addresses, website domains, and LinkedIn profiles that no campaign will contact. A domain excludes everyone at that company. add takes one --kind per call, from arguments, a --file with one value per line and no header row, or both; invalid values are skipped and reported. list pages with --limit and --offset, and remove takes the exclusion_id values that list returns. On a draft campaign, ethos campaigns get reports which staged leads launch would leave out in list_contact_exclusions.

Managed inboxes

ethos inboxes list
ethos inboxes domains
ethos inboxes check-domains acmemail --tlds com,net
ethos inboxes pre-warmed --contains mail
ethos inboxes quote --file order.json
ethos inboxes order --file order.json --yes
ethos inboxes order-status <order-id> --wait 120
ethos inboxes warmup <mailbox-id> --on

To check exact domain variations in bulk, supply a text file with one domain per line (up to 50):

ethos inboxes check-domains-bulk --file domains.txt
printf 'getcluster.com\ntrycluster.co\n' | ethos inboxes check-domains-bulk --file -

Blank lines are ignored; the server lowercases and deduplicates domains. Each result reports available, unavailable, restricted, invalid, workspace_conflict, or error, with the yearly credit price when available. Errors are inconclusive and can be retried. The check does not purchase anything.

Buys and manages managed inboxes and the managed domains they send from, paid for with workspace credits. order without --yes only prints the quote and places nothing; credits are spent only with --yes. order-status --wait blocks for up to 300 seconds until the order finishes. The order file looks like:

{
  "domains": [{ "domain": "acmemail.com", "years": 1 }],
  "mailboxes": {
    "acmemail.com": [
      { "username": "alice", "first_name": "Alice", "last_name": "Example", "on_warmup": true }
    ]
  }
}

Leave domains empty to add inboxes to one domain the workspace already owns.

Quotes include a setup expectation: new inboxes are expected to take around 30 minutes, but can take longer. Sender connection can take hours, and completing setup does not mean warm-up is complete. Pre-warmed orders have separate delivery guidance. Order responses include setup progress separately from purchase status: a succeeded purchase can still have inboxes being created. inboxes list keeps those orders visible until the inboxes arrive. After one bounded status check, use setup.check_after to decide when to run ethos inboxes order-status <order-id> again. Setup continues after the command exits; do not place the order again. When list reports registrant_needed: true, the workspace's first order must also carry a registrant object: email, first_name, last_name, company, address_line_one, address_line_two, city, state, country (2-letter code), postal_code, phone_cc, and phone.

Skills

Cluster workflow guidance is served as resources by the hosted MCP server. Connect an AI client directly to https://api.ethos.hello-cluster.com/mcp using streamable HTTP and OAuth, then approve access in the browser. No plugin is required; the Cluster plugin is retired. Start a fresh client session after connecting and verify the active workspace with the read-only get_workspace_overview tool.

The CLI remains supported independently through ethos auth login. It no longer bundles or installs skills. If a previous CLI version installed skills on this machine, inspect and clean them up with:

ethos skills list
ethos skills remove

Agent Commands

ethos agents list
ethos agents get <agent-id>

ethos agents create \
  --name "Company enricher" \
  --workflow-type company_enrichment \
  --ability web-search \
  --ability linkedin-company-lookup \
  --tool-preset browser

ethos agents configure <agent-id> --workflow-type people_sourcing --clear-abilities
ethos agents delete <agent-id>

Workflow/table compatibility is shown in --help: company tables support company enrichment columns and people sourcing, people tables support person enrichment columns. The CLI sends the configured request and lets backend validation decide the final result.

Org-scoped custom signals appear in ethos signals list with keys such as custom:<assignment-id> and configuration_mode: "managed". Use those keys with ethos signals pull and workflow commands exactly like built-in signals. Use the resulting sourcing job ID with ethos signals pull-status. Omit --config for managed custom signals; Cluster applies the organization’s current configuration revision server-side.

Organization members can manage their active organization’s complete assignment lifecycle through ethos custom-signals list|create|update|apply|preview|preview-status. The backend scopes every command to the active organization; apply atomically moves its active workflows to the current managed revision.

For a one-time pull of connections visible from another LinkedIn profile, list ready connected accounts first, then use the returned Cluster account ID.

ethos signals linkedin-accounts
ethos signals pull linkedin_profile_connections \
  --config '{"connected_account_id":"<cluster-account-id>","target_url":"https://www.linkedin.com/in/target/","max_results":500}' \
  --wait

This signal is one-time only and cannot be used as a workflow trigger.

Workflow Commands

List the Slack channels visible to Cluster, then use a channel ID when creating a workflow:

ethos workflows slack-channels

ethos workflows create "New lead alerts" \
  --signal post_engagers \
  --config '{"post_url":"https://www.linkedin.com/posts/activity-1"}' \
  --step send_to_slack:C123456

Pass explicit JSON to customize the message. Supported variables are {{workflow.id}}, {{workflow.name}}, {{workflow.match_count}}, {{workflow.run_id}}, and {{workflow.run_url}}.

ethos workflows create "New lead alerts" \
  --signal post_engagers \
  --step 'send_to_slack:{"channel_id":"C123456","channel_name":"alerts","message_template":"{{workflow.name}} found {{workflow.match_count}} matches"}'

ethos workflows update <workflow-id> --step ... replaces the complete step set with a new linear chain. Include every existing step you want to preserve. Re-activate an active workflow after changing its steps so new matches use the updated definition.

Table Commands

ethos tables list --limit 50 --offset 0
ethos tables get <table-id>
ethos tables import-csv ./companies.csv --entity-type company
ethos tables import-csv ./people.csv --entity-type people

ethos tables source-people <table-id> \
  --filter <column-id>:contains:example \
  --input-column <column-id>

ethos tables columns create-agent <table-id> \
  --name "ICP fit" \
  --agent-id <agent-id> \
  --prompt "Score this account" \
  --output-field score \
  --output-field reason

ethos tables columns configure <table-id> <column-id> --name "New name"
ethos tables columns delete <table-id> <column-id>
ethos tables columns extract-json <table-id> <column-id>

ethos tables rows delete <table-id> <row-id>
ethos tables rows delete <table-id> 3 --by-index
ethos tables rows delete <table-id> 1 2 3 --by-index

ethos tables agent-edits apply <table-id> \
  --instruction "Delete rows that are no longer relevant" \
  --row-id <focused-row-id>

ethos tables cells set <table-id> <row-id> <column-id> --value "Manual note"
ethos tables cells set <table-id> <row-id> <column-id> --value '{"score":9}' --value-json

ethos tables runs start <table-id> <column-id> --scope first_5
ethos tables runs start <table-id> <column-id> --lower-range 1 --upper-range 50
ethos tables runs get <run-id>
ethos tables runs interrupt <run-id>

ethos tables source-people <company-table-id> \
  --view-id <view-id> \
  --agent-id <people-sourcing-agent-id> \
  --input-column <company-domain-column-id> \
  --targeting-brief "Find funding and finance contacts" \
  --created-by-codex

ethos tables create-people-table <company-table-id> \
  --column-id <people-finder-column-id> \
  --source-column <signal-context-column-id>

List And Campaign Commands

ethos lists import-from-table <people-table-id> \
  --linkedin-column "LinkedIn URL" \
  --custom-variable outreach_message \
  --custom-variable normalized_company_name

ethos campaigns attach-list <campaign-id> <list-id>

ethos campaigns update-sequence <campaign-id> \
  --first-step invitation \
  --messages-json '[{"channel":"EMAIL","subject_template":"Hello {{company}}","body_template":"Email body","wait_amount":1,"wait_unit":"hours"},{"channel":"LINKEDIN","body_template":"Following up","wait_amount":2,"wait_unit":"days"}]'

ethos campaigns update <campaign-id> \
  --exclude-existing-linkedin-connections true \
  --withdraw-invites-after-days 14

campaigns update-sequence changes future, unsent outreach. It replaces the full active sequence, so preserve any timing and step settings you do not intend to change. Existing-step edits are allowed while a campaign is running; pause it before adding or removing steps. Step types and channels cannot change after launch. Sent activity stays intact, pending removed steps are skipped, and completed contacts are not backfilled.

Sequence entries accept action_type: message (default for LINKEDIN), email (default for EMAIL), profile_visit, or like_post. profile_visit views the lead's LinkedIn profile so they see the visit; like_post likes their most recent post inside like_post_recency_days (1-365, default 30), counting reposts only when like_post_include_reposts is true, and is skipped with a visible reason when there is none. Touchpoints take no body_template, subject_template, or media_attachment and need no connection:

ethos campaigns create-with-sequence --name "Warm up first" --list-id list_1 \
  --messages-json '[{"action_type":"profile_visit"},{"action_type":"like_post","wait_amount":1,"wait_unit":"days"},{"body_template":"Loved your recent post, {{first_name}}","wait_amount":2,"wait_unit":"days"}]'

campaigns update changes campaign settings; pass only the flags you want to change: --name, --connected-account-id (LinkedIn sender), --email-connected-account-ids (comma-separated Gmail sender pool, or none), --audience-id (or none to detach), --delivery-mode automatic|manual (drafts only), the daily caps --linkedin-invite-daily-limit, --linkedin-message-daily-limit, --email-daily-limit (1-500 or off), and the safety settings. --withdraw-invites-after-days auto-withdraws LinkedIn invitations still pending after 1-90 days (default 14 for new campaigns); pass off to disable. campaigns sender-accounts lists the accounts to choose from.

Branching sequences, previews, readiness, and progress

The web builder saves sequences as a graph; the CLI accepts the same shape through --sequence-json on campaigns create-with-sequence and campaigns update-sequence. Steps carry key, action_type (connection_request, message, email, profile_visit, like_post, follow_company), content, wait_amount/wait_unit (the wait after the step before its default successor), acceptance_wait_days on a connection request, and reply_wait_days on a message or email that branches on reply. Edges carry source_key (null for the single entry edge), target_key, route_key (default, accepted, not_accepted, replied, no_reply), position, and an optional wait for outcome routes. Every step returned by campaigns get carries its step_id; keep it on steps you retain so sent history survives a save.

ethos campaigns get <campaign-id>                     # campaign, sequence_graph, journeys
ethos campaigns preview-sequence --sequence-json '{"steps":[...],"edges":[...]}'
ethos campaigns preview-sequence <campaign-id> --sequence-json '...'   # also reports applies_live, requires_pause, added/kept/retired steps
ethos campaigns create-with-sequence --name "Branching" --audience-id <audience-id> --sequence-json '...'
ethos campaigns update-sequence <campaign-id> --sequence-json '...' --preview
ethos campaigns update-sequence <campaign-id> --sequence-json '...'
ethos campaigns readiness <campaign-id>               # launch blockers and warnings, same checks as launch
ethos campaigns launch <campaign-id>
ethos campaigns progress <campaign-id> --wait --poll-interval 60   # contact/step counts, next send, failures
ethos campaigns pause <campaign-id>
ethos campaigns resume <campaign-id>
ethos campaigns archive <campaign-id>                 # the app's Delete action; cannot be undone

update-sequence is the builder's "Save workflow": on a draft it saves the draft, on a launched campaign it applies to future outreach immediately, so preview first. Previews return valid, structured errors, the normalised sequence, and journeys, one per outcome (accepted, not accepted, replied, no reply) with conditions and earliest day offsets; journey_summaries gives one line per path. Structure changes to a running campaign need campaigns pause first. like_post steps take like_post_recency_days and like_post_include_reposts in both shapes; there is no selector for a specific post.

lists import-from-table creates contacts from table rows with valid LinkedIn person profile URLs or identifiers. It skips missing, invalid, and duplicate LinkedIn identities, reuses matching workspace contacts by default, and preserves source row IDs plus selected custom variables.

Edit an existing list without recreating it:

ethos lists contacts <list-id> --search berlin
ethos lists add <list-id> linkedin.com/in/ada --contact-id <contact-id> --file profiles.txt
ethos lists move <list-id> <list-contact-id> <list-contact-id> --to <target-list-id>
ethos lists move <list-id> <list-contact-id> --new-list "German speakers"
ethos lists remove <list-id> <list-contact-id> <list-contact-id>

lists contacts returns each person's list_contact_id, which move and remove take. add skips people already in the list. move keeps each contact's custom variables; a contact the target list already holds is only removed from the source list. remove leaves the people in the workspace and in any other lists.

When stdout is not a TTY, output is JSON by default so coding agents can parse it reliably. Table-detail commands such as tables get, tables import-csv, and tables create-people-table include top-level table_id, column_ids, and row_ids in addition to the full nested objects. tables source-people includes top-level table_id, column_id, and run_id.

Analytics

The app, MCP, and CLI share three read operations:

ethos analytics schema
ethos analytics query --spec analytics-query.json
ethos analytics records --query-json '{"start_date":"2026-08-01","end_date":"2026-08-31","metric":"reply_rate","population":"denominator"}'

Example analytics-query.json:

{
  "start_date": "2026-08-01",
  "end_date": "2026-08-31",
  "timezone": "America/Los_Angeles",
  "group_by": ["campaign", "sender", "channel"],
  "scope": [{"dimension": "channel", "value": "EMAIL"}],
  "comparison": "previous_period",
  "interval": "day",
  "sort_by": "leads_reached",
  "sort_direction": "desc"
}

Scope contains exact dimension/value pairs from a selected campaign or breakdown row; all conditions must match. Activity uses event dates. Rates use people reached in those dates and subsequent matching outcomes up to now, without a response deadline. Use returned totals and rate components rather than summing overlapping groups. Chart selection and visible columns stay in the app.

For local testing, use the explicit checkout CLI path in .ethos-dev/context.json after starting make dev.