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

@sinoia/pulse-cli

v0.3.0

Published

CLI Pulse — orienté agent/LLM (--json) : piloter le support (tickets) et le CRM/commerce (prospects, leads, pipeline, tâches, relances) via l'API REST Pulse.

Readme

pulse-cli

CLI pour l'API REST Pulse, pensé pour le pilotage par agent/LLM : sortie --json structurée, auth par clé API (pk_…) ou login JWT. Deux cas d'usage : un agent support qui prend des tickets et y répond, et un agent commercial qui qualifie les prospects entrants, travaille ses leads, fait avancer le pipeline et tient ses relances/tâches à jour.

Vit dans le repo Pulse, sous tools/cli/.

Installation

npm (global — nécessite Node ≥ 20)

npm install -g @sinoia/pulse-cli
pulse --version

Binaire autonome (sans Node)

Des exécutables autonomes (runtime Node embarqué) sont produits par pkg :

cd tools/cli
npm install
npm run package           # → binaries/pulse-cli-{macos-arm64,macos-x64,linux-x64,linux-arm64}
# ciblé : npm run package:macos | npm run package:linux

# installer (ex. macOS arm64)
sudo install binaries/pulse-cli-macos-arm64 /usr/local/bin/pulse
pulse --version

Aucune dépendance Node requise pour exécuter le binaire (~46 Mo, runtime inclus). Pour un agent : déposer le binaire de la plateforme et définir PULSE_TOKEN.

Depuis les sources (Node ≥ 20)

cd tools/cli
npm install
npm run build      # → dist/cli.js (bin: pulse)

En dev : npm run dev -- <commande> (ts-node).

Authentification

Deux modes :

# Humain : login email / mot de passe → JWT (stocké dans ~/.pulse-config.json, 0600)
pulse login -e [email protected] -p '••••••'

# Agent autonome : clé API non-expirante (recommandé)
export PULSE_TOKEN=pk_xxxxxxxx          # générée via /profile + l'API api_keys
# ou : pulse config set-token pk_xxxxxxxx

Surcharges d'environnement : PULSE_API_URL (défaut https://pulse.sinoia.fr), PULSE_TOKEN. La clé/JWT voyage en Authorization: Bearer ….

