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

@visualautomate/cli

v0.6.1

Published

Build and test VisualAutomate plugins.

Readme

@visualautomate/cli

Build and test VisualAutomate plugins.

npm install -g @visualautomate/cli
vsa init my-step
cd my-step
vsa dev                 # the sandbox, and a run again on every save

Nothing here signs in. Your plugin reaches the platform through its GitHub repository: push a commit, the tests run there, and Publish on the portal takes it from the commit that passed.

va and visualautomate are the same command.

That is the whole toolchain. There is nothing else to install: the CLI carries the SDK inside it, compiles your TypeScript itself, and writes the type declarations into the project. A plugin directory has no node_modules, no build step and no dependencies.

What init gives you

plugin.ts            your code
manifest.json        what the canvas shows, with the rest of the options
                     written out as commented examples
plugin.json          name, version, description, category
test.json            the input, config, storage and state `test` runs with
visualautomate.d.ts  the types, written in rather than installed
tsconfig.json        so your editor type-checks it

Templates: blank, http-request, connection, file. Pass one with --template. Each runs unmodified.

Types in your editor

Your plugin's code imports nothing — the platform refuses the word import anywhere in the file, comments included, and the SDK is not a package a step may require. So the types are written into your project instead, and one line names them:

/** @type {PluginModule} */
module.exports = {
  execute: async (input, config, context) => {
    //                              ^ completes, with the documentation
  },
}

dev and test write visualautomate.d.ts and a jsconfig.json beside your code and keep them current; vsa types does it on its own. In an app's repository they go at the root, where they cover every module. Nothing is installed, nothing of it exists while your plugin runs, and a file you wrote yourself is never written over — delete ours and it comes back.

add:property

A field on the node — what somebody fills in when they drop your module into a workflow — written into manifest.json:

vsa add:property string apiKey "The key to call with"
vsa add:property number retries "How many times to try again" 2 --required
vsa add:property select mode "Which way round" fast --options "Fast:fast,Thorough:thorough"

<type> <name> [description] [default], where the name is what your code reads: config.apiKey. The label is made from the name unless --label says otherwise, and the default is stored as its type has it, so 2 is a number and false is a boolean.

Types: string, number, boolean, select, multiselect, json, color, date, connection, file, path, paths. A select or multiselect needs --options, and a connection needs --provider <name>.

The same field is added to visualautomate.test.json under config, with its default or a value of the right shape, so the next vsa test passes something for it instead of nothing. A value you have already put there is kept.

At the root of an app's repository, --module <name> says which one — the same short names test and dev take. The manifest is edited where it stands: your comments, the examples underneath and your own formatting stay as they are.

test

Runs your plugin the way the platform would, in three steps:

  1. The platform's code checks. The same rules the upload applies: only allowed packages in require, no dynamic require, none of the refused patterns (eval, process.env, import, …). Code that fails here would be refused on push, and says why.
  2. The sandbox's require. A step gets the allowed npm packages and nothing else — no fs, no http. Those packages are installed in the sandbox; here they come from your project, and a missing one prints the npm install command for the version the sandbox has.
  3. A context that behaves. storage and state are real, in memory, and the result shows what the step left behind:
✓ passed  success, 3ms
output   success (expected success)
data     { "path": "notes/hello-shouted.txt", "size": 17 }
files    notes/hello.txt, notes/hello-shouted.txt
state    {"runs":1}

A run passes when it succeeds and, if the test file names an expectOutput, leaves by that port — the same verdict as the GitHub check.

The verdict is green when it passed and red when it did not, in the sandbox as well as out here. NO_COLOR turns that off, and output that is not a terminal — a pipe, a file, a CI log — is plain anyway; FORCE_COLOR turns it back on.

In a GitHub repository

test works wherever the platform tests your code on a push:

my-plugin/                    a plugin's repository
  manifest.json
  index.js
  visualautomate.test.json

my-app/                       an app's repository
  modules/
    send-message/
      manifest.json
      index.js
      visualautomate.test.json
    list-channels/
      …

At the root of an app's repository every module runs, one after another. Name one to run only that: vsa test send-message, or any short name that picks it out — vsa test send. --module <name> is the same thing. index.js in a repository is run as written, unbundled, because that is the file the sandbox loads.

dev

vsa dev                    # every module in modules/
vsa dev send-message       # one of them; the shortest name that picks it will do
vsa dev --tier boosted     # a bigger machine
vsa dev --local            # on this machine's Node, without the sandbox

Everything in one command: it starts Docker if it is not running, fetches the sandbox image the first time, runs every module, and runs them again on every save. Only the module whose folder changed runs again; a change anywhere else runs them all. Ctrl+C stops it.

context.connections and context.media are not simulated. One is somebody's OAuth account and the other is an ffmpeg container; pretending to be either would produce a plugin that passes here and fails on a canvas. Asking for one fails with a sentence saying so.

visualautomate.test.json (or test.json, or --input <file>) seeds the run:

{
  "config": { "source": "notes/hello.txt" },
  "input": {},
  "storage": { "notes/hello.txt": "hello from a file" },
  "state": { "runs": 0 },
  "expectOutput": "success",
  "timeoutMs": 30000,
  "allowedDomains": ["api.example.com"]
}

The platform's push check reads config, input, expectOutput and timeoutMs; storage, state, secrets and allowedDomains are for running locally. A file that is not valid JSON stops the run rather than running with nothing.

Where fetch may go

On the platform a step may contact the domains its plugin declares, plus any host in the configuration filled in for that step. A declared domain covers its subdomains; localhost, private networks and metadata addresses are never allowed.

Put the domains in allowedDomains and test applies the same rule: a request anywhere else fails. Without allowedDomains nothing is blocked, but every host the platform would refuse is listed after the run.

--persist

Keeps storage and state between runs, in .visualautomate/ next to the plugin:

.visualautomate/storage/<path>   files the step wrote
.visualautomate/state.json       the workflow state

Once that folder exists it is used without the flag. Delete it to start over, and add it to .gitignore.

The sandbox on your machine

dev uses it by default; test does with --docker:

vsa dev
vsa test --docker
vsa dev --tier boosted

Your plugin runs inside the plugin sandbox image, ghcr.io/visualautomate/plugin-sandbox, at the version of this CLI:

  • the Node version the platform runs plugins on, with every allowed npm package installed — no npm install needed;
  • the CPU and memory of the sandbox tier: standard (¼ vCPU, 1 GB, the default), boosted (½ vCPU, 4 GB), high (1 vCPU, 6 GB), max (2 vCPU, 8 GB);
  • no Linux capabilities, a read-only filesystem apart from your project and a small /tmp, and nothing else from your machine.

A step that runs out of memory or time here does the same on the platform.

Docker does not have to be running: dev starts Docker Desktop and waits for it, then fetches the image if this machine does not have it. --image uses a different image, for example one you built yourself. The watcher stays on your machine, and each run starts a fresh container.

context.media is not available locally, in or out of Docker.

Publishing

Not from here. Connect the repository to your app on the portal, push your commit, and the platform runs the same checks vsa test runs against every module it touched. Publish on the portal takes the code from a commit that passed, and a version snapshot is saved so you can roll back.

Comments in manifest.json are a convenience of this tool: the file on your disk may have them, and they are stripped before the manifest is read.

Where things are

The runtime API your plugin is written against is documented in the types (visualautomate.d.ts in your project, @visualautomate/plugin-sdk if you prefer to install it) and on the platform's own docs page.