Commands
A command adds an entry to Aero's command palette. Commands are the only contribution type that may carry code: declare the command in aero.json, and optionally register a handler in sandboxed JS via the aero.commands API.
1. Declare the command
"contributes": {
"commands": [
{ "command": "dracula-warm.apply", "title": "Dracula Warm: Apply Theme" }
]
}| Field | Required | Constraint |
|---|---|---|
command | yes | ^[A-Za-z0-9_.-]+$. Unique within the IDE. |
title | yes | ≥ 1 char. Shown in the palette. |
- Id convention:
"<name>.<verb>", e.g.dracula-warm.apply. It must be unique across all installed extensions and built-ins. - The
titleis what users see and search for in the palette (Cmd/Ctrl+Shift+P).
2. Three ways a command behaves
| Setup | Result |
|---|---|
| Command + registered JS handler | Running it activates the extension (if needed) and calls your handler. |
Command + no handler + no main | No-op — the palette still lists it. |
| Command bound to a built-in via keybinding | The keybinding triggers built-in behaviour. |
So a command always appears in the palette; whether it does something depends on whether a handler is registered.
3. Add a handler (optional JS)
To make a command do something, set main in the manifest and register a handler in your entry file. Also declare the matching activation event so the host loads your JS lazily.
{
"main": "extension.js",
"activationEvents": ["onCommand:dracula-warm.apply"],
"contributes": {
"commands": [
{ "command": "dracula-warm.apply", "title": "Dracula Warm: Apply Theme" }
]
}
}// extension.js — runs sandboxed; only `aero.*` is available.
'use strict';
function activate(context) {
const sub = aero.commands.registerCommand('dracula-warm.apply', async () => {
await aero.theme.apply('Dracula Warm');
aero.window.setStatusBarMessage('Dracula Warm applied', 2000);
});
context.subscriptions.push(sub);
}
function deactivate() {}When the user runs dracula-warm.apply, the host:
- fires the
onCommand:dracula-warm.applyactivation event; - loads
extension.jsin the sandbox and callsactivate(context); - invokes the handler you registered.
Sandbox boundary
extension.js runs in a sandboxed <iframe sandbox="allow-scripts"> — no DOM, no Node, no window.api, no arbitrary network. The only channel is the postMessage bridge exposing aero.*. The host enforces a method allow-list, a 256 KB arg cap, one in-flight activation per extension, and 5 s timeouts.
4. Calling other commands
Run any command — your own, another extension's, or a built-in — with aero.commands.executeCommand:
const result = await aero.commands.executeCommand('some-other.command', arg1);5. The command lifecycle on the bus
For host/integrator reference, command activity surfaces on AeroBus:
command:run { command, args }— a command was invoked.ext:activated { id }— an extension'smainwas loaded andactivateran.
Extension authors don't touch the bus directly (it's renderer-side, outside the sandbox), but these events are how the IDE wires the palette to your handler.
Related
- Keybindings — bind a chord to a command.
- Activation events — when your JS loads.
- The
aero.*API — everything a handler can call.