Code mode for agents.
Turn any API or host capability into a callable action. Install a plugin, write JavaScript, call actions. Code runs in a QuickJS WASM runtime with plugin globals; enable the built-in node plugin when you want filesystem, path, OS, process, crypto, shell, and fetch actions.
npm install -g runlinenpm install -g runline
runline init
runline connection add bf --plugin brandfetch --set apiKey=xxx
runline exec 'return await brandfetch.brand.getColors({ domain: "nike.com" })'
# => [{ hex: "#E5E5E5", type: "accent" }, { hex: "#111111", type: "dark" }, ...]All 203 built-in plugins ship bundled inside runline — no per-plugin install step. Just add a connection for the one you want to use. Agent code runs in a QuickJS runtime: each configured plugin is a top-level global, dot-chain into resource and action. Configure the built-in node plugin for host work like node.fs.readFile, node.process.execFile, node.path.join, node.crypto.hash, and node.fetch.
// agent writes this
const company = await brandfetch.brand.getCompany({ domain: "stripe.com" });
const deals = await pipedrive.deal.list({ limit: 10 });
const issue = await github.issue.create({
owner: "acme", repo: "api",
title: `New lead: ${company.name}`,
body: `${deals.length} open deals`
});
return { company: company.name, issue: issue.number };203 built-in plugins covering popular SaaS, DevOps, productivity, image-generation APIs, and host Node capabilities. All ship with the package — no separate install needed.
Set the env var shown in the Auth column, add a connection, and go:
export GITHUB_TOKEN=ghp_xxx
runline connection add gh --plugin github --set token=$GITHUB_TOKEN
runline exec 'return await github.user.listRepos({ username: "torvalds" })'# List all available actions
runline actions
# List actions only for connected services
runline actions --connected
# Get Nike's brand colors
runline exec 'return await brandfetch.brand.getColors({ domain: "nike.com" })'
# Create a GitHub issue
runline exec '
return await github.issue.create({
owner: "acme", repo: "api",
title: "Bug: login broken",
labels: ["bug", "urgent"]
})
'
# Search Pipedrive deals
runline exec 'return await pipedrive.deal.search({ term: "Acme" })'
# Chain actions together
runline exec '
const contact = await hubspot.contact.get({ id: "123" });
const task = await todoist.task.create({
content: `Follow up with ${contact.properties.firstname}`,
priority: 4
});
return { contact: contact.properties.email, taskId: task.id };
'
# Discover actions from inside the runtime
runline exec 'return actions.list()'
runline exec 'return actions.list("brandfetch")'
runline exec 'return actions.find("create issue")'
runline exec 'return actions.describe("github.issue.create")'
runline exec 'return actions.check("github.issue.create", { owner: "a" })'
# Output as JSON (for agents)
runline exec 'return await github.repo.list({ owner: "torvalds" })' --jsonPlugins export a function that receives a RunlinePluginAPI and registers actions.
import type { RunlinePluginAPI } from "runline";
export default function orders(rl: RunlinePluginAPI) {
rl.setName("orders");
rl.setVersion("1.0.0");
// Connection config — env vars override config.json values
rl.setConnectionSchema({
apiKey: { type: "string", required: true, env: "ORDERS_API_KEY" },
baseUrl: { type: "string", required: true, env: "ORDERS_BASE_URL" },
});
rl.registerAction("list", {
description: "List orders for an organization",
inputSchema: {
orgId: { type: "string", required: true },
status: { type: "string", required: false, description: "open, closed, or all" },
limit: { type: "number", required: false },
},
async execute(input, ctx) {
const { orgId, status, limit } = input as Record<string, unknown>;
const url = new URL(`${ctx.connection.config.baseUrl}/orgs/${orgId}/orders`);
if (status) url.searchParams.set("status", status as string);
if (limit) url.searchParams.set("limit", String(limit));
const res = await fetch(url.toString(), {
headers: { Authorization: `Bearer ${ctx.connection.config.apiKey}` },
});
if (!res.ok) throw new Error(`Orders API ${res.status}: ${await res.text()}`);
return res.json();
},
});
rl.registerAction("create", {
description: "Create a new order",
inputSchema: {
orgId: { type: "string", required: true },
customer: { type: "string", required: true },
total: { type: "number", required: true },
},
async execute(input, ctx) {
const res = await fetch(`${ctx.connection.config.baseUrl}/orders`, {
method: "POST",
headers: {
Authorization: `Bearer ${ctx.connection.config.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(input),
});
if (!res.ok) throw new Error(`Orders API ${res.status}: ${await res.text()}`);
return res.json();
},
});
}Key points: plugin execute handlers run outside the QuickJS runtime with full Node.js access (fetch, fs, etc). Agent code calls them through configured plugin globals; add a node connection to expose host APIs through the built-in node plugin. ctx.connection.config holds the resolved config with env var overrides applied.
See packages/runline-plugins/ for 202 real-world examples.
Agent code runs in a QuickJS WASM runtime:
- Timeout — configurable, kills infinite loops
- Memory limit — configurable, prevents OOM
console.log— captured and returned inresult.logs- Plugin globals — each installed plugin is a top-level proxy (e.g.
github,slack,brandfetch). Dot-chain into resource and action:github.issue.create(input) - Configured Node globals — when configured, the built-in
nodeplugin exposes host-backed actions such asnode.fs.readFile,node.fs.writeFile,node.path.join,node.os.info,node.process.execFile,node.crypto.hash, andnode.fetch
Every command supports --json. Use runline actions --json for full schemas with input types.
runline actions --json # all actions with schemas
runline actions --connected --json # only connected services
runline exec '<code>' --json # structured { result, logs } outputimport { Runline } from "runline";
import brandfetch from "runline-plugin-brandfetch";
const rl = Runline.create({
plugins: [brandfetch],
connections: [{ name: "bf", plugin: "brandfetch", config: { apiKey: "xxx" } }],
});
const result = await rl.execute(`
const colors = await brandfetch.brand.getColors({ domain: "stripe.com" });
return colors.filter(c => c.type === "accent");
`);
console.log(result.result); // [{ hex: "#635BFF", type: "accent", brightness: 116 }]runline exec "<code>" # execute JS in the QuickJS runtime
runline exec -f ./script.js # execute a file
runline actions # list all actions
runline actions --connected # only actions for configured connections
runline plugin install <source> # install from git/npm/local
runline plugin list # list installed plugins
runline plugin remove <name> # remove a plugin
runline connection add <n> -p <plugin> -s key=val # add connection
runline connection list # list connections
runline connection remove <name> # remove a connection
runline init # create .runline/ directory.runline/config.json:
{
"connections": [
{ "name": "gh", "plugin": "github", "config": { "token": "ghp_xxx" } },
{ "name": "bf", "plugin": "brandfetch", "config": { "apiKey": "xxx" } }
],
"timeoutMs": 30000,
"memoryLimitBytes": 67108864
}Env vars override config values. Plugins declare env var names in their connection schema (e.g. GITHUB_TOKEN).
Runline is a bun workspace monorepo: packages/runline (library + CLI), packages/runline-plugins (203 built-in plugins, bundled into runline's dist at build time), and packages/pi-runline (pi extension that exposes runline to agents).
bun install
bun --filter runline dev -- exec 'return 1 + 2'
bun --filter runline test
bun run checkThe pi-runline package is a pi extension that plugs runline into coding agents as a single native tool:
execute_runline— run JavaScript in the runline runtime. Discovery happens inside the runtime viaactions.list / find / describe / check, so the agent never needs a separate listing tool.
It ships with /runline-plugins, a fuzzy multi-select picker for choosing which of the 202 plugins the agent should see, plus a guided credential prompt for the ones you enable.
pi install npm:pi-runline
# then in any pi session inside a project with .runline/
/runline-pluginsSee packages/pi-runline/README.md for details.
dripline is query mode — SQL tables over live APIs. runline is code mode — JavaScript actions over the same APIs. Same plugin architecture, same connection config, different interface. Use dripline when you want to SELECT rows; use runline when you want to create, update, delete, or chain multiple API calls together.
MIT