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

kolny-cli

v0.1.2

Published

Command line access to the KOLNY colony: deposit, inspect trails, and register a forager agent.

Readme

kolny-cli

Command line access to the KOLNY colony: inspect trails, deposit into the Brood Vault, and register a forager agent.

KOLNY is an autonomous agent fund on Solana. Capital is held in one vault and spread across many independent agents called foragers, each trading from its own isolated sub-account. What a forager realized -- closed and settled trades, not a backtest and not a mark on an open position -- becomes its pheromone score, and pheromone is what sets how much capital flows down that trail in the next epoch. Pheromone evaporates on a fixed schedule, so a trail that stops earning fades and the capital behind it returns to the vault. No human picks the strategy that wins. This tool is how you read that distribution and take part in it from a terminal.

npm i -g kolny-cli

# reads: no key, nothing signed
kolny trails --cluster devnet
kolny trails --cluster devnet --deposit 10000
kolny forager stats --cluster devnet
kolny forager stats --cluster devnet --operator <address> --id 3

# writes: a keypair is required, and the transaction is shown before it is sent
kolny deposit --cluster devnet --amount 10000 --ata <token-account> \
              --keypair ./operator.json
kolny forager register --cluster devnet --id 3 --bond 5000 \
                       --ata <token-account> --strategy momentum-v1 \
                       --keypair ./operator.json

The colony is live on devnet and nowhere else. It is an unaudited test deployment, not production. The default cluster is mainnet-beta, where nothing is deployed, which is why --cluster devnet is on every kolny line above. All four commands check the program on chain before anything else, so without it each of them exits 3.

A mainnet deployment changes every address

There is no mainnet program and no audit. The devnet one, 7whkmFfDcTyoJgf7jFGFmKNFMQn8NoreHnh2wZ9nWbsk, is a test deployment holding test tokens, and no figure read from it describes a real fund.

A mainnet deployment mints a new program keypair, so the mainnet program id will not be this one. Every colony address is a PDA derived from the program id -- all seven of them, and every forager sub-account -- so all of them change with it. That failure is the quiet kind: a devnet address pasted into mainnet code is still a valid address, it just names an account that will never exist.

So derive, never hard-code. No address is written into this package. @kolny/sdk derives all seven from the IDL on every run, which makes the IDL the only thing that has to change, and --idl or --program-id repoints the same binary at a different deployment.

Three states, told apart on chain

A read that comes back with nothing has more than one cause, and this tool never guesses which. Before touching a colony account it looks the program up on chain and requires it to be executable.

The program is not on this cluster (exit 3). Deployed to devnet, queried on mainnet:

$ kolny trails --cluster mainnet-beta
program not deployed

  program id : 7whkmFfDcTyoJgf7jFGFmKNFMQn8NoreHnh2wZ9nWbsk
  cluster    : mainnet-beta
  account    : does not exist
  reason     : no account exists at the program id on this cluster, so the program is not deployed here

This was checked on chain, not assumed. Every colony address derives
from the program id, so on this cluster there is nothing to read and
nothing to send to.

The most likely cause is the wrong cluster: the program may be deployed
on another one. Pass --cluster to pick, and note the default is
mainnet-beta.

The program is there but the colony is not fully set up (exit 4). This is the window between a deploy and the one-time initialization: the program answers on chain, but some or all of the seven colony accounts have not been created yet. The initialization is split across several instructions, so a partly initialized colony is a real resting state and not a flicker, and the output names every account rather than reporting that nothing exists.

Devnet is past this state -- all 7 accounts are there -- so no cluster prints it today. It is still reachable on demand: point --program-id at any other deployed program and the seven addresses derive from an id that owns none of them. Using the SPL Token program as the stand-in:

$ kolny trails --cluster devnet --program-id TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
colony not initialized

  The program is deployed on this cluster -- that was checked on chain --
  but the colony accounts it needs are not all present, so there is
  nothing to read. This is a different state from a colony holding
  nothing, and it needs a different fix: the remaining one-time
  initialization instructions have to be run.

  0 of 7 colony accounts exist:

    absent   config             53YJtekmkEGUgLRci9a87bJdZw8EtQbBmMsHUhjj4E49
    absent   brood              J6GSeCTPt6CekezqJfZSzBr9YWcBzjhfR7xVkvzgCDJC
    ...

The deployment claim in that message is one it earned: the program lookup passed, and only the colony accounts were missing.

The colony is up (exit 0). A real answer, and the one devnet gives:

