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.
Files changed (230) hide show
  1. package/.pi/extensions/runline-context/index.ts +135 -0
  2. package/.pi/extensions/runline-context/package.json +17 -0
  3. package/README.md +273 -0
  4. package/dist/commands/actions.d.ts +3 -0
  5. package/dist/commands/actions.js +43 -0
  6. package/dist/commands/connection.d.ts +11 -0
  7. package/dist/commands/connection.js +56 -0
  8. package/dist/commands/exec.d.ts +5 -0
  9. package/dist/commands/exec.js +46 -0
  10. package/dist/commands/init.d.ts +4 -0
  11. package/dist/commands/init.js +26 -0
  12. package/dist/commands/plugin.d.ts +10 -0
  13. package/dist/commands/plugin.js +57 -0
  14. package/dist/config/index.d.ts +3 -0
  15. package/dist/config/index.js +2 -0
  16. package/dist/config/loader.d.ts +11 -0
  17. package/dist/config/loader.js +82 -0
  18. package/dist/config/types.d.ts +9 -0
  19. package/dist/config/types.js +5 -0
  20. package/dist/core/engine.d.ts +21 -0
  21. package/dist/core/engine.js +280 -0
  22. package/dist/index.d.ts +16 -0
  23. package/dist/index.js +9 -0
  24. package/dist/main.d.ts +2 -0
  25. package/dist/main.js +127 -0
  26. package/dist/plugin/api.d.ts +32 -0
  27. package/dist/plugin/api.js +68 -0
  28. package/dist/plugin/installer.d.ts +27 -0
  29. package/dist/plugin/installer.js +181 -0
  30. package/dist/plugin/loader.d.ts +13 -0
  31. package/dist/plugin/loader.js +164 -0
  32. package/dist/plugin/registry.d.ts +18 -0
  33. package/dist/plugin/registry.js +43 -0
  34. package/dist/plugin/types.d.ts +40 -0
  35. package/dist/plugin/types.js +1 -0
  36. package/dist/plugins/actionNetwork/src/index.js +353 -0
  37. package/dist/plugins/activeCampaign/src/index.js +711 -0
  38. package/dist/plugins/adalo/src/index.js +131 -0
  39. package/dist/plugins/affinity/src/index.js +279 -0
  40. package/dist/plugins/agileCrm/src/index.js +415 -0
  41. package/dist/plugins/airtable/src/index.js +280 -0
  42. package/dist/plugins/airtop/src/index.js +527 -0
  43. package/dist/plugins/apiTemplateIo/src/index.js +86 -0
  44. package/dist/plugins/asana/src/index.js +413 -0
  45. package/dist/plugins/autopilot/src/index.js +203 -0
  46. package/dist/plugins/bambooHr/src/index.js +252 -0
  47. package/dist/plugins/bannerbear/src/index.js +100 -0
  48. package/dist/plugins/baserow/src/index.js +180 -0
  49. package/dist/plugins/beeminder/src/index.js +298 -0
  50. package/dist/plugins/bitly/src/index.js +107 -0
  51. package/dist/plugins/bitwarden/src/index.js +383 -0
  52. package/dist/plugins/box/src/index.js +300 -0
  53. package/dist/plugins/brandfetch/src/index.js +80 -0
  54. package/dist/plugins/brevo/src/index.js +305 -0
  55. package/dist/plugins/bubble/src/index.js +181 -0
  56. package/dist/plugins/chargebee/src/index.js +126 -0
  57. package/dist/plugins/circleci/src/index.js +111 -0
  58. package/dist/plugins/ciscoWebex/src/index.js +245 -0
  59. package/dist/plugins/clearbit/src/index.js +103 -0
  60. package/dist/plugins/clickup/src/index.js +1043 -0
  61. package/dist/plugins/clockify/src/index.js +443 -0
  62. package/dist/plugins/cloudflare/src/index.js +93 -0
  63. package/dist/plugins/cockpit/src/index.js +131 -0
  64. package/dist/plugins/coda/src/index.js +327 -0
  65. package/dist/plugins/coingecko/src/index.js +244 -0
  66. package/dist/plugins/contentful/src/index.js +146 -0
  67. package/dist/plugins/convertkit/src/index.js +270 -0
  68. package/dist/plugins/copper/src/index.js +140 -0
  69. package/dist/plugins/cortex/src/index.js +147 -0
  70. package/dist/plugins/currents/src/index.js +405 -0
  71. package/dist/plugins/customerIo/src/index.js +184 -0
  72. package/dist/plugins/databricks/src/index.js +342 -0
  73. package/dist/plugins/deepl/src/index.js +87 -0
  74. package/dist/plugins/demio/src/index.js +111 -0
  75. package/dist/plugins/dhl/src/index.js +40 -0
  76. package/dist/plugins/discord/src/index.js +275 -0
  77. package/dist/plugins/discourse/src/index.js +273 -0
  78. package/dist/plugins/disqus/src/index.js +145 -0
  79. package/dist/plugins/docker/src/index.js +76 -0
  80. package/dist/plugins/drift/src/index.js +89 -0
  81. package/dist/plugins/dropbox/src/index.js +159 -0
  82. package/dist/plugins/dropcontact/src/index.js +59 -0
  83. package/dist/plugins/egoi/src/index.js +151 -0
  84. package/dist/plugins/elasticsearch/src/index.js +157 -0
  85. package/dist/plugins/emelia/src/index.js +174 -0
  86. package/dist/plugins/erpnext/src/index.js +121 -0
  87. package/dist/plugins/facebookGraph/src/index.js +57 -0
  88. package/dist/plugins/freshdesk/src/index.js +320 -0
  89. package/dist/plugins/freshservice/src/index.js +146 -0
  90. package/dist/plugins/freshworksCrm/src/index.js +149 -0
  91. package/dist/plugins/getresponse/src/index.js +140 -0
  92. package/dist/plugins/ghost/src/index.js +192 -0
  93. package/dist/plugins/github/src/index.js +630 -0
  94. package/dist/plugins/gitlab/src/index.js +358 -0
  95. package/dist/plugins/gong/src/index.js +126 -0
  96. package/dist/plugins/gotify/src/index.js +77 -0
  97. package/dist/plugins/gotowebinar/src/index.js +316 -0
  98. package/dist/plugins/grafana/src/index.js +250 -0
  99. package/dist/plugins/graphql/src/index.js +78 -0
  100. package/dist/plugins/grist/src/index.js +106 -0
  101. package/dist/plugins/hackernews/src/index.js +89 -0
  102. package/dist/plugins/halopsa/src/index.js +79 -0
  103. package/dist/plugins/harvest/src/index.js +163 -0
  104. package/dist/plugins/helpscout/src/index.js +176 -0
  105. package/dist/plugins/highlevel/src/index.js +172 -0
  106. package/dist/plugins/homeAssistant/src/index.js +148 -0
  107. package/dist/plugins/hubspot/src/index.js +176 -0
  108. package/dist/plugins/humanticAi/src/index.js +60 -0
  109. package/dist/plugins/hunter/src/index.js +59 -0
  110. package/dist/plugins/intercom/src/index.js +156 -0
  111. package/dist/plugins/iterable/src/index.js +139 -0
  112. package/dist/plugins/jenkins/src/index.js +132 -0
  113. package/dist/plugins/jira/src/index.js +229 -0
  114. package/dist/plugins/keap/src/index.js +502 -0
  115. package/dist/plugins/kobotoolbox/src/index.js +281 -0
  116. package/dist/plugins/lemlist/src/index.js +231 -0
  117. package/dist/plugins/linear/src/index.js +133 -0
  118. package/dist/plugins/lingvanex/src/index.js +31 -0
  119. package/dist/plugins/linkedin/src/index.js +80 -0
  120. package/dist/plugins/lonescale/src/index.js +119 -0
  121. package/dist/plugins/magento/src/index.js +300 -0
  122. package/dist/plugins/mailcheck/src/index.js +27 -0
  123. package/dist/plugins/mailchimp/src/index.js +321 -0
  124. package/dist/plugins/mailerlite/src/index.js +123 -0
  125. package/dist/plugins/mailgun/src/index.js +48 -0
  126. package/dist/plugins/mailjet/src/index.js +155 -0
  127. package/dist/plugins/mandrill/src/index.js +145 -0
  128. package/dist/plugins/marketstack/src/index.js +97 -0
  129. package/dist/plugins/matrix/src/index.js +194 -0
  130. package/dist/plugins/mattermost/src/index.js +331 -0
  131. package/dist/plugins/mautic/src/index.js +311 -0
  132. package/dist/plugins/medium/src/index.js +77 -0
  133. package/dist/plugins/messagebird/src/index.js +57 -0
  134. package/dist/plugins/metabase/src/index.js +130 -0
  135. package/dist/plugins/misp/src/index.js +476 -0
  136. package/dist/plugins/mocean/src/index.js +67 -0
  137. package/dist/plugins/monday/src/index.js +231 -0
  138. package/dist/plugins/monicaCrm/src/index.js +52 -0
  139. package/dist/plugins/msg91/src/index.js +31 -0
  140. package/dist/plugins/nasa/src/index.js +146 -0
  141. package/dist/plugins/netlify/src/index.js +151 -0
  142. package/dist/plugins/netscalerAdc/src/index.js +131 -0
  143. package/dist/plugins/nextcloud/src/index.js +263 -0
  144. package/dist/plugins/nocodb/src/index.js +130 -0
  145. package/dist/plugins/notion/src/index.js +112 -0
  146. package/dist/plugins/npm/src/index.js +104 -0
  147. package/dist/plugins/odoo/src/index.js +157 -0
  148. package/dist/plugins/okta/src/index.js +141 -0
  149. package/dist/plugins/oneSimpleApi/src/index.js +155 -0
  150. package/dist/plugins/onfleet/src/index.js +254 -0
  151. package/dist/plugins/openThesaurus/src/index.js +32 -0
  152. package/dist/plugins/openweathermap/src/index.js +60 -0
  153. package/dist/plugins/oura/src/index.js +62 -0
  154. package/dist/plugins/paddle/src/index.js +247 -0
  155. package/dist/plugins/pagerduty/src/index.js +201 -0
  156. package/dist/plugins/paypal/src/index.js +106 -0
  157. package/dist/plugins/peekalink/src/index.js +35 -0
  158. package/dist/plugins/phantombuster/src/index.js +94 -0
  159. package/dist/plugins/philipsHue/src/index.js +98 -0
  160. package/dist/plugins/pipedrive/src/index.js +169 -0
  161. package/dist/plugins/plivo/src/index.js +66 -0
  162. package/dist/plugins/postbin/src/index.js +93 -0
  163. package/dist/plugins/posthog/src/index.js +113 -0
  164. package/dist/plugins/profitwell/src/index.js +50 -0
  165. package/dist/plugins/pushbullet/src/index.js +102 -0
  166. package/dist/plugins/pushcut/src/index.js +39 -0
  167. package/dist/plugins/pushover/src/index.js +65 -0
  168. package/dist/plugins/quickbase/src/index.js +153 -0
  169. package/dist/plugins/quickbooks/src/index.js +73 -0
  170. package/dist/plugins/quickchart/src/index.js +36 -0
  171. package/dist/plugins/raindrop/src/index.js +209 -0
  172. package/dist/plugins/reddit/src/index.js +185 -0
  173. package/dist/plugins/rocketchat/src/index.js +53 -0
  174. package/dist/plugins/rundeck/src/index.js +62 -0
  175. package/dist/plugins/salesforce/src/index.js +94 -0
  176. package/dist/plugins/salesmate/src/index.js +83 -0
  177. package/dist/plugins/securityScorecard/src/index.js +200 -0
  178. package/dist/plugins/segment/src/index.js +125 -0
  179. package/dist/plugins/sendgrid/src/index.js +187 -0
  180. package/dist/plugins/sendy/src/index.js +138 -0
  181. package/dist/plugins/sentry/src/index.js +233 -0
  182. package/dist/plugins/servicenow/src/index.js +108 -0
  183. package/dist/plugins/shopify/src/index.js +222 -0
  184. package/dist/plugins/signl4/src/index.js +61 -0
  185. package/dist/plugins/slack/src/index.js +236 -0
  186. package/dist/plugins/sms77/src/index.js +63 -0
  187. package/dist/plugins/splunk/src/index.js +207 -0
  188. package/dist/plugins/spotify/src/index.js +188 -0
  189. package/dist/plugins/stackby/src/index.js +82 -0
  190. package/dist/plugins/storyblok/src/index.js +141 -0
  191. package/dist/plugins/strapi/src/index.js +152 -0
  192. package/dist/plugins/strava/src/index.js +137 -0
  193. package/dist/plugins/stripe/src/index.js +222 -0
  194. package/dist/plugins/supabase/src/index.js +121 -0
  195. package/dist/plugins/syncromsp/src/index.js +255 -0
  196. package/dist/plugins/tapfiliate/src/index.js +125 -0
  197. package/dist/plugins/telegram/src/index.js +233 -0
  198. package/dist/plugins/thehive/src/index.js +142 -0
  199. package/dist/plugins/thehiveProject/src/index.js +194 -0
  200. package/dist/plugins/todoist/src/index.js +244 -0
  201. package/dist/plugins/travisci/src/index.js +71 -0
  202. package/dist/plugins/trello/src/index.js +341 -0
  203. package/dist/plugins/twake/src/index.js +40 -0
  204. package/dist/plugins/twilio/src/index.js +75 -0
  205. package/dist/plugins/twist/src/index.js +90 -0
  206. package/dist/plugins/twitter/src/index.js +123 -0
  207. package/dist/plugins/unleashedSoftware/src/index.js +84 -0
  208. package/dist/plugins/uplead/src/index.js +59 -0
  209. package/dist/plugins/uproc/src/index.js +34 -0
  210. package/dist/plugins/uptimerobot/src/index.js +264 -0
  211. package/dist/plugins/urlscanio/src/index.js +64 -0
  212. package/dist/plugins/vero/src/index.js +80 -0
  213. package/dist/plugins/vonage/src/index.js +42 -0
  214. package/dist/plugins/wekan/src/index.js +91 -0
  215. package/dist/plugins/woocommerce/src/index.js +92 -0
  216. package/dist/plugins/wordpress/src/index.js +121 -0
  217. package/dist/plugins/xero/src/index.js +136 -0
  218. package/dist/plugins/yourls/src/index.js +56 -0
  219. package/dist/plugins/zammad/src/index.js +91 -0
  220. package/dist/plugins/zendesk/src/index.js +137 -0
  221. package/dist/plugins/zoho/src/index.js +85 -0
  222. package/dist/plugins/zoom/src/index.js +122 -0
  223. package/dist/plugins/zulip/src/index.js +170 -0
  224. package/dist/sdk.d.ts +38 -0
  225. package/dist/sdk.js +105 -0
  226. package/dist/utils/cli.d.ts +13 -0
  227. package/dist/utils/cli.js +32 -0
  228. package/dist/utils/output.d.ts +4 -0
  229. package/dist/utils/output.js +13 -0
  230. package/package.json +57 -0