L'auteur des réponses = l'utilisateur de la clé API (doit avoir un rôle support/agent). --as-support masque son nom côté client (« L'équipe support SINOIA »).

Commandes (toutes acceptent --json)

pulse whoami
pulse tickets ls [--status open] [--severity S1] [--queue <id>] [--mine] [--search "connexion"] [--page N]
pulse tickets show <id>
pulse tickets take <id>
pulse tickets assign <id> --agent <agentId>      # dispatch vers un agent (cf. pulse agents)
pulse tickets transfer <id> --queue <queueId>    # transfert vers une file (cf. pulse queues)
pulse tickets severity <id> S1                   # sévérité S1 (critique) → S4 (gênant) ; liste triée S1 d'abord
pulse tickets attachments <ticketId>             # liste les pièces jointes (id, nom, type, taille)
pulse tickets attachment <ticketId> <attId> [-o f] # télécharge le contenu d'une pièce jointe
pulse tickets reply <id> -m "Bonjour, …" [--internal] [--as-support] [--attach capture.png …]
pulse tickets suggest <id>                       # brouillon de réponse ancré sur la KB (relecture humaine)
pulse tickets close <id> [--reason "résolu"]
pulse tickets stats [--queue <id>]               # compteurs par statut
pulse agents                                     # agents (cibles d'assignation)
pulse queues                                     # files (cibles de transfert)

# Base de connaissances (enrichissement + lecture)
pulse kb ls [--search "bannette"] [--status published]
pulse kb show <id>
pulse kb add-from-ticket <ticketId> --title "…" --content "…" [--publish] [--tags a,b]

# Licences & vouchers (Phase 2 — nécessite un token ADMIN : pulse login d'un
# admin, ou une clé API pk_ d'un utilisateur admin)
pulse licenses ls [--status active] [--type pro] [--org <id>] [--expiration expiring_soon] [--limit N]
pulse licenses show <id>
pulse licenses suspend <id> [--reason "…"] | activate <id>
pulse licenses generate-voucher <id> [--max-uses N] [--instance <id>]
pulse licenses stats
pulse vouchers ls [--status …] [--license <id>] [--instance <id>]
pulse vouchers show <id>
pulse vouchers revoke <id> [--reason "…"]
pulse vouchers extend <id> --hours N

# Commandes du store (Phase 2 — token ADMIN)
pulse orders ls [--status failed] [--limit N]
pulse orders show <id>
pulse orders retry <id>          # rejoue le provisioning en échec (idempotent)
pulse orders cancel <id> [--reason "…"]

# Parc d'instances (Phase 2 — token ADMIN)
pulse instances ls [--status running] [--org <id>] [--limit N]
pulse instances show <id>
pulse instances credentials <id>   # identifiants admin (via la commande liée)

CRM / commerce (rôle admin ou commerce — scopes crm:read / crm:write)

# Référentiel & annuaire (rien n'est codé en dur dans le CLI : les valeurs
# admises — statuts, étapes, types, canaux, résultats — viennent de l'API)
pulse crm meta [lead|contact|opportunity|task|follow_up|deal|prospect]
pulse crm sales-reps                            # commerciaux (cibles d'assignation)
pulse crm products [--all]                      # produits (conversion, opportunités)
pulse crm orgs [--search "acme"]                # organisations (création contacts/opportunités)
pulse crm today                                 # briefing : relances/tâches du jour et en retard,
                                                # prospects qualifiés non assignés, pipeline en retard

# Prospects entrants (demandes de démo = tickets « commercial ») → qualifier → convertir en lead
pulse prospects ls [--temperature hot] [--mine] [--unassigned] [--unconverted] [--min-score 50] [--search q]
pulse prospects show <id>
pulse prospects take <id> | assign <id> --user <userId>
pulse prospects qualify <id> [--company …] [--size …] [--budget …] [--timeline …] \
                            [--decision-maker …] [--decision-maker-title …] [--source …] [--auto-convert]
pulse prospects convert <id> [--company …] [--description …] [--notes …]   # score ≥ 50 requis

# Leads
pulse leads ls [--status qualified] [--temperature hot] [--mine] [--unassigned] [--active] \
               [--follow-up overdue|today|week] [--min-score N] [--sort score] [--search "Acme"] [--org <id>]
pulse leads show <id>                           # + contacts, notes, tâches
pulse leads create --company "Acme" [--industry …] [--temperature warm] [--contact-first-name … --contact-last-name … --contact-email …]
pulse leads update <id> [--status qualified] [--temperature hot] [--notes "…"] [--tags a,b]
pulse leads take <id> | assign <id> --user <userId> [--reason …]
pulse leads note <id> -m "…" [--type meeting_notes] [--private]
pulse leads follow-up <id> --at +3d [--notes "…"]           # dates : ISO 8601, +2h, +3d, +1w, today (18 h), tomorrow (9 h)
pulse leads score <id> 85 [--reason "…"]
pulse leads convert <id> [--title …] [--value 12000] [--probability 40] [--close-date 2026-12-31] [--products id1,id2]
pulse leads stats

# Contacts
pulse contacts ls [--search "curie"] [--org <id>] [--mine] [--decision-makers] [--stage customer]
pulse contacts show <id>                        # + leads, opportunités, activités, tâches
pulse contacts create --first-name … --last-name … --email … --org <id> [--job-title …] [--role decision_maker] [--decision-maker]
pulse contacts update <id> [--phone …] [--champion] [--tags vip]
pulse contacts note <id> -m "…"
pulse contacts log-call <id> --direction outbound [--result answered] [--duration 12] [--notes "…"] [--schedule-follow-up]
pulse contacts log-email <id> -m "…" [--subject "…"] [--at …]
pulse contacts log-meeting <id> -m "…" [--at …]

# Opportunités (pipeline) — alias : pulse opps
pulse opportunities ls [--stage demo] [--mine] [--open] [--overdue] [--search q] [--org <id>] [--sort close_date|value]
pulse opportunities show <id>                   # + contacts, produits, timeline, tâches, deal
pulse opportunities create --title "…" --org <id> [--value 5000] [--probability 25] [--close-date …] [--products …] [--contacts …]
pulse opportunities update <id> [--value …] [--close-date …] [--notes "…"]
pulse opportunities advance <id>                # étape suivante (probabilité de l'étape appliquée)
pulse opportunities probability <id> 60
pulse opportunities take <id> | assign <id> --user <userId>
pulse opportunities note <id> -m "…"
pulse opportunities close-won <id> [--value 11000] | close-lost <id> [--reason "…"]
pulse opportunities deal <id> [--value …] [--start …] [--end …] [--type annual] [--payment annual] [--billing annual] [--renewal auto]
pulse opportunities stats                       # pipeline par étape (nombre, valeur, pondérée)

# Tâches
pulse tasks ls [--mine] [--overdue] [--due today|week] [--priority high] [--status …] [--lead|--contact|--opportunity <id>]
pulse tasks show <id>
pulse tasks create --title "…" --due +2d (--lead <id> | --contact <id> | --opportunity <id> | --follow-up <id>) [--type phone_call] [--priority urgent]
pulse tasks update <id> [--title …] [--priority …] [--due …]
pulse tasks start <id> | complete <id> [--notes "…"] [--duration 15] | cancel <id> [--reason "…"]

# Relances planifiées — alias : pulse followups
pulse follow-ups ls [--mine] [--overdue] [--due today|week] [--lead|--contact|--opportunity <id>]
pulse follow-ups show <id>
pulse follow-ups create --title "…" --at +5d (--lead <id> | --contact <id> | --opportunity <id>) [--type demo_follow_up] [--method phone] [--priority high]
pulse follow-ups complete <id> [--outcome meeting_booked] [--notes "…"]
pulse follow-ups reschedule <id> --at +1w [--reason "…"]

# Deals (contrats issus d'opportunités gagnées) — lecture ; création via `opportunities deal`
pulse deals ls [--status signed] [--org <id>] [--expiring] [--search q]
pulse deals show <id>

Les mutations refusent en 422 (error_code: invalid_enum_value, avec la liste allowed) toute valeur hors énumération : un agent peut se corriger sans consulter la doc. Une clé crm:read seule ne peut pas écrire (403).

Usage agent (non interactif)

export PULSE_TOKEN=pk_…
pulse --json tickets ls --status open                 # triage
pulse --json tickets take <id>                        # prendre le ticket
pulse --json tickets suggest <id>                     # brouillon sourcé KB (relecture humaine, aucun envoi)
pulse --json tickets reply <id> -m "…" --attach shot.png --as-support

Boucle type d'un agent commercial (clé pk_ d'un utilisateur au rôle commerce, scopes crm:read + crm:write) :