$ kolny trails --cluster devnet
Colony, epoch 1
  value under colony   0.200000000
  scout reserve        0.020000000
  main pool            0.180000000
  active foragers      0
  insurance cache      0.000000000 (0 bps of value under colony)

No active foragers, so the whole main pool stays un-deployed.

  un-deployed reserve  0.180000000
  ...

Nine decimal places because the devnet base mint is wSOL, which declares nine. That figure is read from the mint account, not assumed -- see Decimals come from the mint.

An initialized colony that genuinely holds nothing is exit 0 as well. That is also a real answer, and a different one from either state above, which is the entire point of keeping the three apart.

Earlier this tool collapsed the first two: it skipped the program lookup and reported "the program is deployed, but the accounts are missing" wherever no colony accounts were found. On mainnet that told the reader to run an initialization that could not possibly work and ruled out the actual cause. Saying only what has been checked is the rule this project runs on, and the command line is not exempt from it.

Exit codes

| Code | Meaning | |---|---| | 0 | Success | | 1 | Error: bad input, an unreadable keypair, a failed RPC call, a usage error | | 3 | The program is not deployed on this cluster, so there is nothing to read or send to. Check the cluster, or deploy it | | 4 | The program is deployed but the colony accounts do not exist yet. Initialize them |

3 and 4 are non-zero deliberately. Both are states where a query returns nothing, and both must stop a pipeline:

kolny trails || echo "cannot read the colony"   # fires on 3 and on 4

A read that comes back empty because the program does not exist is not an empty colony, and automation built on top of a silent zero would treat it as one. They are separate codes because they need different fixes: query the cluster the program is actually on (or deploy it there), versus run the one-time initialization.

Also in kolny --help, so the table is available without reading this file.

Watch the pipe

A pipeline reports the last command's status, not this one's. Reading the output through head, tee or less will make every failure look like a success:

kolny trails --cluster mainnet-beta | head  # $? is head's 0, not kolny's 3

Two ways to keep the code:

set -o pipefail                                # bash, zsh: the pipeline fails
kolny trails --cluster mainnet-beta | head

kolny trails --cluster mainnet-beta > out.txt  # or do not pipe at all

This is worth stating because it has already been mistaken for a bug in this tool twice. Direct invocation returns the right code on every path: node dist/cli.js, npx kolny, and the installed kolny binary all agree.

The wallet is always explicit

--keypair <path> is required on every command that signs. There is no default and no search of the usual location.

The shared Solana CLI wallet is the same file for every program a machine has ever touched. A tool that reaches for it by default signs with an identity the user did not choose for this command, and on a colony that means depositing or bonding from the wrong account. One flag removes the whole class of mistake.

Read commands take an address and need no key at all.

Nothing is sent unseen

Before a transaction goes out you see the cluster, the program, the signer, the destination, the amount and what the colony will do with it:

About to send a deposit.
  cluster            : devnet
  program            : <program id>
  signer             : <your pubkey>
  from token account : <your ATA>
  to vault           : <vault base account>
  amount             : 1.000000000  (1000000000 base units)
  base mint          : So11111111111111111111111111111111111111112
  decimals           : 9, read from So11111111111111111111111111111111111111112
  spreads over       : <n> active foragers

Proceed? [y/N]

--yes skips the prompt for scripts. Without it, a non-interactive terminal is a refusal rather than an assumed yes.

The raw integer is printed beside the human figure because it is the exact number the instruction carries. A summary that formats the amount a second time can agree with the reader while the transfer disagrees, which is precisely what happened here once -- see below.

Decimals come from the mint

Every amount you type is multiplied by ten to the power of the base asset's decimals before it is sent, so that exponent decides how much money moves. The commands read it from the base mint account and there is no built-in default.

There used to be one. A constant 6 sat behind a comment saying amounts must never be guessed -- but a constant compiled into the tool is exactly a guess, and it was wrong: the base mint is wSOL, which declares 9. kolny deposit --amount 1 built a transfer of 1,000,000 base units, a thousandth of a wSOL, while the confirmation printed 1.000000. The display and the transfer disagreed by a factor of a thousand, and the display was the convincing one. trails showed the same fault in reverse, printing a vault holding 0.2 as 200.000000.

If the mint cannot be read the command stops. There is no fallback, because every candidate default is the same guess that caused the fault, and sending a wrong amount silently is worse than sending nothing:

$ kolny deposit --amount 1 --ata <ata> --keypair ./operator.json --cluster devnet
base-asset decimals could not be read

  base mint : <mint>
  cluster   : devnet
  reason    : The base mint <mint> is owned by <program>, which is not a token
              program, so it is not a mint.

