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,321 @@
1
+ async function apiRequest(apiKey, method, endpoint, body, qs) {
2
+ const dc = apiKey.split("-").pop();
3
+ const url = new URL(`https://${dc}.api.mailchimp.com/3.0${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 = {
11
+ method,
12
+ headers: { Authorization: `Basic ${btoa(`anystring:${apiKey}`)}`, Accept: "application/json" },
13
+ };
14
+ if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE") {
15
+ opts.headers["Content-Type"] = "application/json";
16
+ opts.body = JSON.stringify(body);
17
+ }
18
+ const res = await fetch(url.toString(), opts);
19
+ if (!res.ok)
20
+ throw new Error(`Mailchimp API error ${res.status}: ${await res.text()}`);
21
+ if (res.status === 204)
22
+ return { success: true };
23
+ return res.json();
24
+ }
25
+ export default function mailchimp(rl) {
26
+ rl.setName("mailchimp");
27
+ rl.setVersion("0.1.0");
28
+ rl.setConnectionSchema({
29
+ apiKey: { type: "string", required: true, description: "Mailchimp API key (includes datacenter suffix, e.g. xxx-us21)", env: "MAILCHIMP_API_KEY" },
30
+ });
31
+ const key = (ctx) => ctx.connection.config.apiKey;
32
+ // ── Member ──────────────────────────────────────────
33
+ rl.registerAction("member.create", {
34
+ description: "Add a member to a list/audience",
35
+ inputSchema: {
36
+ listId: { type: "string", required: true, description: "List/audience ID" },
37
+ email: { type: "string", required: true },
38
+ status: { type: "string", required: true, description: "subscribed, unsubscribed, cleaned, pending, transactional" },
39
+ mergeFields: { type: "object", required: false, description: "Merge fields as {TAG: value}" },
40
+ tags: { type: "array", required: false, description: "Array of tag name strings" },
41
+ emailType: { type: "string", required: false, description: "html or text" },
42
+ language: { type: "string", required: false },
43
+ vip: { type: "boolean", required: false },
44
+ location: { type: "object", required: false, description: "{latitude: number, longitude: number}" },
45
+ interests: { type: "object", required: false, description: "Interest group IDs as {id: boolean}" },
46
+ ipSignup: { type: "string", required: false },
47
+ ipOpt: { type: "string", required: false },
48
+ timestampSignup: { type: "string", required: false, description: "ISO datetime" },
49
+ timestampOpt: { type: "string", required: false, description: "ISO datetime" },
50
+ },
51
+ async execute(input, ctx) {
52
+ const p = input;
53
+ const body = {
54
+ email_address: p.email,
55
+ status: p.status,
56
+ };
57
+ if (p.mergeFields)
58
+ body.merge_fields = p.mergeFields;
59
+ if (p.tags)
60
+ body.tags = p.tags;
61
+ if (p.emailType)
62
+ body.email_type = p.emailType;
63
+ if (p.language)
64
+ body.language = p.language;
65
+ if (p.vip !== undefined)
66
+ body.vip = p.vip;
67
+ if (p.location)
68
+ body.location = p.location;
69
+ if (p.interests)
70
+ body.interests = p.interests;
71
+ if (p.ipSignup)
72
+ body.ip_signup = p.ipSignup;
73
+ if (p.ipOpt)
74
+ body.ip_opt = p.ipOpt;
75
+ if (p.timestampSignup)
76
+ body.timestamp_signup = p.timestampSignup;
77
+ if (p.timestampOpt)
78
+ body.timestamp_opt = p.timestampOpt;
79
+ return apiRequest(key(ctx), "POST", `/lists/${p.listId}/members`, body);
80
+ },
81
+ });
82
+ rl.registerAction("member.get", {
83
+ description: "Get a list member by email (used directly as subscriber hash)",
84
+ inputSchema: {
85
+ listId: { type: "string", required: true },
86
+ email: { type: "string", required: true, description: "Email address (Mailchimp accepts email or MD5 hash)" },
87
+ fields: { type: "string", required: false, description: "Comma-separated fields to return" },
88
+ excludeFields: { type: "string", required: false, description: "Comma-separated fields to exclude" },
89
+ },
90
+ async execute(input, ctx) {
91
+ const { listId, email, fields, excludeFields } = input;
92
+ const qs = {};
93
+ if (fields)
94
+ qs.fields = fields;
95
+ if (excludeFields)
96
+ qs.exclude_fields = excludeFields;
97
+ return apiRequest(key(ctx), "GET", `/lists/${listId}/members/${email}`, undefined, qs);
98
+ },
99
+ });
100
+ rl.registerAction("member.list", {
101
+ description: "List members in a list/audience",
102
+ inputSchema: {
103
+ listId: { type: "string", required: true },
104
+ count: { type: "number", required: false, description: "Max results (default 500)" },
105
+ offset: { type: "number", required: false },
106
+ status: { type: "string", required: false, description: "subscribed, unsubscribed, cleaned, pending, transactional" },
107
+ emailType: { type: "string", required: false, description: "html or text" },
108
+ sinceLastChanged: { type: "string", required: false, description: "ISO datetime" },
109
+ beforeLastChanged: { type: "string", required: false, description: "ISO datetime" },
110
+ beforeTimestampOpt: { type: "string", required: false, description: "ISO datetime" },
111
+ },
112
+ async execute(input, ctx) {
113
+ const p = input;
114
+ const qs = {};
115
+ if (p.count)
116
+ qs.count = p.count;
117
+ if (p.offset)
118
+ qs.offset = p.offset;
119
+ if (p.status)
120
+ qs.status = p.status;
121
+ if (p.emailType)
122
+ qs.email_type = p.emailType;
123
+ if (p.sinceLastChanged)
124
+ qs.since_last_changed = p.sinceLastChanged;
125
+ if (p.beforeLastChanged)
126
+ qs.before_last_changed = p.beforeLastChanged;
127
+ if (p.beforeTimestampOpt)
128
+ qs.before_timestamp_opt = p.beforeTimestampOpt;
129
+ const data = (await apiRequest(key(ctx), "GET", `/lists/${p.listId}/members`, undefined, qs));
130
+ return data.members;
131
+ },
132
+ });
133
+ rl.registerAction("member.update", {
134
+ description: "Update a list member (PUT — full replace)",
135
+ inputSchema: {
136
+ listId: { type: "string", required: true },
137
+ email: { type: "string", required: true, description: "Email address (used as subscriber hash)" },
138
+ status: { type: "string", required: false },
139
+ mergeFields: { type: "object", required: false },
140
+ interests: { type: "object", required: false },
141
+ emailType: { type: "string", required: false },
142
+ language: { type: "string", required: false },
143
+ vip: { type: "boolean", required: false },
144
+ location: { type: "object", required: false },
145
+ ipSignup: { type: "string", required: false },
146
+ ipOpt: { type: "string", required: false },
147
+ timestampSignup: { type: "string", required: false },
148
+ timestampOpt: { type: "string", required: false },
149
+ skipMergeValidation: { type: "boolean", required: false },
150
+ },
151
+ async execute(input, ctx) {
152
+ const p = input;
153
+ const body = { email_address: p.email };
154
+ const qs = {};
155
+ if (p.status)
156
+ body.status = p.status;
157
+ if (p.mergeFields)
158
+ body.merge_fields = p.mergeFields;
159
+ if (p.interests)
160
+ body.interests = p.interests;
161
+ if (p.emailType)
162
+ body.email_type = p.emailType;
163
+ if (p.language)
164
+ body.language = p.language;
165
+ if (p.vip !== undefined)
166
+ body.vip = p.vip;
167
+ if (p.location)
168
+ body.location = p.location;
169
+ if (p.ipSignup)
170
+ body.ip_signup = p.ipSignup;
171
+ if (p.ipOpt)
172
+ body.ip_opt = p.ipOpt;
173
+ if (p.timestampSignup)
174
+ body.timestamp_signup = p.timestampSignup;
175
+ if (p.timestampOpt)
176
+ body.timestamp_opt = p.timestampOpt;
177
+ if (p.skipMergeValidation)
178
+ qs.skip_merge_validation = p.skipMergeValidation;
179
+ return apiRequest(key(ctx), "PUT", `/lists/${p.listId}/members/${p.email}`, body, Object.keys(qs).length > 0 ? qs : undefined);
180
+ },
181
+ });
182
+ rl.registerAction("member.delete", {
183
+ description: "Permanently delete a list member",
184
+ inputSchema: {
185
+ listId: { type: "string", required: true },
186
+ email: { type: "string", required: true, description: "Email address" },
187
+ },
188
+ async execute(input, ctx) {
189
+ const { listId, email } = input;
190
+ await apiRequest(key(ctx), "POST", `/lists/${listId}/members/${email}/actions/delete-permanent`);
191
+ return { success: true };
192
+ },
193
+ });
194
+ // ── Member Tag ──────────────────────────────────────
195
+ rl.registerAction("memberTag.add", {
196
+ description: "Add tags to a member",
197
+ inputSchema: {
198
+ listId: { type: "string", required: true },
199
+ email: { type: "string", required: true },
200
+ tags: { type: "array", required: true, description: "Array of tag names" },
201
+ isSyncing: { type: "boolean", required: false, description: "If true, automations based on tags won't fire" },
202
+ },
203
+ async execute(input, ctx) {
204
+ const { listId, email, tags, isSyncing } = input;
205
+ const body = { tags: tags.map((t) => ({ name: t, status: "active" })) };
206
+ if (isSyncing)
207
+ body.is_syncing = isSyncing;
208
+ await apiRequest(key(ctx), "POST", `/lists/${listId}/members/${email}/tags`, body);
209
+ return { success: true };
210
+ },
211
+ });
212
+ rl.registerAction("memberTag.remove", {
213
+ description: "Remove tags from a member",
214
+ inputSchema: {
215
+ listId: { type: "string", required: true },
216
+ email: { type: "string", required: true },
217
+ tags: { type: "array", required: true, description: "Array of tag names" },
218
+ isSyncing: { type: "boolean", required: false },
219
+ },
220
+ async execute(input, ctx) {
221
+ const { listId, email, tags, isSyncing } = input;
222
+ const body = { tags: tags.map((t) => ({ name: t, status: "inactive" })) };
223
+ if (isSyncing)
224
+ body.is_syncing = isSyncing;
225
+ await apiRequest(key(ctx), "POST", `/lists/${listId}/members/${email}/tags`, body);
226
+ return { success: true };
227
+ },
228
+ });
229
+ // ── List Group ──────────────────────────────────────
230
+ rl.registerAction("listGroup.list", {
231
+ description: "List interests in a specific interest category for a list",
232
+ inputSchema: {
233
+ listId: { type: "string", required: true },
234
+ categoryId: { type: "string", required: true, description: "Interest category ID" },
235
+ count: { type: "number", required: false, description: "Max results" },
236
+ },
237
+ async execute(input, ctx) {
238
+ const { listId, categoryId, count } = input;
239
+ const qs = {};
240
+ if (count)
241
+ qs.count = count;
242
+ const data = (await apiRequest(key(ctx), "GET", `/lists/${listId}/interest-categories/${categoryId}/interests`, undefined, qs));
243
+ return data.interests;
244
+ },
245
+ });
246
+ // ── Campaign ────────────────────────────────────────
247
+ rl.registerAction("campaign.get", {
248
+ description: "Get a campaign",
249
+ inputSchema: { campaignId: { type: "string", required: true } },
250
+ async execute(input, ctx) { return apiRequest(key(ctx), "GET", `/campaigns/${input.campaignId}`); },
251
+ });
252
+ rl.registerAction("campaign.list", {
253
+ description: "List campaigns",
254
+ inputSchema: {
255
+ count: { type: "number", required: false, description: "Max results" },
256
+ status: { type: "string", required: false, description: "save, sending, sent, schedule" },
257
+ listId: { type: "string", required: false, description: "Filter by list ID" },
258
+ fields: { type: "array", required: false, description: "Array of field names to return" },
259
+ sinceCreateTime: { type: "string", required: false, description: "ISO datetime" },
260
+ beforeCreateTime: { type: "string", required: false, description: "ISO datetime" },
261
+ sinceSendTime: { type: "string", required: false, description: "ISO datetime" },
262
+ beforeSendTime: { type: "string", required: false, description: "ISO datetime" },
263
+ sortField: { type: "string", required: false, description: "create_time or send_time" },
264
+ sortDirection: { type: "string", required: false, description: "ASC or DESC" },
265
+ },
266
+ async execute(input, ctx) {
267
+ const p = (input ?? {});
268
+ const qs = {};
269
+ if (p.count)
270
+ qs.count = p.count;
271
+ if (p.status)
272
+ qs.status = p.status;
273
+ if (p.listId)
274
+ qs.list_id = p.listId;
275
+ if (p.fields && Array.isArray(p.fields))
276
+ qs.fields = p.fields.join(",");
277
+ else
278
+ qs.fields = "campaigns.id,campaigns.status,campaigns.tracking,campaigns.settings.from_name,campaigns.settings.title,campaigns.settings.reply_to";
279
+ if (p.sinceCreateTime)
280
+ qs.since_create_time = p.sinceCreateTime;
281
+ if (p.beforeCreateTime)
282
+ qs.before_create_time = p.beforeCreateTime;
283
+ if (p.sinceSendTime)
284
+ qs.since_send_time = p.sinceSendTime;
285
+ if (p.beforeSendTime)
286
+ qs.before_send_time = p.beforeSendTime;
287
+ if (p.sortField)
288
+ qs.sort_field = p.sortField;
289
+ if (p.sortDirection)
290
+ qs.sort_dir = p.sortDirection;
291
+ const data = (await apiRequest(key(ctx), "GET", "/campaigns", undefined, qs));
292
+ return data.campaigns;
293
+ },
294
+ });
295
+ rl.registerAction("campaign.send", {
296
+ description: "Send a campaign",
297
+ inputSchema: { campaignId: { type: "string", required: true } },
298
+ async execute(input, ctx) {
299
+ await apiRequest(key(ctx), "POST", `/campaigns/${input.campaignId}/actions/send`);
300
+ return { success: true };
301
+ },
302
+ });
303
+ rl.registerAction("campaign.replicate", {
304
+ description: "Replicate a campaign",
305
+ inputSchema: { campaignId: { type: "string", required: true } },
306
+ async execute(input, ctx) { return apiRequest(key(ctx), "POST", `/campaigns/${input.campaignId}/actions/replicate`); },
307
+ });
308
+ rl.registerAction("campaign.resend", {
309
+ description: "Create a resend to non-openers",
310
+ inputSchema: { campaignId: { type: "string", required: true } },
311
+ async execute(input, ctx) { return apiRequest(key(ctx), "POST", `/campaigns/${input.campaignId}/actions/create-resend`); },
312
+ });
313
+ rl.registerAction("campaign.delete", {
314
+ description: "Delete a campaign",
315
+ inputSchema: { campaignId: { type: "string", required: true } },
316
+ async execute(input, ctx) {
317
+ await apiRequest(key(ctx), "DELETE", `/campaigns/${input.campaignId}`);
318
+ return { success: true };
319
+ },
320
+ });
321
+ }
@@ -0,0 +1,123 @@
1
+ const BASE_URL = "https://connect.mailerlite.com/api";
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 = {
11
+ method,
12
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
13
+ };
14
+ if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE")
15
+ opts.body = JSON.stringify(body);
16
+ const res = await fetch(url.toString(), opts);
17
+ if (!res.ok)
18
+ throw new Error(`MailerLite API error ${res.status}: ${await res.text()}`);
19
+ return res.json();
20
+ }
21
+ async function paginateAll(token, method, endpoint, qs = {}) {
22
+ const all = [];
23
+ qs.limit = 1000;
24
+ let cursor = null;
25
+ do {
26
+ if (cursor)
27
+ qs.cursor = cursor;
28
+ const resp = (await apiRequest(token, method, endpoint, undefined, qs));
29
+ const data = resp.data;
30
+ if (data)
31
+ all.push(...data);
32
+ const meta = resp.meta;
33
+ const links = resp.links;
34
+ cursor = meta?.next_cursor ?? null;
35
+ if (!links?.next)
36
+ cursor = null;
37
+ } while (cursor);
38
+ return all;
39
+ }
40
+ export default function mailerlite(rl) {
41
+ rl.setName("mailerlite");
42
+ rl.setVersion("0.1.0");
43
+ rl.setConnectionSchema({
44
+ apiKey: { type: "string", required: true, description: "MailerLite API token", env: "MAILERLITE_API_KEY" },
45
+ });
46
+ const tok = (ctx) => ctx.connection.config.apiKey;
47
+ rl.registerAction("subscriber.create", {
48
+ description: "Create a subscriber",
49
+ inputSchema: {
50
+ email: { type: "string", required: true },
51
+ fields: { type: "object", required: false, description: "Custom fields as {field_key: value}" },
52
+ groups: { type: "array", required: false, description: "Array of group IDs" },
53
+ status: { type: "string", required: false, description: "active, unsubscribed, unconfirmed, bounced, junk" },
54
+ subscribed_at: { type: "string", required: false, description: "ISO datetime" },
55
+ ip_address: { type: "string", required: false },
56
+ opted_in_at: { type: "string", required: false },
57
+ optin_ip: { type: "string", required: false },
58
+ },
59
+ async execute(input, ctx) {
60
+ const { email, fields, ...rest } = input;
61
+ const body = { email };
62
+ if (fields)
63
+ body.fields = fields;
64
+ for (const [k, v] of Object.entries(rest)) {
65
+ if (v !== undefined && v !== null)
66
+ body[k] = v;
67
+ }
68
+ const resp = (await apiRequest(tok(ctx), "POST", "/subscribers", body));
69
+ return resp.data;
70
+ },
71
+ });
72
+ rl.registerAction("subscriber.get", {
73
+ description: "Get a subscriber by ID or email",
74
+ inputSchema: { subscriberId: { type: "string", required: true, description: "Subscriber ID or email" } },
75
+ async execute(input, ctx) {
76
+ const resp = (await apiRequest(tok(ctx), "GET", `/subscribers/${encodeURIComponent(input.subscriberId)}`));
77
+ return resp.data;
78
+ },
79
+ });
80
+ rl.registerAction("subscriber.list", {
81
+ description: "List subscribers",
82
+ inputSchema: {
83
+ limit: { type: "number", required: false },
84
+ status: { type: "string", required: false, description: "Filter by status: active, unsubscribed, unconfirmed, bounced, junk" },
85
+ },
86
+ async execute(input, ctx) {
87
+ const { limit, status } = (input ?? {});
88
+ const qs = {};
89
+ if (status)
90
+ qs["filter[status]"] = status;
91
+ if (limit) {
92
+ qs.limit = limit;
93
+ const resp = (await apiRequest(tok(ctx), "GET", "/subscribers", undefined, qs));
94
+ return resp.data;
95
+ }
96
+ return paginateAll(tok(ctx), "GET", "/subscribers", qs);
97
+ },
98
+ });
99
+ rl.registerAction("subscriber.update", {
100
+ description: "Update a subscriber",
101
+ inputSchema: {
102
+ subscriberId: { type: "string", required: true, description: "Subscriber ID or email" },
103
+ fields: { type: "object", required: false, description: "Custom fields as {field_key: value}" },
104
+ groups: { type: "array", required: false, description: "Array of group IDs" },
105
+ status: { type: "string", required: false },
106
+ subscribed_at: { type: "string", required: false },
107
+ ip_address: { type: "string", required: false },
108
+ opted_in_at: { type: "string", required: false },
109
+ optin_ip: { type: "string", required: false },
110
+ },
111
+ async execute(input, ctx) {
112
+ const { subscriberId, fields, ...rest } = input;
113
+ const body = {};
114
+ if (fields)
115
+ body.fields = fields;
116
+ for (const [k, v] of Object.entries(rest)) {
117
+ if (v !== undefined && v !== null && k !== "subscriberId")
118
+ body[k] = v;
119
+ }
120
+ return apiRequest(tok(ctx), "PUT", `/subscribers/${encodeURIComponent(subscriberId)}`, body);
121
+ },
122
+ });
123
+ }
@@ -0,0 +1,48 @@
1
+ export default function mailgun(rl) {
2
+ rl.setName("mailgun");
3
+ rl.setVersion("0.1.0");
4
+ rl.setConnectionSchema({
5
+ apiKey: { type: "string", required: true, description: "Mailgun API key", env: "MAILGUN_API_KEY" },
6
+ apiDomain: { type: "string", required: false, description: "API domain: 'api.mailgun.net' (US, default) or 'api.eu.mailgun.net' (EU)", default: "api.mailgun.net", env: "MAILGUN_API_DOMAIN" },
7
+ emailDomain: { type: "string", required: true, description: "Sending domain (e.g. mg.example.com)", env: "MAILGUN_EMAIL_DOMAIN" },
8
+ });
9
+ rl.registerAction("email.send", {
10
+ description: "Send an email via Mailgun",
11
+ inputSchema: {
12
+ to: { type: "string", required: true, description: "Recipient email(s), comma-separated" },
13
+ from: { type: "string", required: true, description: "Sender (e.g. 'Name <user@domain.com>')" },
14
+ subject: { type: "string", required: true },
15
+ text: { type: "string", required: false, description: "Plain text body" },
16
+ html: { type: "string", required: false, description: "HTML body" },
17
+ cc: { type: "string", required: false, description: "CC recipients, comma-separated" },
18
+ bcc: { type: "string", required: false, description: "BCC recipients, comma-separated" },
19
+ },
20
+ async execute(input, ctx) {
21
+ const { to, from, subject, text, html, cc, bcc } = input;
22
+ const cfg = ctx.connection.config;
23
+ const apiKey = cfg.apiKey;
24
+ const apiDomain = cfg.apiDomain ?? "api.mailgun.net";
25
+ const emailDomain = cfg.emailDomain;
26
+ const form = new URLSearchParams();
27
+ form.set("to", to);
28
+ form.set("from", from);
29
+ form.set("subject", subject);
30
+ if (text)
31
+ form.set("text", text);
32
+ if (html)
33
+ form.set("html", html);
34
+ if (cc)
35
+ form.set("cc", cc);
36
+ if (bcc)
37
+ form.set("bcc", bcc);
38
+ const res = await fetch(`https://${apiDomain}/v3/${emailDomain}/messages`, {
39
+ method: "POST",
40
+ headers: { Authorization: `Basic ${btoa(`api:${apiKey}`)}` },
41
+ body: form,
42
+ });
43
+ if (!res.ok)
44
+ throw new Error(`Mailgun error ${res.status}: ${await res.text()}`);
45
+ return res.json();
46
+ },
47
+ });
48
+ }
@@ -0,0 +1,155 @@
1
+ const BASE_URL = "https://api.mailjet.com";
2
+ export default function mailjet(rl) {
3
+ rl.setName("mailjet");
4
+ rl.setVersion("0.1.0");
5
+ rl.setConnectionSchema({
6
+ apiKeyPublic: { type: "string", required: true, description: "Mailjet API key (public)", env: "MAILJET_API_KEY" },
7
+ apiKeyPrivate: { type: "string", required: true, description: "Mailjet secret key (private)", env: "MAILJET_SECRET_KEY" },
8
+ sandboxMode: { type: "boolean", required: false, description: "Enable sandbox mode (emails not actually sent)", default: false },
9
+ smsToken: { type: "string", required: false, description: "Mailjet SMS API token (if using SMS)", env: "MAILJET_SMS_TOKEN" },
10
+ });
11
+ const emailAuth = (ctx) => `Basic ${btoa(`${ctx.connection.config.apiKeyPublic}:${ctx.connection.config.apiKeyPrivate}`)}`;
12
+ const sandbox = (ctx) => ctx.connection.config.sandboxMode ?? false;
13
+ rl.registerAction("email.send", {
14
+ description: "Send an email via Mailjet Send API v3.1",
15
+ inputSchema: {
16
+ fromEmail: { type: "string", required: true },
17
+ fromName: { type: "string", required: false },
18
+ toEmail: { type: "string", required: true, description: "Comma-separated recipient emails" },
19
+ subject: { type: "string", required: true },
20
+ htmlPart: { type: "string", required: false, description: "HTML body" },
21
+ textPart: { type: "string", required: false, description: "Plain text body" },
22
+ cc: { type: "string", required: false, description: "Comma-separated CC emails" },
23
+ bcc: { type: "string", required: false, description: "Comma-separated BCC emails" },
24
+ replyTo: { type: "string", required: false, description: "Reply-to email" },
25
+ variables: { type: "object", required: false, description: "Template variables as key-value pairs" },
26
+ trackOpens: { type: "string", required: false, description: "account_default, disabled, enabled" },
27
+ trackClicks: { type: "string", required: false, description: "account_default, disabled, enabled" },
28
+ templateLanguage: { type: "boolean", required: false, description: "Enable template language in body" },
29
+ priority: { type: "number", required: false, description: "1-4, lower is higher priority" },
30
+ customCampaign: { type: "string", required: false },
31
+ deduplicateCampaign: { type: "boolean", required: false },
32
+ },
33
+ async execute(input, ctx) {
34
+ const p = input;
35
+ const message = {
36
+ From: { Email: p.fromEmail, ...(p.fromName ? { Name: p.fromName } : {}) },
37
+ Subject: p.subject,
38
+ To: p.toEmail.split(",").map((e) => ({ Email: e.trim() })),
39
+ };
40
+ if (p.htmlPart)
41
+ message.HTMLPart = p.htmlPart;
42
+ if (p.textPart)
43
+ message.TextPart = p.textPart;
44
+ if (p.cc)
45
+ message.Cc = p.cc.split(",").map((e) => ({ Email: e.trim() }));
46
+ if (p.bcc)
47
+ message.Bcc = p.bcc.split(",").map((e) => ({ Email: e.trim() }));
48
+ if (p.replyTo)
49
+ message.ReplyTo = { Email: p.replyTo };
50
+ if (p.variables)
51
+ message.Variables = p.variables;
52
+ if (p.trackOpens)
53
+ message.TrackOpens = p.trackOpens;
54
+ if (p.trackClicks)
55
+ message.TrackClicks = p.trackClicks;
56
+ if (p.templateLanguage !== undefined)
57
+ message.TemplateLanguage = p.templateLanguage;
58
+ if (p.priority)
59
+ message.Priority = p.priority;
60
+ if (p.customCampaign)
61
+ message.CustomCampaign = p.customCampaign;
62
+ if (p.deduplicateCampaign !== undefined)
63
+ message.DeduplicateCampaign = p.deduplicateCampaign;
64
+ const res = await fetch(`${BASE_URL}/v3.1/send`, {
65
+ method: "POST",
66
+ headers: { Authorization: emailAuth(ctx), "Content-Type": "application/json", Accept: "application/json" },
67
+ body: JSON.stringify({ Messages: [message], SandboxMode: sandbox(ctx) }),
68
+ });
69
+ if (!res.ok)
70
+ throw new Error(`Mailjet API error ${res.status}: ${await res.text()}`);
71
+ const data = (await res.json());
72
+ return data.Messages;
73
+ },
74
+ });
75
+ rl.registerAction("email.sendTemplate", {
76
+ description: "Send an email using a Mailjet template",
77
+ inputSchema: {
78
+ fromEmail: { type: "string", required: true },
79
+ fromName: { type: "string", required: false },
80
+ toEmail: { type: "string", required: true, description: "Comma-separated recipient emails" },
81
+ subject: { type: "string", required: true },
82
+ templateId: { type: "number", required: true, description: "Mailjet template ID" },
83
+ variables: { type: "object", required: false, description: "Template variables" },
84
+ cc: { type: "string", required: false },
85
+ bcc: { type: "string", required: false },
86
+ replyTo: { type: "string", required: false },
87
+ trackOpens: { type: "string", required: false },
88
+ trackClicks: { type: "string", required: false },
89
+ templateLanguage: { type: "boolean", required: false },
90
+ priority: { type: "number", required: false },
91
+ customCampaign: { type: "string", required: false },
92
+ deduplicateCampaign: { type: "boolean", required: false },
93
+ },
94
+ async execute(input, ctx) {
95
+ const p = input;
96
+ const message = {
97
+ From: { Email: p.fromEmail, ...(p.fromName ? { Name: p.fromName } : {}) },
98
+ Subject: p.subject,
99
+ To: p.toEmail.split(",").map((e) => ({ Email: e.trim() })),
100
+ TemplateID: p.templateId,
101
+ };
102
+ if (p.variables)
103
+ message.Variables = p.variables;
104
+ if (p.cc)
105
+ message.Cc = p.cc.split(",").map((e) => ({ Email: e.trim() }));
106
+ if (p.bcc)
107
+ message.Bcc = p.bcc.split(",").map((e) => ({ Email: e.trim() }));
108
+ if (p.replyTo)
109
+ message.ReplyTo = { Email: p.replyTo };
110
+ if (p.trackOpens)
111
+ message.TrackOpens = p.trackOpens;
112
+ if (p.trackClicks)
113
+ message.TrackClicks = p.trackClicks;
114
+ if (p.templateLanguage !== undefined)
115
+ message.TemplateLanguage = p.templateLanguage;
116
+ if (p.priority)
117
+ message.Priority = p.priority;
118
+ if (p.customCampaign)
119
+ message.CustomCampaign = p.customCampaign;
120
+ if (p.deduplicateCampaign !== undefined)
121
+ message.DeduplicateCampaign = p.deduplicateCampaign;
122
+ const res = await fetch(`${BASE_URL}/v3.1/send`, {
123
+ method: "POST",
124
+ headers: { Authorization: emailAuth(ctx), "Content-Type": "application/json", Accept: "application/json" },
125
+ body: JSON.stringify({ Messages: [message], SandboxMode: sandbox(ctx) }),
126
+ });
127
+ if (!res.ok)
128
+ throw new Error(`Mailjet API error ${res.status}: ${await res.text()}`);
129
+ const data = (await res.json());
130
+ return data.Messages;
131
+ },
132
+ });
133
+ rl.registerAction("sms.send", {
134
+ description: "Send an SMS via Mailjet SMS API",
135
+ inputSchema: {
136
+ from: { type: "string", required: true, description: "Sender name or number" },
137
+ to: { type: "string", required: true, description: "Recipient phone number (international format)" },
138
+ text: { type: "string", required: true, description: "SMS message text" },
139
+ },
140
+ async execute(input, ctx) {
141
+ const { from, to, text } = input;
142
+ const smsToken = ctx.connection.config.smsToken;
143
+ if (!smsToken)
144
+ throw new Error("SMS token not configured — set smsToken in connection config");
145
+ const res = await fetch(`${BASE_URL}/v4/sms-send`, {
146
+ method: "POST",
147
+ headers: { Authorization: `Bearer ${smsToken}`, "Content-Type": "application/json" },
148
+ body: JSON.stringify({ From: from, To: to, Text: text }),
149
+ });
150
+ if (!res.ok)
151
+ throw new Error(`Mailjet SMS error ${res.status}: ${await res.text()}`);
152
+ return res.json();
153
+ },
154
+ });
155
+ }