Skip to main content
Menu

Documentation

Plugins

A plugin is a directory of JavaScript that adds buttons, panels, tabs, modals and background services to the app.

Shape

Two files minimum: a JSON manifest and an ES module entry point.

my-plugin/
  plugin.json
  index.js
{
  "id": "acme.my-plugin",
  "name": "My Plugin",
  "version": "0.1.0",
  "apiVersion": 1,
  "entry": "index.js",
  "permissions": ["storage"],
  "contributes": {
    "commands": [{ "id": "acme.my-plugin.hello", "title": "My Plugin: Hello" }]
  }
}

The entry exports an activate(ctx) function. ctx is the only handle you get — commands, storage, and the surfaces you declared.

Your id must look like publisher.plugin in lowercase. The bentomux publisher is reserved.

Surfaces

Nine places a plugin can appear:

KeyAdds
commandsPalette entries and keybindable actions
topbarA button in the top bar
sidebarAn entry in the left sidebar
dockAn item in the bottom dock
tabsA custom tab type
modalsA modal dialog
widgetsA panel in the right sidebar
settingsA section in the Settings modal
servicesA headless background task

Permissions

Declare what you use. The manifest rejects any name it does not know.

storage            app.read           app.prefs.write
app.window         files.temp         workspaces.write
tabs.write         terminal.read      terminal.input
git.read          git.push           agents.read
agents.write       remote.control     backend.invoke

Be honest about the ceiling here: permissions are a declared contract, not a sandbox. Plugins run in the app's own web context, with the same reach the host has. Review what you install the way you would review a shell alias.

Validate before you ship

The validator runs from a terminal — no need to open the app. Exit 0 is clean, 1 means errors, 2 is a usage mistake.

bentomux --plugin-validate ./my-plugin
bentomux --plugin-validate ./my-plugin --json

It checks, without running any of your code:

  • the manifest exists, parses, and matches the schema
  • the id is well formed and not using the reserved publisher
  • version and minAppVersion are semver, apiVersion is known
  • the entry file exists, is inside the plugin root, and is not empty
  • every contribution id is namespaced under your plugin id and unique
  • every permission name is one the app knows
  • every command referenced by a button, sidebar entry or dock item is declared
  • icons resolve — a builtin name, or a file that exists
  • every network origin is a bare https:// host with no path
  • the whole plugin is under 5 MB

The CLI additionally runs node --check on the entry to catch syntax errors. The in-app validator skips that step, because Node is not guaranteed at runtime; a syntax error surfaces at activation instead.

Install

From a folder on disk, from a zip, or over HTTPS. Remote installs must be https:// and are checked against a pinned SHA-256 before anything is unpacked.

There is no hosted registry of our own. GitHub is the catalog: a public repository carrying the topic bentomux-plugin and a release with exactly one .zip asset is discoverable, and the app verifies the asset's published sha256 before anything is unpacked.

The plugin marketplace page lists what is currently published, most-starred first. It is a discovery surface, not an installer: the app is what downloads, verifies and installs a release.

Network access

A plugin that fetches an outside website declares it in plugin.json under network, as bare https://host[:port] origins with no path. The Studio install review shows the declared hosts next to the permissions, and a host outside the last build stays blocked until the next release.

Start from a template

Seven scaffolding templates ship with the app, one per surface. Each comes with a manifest, an entry, and the TypeScript definitions for ctx.

basic  ·  topbar  ·  sidebar  ·  tab  ·  modal  ·  widget  ·  service

Plugin Studio and the plugin creator are in the app, under Plugins. Studio lists what is installed, validates, and lets you remove or reinstall.

Configuration →

Edit this page (opens in a new tab)