Every amount is multiplied by ten to this power before it is sent, so a guess
here transfers the wrong amount while the confirmation prints the amount you
meant. There is no default to fall back to and this stops rather than assume
one.

Pass --decimals <n> to proceed with a value you have checked yourself.

--decimals <n> remains for a mint this tool cannot reach. When it is in force the confirmation says so, so an override is never silent.

Allocation figures come from the engine, not from here

kolny trails computes the preview with @kolny/pheromone-engine, which mirrors the on-chain arithmetic in the same integer fixed point, down to leaving the truncation dust where the chain leaves it. The CLI does not reimplement any of it.

That is deliberate. A second implementation would be a third answer to the same question, and the number a user is shown before a rebalance has to equal the number the chain pays after it. The un-deployed reserve is printed rather than folded away, for the same reason.

Amounts are parsed and formatted as integers throughout. parseBaseUnits rejects more decimal places than the asset has instead of rounding, and handles values well past 2^53 that a double would quietly change. The scale it works in is read from the mint rather than assumed -- see Decimals come from the mint.

Where the IDL comes from

Every colony address derives from the program id the IDL carries, and every account is decoded against the layouts in it, so which IDL is in force decides what the numbers mean. Three sources, tried in this order:

  1. --idl <path>, when given. It wins outright and never falls back. Naming a path is an instruction, and an IDL other than the one you asked for still decodes every account -- into values that look right and are not.
  2. The Anchor build output, found by searching up from the working directory for anchor-program/target/idl/kolny_colony.json. This is the path taken inside a clone of the repository, so a program rebuilt a minute ago takes effect with no second step. If it is there but unreadable the command stops rather than moving on, for the reason in 1: the packaged copy below is older by definition, and substituting it quietly is the failure this order exists to prevent.
  3. The copy inside this package, written beside the compiled modules when the package is built. This is what an npm i -g kolny-cli install uses, and without it that install could not run at all -- there is no Anchor build output on a machine outside the repository, and no program source to produce one.

Carrying the IDL here is version pinning rather than a drifting snapshot: a published tarball is already immutable, and a changed IDL means a new version of it. @kolny/sdk makes the opposite choice for the opposite reason. A library that carried a snapshot would hand it to callers building against a program that had since moved, so the SDK takes the IDL as an argument, and this tool is one of the callers that supplies it.

Commands

Four of them. trails and forager stats are reads: they take no key and sign nothing. deposit and forager register sign, so each requires --keypair and shows the whole transaction before it goes out.

Global options work in either position -- kolny --cluster devnet trails and kolny trails --cluster devnet both run. The per-command options below must follow their command.

kolny trails

The pheromone distribution, and where a deposit would land.

| Option | Required | Meaning | |---|---|---| | --deposit <amount> | no | Preview how this amount would be spread on top of what the colony already holds. Nothing is sent and no keypair is read |

--deposit is a preview and only a preview. On devnet today, where there is no active forager to place it with:

$ kolny trails --cluster devnet --deposit 10000
Colony, epoch 1
  value under colony   0.200000000
  your deposit         10000.000000000
  deployable after     10000.200000000
  scout reserve        1000.020000000
  main pool            9000.180000000
  active foragers      0
  insurance cache      0.000000000 (0 bps of value under colony)

No active foragers, so the whole main pool stays un-deployed.

  un-deployed reserve  9000.180000000
  Capital the cap could not place, plus per-forager truncation dust. It
  stays in the vault; the chain leaves it there too.

Figures are realized and on-chain. This is what the current pheromone
distribution implies, not a projection of future performance.

The amount is parsed at the base asset's own scale, so more decimal places than the asset has is an error rather than a rounding:

$ kolny trails --cluster devnet --deposit 0.0000000001
"0.0000000001" has 10 decimal places but the asset has 9. Rounding here would silently change the amount.

--json is implemented on this command and on no other. It emits base units as strings, because a base unit is the integer the chain actually stores and these values can exceed what a double holds exactly:

{
  "epoch": "1",
  "baseMint": "So11111111111111111111111111111111111111112",
  "baseDecimals": 9,
  "navBaseUnits": "200000000",
  "depositBaseUnits": "0",
  "mainPoolBaseUnits": "180000000",
  "scoutPoolBaseUnits": "20000000",
  "undeployedCapitalBaseUnits": "180000000",
  "cacheBalanceBaseUnits": "0",
  "cacheReserveRatioBps": "0",
  "trails": []
}

kolny forager stats

Forager records, with drawdown and slash history. Two modes.

| Option | Required | Meaning | |---|---|---| | --operator <address> | with --id | Narrow to a single operator | | --id <n> | with --operator | Forager id, unique per operator |

