runline 0.1.0
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/.pi/extensions/runline-context/index.ts +135 -0
- package/.pi/extensions/runline-context/package.json +17 -0
- package/README.md +273 -0
- package/dist/commands/actions.d.ts +3 -0
- package/dist/commands/actions.js +43 -0
- package/dist/commands/connection.d.ts +11 -0
- package/dist/commands/connection.js +56 -0
- package/dist/commands/exec.d.ts +5 -0
- package/dist/commands/exec.js +46 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +26 -0
- package/dist/commands/plugin.d.ts +10 -0
- package/dist/commands/plugin.js +57 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +2 -0
- package/dist/config/loader.d.ts +11 -0
- package/dist/config/loader.js +82 -0
- package/dist/config/types.d.ts +9 -0
- package/dist/config/types.js +5 -0
- package/dist/core/engine.d.ts +21 -0
- package/dist/core/engine.js +280 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +9 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +127 -0
- package/dist/plugin/api.d.ts +32 -0
- package/dist/plugin/api.js +68 -0
- package/dist/plugin/installer.d.ts +27 -0
- package/dist/plugin/installer.js +181 -0
- package/dist/plugin/loader.d.ts +13 -0
- package/dist/plugin/loader.js +164 -0
- package/dist/plugin/registry.d.ts +18 -0
- package/dist/plugin/registry.js +43 -0
- package/dist/plugin/types.d.ts +40 -0
- package/dist/plugin/types.js +1 -0
- package/dist/plugins/actionNetwork/src/index.js +353 -0
- package/dist/plugins/activeCampaign/src/index.js +711 -0
- package/dist/plugins/adalo/src/index.js +131 -0
- package/dist/plugins/affinity/src/index.js +279 -0
- package/dist/plugins/agileCrm/src/index.js +415 -0
- package/dist/plugins/airtable/src/index.js +280 -0
- package/dist/plugins/airtop/src/index.js +527 -0
- package/dist/plugins/apiTemplateIo/src/index.js +86 -0
- package/dist/plugins/asana/src/index.js +413 -0
- package/dist/plugins/autopilot/src/index.js +203 -0
- package/dist/plugins/bambooHr/src/index.js +252 -0
- package/dist/plugins/bannerbear/src/index.js +100 -0
- package/dist/plugins/baserow/src/index.js +180 -0
- package/dist/plugins/beeminder/src/index.js +298 -0
- package/dist/plugins/bitly/src/index.js +107 -0
- package/dist/plugins/bitwarden/src/index.js +383 -0
- package/dist/plugins/box/src/index.js +300 -0
- package/dist/plugins/brandfetch/src/index.js +80 -0
- package/dist/plugins/brevo/src/index.js +305 -0
- package/dist/plugins/bubble/src/index.js +181 -0
- package/dist/plugins/chargebee/src/index.js +126 -0
- package/dist/plugins/circleci/src/index.js +111 -0
- package/dist/plugins/ciscoWebex/src/index.js +245 -0
- package/dist/plugins/clearbit/src/index.js +103 -0
- package/dist/plugins/clickup/src/index.js +1043 -0
- package/dist/plugins/clockify/src/index.js +443 -0
- package/dist/plugins/cloudflare/src/index.js +93 -0
- package/dist/plugins/cockpit/src/index.js +131 -0
- package/dist/plugins/coda/src/index.js +327 -0
- package/dist/plugins/coingecko/src/index.js +244 -0
- package/dist/plugins/contentful/src/index.js +146 -0
- package/dist/plugins/convertkit/src/index.js +270 -0
- package/dist/plugins/copper/src/index.js +140 -0
- package/dist/plugins/cortex/src/index.js +147 -0
- package/dist/plugins/currents/src/index.js +405 -0
- package/dist/plugins/customerIo/src/index.js +184 -0
- package/dist/plugins/databricks/src/index.js +342 -0
- package/dist/plugins/deepl/src/index.js +87 -0
- package/dist/plugins/demio/src/index.js +111 -0
- package/dist/plugins/dhl/src/index.js +40 -0
- package/dist/plugins/discord/src/index.js +275 -0
- package/dist/plugins/discourse/src/index.js +273 -0
- package/dist/plugins/disqus/src/index.js +145 -0
- package/dist/plugins/docker/src/index.js +76 -0
- package/dist/plugins/drift/src/index.js +89 -0
- package/dist/plugins/dropbox/src/index.js +159 -0
- package/dist/plugins/dropcontact/src/index.js +59 -0
- package/dist/plugins/egoi/src/index.js +151 -0
- package/dist/plugins/elasticsearch/src/index.js +157 -0
- package/dist/plugins/emelia/src/index.js +174 -0
- package/dist/plugins/erpnext/src/index.js +121 -0
- package/dist/plugins/facebookGraph/src/index.js +57 -0
- package/dist/plugins/freshdesk/src/index.js +320 -0
- package/dist/plugins/freshservice/src/index.js +146 -0
- package/dist/plugins/freshworksCrm/src/index.js +149 -0
- package/dist/plugins/getresponse/src/index.js +140 -0
- package/dist/plugins/ghost/src/index.js +192 -0
- package/dist/plugins/github/src/index.js +630 -0
- package/dist/plugins/gitlab/src/index.js +358 -0
- package/dist/plugins/gong/src/index.js +126 -0
- package/dist/plugins/gotify/src/index.js +77 -0
- package/dist/plugins/gotowebinar/src/index.js +316 -0
- package/dist/plugins/grafana/src/index.js +250 -0
- package/dist/plugins/graphql/src/index.js +78 -0
- package/dist/plugins/grist/src/index.js +106 -0
- package/dist/plugins/hackernews/src/index.js +89 -0
- package/dist/plugins/halopsa/src/index.js +79 -0
- package/dist/plugins/harvest/src/index.js +163 -0
- package/dist/plugins/helpscout/src/index.js +176 -0
- package/dist/plugins/highlevel/src/index.js +172 -0
- package/dist/plugins/homeAssistant/src/index.js +148 -0
- package/dist/plugins/hubspot/src/index.js +176 -0
- package/dist/plugins/humanticAi/src/index.js +60 -0
- package/dist/plugins/hunter/src/index.js +59 -0
- package/dist/plugins/intercom/src/index.js +156 -0
- package/dist/plugins/iterable/src/index.js +139 -0
- package/dist/plugins/jenkins/src/index.js +132 -0
- package/dist/plugins/jira/src/index.js +229 -0
- package/dist/plugins/keap/src/index.js +502 -0
- package/dist/plugins/kobotoolbox/src/index.js +281 -0
- package/dist/plugins/lemlist/src/index.js +231 -0
- package/dist/plugins/linear/src/index.js +133 -0
- package/dist/plugins/lingvanex/src/index.js +31 -0
- package/dist/plugins/linkedin/src/index.js +80 -0
- package/dist/plugins/lonescale/src/index.js +119 -0
- package/dist/plugins/magento/src/index.js +300 -0
- package/dist/plugins/mailcheck/src/index.js +27 -0
- package/dist/plugins/mailchimp/src/index.js +321 -0
- package/dist/plugins/mailerlite/src/index.js +123 -0
- package/dist/plugins/mailgun/src/index.js +48 -0
- package/dist/plugins/mailjet/src/index.js +155 -0
- package/dist/plugins/mandrill/src/index.js +145 -0
- package/dist/plugins/marketstack/src/index.js +97 -0
- package/dist/plugins/matrix/src/index.js +194 -0
- package/dist/plugins/mattermost/src/index.js +331 -0
- package/dist/plugins/mautic/src/index.js +311 -0
- package/dist/plugins/medium/src/index.js +77 -0
- package/dist/plugins/messagebird/src/index.js +57 -0
- package/dist/plugins/metabase/src/index.js +130 -0
- package/dist/plugins/misp/src/index.js +476 -0
- package/dist/plugins/mocean/src/index.js +67 -0
- package/dist/plugins/monday/src/index.js +231 -0
- package/dist/plugins/monicaCrm/src/index.js +52 -0
- package/dist/plugins/msg91/src/index.js +31 -0
- package/dist/plugins/nasa/src/index.js +146 -0
- package/dist/plugins/netlify/src/index.js +151 -0
- package/dist/plugins/netscalerAdc/src/index.js +131 -0
- package/dist/plugins/nextcloud/src/index.js +263 -0
- package/dist/plugins/nocodb/src/index.js +130 -0
- package/dist/plugins/notion/src/index.js +112 -0
- package/dist/plugins/npm/src/index.js +104 -0
- package/dist/plugins/odoo/src/index.js +157 -0
- package/dist/plugins/okta/src/index.js +141 -0
- package/dist/plugins/oneSimpleApi/src/index.js +155 -0
- package/dist/plugins/onfleet/src/index.js +254 -0
- package/dist/plugins/openThesaurus/src/index.js +32 -0
- package/dist/plugins/openweathermap/src/index.js +60 -0
- package/dist/plugins/oura/src/index.js +62 -0
- package/dist/plugins/paddle/src/index.js +247 -0
- package/dist/plugins/pagerduty/src/index.js +201 -0
- package/dist/plugins/paypal/src/index.js +106 -0
- package/dist/plugins/peekalink/src/index.js +35 -0
- package/dist/plugins/phantombuster/src/index.js +94 -0
- package/dist/plugins/philipsHue/src/index.js +98 -0
- package/dist/plugins/pipedrive/src/index.js +169 -0
- package/dist/plugins/plivo/src/index.js +66 -0
- package/dist/plugins/postbin/src/index.js +93 -0
- package/dist/plugins/posthog/src/index.js +113 -0
- package/dist/plugins/profitwell/src/index.js +50 -0
- package/dist/plugins/pushbullet/src/index.js +102 -0
- package/dist/plugins/pushcut/src/index.js +39 -0
- package/dist/plugins/pushover/src/index.js +65 -0
- package/dist/plugins/quickbase/src/index.js +153 -0
- package/dist/plugins/quickbooks/src/index.js +73 -0
- package/dist/plugins/quickchart/src/index.js +36 -0
- package/dist/plugins/raindrop/src/index.js +209 -0
- package/dist/plugins/reddit/src/index.js +185 -0
- package/dist/plugins/rocketchat/src/index.js +53 -0
- package/dist/plugins/rundeck/src/index.js +62 -0
- package/dist/plugins/salesforce/src/index.js +94 -0
- package/dist/plugins/salesmate/src/index.js +83 -0
- package/dist/plugins/securityScorecard/src/index.js +200 -0
- package/dist/plugins/segment/src/index.js +125 -0
- package/dist/plugins/sendgrid/src/index.js +187 -0
- package/dist/plugins/sendy/src/index.js +138 -0
- package/dist/plugins/sentry/src/index.js +233 -0
- package/dist/plugins/servicenow/src/index.js +108 -0
- package/dist/plugins/shopify/src/index.js +222 -0
- package/dist/plugins/signl4/src/index.js +61 -0
- package/dist/plugins/slack/src/index.js +236 -0
- package/dist/plugins/sms77/src/index.js +63 -0
- package/dist/plugins/splunk/src/index.js +207 -0
- package/dist/plugins/spotify/src/index.js +188 -0
- package/dist/plugins/stackby/src/index.js +82 -0
- package/dist/plugins/storyblok/src/index.js +141 -0
- package/dist/plugins/strapi/src/index.js +152 -0
- package/dist/plugins/strava/src/index.js +137 -0
- package/dist/plugins/stripe/src/index.js +222 -0
- package/dist/plugins/supabase/src/index.js +121 -0
- package/dist/plugins/syncromsp/src/index.js +255 -0
- package/dist/plugins/tapfiliate/src/index.js +125 -0
- package/dist/plugins/telegram/src/index.js +233 -0
- package/dist/plugins/thehive/src/index.js +142 -0
- package/dist/plugins/thehiveProject/src/index.js +194 -0
- package/dist/plugins/todoist/src/index.js +244 -0
- package/dist/plugins/travisci/src/index.js +71 -0
- package/dist/plugins/trello/src/index.js +341 -0
- package/dist/plugins/twake/src/index.js +40 -0
- package/dist/plugins/twilio/src/index.js +75 -0
- package/dist/plugins/twist/src/index.js +90 -0
- package/dist/plugins/twitter/src/index.js +123 -0
- package/dist/plugins/unleashedSoftware/src/index.js +84 -0
- package/dist/plugins/uplead/src/index.js +59 -0
- package/dist/plugins/uproc/src/index.js +34 -0
- package/dist/plugins/uptimerobot/src/index.js +264 -0
- package/dist/plugins/urlscanio/src/index.js +64 -0
- package/dist/plugins/vero/src/index.js +80 -0
- package/dist/plugins/vonage/src/index.js +42 -0
- package/dist/plugins/wekan/src/index.js +91 -0
- package/dist/plugins/woocommerce/src/index.js +92 -0
- package/dist/plugins/wordpress/src/index.js +121 -0
- package/dist/plugins/xero/src/index.js +136 -0
- package/dist/plugins/yourls/src/index.js +56 -0
- package/dist/plugins/zammad/src/index.js +91 -0
- package/dist/plugins/zendesk/src/index.js +137 -0
- package/dist/plugins/zoho/src/index.js +85 -0
- package/dist/plugins/zoom/src/index.js +122 -0
- package/dist/plugins/zulip/src/index.js +170 -0
- package/dist/sdk.d.ts +38 -0
- package/dist/sdk.js +105 -0
- package/dist/utils/cli.d.ts +13 -0
- package/dist/utils/cli.js +32 -0
- package/dist/utils/output.d.ts +4 -0
- package/dist/utils/output.js +13 -0
- package/package.json +57 -0
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# runline ⚡
|
|
2
|
+
|
|
3
|
+
Code mode for agents.
|
|
4
|
+
|
|
5
|
+
Turn any API into a callable action. Install a plugin, write JavaScript, call actions. The code runs in a QuickJS WASM sandbox — no filesystem, no network, just plugin actions via a proxy.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g runline
|
|
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
|
+
});
|
|
138
|
+
|
|
139
|
+
rl.registerAction("list", {
|
|
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
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
npm install
|
|
262
|
+
npm run dev -- exec 'return 1 + 2'
|
|
263
|
+
npm test
|
|
264
|
+
npm run check
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## How It Relates to dripline
|
|
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
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { loadAllPlugins } from "../plugin/loader.js";
|
|
3
|
+
import { registry } from "../plugin/registry.js";
|
|
4
|
+
import { printJson } from "../utils/output.js";
|
|
5
|
+
export async function actions(options) {
|
|
6
|
+
await loadAllPlugins();
|
|
7
|
+
const all = registry.getAllActions();
|
|
8
|
+
if (options.json) {
|
|
9
|
+
printJson(all.map(({ plugin, action }) => ({
|
|
10
|
+
plugin,
|
|
11
|
+
action: action.name,
|
|
12
|
+
description: action.description,
|
|
13
|
+
inputSchema: action.inputSchema,
|
|
14
|
+
})));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (all.length === 0) {
|
|
18
|
+
console.log("No actions registered. Install a plugin first.");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const grouped = new Map();
|
|
22
|
+
for (const entry of all) {
|
|
23
|
+
const list = grouped.get(entry.plugin) ?? [];
|
|
24
|
+
list.push(entry);
|
|
25
|
+
grouped.set(entry.plugin, list);
|
|
26
|
+
}
|
|
27
|
+
for (const [plugin, entries] of grouped) {
|
|
28
|
+
console.log(chalk.bold(`\n${plugin}`));
|
|
29
|
+
for (const { action } of entries) {
|
|
30
|
+
const path = chalk.cyan(`${plugin}.${action.name}`);
|
|
31
|
+
const desc = action.description
|
|
32
|
+
? chalk.dim(` — ${action.description}`)
|
|
33
|
+
: "";
|
|
34
|
+
const schema = action.inputSchema
|
|
35
|
+
? chalk.dim(` (${Object.entries(action.inputSchema)
|
|
36
|
+
.map(([k, v]) => `${k}: ${v.type}${v.required ? "" : "?"}`)
|
|
37
|
+
.join(", ")})`)
|
|
38
|
+
: "";
|
|
39
|
+
console.log(` ${path}${schema}${desc}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
console.log();
|
|
43
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function connectionAdd(name: string, options: {
|
|
2
|
+
plugin: string;
|
|
3
|
+
set: string[];
|
|
4
|
+
json?: boolean;
|
|
5
|
+
}): Promise<void>;
|
|
6
|
+
export declare function connectionRemove(name: string, options: {
|
|
7
|
+
json?: boolean;
|
|
8
|
+
}): Promise<void>;
|
|
9
|
+
export declare function connectionList(options: {
|
|
10
|
+
json?: boolean;
|
|
11
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { addConnection, loadConfig, removeConnection, } from "../config/loader.js";
|
|
3
|
+
import { printError, printJson, printSuccess } from "../utils/output.js";
|
|
4
|
+
export async function connectionAdd(name, options) {
|
|
5
|
+
const configValues = {};
|
|
6
|
+
for (const kv of options.set) {
|
|
7
|
+
const eq = kv.indexOf("=");
|
|
8
|
+
if (eq < 0) {
|
|
9
|
+
printError(`Invalid --set value: ${kv} (expected key=value)`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
configValues[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
13
|
+
}
|
|
14
|
+
addConnection(name, options.plugin, configValues);
|
|
15
|
+
if (options.json) {
|
|
16
|
+
printJson({ ok: true, name, plugin: options.plugin });
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
printSuccess(`Connection ${chalk.bold(name)} added (plugin: ${options.plugin})`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export async function connectionRemove(name, options) {
|
|
23
|
+
const removed = removeConnection(name);
|
|
24
|
+
if (!removed) {
|
|
25
|
+
printError(`Connection "${name}" not found`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
if (options.json) {
|
|
29
|
+
printJson({ ok: true, removed: name });
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
printSuccess(`Connection ${chalk.bold(name)} removed`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export async function connectionList(options) {
|
|
36
|
+
const config = loadConfig();
|
|
37
|
+
const connections = config.connections;
|
|
38
|
+
if (options.json) {
|
|
39
|
+
printJson(connections.map((c) => ({
|
|
40
|
+
name: c.name,
|
|
41
|
+
plugin: c.plugin,
|
|
42
|
+
config: Object.fromEntries(Object.entries(c.config).map(([k, v]) => [
|
|
43
|
+
k,
|
|
44
|
+
typeof v === "string" && v.length > 8 ? `${v.slice(0, 4)}...` : v,
|
|
45
|
+
])),
|
|
46
|
+
})));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (connections.length === 0) {
|
|
50
|
+
console.log("No connections configured.");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
for (const c of connections) {
|
|
54
|
+
console.log(` ${chalk.bold(c.name)} (${c.plugin})`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { loadConfig } from "../config/loader.js";
|
|
3
|
+
import { ExecutionEngine } from "../core/engine.js";
|
|
4
|
+
import { loadAllPlugins } from "../plugin/loader.js";
|
|
5
|
+
import { registry } from "../plugin/registry.js";
|
|
6
|
+
import { printError, printJson } from "../utils/output.js";
|
|
7
|
+
export async function exec(code, options) {
|
|
8
|
+
await loadAllPlugins();
|
|
9
|
+
const config = loadConfig();
|
|
10
|
+
const engine = new ExecutionEngine(registry, config);
|
|
11
|
+
if (options.file) {
|
|
12
|
+
if (!existsSync(code)) {
|
|
13
|
+
printError(`File not found: ${code}`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
code = readFileSync(code, "utf-8");
|
|
17
|
+
}
|
|
18
|
+
const result = await engine.execute(code);
|
|
19
|
+
if (result.error) {
|
|
20
|
+
if (options.json) {
|
|
21
|
+
printJson({ error: result.error, logs: result.logs });
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
if (result.logs.length > 0 && !options.quiet) {
|
|
25
|
+
for (const log of result.logs)
|
|
26
|
+
console.error(log);
|
|
27
|
+
}
|
|
28
|
+
printError(result.error);
|
|
29
|
+
}
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
if (options.json) {
|
|
33
|
+
printJson({ result: result.result, logs: result.logs });
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
if (result.logs.length > 0 && !options.quiet) {
|
|
37
|
+
for (const log of result.logs)
|
|
38
|
+
console.error(log);
|
|
39
|
+
}
|
|
40
|
+
if (result.result !== null && result.result !== undefined) {
|
|
41
|
+
console.log(typeof result.result === "string"
|
|
42
|
+
? result.result
|
|
43
|
+
: JSON.stringify(result.result, null, 2));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { DEFAULT_CONFIG } from "../config/types.js";
|
|
5
|
+
import { printJson, printSuccess, printWarn } from "../utils/output.js";
|
|
6
|
+
export async function init(options) {
|
|
7
|
+
const dir = join(process.cwd(), ".runline");
|
|
8
|
+
if (existsSync(dir)) {
|
|
9
|
+
if (options.json) {
|
|
10
|
+
printJson({ ok: true, exists: true, path: dir });
|
|
11
|
+
}
|
|
12
|
+
else if (!options.quiet) {
|
|
13
|
+
printWarn(`${chalk.bold(".runline/")} already exists`);
|
|
14
|
+
}
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
mkdirSync(join(dir, "plugins"), { recursive: true });
|
|
19
|
+
writeFileSync(join(dir, "config.json"), `${JSON.stringify(DEFAULT_CONFIG, null, 2)}\n`);
|
|
20
|
+
if (options.json) {
|
|
21
|
+
printJson({ ok: true, path: dir });
|
|
22
|
+
}
|
|
23
|
+
else if (!options.quiet) {
|
|
24
|
+
printSuccess(`Created ${chalk.bold(".runline/")} in current directory`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function pluginInstall(source: string, options: {
|
|
2
|
+
global?: boolean;
|
|
3
|
+
json?: boolean;
|
|
4
|
+
}): Promise<void>;
|
|
5
|
+
export declare function pluginRemove(name: string, options: {
|
|
6
|
+
json?: boolean;
|
|
7
|
+
}): Promise<void>;
|
|
8
|
+
export declare function pluginList(options: {
|
|
9
|
+
json?: boolean;
|
|
10
|
+
}): Promise<void>;
|