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,156 @@
1
+ const BASE_URL = "https://api.intercom.io";
2
+ async function apiRequest(token, method, endpoint, body, qs) {
3
+ const url = new URL(`${BASE_URL}${endpoint}`);
4
+ if (qs) {
5
+ for (const [k, v] of Object.entries(qs)) {
6
+ if (v !== undefined && v !== null)
7
+ url.searchParams.set(k, String(v));
8
+ }
9
+ }
10
+ const opts = { method, headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", Accept: "application/json" } };
11
+ if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE")
12
+ opts.body = JSON.stringify(body);
13
+ const res = await fetch(url.toString(), opts);
14
+ if (!res.ok)
15
+ throw new Error(`Intercom API error ${res.status}: ${await res.text()}`);
16
+ if (res.status === 204)
17
+ return { success: true };
18
+ return res.json();
19
+ }
20
+ export default function intercom(rl) {
21
+ rl.setName("intercom");
22
+ rl.setVersion("0.1.0");
23
+ rl.setConnectionSchema({ accessToken: { type: "string", required: true, description: "Intercom access token", env: "INTERCOM_ACCESS_TOKEN" } });
24
+ const tok = (ctx) => ctx.connection.config.accessToken;
25
+ // ── Contact (unified leads + users in v2) ───────────
26
+ rl.registerAction("contact.create", {
27
+ description: "Create a contact (lead or user)",
28
+ inputSchema: {
29
+ role: { type: "string", required: true, description: "lead or user" },
30
+ email: { type: "string", required: false, description: "Email" },
31
+ name: { type: "string", required: false, description: "Full name" },
32
+ phone: { type: "string", required: false, description: "Phone" },
33
+ externalId: { type: "string", required: false, description: "External ID (for users)" },
34
+ customAttributes: { type: "object", required: false, description: "Custom attributes" },
35
+ },
36
+ async execute(input, ctx) {
37
+ const { role, email, name, phone, externalId, customAttributes } = input;
38
+ const body = { role };
39
+ if (email)
40
+ body.email = email;
41
+ if (name)
42
+ body.name = name;
43
+ if (phone)
44
+ body.phone = phone;
45
+ if (externalId)
46
+ body.external_id = externalId;
47
+ if (customAttributes)
48
+ body.custom_attributes = customAttributes;
49
+ return apiRequest(tok(ctx), "POST", "/contacts", body);
50
+ },
51
+ });
52
+ rl.registerAction("contact.get", {
53
+ description: "Get a contact by ID",
54
+ inputSchema: { contactId: { type: "string", required: true, description: "Contact ID" } },
55
+ async execute(input, ctx) { return apiRequest(tok(ctx), "GET", `/contacts/${input.contactId}`); },
56
+ });
57
+ rl.registerAction("contact.list", {
58
+ description: "List contacts",
59
+ inputSchema: { limit: { type: "number", required: false, description: "Max results" }, startingAfter: { type: "string", required: false, description: "Pagination cursor" } },
60
+ async execute(input, ctx) {
61
+ const { limit, startingAfter } = (input ?? {});
62
+ const qs = {};
63
+ if (limit)
64
+ qs.per_page = limit;
65
+ if (startingAfter)
66
+ qs.starting_after = startingAfter;
67
+ return apiRequest(tok(ctx), "GET", "/contacts", undefined, qs);
68
+ },
69
+ });
70
+ rl.registerAction("contact.update", {
71
+ description: "Update a contact",
72
+ inputSchema: {
73
+ contactId: { type: "string", required: true, description: "Contact ID" },
74
+ email: { type: "string", required: false, description: "Email" },
75
+ name: { type: "string", required: false, description: "Name" },
76
+ phone: { type: "string", required: false, description: "Phone" },
77
+ customAttributes: { type: "object", required: false, description: "Custom attributes" },
78
+ },
79
+ async execute(input, ctx) {
80
+ const { contactId, email, name, phone, customAttributes } = input;
81
+ const body = {};
82
+ if (email)
83
+ body.email = email;
84
+ if (name)
85
+ body.name = name;
86
+ if (phone)
87
+ body.phone = phone;
88
+ if (customAttributes)
89
+ body.custom_attributes = customAttributes;
90
+ return apiRequest(tok(ctx), "PUT", `/contacts/${contactId}`, body);
91
+ },
92
+ });
93
+ rl.registerAction("contact.delete", {
94
+ description: "Delete a contact",
95
+ inputSchema: { contactId: { type: "string", required: true, description: "Contact ID" } },
96
+ async execute(input, ctx) { return apiRequest(tok(ctx), "DELETE", `/contacts/${input.contactId}`); },
97
+ });
98
+ rl.registerAction("contact.search", {
99
+ description: "Search contacts",
100
+ inputSchema: {
101
+ query: { type: "object", required: true, description: "Search query object (Intercom search format)" },
102
+ limit: { type: "number", required: false, description: "Max results per page" },
103
+ },
104
+ async execute(input, ctx) {
105
+ const { query, limit } = input;
106
+ const body = { query };
107
+ if (limit)
108
+ body.pagination = { per_page: limit };
109
+ return apiRequest(tok(ctx), "POST", "/contacts/search", body);
110
+ },
111
+ });
112
+ // ── Company ─────────────────────────────────────────
113
+ rl.registerAction("company.create", {
114
+ description: "Create or update a company",
115
+ inputSchema: {
116
+ companyId: { type: "string", required: true, description: "Company ID (your identifier)" },
117
+ name: { type: "string", required: false, description: "Company name" },
118
+ plan: { type: "string", required: false, description: "Plan name" },
119
+ customAttributes: { type: "object", required: false, description: "Custom attributes" },
120
+ },
121
+ async execute(input, ctx) {
122
+ const { companyId, name, plan, customAttributes } = input;
123
+ const body = { company_id: companyId };
124
+ if (name)
125
+ body.name = name;
126
+ if (plan)
127
+ body.plan = plan;
128
+ if (customAttributes)
129
+ body.custom_attributes = customAttributes;
130
+ return apiRequest(tok(ctx), "POST", "/companies", body);
131
+ },
132
+ });
133
+ rl.registerAction("company.get", {
134
+ description: "Get a company",
135
+ inputSchema: { companyId: { type: "string", required: true, description: "Intercom company ID" } },
136
+ async execute(input, ctx) { return apiRequest(tok(ctx), "GET", `/companies/${input.companyId}`); },
137
+ });
138
+ rl.registerAction("company.list", {
139
+ description: "List companies",
140
+ inputSchema: { limit: { type: "number", required: false, description: "Max results" }, page: { type: "number", required: false, description: "Page" } },
141
+ async execute(input, ctx) {
142
+ const { limit, page } = (input ?? {});
143
+ const qs = {};
144
+ if (limit)
145
+ qs.per_page = limit;
146
+ if (page)
147
+ qs.page = page;
148
+ return apiRequest(tok(ctx), "GET", "/companies", undefined, qs);
149
+ },
150
+ });
151
+ rl.registerAction("company.listUsers", {
152
+ description: "List users of a company",
153
+ inputSchema: { companyId: { type: "string", required: true, description: "Company ID" } },
154
+ async execute(input, ctx) { return apiRequest(tok(ctx), "GET", `/companies/${input.companyId}/contacts`); },
155
+ });
156
+ }
@@ -0,0 +1,139 @@
1
+ async function apiRequest(baseUrl, apiKey, method, endpoint, body, qs) {
2
+ const url = new URL(`${baseUrl}/api${endpoint}`);
3
+ if (qs) {
4
+ for (const [k, v] of Object.entries(qs)) {
5
+ if (v !== undefined && v !== null)
6
+ url.searchParams.set(k, String(v));
7
+ }
8
+ }
9
+ const opts = {
10
+ method,
11
+ headers: { "Api-Key": apiKey, "Content-Type": "application/json" },
12
+ };
13
+ if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE")
14
+ opts.body = JSON.stringify(body);
15
+ const res = await fetch(url.toString(), opts);
16
+ if (!res.ok)
17
+ throw new Error(`Iterable API error ${res.status}: ${await res.text()}`);
18
+ return res.json();
19
+ }
20
+ export default function iterable(rl) {
21
+ rl.setName("iterable");
22
+ rl.setVersion("0.1.0");
23
+ rl.setConnectionSchema({
24
+ apiKey: { type: "string", required: true, description: "Iterable API key", env: "ITERABLE_API_KEY" },
25
+ region: { type: "string", required: false, description: "API base URL (default: https://api.iterable.com)", env: "ITERABLE_REGION", default: "https://api.iterable.com" },
26
+ });
27
+ const conn = (ctx) => ({
28
+ baseUrl: (ctx.connection.config.region ?? "https://api.iterable.com").replace(/\/$/, ""),
29
+ apiKey: ctx.connection.config.apiKey,
30
+ });
31
+ // ── Event ───────────────────────────────────────────
32
+ rl.registerAction("event.track", {
33
+ description: "Track events for users (bulk). Each event requires email or userId, plus eventName.",
34
+ inputSchema: {
35
+ events: {
36
+ type: "array", required: true,
37
+ description: "Array of event objects. Each needs: eventName (string, required), email or id (string, one required), dataFields (object, optional), createdAt (unix timestamp, optional)",
38
+ },
39
+ },
40
+ async execute(input, ctx) {
41
+ const { events } = input;
42
+ const { baseUrl, apiKey } = conn(ctx);
43
+ return apiRequest(baseUrl, apiKey, "POST", "/events/trackBulk", { events });
44
+ },
45
+ });
46
+ // ── User ────────────────────────────────────────────
47
+ rl.registerAction("user.upsert", {
48
+ description: "Create or update a user",
49
+ inputSchema: {
50
+ identifier: { type: "string", required: true, description: "'email' or 'userId'" },
51
+ value: { type: "string", required: true, description: "The email address or userId value" },
52
+ preferUserId: { type: "boolean", required: false, description: "When identifier is userId, prefer userId for lookups (default false)" },
53
+ dataFields: { type: "object", required: false, description: "User data fields as key-value pairs" },
54
+ },
55
+ async execute(input, ctx) {
56
+ const { identifier, value, preferUserId, dataFields } = input;
57
+ const { baseUrl, apiKey } = conn(ctx);
58
+ const body = {};
59
+ if (identifier === "email") {
60
+ body.email = value;
61
+ }
62
+ else {
63
+ body.userId = value;
64
+ if (preferUserId !== undefined)
65
+ body.preferUserId = preferUserId;
66
+ }
67
+ if (dataFields)
68
+ body.dataFields = dataFields;
69
+ return apiRequest(baseUrl, apiKey, "POST", "/users/update", body);
70
+ },
71
+ });
72
+ rl.registerAction("user.get", {
73
+ description: "Get a user by email or userId",
74
+ inputSchema: {
75
+ by: { type: "string", required: true, description: "'email' or 'userId'" },
76
+ value: { type: "string", required: true, description: "The email address or userId" },
77
+ },
78
+ async execute(input, ctx) {
79
+ const { by, value } = input;
80
+ const { baseUrl, apiKey } = conn(ctx);
81
+ if (by === "email") {
82
+ const data = await apiRequest(baseUrl, apiKey, "GET", "/users/getByEmail", undefined, { email: value });
83
+ return data.user ?? data;
84
+ }
85
+ return apiRequest(baseUrl, apiKey, "GET", `/users/byUserId/${encodeURIComponent(value)}`);
86
+ },
87
+ });
88
+ rl.registerAction("user.delete", {
89
+ description: "Delete a user by email or userId",
90
+ inputSchema: {
91
+ by: { type: "string", required: true, description: "'email' or 'userId'" },
92
+ value: { type: "string", required: true, description: "The email address or userId" },
93
+ },
94
+ async execute(input, ctx) {
95
+ const { by, value } = input;
96
+ const { baseUrl, apiKey } = conn(ctx);
97
+ const endpoint = by === "email"
98
+ ? `/users/${encodeURIComponent(value)}`
99
+ : `/users/byUserId/${encodeURIComponent(value)}`;
100
+ return apiRequest(baseUrl, apiKey, "DELETE", endpoint);
101
+ },
102
+ });
103
+ // ── User List ───────────────────────────────────────
104
+ rl.registerAction("userList.add", {
105
+ description: "Subscribe users to a list",
106
+ inputSchema: {
107
+ listId: { type: "number", required: true, description: "List ID" },
108
+ identifier: { type: "string", required: true, description: "'email' or 'userId'" },
109
+ values: { type: "array", required: true, description: "Array of email addresses or userIds to subscribe" },
110
+ },
111
+ async execute(input, ctx) {
112
+ const { listId, identifier, values } = input;
113
+ const { baseUrl, apiKey } = conn(ctx);
114
+ const subscribers = values.map((v) => identifier === "email" ? { email: v } : { userId: v });
115
+ return apiRequest(baseUrl, apiKey, "POST", "/lists/subscribe", { listId, subscribers });
116
+ },
117
+ });
118
+ rl.registerAction("userList.remove", {
119
+ description: "Unsubscribe users from a list",
120
+ inputSchema: {
121
+ listId: { type: "number", required: true, description: "List ID" },
122
+ identifier: { type: "string", required: true, description: "'email' or 'userId'" },
123
+ values: { type: "array", required: true, description: "Array of email addresses or userIds to unsubscribe" },
124
+ campaignId: { type: "number", required: false, description: "Campaign ID for attribution" },
125
+ channelUnsubscribe: { type: "boolean", required: false, description: "Unsubscribe from channel" },
126
+ },
127
+ async execute(input, ctx) {
128
+ const { listId, identifier, values, campaignId, channelUnsubscribe } = input;
129
+ const { baseUrl, apiKey } = conn(ctx);
130
+ const subscribers = values.map((v) => identifier === "email" ? { email: v } : { userId: v });
131
+ const body = { listId, subscribers };
132
+ if (campaignId !== undefined)
133
+ body.campaignId = campaignId;
134
+ if (channelUnsubscribe !== undefined)
135
+ body.channelUnsubscribe = channelUnsubscribe;
136
+ return apiRequest(baseUrl, apiKey, "POST", "/lists/unsubscribe", body);
137
+ },
138
+ });
139
+ }
@@ -0,0 +1,132 @@
1
+ async function apiRequest(baseUrl, username, apiToken, method, endpoint, body, contentType, qs) {
2
+ const url = new URL(`${baseUrl}${endpoint}`);
3
+ if (qs) {
4
+ for (const [k, v] of Object.entries(qs)) {
5
+ if (v !== undefined && v !== null)
6
+ url.searchParams.set(k, String(v));
7
+ }
8
+ }
9
+ const headers = { Authorization: `Basic ${btoa(`${username}:${apiToken}`)}` };
10
+ if (contentType)
11
+ headers["Content-Type"] = contentType;
12
+ const opts = { method, headers };
13
+ if (body && method !== "GET")
14
+ opts.body = typeof body === "string" ? body : JSON.stringify(body);
15
+ const res = await fetch(url.toString(), opts);
16
+ if (!res.ok)
17
+ throw new Error(`Jenkins error ${res.status}: ${await res.text()}`);
18
+ const ct = res.headers.get("content-type") ?? "";
19
+ if (ct.includes("json"))
20
+ return res.json();
21
+ return { success: true };
22
+ }
23
+ function getConn(ctx) {
24
+ return {
25
+ baseUrl: ctx.connection.config.baseUrl.replace(/\/$/, ""),
26
+ username: ctx.connection.config.username,
27
+ apiToken: ctx.connection.config.apiToken,
28
+ };
29
+ }
30
+ function jk(ctx, method, endpoint, body, ct, qs) {
31
+ const { baseUrl, username, apiToken } = getConn(ctx);
32
+ return apiRequest(baseUrl, username, apiToken, method, endpoint, body, ct, qs);
33
+ }
34
+ export default function jenkins(rl) {
35
+ rl.setName("jenkins");
36
+ rl.setVersion("0.1.0");
37
+ rl.setConnectionSchema({
38
+ baseUrl: { type: "string", required: true, description: "Jenkins URL (e.g. https://jenkins.example.com)", env: "JENKINS_URL" },
39
+ username: { type: "string", required: true, description: "Jenkins username", env: "JENKINS_USER" },
40
+ apiToken: { type: "string", required: true, description: "Jenkins API token", env: "JENKINS_TOKEN" },
41
+ });
42
+ // ── Job ─────────────────────────────────────────────
43
+ rl.registerAction("job.trigger", {
44
+ description: "Trigger a job build",
45
+ inputSchema: {
46
+ jobName: { type: "string", required: true, description: "Job name (URL-encoded if nested)" },
47
+ parameters: { type: "object", required: false, description: "Build parameters as key-value pairs" },
48
+ },
49
+ async execute(input, ctx) {
50
+ const { jobName, parameters } = input;
51
+ if (parameters && Object.keys(parameters).length > 0) {
52
+ const qs = parameters;
53
+ await jk(ctx, "POST", `/job/${jobName}/buildWithParameters`, undefined, undefined, qs);
54
+ }
55
+ else {
56
+ await jk(ctx, "POST", `/job/${jobName}/build`);
57
+ }
58
+ return { success: true };
59
+ },
60
+ });
61
+ rl.registerAction("job.getParameters", {
62
+ description: "Get build parameters for a job",
63
+ inputSchema: { jobName: { type: "string", required: true, description: "Job name" } },
64
+ async execute(input, ctx) {
65
+ const data = (await jk(ctx, "GET", `/job/${input.jobName}/api/json`, undefined, undefined, { tree: "actions[parameterDefinitions[*]]" }));
66
+ return data.actions;
67
+ },
68
+ });
69
+ rl.registerAction("job.copy", {
70
+ description: "Copy/create a job from an existing one",
71
+ inputSchema: {
72
+ fromJob: { type: "string", required: true, description: "Source job name" },
73
+ newName: { type: "string", required: true, description: "New job name" },
74
+ },
75
+ async execute(input, ctx) {
76
+ const { fromJob, newName } = input;
77
+ await jk(ctx, "POST", "/createItem", undefined, undefined, { name: newName, mode: "copy", from: fromJob });
78
+ return { success: true };
79
+ },
80
+ });
81
+ rl.registerAction("job.create", {
82
+ description: "Create a job from XML config",
83
+ inputSchema: {
84
+ name: { type: "string", required: true, description: "Job name" },
85
+ xml: { type: "string", required: true, description: "Jenkins job config XML" },
86
+ },
87
+ async execute(input, ctx) {
88
+ const { name, xml } = input;
89
+ await jk(ctx, "POST", "/createItem", xml, "application/xml", { name });
90
+ return { success: true };
91
+ },
92
+ });
93
+ // ── Build ───────────────────────────────────────────
94
+ rl.registerAction("build.list", {
95
+ description: "List builds for a job",
96
+ inputSchema: {
97
+ jobName: { type: "string", required: true, description: "Job name" },
98
+ limit: { type: "number", required: false, description: "Max results" },
99
+ },
100
+ async execute(input, ctx) {
101
+ const { jobName, limit } = input;
102
+ const tree = limit ? `builds[*]{0,${limit}}` : "builds[*]";
103
+ const data = (await jk(ctx, "GET", `/job/${jobName}/api/json`, undefined, undefined, { tree }));
104
+ return data.builds;
105
+ },
106
+ });
107
+ // ── Instance ────────────────────────────────────────
108
+ rl.registerAction("instance.quietDown", {
109
+ description: "Put Jenkins into quiet mode (no new builds)",
110
+ async execute(_input, ctx) { await jk(ctx, "POST", "/quietDown"); return { success: true }; },
111
+ });
112
+ rl.registerAction("instance.cancelQuietDown", {
113
+ description: "Cancel quiet mode",
114
+ async execute(_input, ctx) { await jk(ctx, "POST", "/cancelQuietDown"); return { success: true }; },
115
+ });
116
+ rl.registerAction("instance.restart", {
117
+ description: "Restart Jenkins immediately",
118
+ async execute(_input, ctx) { await jk(ctx, "POST", "/restart"); return { success: true }; },
119
+ });
120
+ rl.registerAction("instance.safeRestart", {
121
+ description: "Restart Jenkins after running builds finish",
122
+ async execute(_input, ctx) { await jk(ctx, "POST", "/safeRestart"); return { success: true }; },
123
+ });
124
+ rl.registerAction("instance.exit", {
125
+ description: "Shut down Jenkins immediately",
126
+ async execute(_input, ctx) { await jk(ctx, "POST", "/exit"); return { success: true }; },
127
+ });
128
+ rl.registerAction("instance.safeExit", {
129
+ description: "Shut down Jenkins after running builds finish",
130
+ async execute(_input, ctx) { await jk(ctx, "POST", "/safeExit"); return { success: true }; },
131
+ });
132
+ }
@@ -0,0 +1,229 @@
1
+ async function apiRequest(domain, email, apiToken, method, endpoint, body, qs) {
2
+ const url = new URL(`${domain}/rest${endpoint}`);
3
+ if (qs) {
4
+ for (const [k, v] of Object.entries(qs)) {
5
+ if (v !== undefined && v !== null)
6
+ url.searchParams.set(k, String(v));
7
+ }
8
+ }
9
+ const opts = {
10
+ method,
11
+ headers: { Authorization: `Basic ${btoa(`${email}:${apiToken}`)}`, "Content-Type": "application/json", Accept: "application/json" },
12
+ };
13
+ if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE")
14
+ opts.body = JSON.stringify(body);
15
+ const res = await fetch(url.toString(), opts);
16
+ if (!res.ok)
17
+ throw new Error(`Jira API error ${res.status}: ${await res.text()}`);
18
+ if (res.status === 204)
19
+ return { success: true };
20
+ const ct = res.headers.get("content-type") ?? "";
21
+ if (ct.includes("json"))
22
+ return res.json();
23
+ return { success: true };
24
+ }
25
+ function getConn(ctx) {
26
+ return {
27
+ domain: ctx.connection.config.domain.replace(/\/$/, ""),
28
+ email: ctx.connection.config.email,
29
+ apiToken: ctx.connection.config.apiToken,
30
+ };
31
+ }
32
+ function jr(ctx, method, endpoint, body, qs) {
33
+ const { domain, email, apiToken } = getConn(ctx);
34
+ return apiRequest(domain, email, apiToken, method, endpoint, body, qs);
35
+ }
36
+ export default function jira(rl) {
37
+ rl.setName("jira");
38
+ rl.setVersion("0.1.0");
39
+ rl.setConnectionSchema({
40
+ domain: { type: "string", required: true, description: "Jira domain (e.g. https://mycompany.atlassian.net)", env: "JIRA_DOMAIN" },
41
+ email: { type: "string", required: true, description: "Jira account email", env: "JIRA_EMAIL" },
42
+ apiToken: { type: "string", required: true, description: "Jira API token", env: "JIRA_API_TOKEN" },
43
+ });
44
+ // ── Issue ───────────────────────────────────────────
45
+ rl.registerAction("issue.create", {
46
+ description: "Create an issue",
47
+ inputSchema: {
48
+ projectKey: { type: "string", required: true, description: "Project key (e.g. PROJ)" },
49
+ issueType: { type: "string", required: true, description: "Issue type (Bug, Task, Story, Epic...)" },
50
+ summary: { type: "string", required: true, description: "Issue summary" },
51
+ description: { type: "string", required: false, description: "Description" },
52
+ assigneeId: { type: "string", required: false, description: "Assignee account ID" },
53
+ priority: { type: "string", required: false, description: "Priority name (Highest, High, Medium, Low, Lowest)" },
54
+ labels: { type: "array", required: false, description: "Labels" },
55
+ parentKey: { type: "string", required: false, description: "Parent issue key (for subtasks)" },
56
+ customFields: { type: "object", required: false, description: "Custom fields" },
57
+ },
58
+ async execute(input, ctx) {
59
+ const { projectKey, issueType, summary, description: desc, assigneeId, priority, labels, parentKey, customFields } = input;
60
+ const fields = {
61
+ project: { key: projectKey },
62
+ issuetype: { name: issueType },
63
+ summary,
64
+ };
65
+ if (desc)
66
+ fields.description = desc;
67
+ if (assigneeId)
68
+ fields.assignee = { accountId: assigneeId };
69
+ if (priority)
70
+ fields.priority = { name: priority };
71
+ if (labels)
72
+ fields.labels = labels;
73
+ if (parentKey)
74
+ fields.parent = { key: parentKey };
75
+ if (customFields)
76
+ Object.assign(fields, customFields);
77
+ return jr(ctx, "POST", "/api/2/issue", { fields });
78
+ },
79
+ });
80
+ rl.registerAction("issue.get", {
81
+ description: "Get an issue",
82
+ inputSchema: {
83
+ issueKey: { type: "string", required: true, description: "Issue key (e.g. PROJ-123)" },
84
+ fields: { type: "string", required: false, description: "Comma-separated fields to return" },
85
+ expand: { type: "string", required: false, description: "Comma-separated expansions" },
86
+ },
87
+ async execute(input, ctx) {
88
+ const { issueKey, fields, expand } = (input ?? {});
89
+ const qs = {};
90
+ if (fields)
91
+ qs.fields = fields;
92
+ if (expand)
93
+ qs.expand = expand;
94
+ return jr(ctx, "GET", `/api/2/issue/${issueKey}`, undefined, qs);
95
+ },
96
+ });
97
+ rl.registerAction("issue.search", {
98
+ description: "Search issues using JQL",
99
+ inputSchema: {
100
+ jql: { type: "string", required: true, description: "JQL query" },
101
+ fields: { type: "array", required: false, description: "Fields to return" },
102
+ maxResults: { type: "number", required: false, description: "Max results" },
103
+ startAt: { type: "number", required: false, description: "Start index" },
104
+ },
105
+ async execute(input, ctx) {
106
+ const { jql, fields, maxResults, startAt } = input;
107
+ const body = { jql };
108
+ if (fields)
109
+ body.fields = fields;
110
+ if (maxResults)
111
+ body.maxResults = maxResults;
112
+ if (startAt)
113
+ body.startAt = startAt;
114
+ return jr(ctx, "POST", "/api/2/search", body);
115
+ },
116
+ });
117
+ rl.registerAction("issue.update", {
118
+ description: "Update an issue",
119
+ inputSchema: {
120
+ issueKey: { type: "string", required: true, description: "Issue key" },
121
+ fields: { type: "object", required: false, description: "Fields to set" },
122
+ update: { type: "object", required: false, description: "Update operations" },
123
+ transition: { type: "object", required: false, description: "Transition {id}" },
124
+ },
125
+ async execute(input, ctx) {
126
+ const { issueKey, fields, update, transition } = input;
127
+ const body = {};
128
+ if (fields)
129
+ body.fields = fields;
130
+ if (update)
131
+ body.update = update;
132
+ if (transition)
133
+ body.transition = transition;
134
+ return jr(ctx, "PUT", `/api/2/issue/${issueKey}`, body);
135
+ },
136
+ });
137
+ rl.registerAction("issue.delete", {
138
+ description: "Delete an issue",
139
+ inputSchema: { issueKey: { type: "string", required: true, description: "Issue key" } },
140
+ async execute(input, ctx) { await jr(ctx, "DELETE", `/api/2/issue/${input.issueKey}`); return { success: true }; },
141
+ });
142
+ rl.registerAction("issue.transition", {
143
+ description: "Transition an issue to a new status",
144
+ inputSchema: {
145
+ issueKey: { type: "string", required: true, description: "Issue key" },
146
+ transitionId: { type: "string", required: true, description: "Transition ID" },
147
+ comment: { type: "string", required: false, description: "Comment to add" },
148
+ },
149
+ async execute(input, ctx) {
150
+ const { issueKey, transitionId, comment } = input;
151
+ const body = { transition: { id: transitionId } };
152
+ if (comment)
153
+ body.update = { comment: [{ add: { body: comment } }] };
154
+ return jr(ctx, "POST", `/api/2/issue/${issueKey}/transitions`, body);
155
+ },
156
+ });
157
+ rl.registerAction("issue.getTransitions", {
158
+ description: "Get available transitions for an issue",
159
+ inputSchema: { issueKey: { type: "string", required: true, description: "Issue key" } },
160
+ async execute(input, ctx) { return jr(ctx, "GET", `/api/2/issue/${input.issueKey}/transitions`); },
161
+ });
162
+ rl.registerAction("issue.getChangelog", {
163
+ description: "Get issue changelog",
164
+ inputSchema: { issueKey: { type: "string", required: true, description: "Issue key" } },
165
+ async execute(input, ctx) { return jr(ctx, "GET", `/api/2/issue/${input.issueKey}/changelog`); },
166
+ });
167
+ rl.registerAction("issue.notify", {
168
+ description: "Send notification about an issue",
169
+ inputSchema: {
170
+ issueKey: { type: "string", required: true, description: "Issue key" },
171
+ subject: { type: "string", required: true, description: "Email subject" },
172
+ htmlBody: { type: "string", required: true, description: "HTML body" },
173
+ to: { type: "object", required: true, description: "{users: [{accountId}], groups: [{name}]}" },
174
+ },
175
+ async execute(input, ctx) {
176
+ const { issueKey, subject, htmlBody, to } = input;
177
+ return jr(ctx, "POST", `/api/2/issue/${issueKey}/notify`, { subject, htmlBody, to });
178
+ },
179
+ });
180
+ // ── Issue Comment ───────────────────────────────────
181
+ rl.registerAction("issueComment.add", {
182
+ description: "Add a comment to an issue",
183
+ inputSchema: {
184
+ issueKey: { type: "string", required: true, description: "Issue key" },
185
+ body: { type: "string", required: true, description: "Comment body" },
186
+ },
187
+ async execute(input, ctx) {
188
+ const { issueKey, body: commentBody } = input;
189
+ return jr(ctx, "POST", `/api/2/issue/${issueKey}/comment`, { body: commentBody });
190
+ },
191
+ });
192
+ rl.registerAction("issueComment.get", {
193
+ description: "Get a comment",
194
+ inputSchema: { issueKey: { type: "string", required: true, description: "Issue key" }, commentId: { type: "string", required: true, description: "Comment ID" } },
195
+ async execute(input, ctx) { const { issueKey, commentId } = input; return jr(ctx, "GET", `/api/2/issue/${issueKey}/comment/${commentId}`); },
196
+ });
197
+ rl.registerAction("issueComment.list", {
198
+ description: "List comments on an issue",
199
+ inputSchema: { issueKey: { type: "string", required: true, description: "Issue key" } },
200
+ async execute(input, ctx) { return jr(ctx, "GET", `/api/2/issue/${input.issueKey}/comment`); },
201
+ });
202
+ rl.registerAction("issueComment.update", {
203
+ description: "Update a comment",
204
+ inputSchema: { issueKey: { type: "string", required: true, description: "Issue key" }, commentId: { type: "string", required: true, description: "Comment ID" }, body: { type: "string", required: true, description: "New body" } },
205
+ async execute(input, ctx) { const { issueKey, commentId, body: b } = input; return jr(ctx, "PUT", `/api/2/issue/${issueKey}/comment/${commentId}`, { body: b }); },
206
+ });
207
+ rl.registerAction("issueComment.delete", {
208
+ description: "Delete a comment",
209
+ inputSchema: { issueKey: { type: "string", required: true, description: "Issue key" }, commentId: { type: "string", required: true, description: "Comment ID" } },
210
+ async execute(input, ctx) { const { issueKey, commentId } = input; await jr(ctx, "DELETE", `/api/2/issue/${issueKey}/comment/${commentId}`); return { success: true }; },
211
+ });
212
+ // ── User ────────────────────────────────────────────
213
+ rl.registerAction("user.get", {
214
+ description: "Get a user by account ID",
215
+ inputSchema: { accountId: { type: "string", required: true, description: "Account ID" } },
216
+ async execute(input, ctx) { return jr(ctx, "GET", "/api/2/user", undefined, { accountId: input.accountId }); },
217
+ });
218
+ rl.registerAction("user.search", {
219
+ description: "Search users",
220
+ inputSchema: { query: { type: "string", required: true, description: "Search query" }, maxResults: { type: "number", required: false, description: "Max results" } },
221
+ async execute(input, ctx) {
222
+ const { query, maxResults } = input;
223
+ const qs = { query };
224
+ if (maxResults)
225
+ qs.maxResults = maxResults;
226
+ return jr(ctx, "GET", "/api/2/user/search", undefined, qs);
227
+ },
228
+ });
229
+ }