runline 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -265
- package/dist/sdk.d.ts +3 -0
- package/dist/sdk.js +5 -0
- package/package.json +29 -19
- package/.pi/extensions/runline-context/index.ts +0 -135
- package/.pi/extensions/runline-context/package.json +0 -17
package/README.md
CHANGED
|
@@ -1,273 +1,20 @@
|
|
|
1
|
-
# runline
|
|
1
|
+
# runline
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The runline library and CLI. Published on npm as [`runline`](https://www.npmjs.com/package/runline). See the [monorepo README](../../README.md) for the full story, quickstart, and plugin catalog.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## What lives here
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## Quick Start
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
runline init
|
|
15
|
-
runline plugin install git:github.com/Michaelliv/runline#plugins/brandfetch
|
|
16
|
-
runline connection add bf --plugin brandfetch --set apiKey=xxx
|
|
17
|
-
|
|
18
|
-
runline exec 'return await brandfetch.brand.getColors({ domain: "nike.com" })'
|
|
19
|
-
# => [{ hex: "#E5E5E5", type: "accent" }, { hex: "#111111", type: "dark" }, ...]
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Agent code runs in a QuickJS sandbox. Each installed plugin is a top-level global — no bracket notation, just dot-chain into resource and action. Plugins execute outside the sandbox with full network access; the agent can only reach APIs through the actions you've installed.
|
|
23
|
-
|
|
24
|
-
```js
|
|
25
|
-
// agent writes this
|
|
26
|
-
const company = await brandfetch.brand.getCompany({ domain: "stripe.com" });
|
|
27
|
-
const deals = await pipedrive.deal.list({ limit: 10 });
|
|
28
|
-
const issue = await github.issue.create({
|
|
29
|
-
owner: "acme", repo: "api",
|
|
30
|
-
title: `New lead: ${company.name}`,
|
|
31
|
-
body: `${deals.length} open deals`
|
|
32
|
-
});
|
|
33
|
-
return { company: company.name, issue: issue.number };
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Plugins
|
|
37
|
-
|
|
38
|
-
188 plugins covering popular SaaS, DevOps, and productivity APIs. Each wraps a single service's REST/GraphQL API with typed actions.
|
|
39
|
-
|
|
40
|
-
All plugins install via `runline plugin install git:github.com/Michaelliv/runline#plugins/<name>`.
|
|
41
|
-
|
|
42
|
-
| Plugin | Actions | Auth |
|
|
43
|
-
|--------|---------|------|
|
|
44
|
-
| **github** | file/issue/pr/release/repo/review/user CRUD, search | Bearer token |
|
|
45
|
-
| **gitlab** | issue/merge request/repo/user CRUD | Bearer token |
|
|
46
|
-
| **jira** | issue/project/user CRUD, transitions | Basic auth |
|
|
47
|
-
| **slack** | channel/message/user/reaction/star/file ops | Bearer token |
|
|
48
|
-
| **discord** | channel/message/member CRUD, reactions | Bot token |
|
|
49
|
-
| **notion** | block/database/page/user CRUD, search | Bearer token |
|
|
50
|
-
| **todoist** | task/project/section/comment/label CRUD | Bearer token |
|
|
51
|
-
| **linear** | issue/project/team/comment CRUD (GraphQL) | Bearer token |
|
|
52
|
-
| **hubspot** | contact/company/deal/ticket/engagement CRUD | Bearer token |
|
|
53
|
-
| **pipedrive** | deal/person/org/activity/lead/note/product CRUD, search | API token |
|
|
54
|
-
| **salesforce** | account/contact/lead/opportunity/case/task CRUD | OAuth2 |
|
|
55
|
-
| **shopify** | order/product/customer CRUD | API key |
|
|
56
|
-
| **stripe** | charge/customer/source/coupon CRUD | Bearer token |
|
|
57
|
-
| **airtable** | base/record CRUD, search, upsert | Bearer token |
|
|
58
|
-
| **supabase** | row CRUD | API key |
|
|
59
|
-
| **docker** | container/image/volume/network ops | Unix socket |
|
|
60
|
-
| **telegram** | message/chat/callback/pin ops | Bot token in URL |
|
|
61
|
-
| **twitter** | tweet/user/dm/list ops | OAuth2 Bearer |
|
|
62
|
-
| **clickup** | task/list/folder/space/comment/checklist/team CRUD | Bearer token |
|
|
63
|
-
| **asana** | task/project/section/subtask/tag/user CRUD | Bearer token |
|
|
64
|
-
| **trello** | board/list/card/checklist/attachment/label/member CRUD | API key |
|
|
65
|
-
| **monday** | board/group/item/column/update (GraphQL) | Bearer token |
|
|
66
|
-
| **mailchimp** | list/member/campaign/tag ops | Bearer token |
|
|
67
|
-
| **sendgrid** | contact/list/email ops | Bearer token |
|
|
68
|
-
| **elasticsearch** | document/index CRUD | Basic auth |
|
|
69
|
-
| **cloudflare** | zone/dns/worker/kv/r2/d1/pages/queue CRUD | Bearer token |
|
|
70
|
-
| **databricks** | sql/files/genie/catalog/table/volume/function/vector search | Bearer token |
|
|
71
|
-
| **splunk** | search/alert/report/user CRUD | Bearer token |
|
|
72
|
-
| **home-assistant** | state/service/history/config/template/event ops | Bearer token |
|
|
73
|
-
| **openweathermap** | current/5-day forecast | API key |
|
|
74
|
-
| **brandfetch** | logos/colors/fonts/company/industry lookup | Bearer token |
|
|
75
|
-
|
|
76
|
-
<details>
|
|
77
|
-
<summary>All 188 plugins</summary>
|
|
78
|
-
|
|
79
|
-
actionNetwork, activeCampaign, adalo, affinity, agileCrm, airtable, airtop, apiTemplateIo, asana, autopilot, bambooHr, bannerbear, baserow, beeminder, bitly, bitwarden, box, brandfetch, brevo, bubble, chargebee, circleci, ciscoWebex, clearbit, clickup, clockify, cloudflare, cockpit, coda, coingecko, contentful, convertkit, copper, cortex, currents, customerIo, databricks, deepl, demio, dhl, discord, discourse, disqus, docker, drift, dropbox, dropcontact, egoi, elasticsearch, emelia, erpnext, facebookGraph, freshdesk, freshservice, freshworksCrm, getresponse, ghost, github, gitlab, gong, gotify, gotowebinar, grafana, graphql, grist, hackernews, halopsa, harvest, helpscout, highlevel, homeAssistant, hubspot, humanticAi, hunter, intercom, iterable, jenkins, jira, keap, kobotoolbox, lemlist, line, linear, lingvanex, linkedin, lonescale, magento, mailcheck, mailchimp, mailerlite, mailgun, mailjet, mandrill, marketstack, matrix, mattermost, mautic, medium, messagebird, metabase, misp, mocean, monday, monicaCrm, msg91, nasa, netlify, netscalerAdc, nextcloud, nocodb, notion, npm, odoo, okta, oneSimpleApi, onfleet, openThesaurus, openweathermap, oura, paddle, pagerduty, paypal, peekalink, phantombuster, philipsHue, pipedrive, plivo, postbin, posthog, profitwell, pushbullet, pushcut, pushover, quickbase, quickbooks, quickchart, raindrop, reddit, rocketchat, rundeck, salesforce, salesmate, securityScorecard, segment, sendgrid, sendy, sentry, servicenow, shopify, signl4, slack, sms77, splunk, spotify, stackby, storyblok, strapi, strava, stripe, supabase, syncromsp, tapfiliate, telegram, thehive, thehiveProject, todoist, travisci, trello, twake, twilio, twist, twitter, unleashedSoftware, uplead, uproc, uptimerobot, urlscanio, vero, vonage, wekan, woocommerce, wordpress, xero, yourls, zammad, zendesk, zoho, zoom, zulip
|
|
80
|
-
|
|
81
|
-
</details>
|
|
82
|
-
|
|
83
|
-
## Examples
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
# List all available actions
|
|
87
|
-
runline actions
|
|
88
|
-
|
|
89
|
-
# Get Nike's brand colors
|
|
90
|
-
runline exec 'return await brandfetch.brand.getColors({ domain: "nike.com" })'
|
|
91
|
-
|
|
92
|
-
# Create a GitHub issue
|
|
93
|
-
runline exec '
|
|
94
|
-
return await github.issue.create({
|
|
95
|
-
owner: "acme", repo: "api",
|
|
96
|
-
title: "Bug: login broken",
|
|
97
|
-
labels: ["bug", "urgent"]
|
|
98
|
-
})
|
|
99
|
-
'
|
|
100
|
-
|
|
101
|
-
# Search Pipedrive deals
|
|
102
|
-
runline exec 'return await pipedrive.deal.search({ term: "Acme" })'
|
|
103
|
-
|
|
104
|
-
# Chain actions together
|
|
105
|
-
runline exec '
|
|
106
|
-
const contact = await hubspot.contact.get({ id: "123" });
|
|
107
|
-
const task = await todoist.task.create({
|
|
108
|
-
content: `Follow up with ${contact.properties.firstname}`,
|
|
109
|
-
priority: 4
|
|
110
|
-
});
|
|
111
|
-
return { contact: contact.properties.email, taskId: task.id };
|
|
112
|
-
'
|
|
113
|
-
|
|
114
|
-
# Discover actions from inside the sandbox
|
|
115
|
-
runline exec 'return brandfetch.help()'
|
|
116
|
-
runline exec 'return help()'
|
|
117
|
-
|
|
118
|
-
# Output as JSON (for agents)
|
|
119
|
-
runline exec 'return await github.repo.list({ owner: "torvalds" })' --json
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Writing a Plugin
|
|
123
|
-
|
|
124
|
-
Plugins export a function that receives a `RunlinePluginAPI` and registers actions.
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
import type { RunlinePluginAPI } from "runline";
|
|
128
|
-
|
|
129
|
-
export default function orders(rl: RunlinePluginAPI) {
|
|
130
|
-
rl.setName("orders");
|
|
131
|
-
rl.setVersion("1.0.0");
|
|
132
|
-
|
|
133
|
-
// Connection config — env vars override config.json values
|
|
134
|
-
rl.setConnectionSchema({
|
|
135
|
-
apiKey: { type: "string", required: true, env: "ORDERS_API_KEY" },
|
|
136
|
-
baseUrl: { type: "string", required: true, env: "ORDERS_BASE_URL" },
|
|
137
|
-
});
|
|
7
|
+
- `src/` — the library source (SDK, engine, plugin API, loader, CLI commands)
|
|
8
|
+
- `scripts/` — tooling (e.g. `generate-plugin-table.js` for the root README)
|
|
9
|
+
- `dist/plugins/` — populated at build time by copying `packages/runline-plugins/dist`
|
|
138
10
|
|
|
139
|
-
|
|
140
|
-
description: "List orders for an organization",
|
|
141
|
-
inputSchema: {
|
|
142
|
-
orgId: { type: "string", required: true },
|
|
143
|
-
status: { type: "string", required: false, description: "open, closed, or all" },
|
|
144
|
-
limit: { type: "number", required: false },
|
|
145
|
-
},
|
|
146
|
-
async execute(input, ctx) {
|
|
147
|
-
const { orgId, status, limit } = input as Record<string, unknown>;
|
|
148
|
-
const url = new URL(`${ctx.connection.config.baseUrl}/orgs/${orgId}/orders`);
|
|
149
|
-
if (status) url.searchParams.set("status", status as string);
|
|
150
|
-
if (limit) url.searchParams.set("limit", String(limit));
|
|
151
|
-
|
|
152
|
-
const res = await fetch(url.toString(), {
|
|
153
|
-
headers: { Authorization: `Bearer ${ctx.connection.config.apiKey}` },
|
|
154
|
-
});
|
|
155
|
-
if (!res.ok) throw new Error(`Orders API ${res.status}: ${await res.text()}`);
|
|
156
|
-
return res.json();
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
rl.registerAction("create", {
|
|
161
|
-
description: "Create a new order",
|
|
162
|
-
inputSchema: {
|
|
163
|
-
orgId: { type: "string", required: true },
|
|
164
|
-
customer: { type: "string", required: true },
|
|
165
|
-
total: { type: "number", required: true },
|
|
166
|
-
},
|
|
167
|
-
async execute(input, ctx) {
|
|
168
|
-
const res = await fetch(`${ctx.connection.config.baseUrl}/orders`, {
|
|
169
|
-
method: "POST",
|
|
170
|
-
headers: {
|
|
171
|
-
Authorization: `Bearer ${ctx.connection.config.apiKey}`,
|
|
172
|
-
"Content-Type": "application/json",
|
|
173
|
-
},
|
|
174
|
-
body: JSON.stringify(input),
|
|
175
|
-
});
|
|
176
|
-
if (!res.ok) throw new Error(`Orders API ${res.status}: ${await res.text()}`);
|
|
177
|
-
return res.json();
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Key points: `execute` runs **outside** the sandbox with full Node.js access (fetch, fs, etc). The sandbox can only call your actions through the proxy. `ctx.connection.config` holds the resolved config with env var overrides applied.
|
|
184
|
-
|
|
185
|
-
See [plugins/](plugins/) for 188 real-world examples.
|
|
186
|
-
|
|
187
|
-
## Sandbox
|
|
188
|
-
|
|
189
|
-
Agent code runs in a [QuickJS](https://bellard.org/quickjs/) WASM sandbox:
|
|
190
|
-
|
|
191
|
-
- **No `fetch`** — network access is only through plugin actions
|
|
192
|
-
- **No `fs`** — no filesystem access
|
|
193
|
-
- **Timeout** — configurable, kills infinite loops
|
|
194
|
-
- **Memory limit** — configurable, prevents OOM
|
|
195
|
-
- **`console.log`** — captured and returned in `result.logs`
|
|
196
|
-
- **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)`
|
|
197
|
-
|
|
198
|
-
## For Agents
|
|
199
|
-
|
|
200
|
-
Every command supports `--json`. Use `runline actions --json` for full schemas with input types.
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
runline actions --json # all actions with schemas
|
|
204
|
-
runline exec '<code>' --json # structured { result, logs } output
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
## SDK
|
|
208
|
-
|
|
209
|
-
```typescript
|
|
210
|
-
import { Runline } from "runline";
|
|
211
|
-
import brandfetch from "runline-plugin-brandfetch";
|
|
212
|
-
|
|
213
|
-
const rl = Runline.create({
|
|
214
|
-
plugins: [brandfetch],
|
|
215
|
-
connections: [{ name: "bf", plugin: "brandfetch", config: { apiKey: "xxx" } }],
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
const result = await rl.execute(`
|
|
219
|
-
const colors = await brandfetch.brand.getColors({ domain: "stripe.com" });
|
|
220
|
-
return colors.filter(c => c.type === "accent");
|
|
221
|
-
`);
|
|
222
|
-
|
|
223
|
-
console.log(result.result); // [{ hex: "#635BFF", type: "accent", brightness: 116 }]
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## CLI Reference
|
|
227
|
-
|
|
228
|
-
```bash
|
|
229
|
-
runline exec "<code>" # execute JS in sandbox
|
|
230
|
-
runline exec -f ./script.js # execute a file
|
|
231
|
-
runline actions # list all actions
|
|
232
|
-
runline plugin install <source> # install from git/npm/local
|
|
233
|
-
runline plugin list # list installed plugins
|
|
234
|
-
runline plugin remove <name> # remove a plugin
|
|
235
|
-
runline connection add <n> -p <plugin> -s key=val # add connection
|
|
236
|
-
runline connection list # list connections
|
|
237
|
-
runline connection remove <name> # remove a connection
|
|
238
|
-
runline init # create .runline/ directory
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
## Configuration
|
|
242
|
-
|
|
243
|
-
`.runline/config.json`:
|
|
244
|
-
|
|
245
|
-
```json
|
|
246
|
-
{
|
|
247
|
-
"connections": [
|
|
248
|
-
{ "name": "gh", "plugin": "github", "config": { "token": "ghp_xxx" } },
|
|
249
|
-
{ "name": "bf", "plugin": "brandfetch", "config": { "apiKey": "xxx" } }
|
|
250
|
-
],
|
|
251
|
-
"timeoutMs": 30000,
|
|
252
|
-
"memoryLimitBytes": 67108864
|
|
253
|
-
}
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
Env vars override config values. Plugins declare env var names in their connection schema (e.g. `GITHUB_TOKEN`).
|
|
257
|
-
|
|
258
|
-
## Development
|
|
11
|
+
## Scripts
|
|
259
12
|
|
|
260
13
|
```bash
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
14
|
+
bun run dev -- exec 'return 1 + 2' # run the CLI from source
|
|
15
|
+
bun run build # compile + bundle built-in plugins
|
|
16
|
+
bun run test # bun test src/tests
|
|
17
|
+
bun run check # biome check
|
|
265
18
|
```
|
|
266
19
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
[dripline](https://github.com/Michaelliv/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.
|
|
270
|
-
|
|
271
|
-
## License
|
|
272
|
-
|
|
273
|
-
MIT
|
|
20
|
+
`build` does two things: compiles `src/` with `tsc`, then invokes `bun --filter runline-plugins build` and copies the output into `dist/plugins/` so the published package ships with every built-in plugin.
|
package/dist/sdk.d.ts
CHANGED
|
@@ -28,7 +28,10 @@ export declare class Runline {
|
|
|
28
28
|
name: string;
|
|
29
29
|
version: string;
|
|
30
30
|
actions: string[];
|
|
31
|
+
connectionConfigSchema?: PluginDef["connectionConfigSchema"];
|
|
31
32
|
}>;
|
|
33
|
+
/** Return all connections currently configured. */
|
|
34
|
+
connections(): ConnectionConfig[];
|
|
32
35
|
/**
|
|
33
36
|
* Load runline from a project directory.
|
|
34
37
|
* Discovers .runline/ config and installed plugins, just like the CLI.
|
package/dist/sdk.js
CHANGED
|
@@ -54,8 +54,13 @@ export class Runline {
|
|
|
54
54
|
name: p.name,
|
|
55
55
|
version: p.version,
|
|
56
56
|
actions: p.actions.map((a) => a.name),
|
|
57
|
+
connectionConfigSchema: p.connectionConfigSchema,
|
|
57
58
|
}));
|
|
58
59
|
}
|
|
60
|
+
/** Return all connections currently configured. */
|
|
61
|
+
connections() {
|
|
62
|
+
return [...this._config.connections];
|
|
63
|
+
}
|
|
59
64
|
/**
|
|
60
65
|
* Load runline from a project directory.
|
|
61
66
|
* Discovers .runline/ config and installed plugins, just like the CLI.
|
package/package.json
CHANGED
|
@@ -1,41 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runline",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Code mode for agents — turn any API or command into a callable action",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./utils/http": {
|
|
14
|
+
"types": "./dist/utils/http.d.ts",
|
|
15
|
+
"import": "./dist/utils/http.js",
|
|
16
|
+
"default": "./dist/utils/http.js"
|
|
17
|
+
},
|
|
18
|
+
"./utils/cli": {
|
|
19
|
+
"types": "./dist/utils/cli.d.ts",
|
|
20
|
+
"import": "./dist/utils/cli.js",
|
|
21
|
+
"default": "./dist/utils/cli.js"
|
|
22
|
+
}
|
|
11
23
|
},
|
|
12
24
|
"bin": {
|
|
13
25
|
"runline": "./dist/main.js"
|
|
14
26
|
},
|
|
15
27
|
"files": [
|
|
16
|
-
"dist"
|
|
17
|
-
".pi/extensions"
|
|
28
|
+
"dist"
|
|
18
29
|
],
|
|
19
|
-
"pi": {
|
|
20
|
-
"extensions": [
|
|
21
|
-
".pi/extensions/runline-context"
|
|
22
|
-
]
|
|
23
|
-
},
|
|
24
30
|
"scripts": {
|
|
25
|
-
"build": "tsc &&
|
|
26
|
-
"prepublishOnly": "
|
|
27
|
-
"dev": "
|
|
28
|
-
"test": "
|
|
29
|
-
"format": "
|
|
30
|
-
"lint": "
|
|
31
|
-
"check": "
|
|
31
|
+
"build": "tsc && bun --filter runline-plugins build && rm -rf dist/plugins && cp -R ../runline-plugins/dist dist/plugins",
|
|
32
|
+
"prepublishOnly": "bun run build",
|
|
33
|
+
"dev": "bun run src/main.ts",
|
|
34
|
+
"test": "bun test src/tests",
|
|
35
|
+
"format": "bunx @biomejs/biome format --write src/",
|
|
36
|
+
"lint": "bunx @biomejs/biome lint src/",
|
|
37
|
+
"check": "bunx @biomejs/biome check src/"
|
|
32
38
|
},
|
|
33
39
|
"repository": {
|
|
34
40
|
"type": "git",
|
|
35
41
|
"url": "git+https://github.com/Michaelliv/runline.git"
|
|
36
42
|
},
|
|
37
43
|
"homepage": "https://github.com/Michaelliv/runline#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/Michaelliv/runline/issues"
|
|
46
|
+
},
|
|
38
47
|
"keywords": [
|
|
48
|
+
"pi-package",
|
|
39
49
|
"agent",
|
|
40
50
|
"tools",
|
|
41
51
|
"mcp",
|
|
@@ -45,8 +55,8 @@
|
|
|
45
55
|
"license": "MIT",
|
|
46
56
|
"devDependencies": {
|
|
47
57
|
"@biomejs/biome": "^2.3.14",
|
|
58
|
+
"@types/bun": "^1.2.17",
|
|
48
59
|
"@types/node": "^25.5.0",
|
|
49
|
-
"tsx": "^4.21.0",
|
|
50
60
|
"typescript": "^5.8.0"
|
|
51
61
|
},
|
|
52
62
|
"dependencies": {
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import { Markdown, Text } from "@mariozechner/pi-tui";
|
|
3
|
-
import { Runline } from "../../../src/sdk.js";
|
|
4
|
-
|
|
5
|
-
function formatActions(
|
|
6
|
-
actions: Array<{
|
|
7
|
-
plugin: string;
|
|
8
|
-
action: string;
|
|
9
|
-
description?: string;
|
|
10
|
-
inputSchema?: Record<string, { type: string; required?: boolean; description?: string }>;
|
|
11
|
-
}>,
|
|
12
|
-
): string {
|
|
13
|
-
const grouped = new Map<string, typeof actions>();
|
|
14
|
-
for (const a of actions) {
|
|
15
|
-
const list = grouped.get(a.plugin) ?? [];
|
|
16
|
-
list.push(a);
|
|
17
|
-
grouped.set(a.plugin, list);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const lines: string[] = [];
|
|
21
|
-
for (const [plugin, entries] of grouped) {
|
|
22
|
-
lines.push(`### ${plugin}`);
|
|
23
|
-
for (const a of entries) {
|
|
24
|
-
const inputs = a.inputSchema
|
|
25
|
-
? Object.entries(a.inputSchema)
|
|
26
|
-
.map(
|
|
27
|
-
([k, v]) =>
|
|
28
|
-
`${k}: ${v.type}${v.required ? "" : "?"}`,
|
|
29
|
-
)
|
|
30
|
-
.join(", ")
|
|
31
|
-
: "";
|
|
32
|
-
const sig = inputs
|
|
33
|
-
? `\`${plugin}.${a.action}({ ${inputs} })\``
|
|
34
|
-
: `\`${plugin}.${a.action}()\``;
|
|
35
|
-
const desc = a.description ? ` — ${a.description}` : "";
|
|
36
|
-
lines.push(`- ${sig}${desc}`);
|
|
37
|
-
}
|
|
38
|
-
lines.push("");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return lines.join("\n");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export default function (pi: ExtensionAPI) {
|
|
45
|
-
pi.registerMessageRenderer(
|
|
46
|
-
"runline-context",
|
|
47
|
-
(message, { expanded }, theme) => {
|
|
48
|
-
if (!expanded) {
|
|
49
|
-
const label = theme.fg("customMessageLabel", "⚡ runline actions");
|
|
50
|
-
const hint = theme.fg("dim", " — Ctrl+O to expand");
|
|
51
|
-
return new Text(label + hint, 1, 0);
|
|
52
|
-
}
|
|
53
|
-
return new Markdown(
|
|
54
|
-
message.content,
|
|
55
|
-
1,
|
|
56
|
-
0,
|
|
57
|
-
{
|
|
58
|
-
heading: (t) => theme.fg("mdHeading", t),
|
|
59
|
-
link: (t) => theme.fg("mdLink", t),
|
|
60
|
-
linkUrl: (t) => theme.fg("mdLinkUrl", t),
|
|
61
|
-
code: (t) => theme.fg("mdCode", t),
|
|
62
|
-
codeBlock: (t) => theme.fg("mdCodeBlock", t),
|
|
63
|
-
codeBlockBorder: (t) => theme.fg("mdCodeBlockBorder", t),
|
|
64
|
-
quote: (t) => theme.fg("mdQuote", t),
|
|
65
|
-
quoteBorder: (t) => theme.fg("mdQuoteBorder", t),
|
|
66
|
-
hr: (t) => theme.fg("mdHr", t),
|
|
67
|
-
listBullet: (t) => theme.fg("mdListBullet", t),
|
|
68
|
-
bold: (t) => theme.bold(t),
|
|
69
|
-
italic: (t) => theme.italic(t),
|
|
70
|
-
strikethrough: (t) => theme.strikethrough(t),
|
|
71
|
-
underline: (t) => theme.underline(t),
|
|
72
|
-
},
|
|
73
|
-
{ color: (t) => theme.fg("customMessageText", t) },
|
|
74
|
-
);
|
|
75
|
-
},
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
79
|
-
const rl = await Runline.fromProject(ctx.cwd);
|
|
80
|
-
|
|
81
|
-
if (!rl) {
|
|
82
|
-
if (ctx.hasUI) {
|
|
83
|
-
ctx.ui.setStatus("runline", ctx.ui.theme.fg("dim", "runline: no .runline/"));
|
|
84
|
-
}
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const actions = rl.actions();
|
|
89
|
-
const plugins = rl.plugins();
|
|
90
|
-
|
|
91
|
-
if (actions.length === 0) {
|
|
92
|
-
if (ctx.hasUI) {
|
|
93
|
-
ctx.ui.setStatus("runline", ctx.ui.theme.fg("dim", "runline: no plugins"));
|
|
94
|
-
}
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Check if already injected
|
|
99
|
-
const alreadyInjected = ctx.sessionManager
|
|
100
|
-
.getEntries()
|
|
101
|
-
.some(
|
|
102
|
-
(e: any) =>
|
|
103
|
-
e.type === "message" &&
|
|
104
|
-
e.message.role === "custom" &&
|
|
105
|
-
e.message.customType === "runline-context",
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
if (!alreadyInjected) {
|
|
109
|
-
const header =
|
|
110
|
-
"## Runline actions\n\n" +
|
|
111
|
-
"This project has runline installed. You can execute JavaScript in a sandbox " +
|
|
112
|
-
"where each installed plugin is a top-level global. Chain actions together, " +
|
|
113
|
-
"call `help()` or `pluginName.help()` inside the sandbox for discovery.\n\n" +
|
|
114
|
-
`**${plugins.length} plugins, ${actions.length} actions available.**\n\n` +
|
|
115
|
-
"Use `runline exec '<code>'` to run code. Examples:\n" +
|
|
116
|
-
"```js\n" +
|
|
117
|
-
"return await github.issue.create({ owner: \"acme\", repo: \"api\", title: \"Bug\" })\n" +
|
|
118
|
-
"```\n\n";
|
|
119
|
-
|
|
120
|
-
ctx.sessionManager.appendCustomMessageEntry(
|
|
121
|
-
"runline-context",
|
|
122
|
-
header + formatActions(actions),
|
|
123
|
-
true,
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (ctx.hasUI) {
|
|
128
|
-
const theme = ctx.ui.theme;
|
|
129
|
-
ctx.ui.setStatus(
|
|
130
|
-
"runline",
|
|
131
|
-
`⚡${theme.fg("dim", ` runline: ${plugins.length} plugins, ${actions.length} actions`)}`,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@miclivs/pi-runline-context",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"description": "Pi extension — injects runline actions into agent context",
|
|
5
|
-
"type": "commonjs",
|
|
6
|
-
"keywords": ["pi-package"],
|
|
7
|
-
"files": ["index.ts", "README.md"],
|
|
8
|
-
"pi": {
|
|
9
|
-
"extensions": ["."]
|
|
10
|
-
},
|
|
11
|
-
"peerDependencies": {
|
|
12
|
-
"@mariozechner/pi-coding-agent": "*",
|
|
13
|
-
"@mariozechner/pi-tui": "*"
|
|
14
|
-
},
|
|
15
|
-
"author": "michaelliv",
|
|
16
|
-
"license": "MIT"
|
|
17
|
-
}
|