@@ -0,0 +1,68 @@
1
+ export function createPluginAPI(pluginId) {
2
+ let name = pluginId;
3
+ let version = "0.0.0";
4
+ const actions = [];
5
+ let connectionConfigSchema;
6
+ const initHooks = [];
7
+ const api = {
8
+ setName(n) {
9
+ if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(n)) {
10
+ throw new Error(`Invalid plugin name "${n}": must be a valid JS identifier (no hyphens, no dots). Use camelCase.`);
11
+ }
12
+ name = n;
13
+ },
14
+ setVersion(v) {
15
+ version = v;
16
+ },
17
+ registerAction(actionName, def) {
18
+ actions.push({ name: actionName, ...def });
19
+ },
20
+ setConnectionSchema(schema) {
21
+ connectionConfigSchema = {};
22
+ for (const [key, field] of Object.entries(schema)) {
23
+ connectionConfigSchema[key] = { ...field };
24
+ }
25
+ },
26
+ onInit(fn) {
27
+ initHooks.push(fn);
28
+ },
29
+ log: {
30
+ info(msg) {
31
+ console.log(`[${name}] ${msg}`);
32
+ },
33
+ warn(msg) {
34
+ console.warn(`[${name}] ${msg}`);
35
+ },
36
+ error(msg) {
37
+ console.error(`[${name}] ${msg}`);
38
+ },
39
+ },
40
+ };
41
+ function resolve() {
42
+ const plugin = {
43
+ name,
44
+ version,
45
+ actions,
46
+ connectionConfigSchema,
47
+ };
48
+ if (initHooks.length > 0) {
49
+ plugin.initHooks = initHooks;
50
+ }
51
+ return plugin;
52
+ }
53
+ return { api, resolve };
54
+ }
55
+ export function isPluginFunction(val) {
56
+ return typeof val === "function";
57
+ }
58
+ export function resolvePluginExport(exported, pluginId) {
59
+ if (isPluginFunction(exported)) {
60
+ const { api, resolve } = createPluginAPI(pluginId);
61
+ exported(api);
62
+ return resolve();
63
+ }
64
+ if (exported && typeof exported === "object" && "actions" in exported) {
65
+ return exported;
66
+ }
67
+ throw new Error(`Invalid plugin export from "${pluginId}": expected a function or { name, actions } object`);
68
+ }
@@ -0,0 +1,27 @@
1
+ export interface PluginSource {
2
+ type: "npm" | "git" | "local";
3
+ name: string;
4
+ ref?: string;
5
+ url?: string;
6
+ path?: string;
7
+ subpath?: string;
8
+ }
9
+ export interface InstalledPlugin {
10
+ source: string;
11
+ path: string;
12
+ name?: string;
13
+ }
14
+ export declare function parsePluginSource(source: string): PluginSource;
15
+ export declare function installPlugin(source: string, options?: {
16
+ global?: boolean;
17
+ }): Promise<{
18
+ path: string;
19
+ name: string;
20
+ }>;
21
+ export declare function removePlugin(name: string): boolean;
22
+ export declare function listInstalled(): Array<{
23
+ name: string;
24
+ type: string;
25
+ source: string;
26
+ path: string;
27
+ }>;
@@ -0,0 +1,181 @@
1
+ import { execSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { basename, join, resolve } from "node:path";
5
+ import { findConfigDir } from "../config/loader.js";
6
+ const PLUGINS_FILE = "plugins.json";
7
+ export function parsePluginSource(source) {
8
+ if (source.startsWith("npm:")) {
9
+ const rest = source.slice(4);
10
+ let name;
11
+ let ref;
12
+ if (rest.startsWith("@")) {
13
+ const lastAt = rest.lastIndexOf("@");
14
+ if (lastAt > 0 && lastAt !== rest.indexOf("@")) {
15
+ name = rest.slice(0, lastAt);
16
+ ref = rest.slice(lastAt + 1);
17
+ }
18
+ else {
19
+ name = rest;
20
+ }
21
+ }
22
+ else {
23
+ const atIdx = rest.indexOf("@");
24
+ if (atIdx > 0) {
25
+ name = rest.slice(0, atIdx);
26
+ ref = rest.slice(atIdx + 1);
27
+ }
28
+ else {
29
+ name = rest;
30
+ }
31
+ }
32
+ return { type: "npm", name, ref };
33
+ }
34
+ if (source.startsWith("git:") ||
35
+ source.startsWith("https://") ||
36
+ source.startsWith("http://") ||
37
+ source.startsWith("ssh://")) {
38
+ let raw = source;
39
+ if (raw.startsWith("git:"))
40
+ raw = raw.slice(4);
41
+ let subpath;
42
+ const hashIdx = raw.indexOf("#");
43
+ if (hashIdx > 0) {
44
+ subpath = raw.slice(hashIdx + 1);
45
+ raw = raw.slice(0, hashIdx);
46
+ }
47
+ let ref;
48
+ const atIdx = raw.lastIndexOf("@");
49
+ if (atIdx > 0 && !raw.slice(atIdx).includes("/")) {
50
+ ref = raw.slice(atIdx + 1);
51
+ raw = raw.slice(0, atIdx);
52
+ }
53
+ let url = raw;
54
+ if (!url.startsWith("http://") &&
55
+ !url.startsWith("https://") &&
56
+ !url.startsWith("ssh://")) {
57
+ url = `https://${url}`;
58
+ }
59
+ if (!url.endsWith(".git"))
60
+ url += ".git";
61
+ const name = subpath ? basename(subpath) : basename(url, ".git");
62
+ return { type: "git", name, url, ref, subpath };
63
+ }
64
+ const absPath = resolve(source);
65
+ const name = basename(absPath).replace(/\.(ts|js)$/, "");
66
+ return { type: "local", name, path: absPath };
67
+ }
68
+ function getPluginsDir(global) {
69
+ if (global) {
70
+ const dir = join(homedir(), ".runline", "plugins");
71
+ mkdirSync(dir, { recursive: true });
72
+ return dir;
73
+ }
74
+ const configDir = findConfigDir();
75
+ const dir = configDir
76
+ ? join(configDir, "..", ".runline", "plugins")
77
+ : join(process.cwd(), ".runline", "plugins");
78
+ mkdirSync(dir, { recursive: true });
79
+ return dir;
80
+ }
81
+ function getPluginsJsonPath() {
82
+ const configDir = findConfigDir();
83
+ return configDir
84
+ ? join(configDir, PLUGINS_FILE)
85
+ : join(process.cwd(), ".runline", PLUGINS_FILE);
86
+ }
87
+ function readPluginsJson() {
88
+ const path = getPluginsJsonPath();
89
+ if (!existsSync(path))
90
+ return { plugins: [] };
91
+ return JSON.parse(readFileSync(path, "utf-8"));
92
+ }
93
+ function writePluginsJson(data) {
94
+ const path = getPluginsJsonPath();
95
+ mkdirSync(join(path, ".."), { recursive: true });
96
+ writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`);
97
+ }
98
+ export async function installPlugin(source, options) {
99
+ const parsed = parsePluginSource(source);
100
+ const pluginsDir = getPluginsDir(options?.global ?? false);
101
+ let installPath;
102
+ switch (parsed.type) {
103
+ case "npm": {
104
+ const npmDir = join(pluginsDir, "npm");
105
+ mkdirSync(npmDir, { recursive: true });
106
+ const spec = parsed.ref ? `${parsed.name}@${parsed.ref}` : parsed.name;
107
+ execSync(`npm install ${spec}`, { cwd: npmDir, stdio: "pipe" });
108
+ installPath = join(npmDir, "node_modules", parsed.name);
109
+ break;
110
+ }
111
+ case "git": {
112
+ const gitUrl = parsed.url;
113
+ const urlObj = new URL(gitUrl);
114
+ const gitDir = join(pluginsDir, "git", urlObj.hostname, urlObj.pathname.replace(/\.git$/, ""));
115
+ if (existsSync(gitDir)) {
116
+ execSync("git pull", { cwd: gitDir, stdio: "pipe" });
117
+ }
118
+ else {
119
+ mkdirSync(join(gitDir, ".."), { recursive: true });
120
+ const refArg = parsed.ref ? `--branch ${parsed.ref}` : "";
121
+ execSync(`git clone ${refArg} ${parsed.url} ${gitDir}`, {
122
+ stdio: "pipe",
123
+ });
124
+ }
125
+ if (existsSync(join(gitDir, "package.json"))) {
126
+ execSync("npm install", { cwd: gitDir, stdio: "pipe" });
127
+ }
128
+ installPath = parsed.subpath ? join(gitDir, parsed.subpath) : gitDir;
129
+ break;
130
+ }
131
+ case "local": {
132
+ const localPath = parsed.path;
133
+ if (!existsSync(localPath)) {
134
+ throw new Error(`Path does not exist: ${localPath}`);
135
+ }
136
+ installPath = localPath;
137
+ break;
138
+ }
139
+ }
140
+ const data = readPluginsJson();
141
+ const existing = data.plugins.findIndex((p) => p.source === source);
142
+ const entry = {
143
+ source,
144
+ path: installPath,
145
+ name: parsed.name,
146
+ };
147
+ if (existing >= 0) {
148
+ data.plugins[existing] = entry;
149
+ }
150
+ else {
151
+ data.plugins.push(entry);
152
+ }
153
+ writePluginsJson(data);
154
+ return { path: installPath, name: parsed.name };
155
+ }
156
+ export function removePlugin(name) {
157
+ const data = readPluginsJson();
158
+ const idx = data.plugins.findIndex((p) => p.name === name || p.source.includes(name));
159
+ if (idx < 0)
160
+ return false;
161
+ const plugin = data.plugins[idx];
162
+ const parsed = parsePluginSource(plugin.source);
163
+ if (parsed.type !== "local" && existsSync(plugin.path)) {
164
+ rmSync(plugin.path, { recursive: true, force: true });
165
+ }
166
+ data.plugins.splice(idx, 1);
167
+ writePluginsJson(data);
168
+ return true;
169
+ }
170
+ export function listInstalled() {
171
+ const data = readPluginsJson();
172
+ return data.plugins.map((p) => {
173
+ const parsed = parsePluginSource(p.source);
174
+ return {
175
+ name: p.name || parsed.name,
176
+ type: parsed.type,
177
+ source: p.source,
178
+ path: p.path,
179
+ };
180
+ });
181
+ }
@@ -0,0 +1,13 @@
1
+ import type { PluginDef } from "./types.js";
2
+ export declare function loadPluginFromPath(path: string): Promise<PluginDef>;
3
+ export declare function loadPluginsFromConfig(configDir: string): Promise<PluginDef[]>;
4
+ /**
5
+ * Discover and return all plugins from a config directory and global dir.
6
+ * Does NOT mutate any global state.
7
+ */
8
+ export declare function discoverPlugins(configDir?: string | null): Promise<PluginDef[]>;
9
+ /**
10
+ * Load all plugins and register them into the global registry.
11
+ * Used by the CLI.
12
+ */
13
+ export declare function loadAllPlugins(): Promise<void>;
@@ -0,0 +1,164 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import { findConfigDir } from "../config/loader.js";
6
+ import { resolvePluginExport } from "./api.js";
7
+ import { registry } from "./registry.js";
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ export async function loadPluginFromPath(path) {
10
+ let absPath = resolve(path);
11
+ if (existsSync(absPath) && statSync(absPath).isDirectory()) {
12
+ const candidates = [
13
+ join(absPath, "src", "index.ts"),
14
+ join(absPath, "src", "index.js"),
15
+ join(absPath, "index.ts"),
16
+ join(absPath, "index.js"),
17
+ ];
18
+ const pkgPath = join(absPath, "package.json");
19
+ if (existsSync(pkgPath)) {
20
+ try {
21
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
22
+ if (pkg.main)
23
+ candidates.unshift(join(absPath, pkg.main));
24
+ }
25
+ catch (err) {
26
+ console.error(`[runline] Failed to parse ${pkgPath}:`, err.message);
27
+ }
28
+ }
29
+ const found = candidates.find((c) => existsSync(c));
30
+ if (found) {
31
+ absPath = found;
32
+ }
33
+ else {
34
+ throw new Error(`No entry point found in ${absPath}`);
35
+ }
36
+ }
37
+ const mod = await import(pathToFileURL(absPath).href);
38
+ const pluginId = absPath.replace(/.*\//, "").replace(/\.(ts|js)$/, "");
39
+ return resolvePluginExport(mod.default, pluginId);
40
+ }
41
+ async function loadFromDirectory(dir) {
42
+ const plugins = [];
43
+ if (!existsSync(dir))
44
+ return plugins;
45
+ const entries = readdirSync(dir);
46
+ for (const entry of entries) {
47
+ const fullPath = join(dir, entry);
48
+ const stat = statSync(fullPath);
49
+ if (stat.isFile() &&
50
+ (entry.endsWith(".ts") || entry.endsWith(".js")) &&
51
+ !entry.endsWith(".d.ts") &&
52
+ !entry.endsWith(".test.ts")) {
53
+ try {
54
+ plugins.push(await loadPluginFromPath(fullPath));
55
+ }
56
+ catch (err) {
57
+ console.error(`[runline] Failed to load plugin from ${fullPath}:`, err.message);
58
+ }
59
+ }
60
+ else if (stat.isDirectory()) {
61
+ const candidates = [
62
+ join(fullPath, "index.ts"),
63
+ join(fullPath, "index.js"),
64
+ join(fullPath, "src", "index.ts"),
65
+ join(fullPath, "src", "index.js"),
66
+ ];
67
+ const found = candidates.find((c) => existsSync(c));
68
+ if (found) {
69
+ try {
70
+ plugins.push(await loadPluginFromPath(found));
71
+ }
72
+ catch (err) {
73
+ console.error(`[runline] Failed to load plugin from ${found}:`, err.message);
74
+ }
75
+ }
76
+ const pkgJson = join(fullPath, "package.json");
77
+ if (existsSync(pkgJson)) {
78
+ try {
79
+ const pkg = JSON.parse(readFileSync(pkgJson, "utf-8"));
80
+ const pluginPaths = pkg.runline?.plugins ?? [];
81
+ for (const p of pluginPaths) {
82
+ try {
83
+ plugins.push(await loadPluginFromPath(join(fullPath, p)));
84
+ }
85
+ catch (err) {
86
+ console.error(`[runline] Failed to load plugin from ${join(fullPath, p)}:`, err.message);
87
+ }
88
+ }
89
+ }
90
+ catch (err) {
91
+ console.error(`[runline] Failed to parse ${pkgJson}:`, err.message);
92
+ }
93
+ }
94
+ }
95
+ }
96
+ return plugins;
97
+ }
98
+ export async function loadPluginsFromConfig(configDir) {
99
+ const plugins = [];
100
+ const pluginsFile = join(configDir, "plugins.json");
101
+ if (!existsSync(pluginsFile))
102
+ return plugins;
103
+ try {
104
+ const data = JSON.parse(readFileSync(pluginsFile, "utf-8"));
105
+ const entries = data.plugins ?? data;
106
+ for (const entry of entries) {
107
+ const p = typeof entry === "string" ? entry : entry.path;
108
+ try {
109
+ plugins.push(await loadPluginFromPath(p));
110
+ }
111
+ catch (err) {
112
+ console.error(`[runline] Failed to load plugin from ${p}:`, err.message);
113
+ }
114
+ }
115
+ }
116
+ catch (err) {
117
+ console.error(`[runline] Failed to parse ${pluginsFile}:`, err.message);
118
+ }
119
+ return plugins;
120
+ }
121
+ /**
122
+ * Discover and return all plugins from a config directory and global dir.
123
+ * Does NOT mutate any global state.
124
+ */
125
+ export async function discoverPlugins(configDir) {
126
+ const loaded = new Set();
127
+ const result = [];
128
+ function addIfNew(plugin) {
129
+ if (!loaded.has(plugin.name)) {
130
+ result.push(plugin);
131
+ loaded.add(plugin.name);
132
+ }
133
+ }
134
+ if (configDir) {
135
+ const projectPluginsDir = join(configDir, "plugins");
136
+ const projectPlugins = await loadFromDirectory(projectPluginsDir);
137
+ for (const p of projectPlugins)
138
+ addIfNew(p);
139
+ const configPlugins = await loadPluginsFromConfig(configDir);
140
+ for (const p of configPlugins)
141
+ addIfNew(p);
142
+ }
143
+ const globalDir = join(homedir(), ".runline", "plugins");
144
+ const globalPlugins = await loadFromDirectory(globalDir);
145
+ for (const p of globalPlugins)
146
+ addIfNew(p);
147
+ // Built-in plugins shipped with the package
148
+ const builtinDir = join(__dirname, "..", "plugins");
149
+ const builtinPlugins = await loadFromDirectory(builtinDir);
150
+ for (const p of builtinPlugins)
151
+ addIfNew(p);
152
+ return result;
153
+ }
154
+ /**
155
+ * Load all plugins and register them into the global registry.
156
+ * Used by the CLI.
157
+ */
158
+ export async function loadAllPlugins() {
159
+ const configDir = findConfigDir();
160
+ const plugins = await discoverPlugins(configDir);
161
+ for (const p of plugins) {
162
+ registry.register(p);
163
+ }
164
+ }
@@ -0,0 +1,18 @@
1
+ import type { ActionDef, PluginDef } from "./types.js";
2
+ export declare class PluginRegistry {
3
+ private plugins;
4
+ register(plugin: PluginDef): void;
5
+ getPlugin(name: string): PluginDef | undefined;
6
+ getAction(pluginName: string, actionName: string): ActionDef | undefined;
7
+ getAllActions(): Array<{
8
+ plugin: string;
9
+ action: ActionDef;
10
+ }>;
11
+ /** Resolve a dotted path like "docker.containers.list" to a plugin + action. */
12
+ resolveAction(path: string): {
13
+ plugin: PluginDef;
14
+ action: ActionDef;
15
+ } | undefined;
16
+ listPlugins(): PluginDef[];
17
+ }
18
+ export declare const registry: PluginRegistry;
@@ -0,0 +1,43 @@
1
+ export class PluginRegistry {
2
+ plugins = new Map();
3
+ register(plugin) {
4
+ this.plugins.set(plugin.name, plugin);
5
+ }
6
+ getPlugin(name) {
7
+ return this.plugins.get(name);
8
+ }
9
+ getAction(pluginName, actionName) {
10
+ const plugin = this.plugins.get(pluginName);
11
+ if (!plugin)
12
+ return undefined;
13
+ return plugin.actions.find((a) => a.name === actionName);
14
+ }
15
+ getAllActions() {
16
+ const result = [];
17
+ for (const [pluginName, plugin] of this.plugins) {
18
+ for (const action of plugin.actions) {
19
+ result.push({ plugin: pluginName, action });
20
+ }
21
+ }
22
+ return result;
23
+ }
24
+ /** Resolve a dotted path like "docker.containers.list" to a plugin + action. */
25
+ resolveAction(path) {
26
+ const dot = path.indexOf(".");
27
+ if (dot < 0)
28
+ return undefined;
29
+ const pluginName = path.slice(0, dot);
30
+ const actionName = path.slice(dot + 1);
31
+ const plugin = this.plugins.get(pluginName);
32
+ if (!plugin)
33
+ return undefined;
34
+ const action = plugin.actions.find((a) => a.name === actionName);
35
+ if (!action)
36
+ return undefined;
37
+ return { plugin, action };
38
+ }
39
+ listPlugins() {
40
+ return Array.from(this.plugins.values());
41
+ }
42
+ }
43
+ export const registry = new PluginRegistry();
@@ -0,0 +1,40 @@
1
+ export interface InputField {
2
+ type: "string" | "number" | "boolean" | "object" | "array";
3
+ description?: string;
4
+ required?: boolean;
5
+ default?: unknown;
6
+ }
7
+ export type InputSchema = Record<string, InputField>;
8
+ export interface ActionDef {
9
+ name: string;
10
+ description?: string;
11
+ inputSchema?: InputSchema;
12
+ execute: (input: unknown, ctx: ActionContext) => unknown | Promise<unknown>;
13
+ }
14
+ export interface ConnectionConfig {
15
+ name: string;
16
+ plugin: string;
17
+ config: Record<string, unknown>;
18
+ }
19
+ export interface ActionContext {
20
+ connection: ConnectionConfig;
21
+ log: {
22
+ info(msg: string): void;
23
+ warn(msg: string): void;
24
+ error(msg: string): void;
25
+ };
26
+ }
27
+ export interface PluginDef {
28
+ name: string;
29
+ version: string;
30
+ actions: ActionDef[];
31
+ connectionConfigSchema?: Record<string, {
32
+ type: string;
33
+ required?: boolean;
34
+ description?: string;
35
+ default?: unknown;
36
+ env?: string;
37
+ }>;
38
+ /** @internal */
39
+ initHooks?: Array<(config: Record<string, unknown>) => void>;
40
+ }
@@ -0,0 +1 @@
1
+ export {};