Given neither, it lists every forager. Given both, it prints that one record in full. Given only one of the two it falls back to the list rather than refusing, so a table where you expected a single record means the other flag is missing.

$ kolny forager stats --cluster devnet
  id         operator        status     bond            pheromone    max dd
  --------------------------------------------------------------------------
  0          5UtWv1...Mq8B   scout         0.010000000            0    0.00%
  1          5UtWv1...Mq8B   scout         0.010000000            0    0.00%

2 forager(s). Drawdown and slash history are shown for every one.
$ kolny forager stats --cluster devnet \
    --operator 5UtWv13wHhkG3sgB1qPx2GGgWmXTQ3Px1rA6hXXoMq8B --id 0
Forager 0
  operator             5UtWv13wHhkG3sgB1qPx2GGgWmXTQ3Px1rA6hXXoMq8B
  sub-account          ANok7Y5harD5bDkyqVqh6BgLYUoBF8j1StsKrCFGcwJb
  status               scout
  bond                 0.010000000
  principal            0.000000000
  pheromone            0 (fp6)
  realized cumulative  +0
  current drawdown     0.00%
  max drawdown         0.00%
  slashes              0
  realized epochs      0
  scout epochs         0
  last settled epoch   0

Those are devnet figures on a test cluster. --json is not implemented here: it is accepted and ignored, and the table is what you get.

kolny deposit

Moves base asset into the Brood Vault and mints vault shares to the signer.

| Option | Required | Meaning | |---|---|---| | --amount <amount> | yes | Amount in base asset, as a decimal, scaled by the mint's decimals | | --ata <address> | yes | Your base-asset token account, the source of the transfer | | --keypair <path> | yes | Signing keypair. No default, and no search of the usual location |

All three are enforced by the parser, so a missing one is a usage error before any network call:

$ kolny deposit
error: required option '--amount <amount>' not specified

The colony must not be paused, which is checked on chain before anything is built. What the confirmation looks like is above, and --yes skips it.

kolny forager register

Registers an agent as a forager and posts its bond in one transaction.

| Option | Required | Meaning | |---|---|---| | --id <n> | yes | Forager id, unique per operator. An id already registered to this operator is refused rather than overwritten | | --bond <amount> | yes | Bond to post, as a decimal, in the same base asset as deposits | | --ata <address> | yes | Token account the bond comes from | | --keypair <path> | yes | Operator keypair. No default | | --strategy <tag> | no | Opaque strategy tag, defaulting to unspecified. The on-chain field holds 32 bytes, measured in UTF-8 bytes rather than characters, and a longer tag is refused rather than truncated |

A newly registered forager starts as a scout at zero pheromone, drawing on the scout budget rather than the main pool until it is promoted. Both foragers on devnet are still in that state.

Global options

These go on any command, before it or after it.

| Option | Meaning | |---|---| | --cluster <name> | mainnet-beta (default), devnet, testnet, localnet | | --rpc <url> | Private endpoint, overriding the cluster default | | --idl <path> | Read the IDL from this file instead of the two sources found automatically. No fallback: a missing file, or one that is not the colony IDL, stops the command | | --program-id <address> | Override the address the IDL carries | | --decimals <n> | Override base-asset decimals. No default: read from the mint account when omitted | | --json | Machine-readable output. Implemented on kolny trails and no other command | | -y, --yes | Skip the confirmation prompt | | -V, --version | Print the version | | -h, --help | Print help for the command it follows |

Cluster defaults are public RPC endpoints. No paid endpoint is built in: a paid URL carries its key in the query string, and a globally installed CLI shipping one would hand that key to everyone who installed it. A custom --rpc is never echoed back in output, only labelled as custom.

Honesty

Agent trading loses money. Every performance figure this tool prints is realized and read from chain state, and drawdown and slash history are shown alongside it rather than filtered out. There are no projections here.

Development

npm install
npm run typecheck
npm run build
npm test          # exercises the built executable, including the refusals above

Nothing in this package deploys the program, generates a keypair, or changes any global Solana configuration. It reads the cluster you name and signs with the keypair you name, and that is the whole of its authority.

No colony address is written into this code, for the reason in A mainnet deployment changes every address: @kolny/sdk derives all seven from the IDL on every run.

The IDL itself is not written into the code either, but a published build does ship a copy of it, frozen at the version that shipped -- so a released kolny-cli speaks to the program that existed when it was released. A clone of this repository uses its own Anchor build output ahead of that copy, and --idl overrides both, as does --program-id for the address alone. See Where the IDL comes from.