export PULSE_TOKEN=pk_…
pulse --json crm today                                        # plan de travail du jour
pulse --json prospects ls --unassigned --unconverted           # nouvelles demandes de démo
pulse --json prospects take <id>
pulse --json prospects qualify <id> --decision-maker "…" --decision-maker-title DAF --auto-convert
pulse --json leads note <leadId> -m "Appel de découverte : …" --type call_log
pulse --json leads follow-up <leadId> --at +3d
pulse --json leads convert <leadId> --value 12000 --products <productId>
pulse --json opps advance <oppId>
pulse --json tasks create --opportunity <oppId> --title "Envoyer la proposition" --due tomorrow --type send_proposal
pulse --json follow-ups complete <id> --outcome meeting_booked --notes "RDV le 12"

En mode --json, objets/listes sortent sur stdout ; les erreurs en JSON sur stderr ({ "ok": false, "error": … }), code de sortie 1.

Contrat d'API

OpenAPI servi par Pulse sur GET /api/v1/docs/openapi.json. Vendoré localement via npm run update:spec (→ openapi/pulse-api.json).

Architecture

src/
├── cli.ts      # entrée Commander, --json global, wiring des commandes
├── config.ts   # ~/.pulse-config.json (+ surcharges env)
├── api.ts      # client axios (Bearer clé API / JWT)
├── auth.ts     # login email/mot de passe → JWT
├── output.ts   # bascule JSON / tables humaines
├── query.ts    # mapping options → params API, dates relatives (+3d, tomorrow)
└── crm.ts      # commandes CRM / commerce (prospects, leads, contacts, opportunités, tâches, relances, deals, crm)