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

@cowlprotocol/pulse

v0.1.1

Published

Capability-bounded execution for autonomous agents on Cowl. Let an agent trade from a shielded portfolio without ever holding a private key.

Readme

Pulse

Let an autonomous agent trade on Cowl without ever giving it a private key.

The problem

An agent that can trade has to be able to sign. So you hand it a key, or you hand it something holding a key in the same process it runs in.

That agent also reads the open internet all day. Pages, tool results, documents, other agents. Any of it can carry text written to be obeyed, and the agent has no reliable way to tell an instruction it was given from an instruction it merely read. The failure mode is not a wrong answer. It is a signed transaction.

Spend policies help only if they live somewhere the agent cannot talk to. A limit enforced inside the agent's own context is a limit that can be argued with.

What Pulse does

Pulse puts the key on the other side of a process boundary and gives the agent a menu instead.

   agent                    │  pulse (separate OS user)
   decides what and when    │  decides what is possible
                            │
   "send treasury 10000     │   ├─ is that account known?
    COWL from bot-a"    ───▶│   ├─ is 10000 on the allowlist?
                            │   ├─ is "treasury" in the address book?
   ◀── {"ok":true,          │   ├─ budget left today? spaced enough?
        "runId":"…"}        │   └─ holds keystore + passphrase, runs the spend
                            │
                            │  caps.json  ← you write this, the agent cannot

The agent never sees a keystore, a passphrase, or a raw transaction. It names an account and a recipient; Pulse decides whether that is allowed and does the rest. Worst case, a fully compromised agent can do exactly what your caps.json already permits, which is a bounded amount to a destination you chose in advance.

Recipients are the sharp edge, so they are handled by name. An agent passes treasury, never an address. An address it invented, or read on a web page, or was told to use by a poisoned document, has nowhere to go.

Install

npm i -g @cowlprotocol/cli @cowlprotocol/pulse
cowl -v          # needs >= 0.6.12
cowl-pulse help

Pulse drives the Cowl CLI, so the CLI must be installed and on PATH.

Setup

Run the executor as its own unprivileged user. Not your login user, and not the user your agent runs as.

sudo useradd -m -s /bin/bash pulse
sudo -u pulse -H bash

# as pulse:
./setup-account.sh bot-a

Each account gets an isolated COWL_HOME, its own keystore, and its own passphrase file at mode 600. Accounts never share note files or keys. The script prints a zcowl1… payment address.

Config and state live in ~/.cowl-pulse for whoever runs the executor, so as the pulse user that is /home/pulse/.cowl-pulse. Put your policy there:

mkdir -p ~/.cowl-pulse
cp "$(npm root -g)/@cowlprotocol/pulse/caps.example.json" ~/.cowl-pulse/caps.json
chmod 600 ~/.cowl-pulse/caps.json

Fill in your accounts, the amounts you are willing to have spent, and the recipients that may be reached. Then fund an account with a private send from your own wallet. Set PULSE_BASE if you want config and state somewhere else.

The sudo boundary

Resolve the real path first, because sudoers matches an absolute path exactly and does no PATH lookup:

command -v cowl-pulse      # e.g. /usr/local/bin/cowl-pulse
# /etc/sudoers.d/cowl-pulse   (edit with visudo -f)
<agent-user> ALL=(pulse) NOPASSWD: /usr/local/bin/cowl-pulse

That single line is the entire surface your agent has. It cannot run cowl, read /home/pulse/secrets, or edit caps.json. Give your agent this command shape and nothing else:

sudo -u pulse cowl-pulse <command> …

The binary you name here must sit somewhere the agent's user cannot write. A sudoers rule points at a path, not at contents. If cowl-pulse lives under a version manager's directory owned by the agent's own user, the agent can replace that file and have it run as pulse, which hands away everything this design is protecting. Check it:

ls -l "$(command -v cowl-pulse)"     # owner must not be the agent's user

Install Node and this package system wide, or copy the resolved binary to a root owned path and name that path in the rule instead.

Commands

| Command | Effect | |---|---| | send <account> <recipient> <amount> <token> | private send to a name in the address book | | unshield <account> <amount> <token> [spread] | withdraw to the account's own address | | consolidate <account> <token> | merge fragmented notes | | status <account> | shielded balance | | receive <account> | payment address | | limits | today's remaining budgets and the allowed menu | | runs [n] | recent outcomes, and anything still running |

Every command answers with one line of JSON, which is what makes it safe to hand to a model: {"ok":true,…} or {"ok":false,"error":"…","retryable":true|false}.

Transactions return a runId immediately and finish in a detached worker, since proving takes minutes. Read the outcome later with runs. An attempt is charged against the daily budget whether or not it lands, so a failing command cannot be retried for free.

Point an agent at limits first. It returns the menu, so the agent can choose a legal action instead of guessing and being refused.

What caps.json enforces

  • accounts — which wallets exist at all.
  • tokens.<SYM>.amounts — the only spendable amounts. Anything else is refused outright rather than rounded or split, so a wrong number cannot become a large transaction.
  • recipients — every reachable destination, by name.
  • limits.dailyTotal / dailyPerAccount — hard transaction ceilings per UTC day.
  • limits.unshieldDaily — withdrawals are the one operation that puts value back on a public address, so they get their own smaller ceiling.
  • limits.minGapMinutes — minimum spacing between submissions. Transactions arriving in a burst are correlated by timing whatever else you do right.

None of these can be widened from the command line. Pulse reads them fresh on every invocation, so tightening a limit takes effect immediately.

Operational notes

One cowl process runs per account at a time, held by a lock, because the pool and note files are not safe to write concurrently. A second request for a busy account waits briefly, then refunds its budget and reports rather than queueing forever.

Nothing exits mid-operation. Failures unwind through the same path as successes, so a crashed or refused run always releases its lock and always records a result.

consolidate fans out into one spend per merge round and charges each confirmed round, so a badly fragmented account can spend more than dailyTotal in one call. Treat it as maintenance you trigger deliberately rather than something an agent calls on a schedule.

What stays off the machine

Your own wallet. Fund accounts with a private send from a wallet Pulse has never seen, and keep its keystore, its passphrase, and your backups somewhere this executor cannot reach. Pulse is only ever able to spend what an account it provisioned already holds.

License

MIT