Custom plugins

Bring the Grasshopper plug-ins your team already uses to NodePen. Once one is enabled, its components sit in the component library for every script your team owns, and they solve, preview, export and work with your agent like any built-in component.

Plug-ins are part of the Team and Custom plans; see the pricing page. They live in the team's own Plugins tab, under Files. Only a team's admins upload, enable, disable and remove them. A team that moves to the Personal plan keeps the plug-ins it has, still enabled as they were, but can no longer change them.

What NodePen supports

NodePen runs Grasshopper on Linux, under .NET. A plug-in works when it is .NET code from top to bottom.

Works Does not work
Components written in C#, or any other .NET language Native Windows libraries (.dll files written in C or C++), such as a geometry kernel built for Windows
Plug-ins that ship their own .NET .dll files beside the .gha, uploaded as a .zip Anything that needs Windows itself, or Rhino's interface: dialogs, the viewport, the command line

A plug-in that calls into a native library usually uploads fine, and its components fail when they solve, with the error on the component.

Each plug-in also has to:

  • Declare a library and at least one component. The library's id is how NodePen tells one plug-in from another, and its version from the next.
  • Bring its own components. A plug-in is refused if any component has the same id as one Grasshopper ships, or one another of your team's plug-ins already has: there would be no way to tell which one a script meant.
  • Be one version per team. Uploading a new version of a library your team has replaces the plug-in that has it, in place.

Upload a plug-in

In Team settings → Grasshopper plug-ins, choose Upload plug-in. Upload either:

  • The .gha file itself, when the plug-in is that one file.
  • A .zip, when the plug-in needs .dll files beside it. Put the .gha at the top of the zip, or inside a single folder at the top (what Compress makes of a folder on Windows and macOS), with the .dll files it needs.

A zip can hold any number of .gha files at that level, as plug-ins that ship several assemblies sharing one set of .dll files do. Each .gha is a library of its own, with its own name, and together they are one plug-in: enabled, updated and removed together. The plug-in is named after the library with the most components, and the settings list shows each library under it. A .rhp beside them is a Rhino plug-in, not a Grasshopper one, and is left out. If any .gha in the zip can't be loaded, the whole upload is refused, naming it.

Either can be up to 64 MB. A zip can hold up to 500 files and unpack to up to 256 MB. Files the macOS Finder adds to a zip (__MACOSX, .DS_Store) are ignored.

NodePen loads the plug-in to check it before keeping it, then lists it with its version and how many components it has, along with anything Grasshopper had to say while loading it. If it can't be used, the upload is refused with the reason.

A new plug-in starts disabled, so nobody's scripts change until you choose. Enable it, and its components appear in every script your team owns from the next time they load.

A .dll that Grasshopper or another of your plug-ins has already loaded, such as Newtonsoft.Json, is used as already loaded, whatever version your zip carries. That is how Grasshopper behaves on the desktop too.

Update, rename, disable or remove

  • Update by uploading the new version. It replaces the plug-in that has any of the same libraries, keeping its name and staying enabled if it was. A zip whose libraries are spread across two or more of your plug-ins is refused: remove all but one of them first.
  • Rename to call it what your team calls it. Its libraries keep their own names, and so do their components.
  • Disable to stop your team's scripts from using it. Its components stay where they are, but stop solving until it is enabled again.
  • Remove to delete it. A script that uses one of its components stops solving, with the missing component named, until the plug-in is uploaded again.

Each takes effect on your team's next solve.

From a script

Everything above can be done through the API as well, with an API token a team admin minted with the plugin:write scope — to publish a new build of your plug-in from CI, say.

An upload goes straight to storage, in three steps: start it with POST /api/v1/plugins/uploads and the file's size, PUT the file to the uploadUrl that answers with, then complete it with POST /api/v1/plugins/uploads/{uploadId}.

START=$(curl -s -X POST https://app.nodepen.io/api/v1/plugins/uploads \
  -H "Authorization: Bearer $NODEPEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"fileName\": \"MyPlugin.gha\", \"sizeBytes\": $(wc -c < MyPlugin.gha)}")
curl -T MyPlugin.gha "$(echo "$START" | jq -r .uploadUrl)"
curl -X POST "https://app.nodepen.io/api/v1/plugins/uploads/$(echo "$START" | jq -r .uploadId)" \
  -H "Authorization: Bearer $NODEPEN_TOKEN"

The upload is checked exactly as one from the settings page is, and a new version replaces the old one and stays enabled if it was. Listing plug-ins, and the components each contributes, needs team:read. See Plugins in the API reference for every endpoint.