outl docs
Inspire
Capability:
network· Source on GitHub
A one-command plugin that reaches the outside world.
The inspire command fetches a random quote over HTTP and shows it as a notification.
It is the canonical template for any external integration from a plugin — swap the URL and add an auth header and you have a GPT call, a webhook, or a REST sync.
What it demonstrates
ctx.net.fetch and how the host gates it.
Network is a permission, not a capability.
The plugin declares the exact domain it will hit, and the host checks every request against the approved set.
A URL outside the approved domains is refused with { ok: false } — it does not throw — so the handler always checks r.ok before reading the body.
A bare network:* is rejected by the host.
You scope to a domain (network:api.quotable.io) or a leading-label wildcard (network:*.quotable.io).
The fetch is blocking under the hood (it runs on the plugin’s own thread), which is why timeoutMs is required: no unbounded calls.
The code
import { definePlugin, type PluginContext } from "@outl/plugin-sdk";
/** Public quote API. One specific domain — `network:*` is rejected by the host. */
const QUOTE_URL = "https://api.quotable.io/random";
/** Shape of the JSON body the quote API returns. */
interface Quote {
content: string;
author: string;
}
export default definePlugin({
activate(ctx: PluginContext) {
ctx.commands.register("inspire", async () => {
// The fetch is gated by the `network:api.quotable.io` permission. A host
// outside the approved set comes back as `{ ok: false }`, never a throw.
const r = await ctx.net.fetch(QUOTE_URL, { timeoutMs: 8000 });
if (!r.ok) {
const reason = `HTTP ${r.status}`;
ctx.log.error(`[inspire] fetch failed: ${reason}`);
ctx.ui.notify(`Could not reach the quote service (${reason})`);
return;
}
const quote = await r.json<Quote>();
ctx.ui.notify(`💬 ${quote.content} — ${quote.author}`);
});
},
});
Manifest
{
"capabilities": ["slash-command"],
"permissions": ["network:api.quotable.io"],
"contributes": {
"commands": [{ "id": "inspire", "title": "Inspire me" }]
}
}
Note that capabilities lists only slash-command.
The network access lives entirely in permissions, scoped to the one domain the plugin talks to.
Try it
outl -w <workspace> plugin install ./examples/inspire --yes
# then run the `inspire` command from the slash menu / command palette
Adapting it
Point it at a different API and read the credentials from config.
const { apiKey } = ctx.config.get<{ apiKey: string }>();
const r = await ctx.net.fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}`, "content-type": "application/json" },
body: JSON.stringify({ /* ... */ }),
timeoutMs: 30_000,
});
Two manifest changes follow from that: change the permission to network:api.openai.com, and add an apiKey field to a config.schema.json so the host renders a settings form and validates the value before ctx.config.get hands it back.