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,281 @@
1
+ async function apiRequest(baseUrl, token, method, endpoint, body, 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 opts = {
10
+ method,
11
+ headers: { Authorization: `Token ${token}`, Accept: "application/json", "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(`KoBoToolbox API error ${res.status}: ${await res.text()}`);
18
+ if (res.status === 204)
19
+ return { success: true };
20
+ return res.json();
21
+ }
22
+ async function paginate(baseUrl, token, endpoint, qs = {}) {
23
+ const all = [];
24
+ qs.limit = 3000;
25
+ let nextUrl = `${baseUrl}${endpoint}`;
26
+ const initialUrl = new URL(nextUrl);
27
+ for (const [k, v] of Object.entries(qs)) {
28
+ if (v !== undefined && v !== null)
29
+ initialUrl.searchParams.set(k, String(v));
30
+ }
31
+ nextUrl = initialUrl.toString();
32
+ while (nextUrl) {
33
+ const res = await fetch(nextUrl, {
34
+ headers: { Authorization: `Token ${token}`, Accept: "application/json" },
35
+ });
36
+ if (!res.ok)
37
+ throw new Error(`KoBoToolbox API error ${res.status}: ${await res.text()}`);
38
+ const data = (await res.json());
39
+ if (data.results && Array.isArray(data.results)) {
40
+ all.push(...data.results);
41
+ nextUrl = data.next ?? null;
42
+ }
43
+ else {
44
+ // Non-paginated response
45
+ return Array.isArray(data) ? data : [data];
46
+ }
47
+ }
48
+ return all;
49
+ }
50
+ export default function kobotoolbox(rl) {
51
+ rl.setName("kobotoolbox");
52
+ rl.setVersion("0.1.0");
53
+ rl.setConnectionSchema({
54
+ url: { type: "string", required: true, description: "KoBoToolbox server URL (e.g. https://kf.kobotoolbox.org)", env: "KOBOTOOLBOX_URL" },
55
+ token: { type: "string", required: true, description: "API token", env: "KOBOTOOLBOX_TOKEN" },
56
+ });
57
+ const conn = (ctx) => ({
58
+ baseUrl: ctx.connection.config.url.replace(/\/$/, ""),
59
+ token: ctx.connection.config.token,
60
+ });
61
+ // ── Form ────────────────────────────────────────────
62
+ rl.registerAction("form.get", {
63
+ description: "Get a form (asset) by ID",
64
+ inputSchema: { formId: { type: "string", required: true, description: "Form/asset UID" } },
65
+ async execute(input, ctx) {
66
+ const { baseUrl, token } = conn(ctx);
67
+ return apiRequest(baseUrl, token, "GET", `/api/v2/assets/${input.formId}`);
68
+ },
69
+ });
70
+ rl.registerAction("form.list", {
71
+ description: "List all forms/assets",
72
+ inputSchema: {
73
+ limit: { type: "number", required: false },
74
+ filter: { type: "string", required: false, description: "Search query (q parameter)" },
75
+ ordering: { type: "string", required: false, description: "Field to sort by" },
76
+ descending: { type: "boolean", required: false, description: "Sort descending" },
77
+ },
78
+ async execute(input, ctx) {
79
+ const { baseUrl, token } = conn(ctx);
80
+ const p = (input ?? {});
81
+ const qs = {};
82
+ if (p.filter)
83
+ qs.q = p.filter;
84
+ if (p.ordering)
85
+ qs.ordering = (p.descending ? "-" : "") + p.ordering;
86
+ if (p.limit) {
87
+ qs.limit = p.limit;
88
+ return apiRequest(baseUrl, token, "GET", "/api/v2/assets/", undefined, qs);
89
+ }
90
+ return paginate(baseUrl, token, "/api/v2/assets/", qs);
91
+ },
92
+ });
93
+ rl.registerAction("form.redeploy", {
94
+ description: "Redeploy a form",
95
+ inputSchema: { formId: { type: "string", required: true } },
96
+ async execute(input, ctx) {
97
+ const { baseUrl, token } = conn(ctx);
98
+ return apiRequest(baseUrl, token, "PATCH", `/api/v2/assets/${input.formId}/deployment/`);
99
+ },
100
+ });
101
+ // ── Submission ──────────────────────────────────────
102
+ rl.registerAction("submission.get", {
103
+ description: "Get a submission by ID",
104
+ inputSchema: {
105
+ formId: { type: "string", required: true },
106
+ submissionId: { type: "string", required: true },
107
+ fields: { type: "array", required: false, description: "Fields to include" },
108
+ },
109
+ async execute(input, ctx) {
110
+ const { formId, submissionId, fields } = input;
111
+ const { baseUrl, token } = conn(ctx);
112
+ const qs = {};
113
+ if (fields && Array.isArray(fields))
114
+ qs.fields = JSON.stringify(fields);
115
+ return apiRequest(baseUrl, token, "GET", `/api/v2/assets/${formId}/data/${submissionId}`, undefined, qs);
116
+ },
117
+ });
118
+ rl.registerAction("submission.list", {
119
+ description: "List submissions for a form",
120
+ inputSchema: {
121
+ formId: { type: "string", required: true },
122
+ limit: { type: "number", required: false },
123
+ query: { type: "string", required: false, description: "JSON filter query" },
124
+ sort: { type: "string", required: false, description: "Sort JSON" },
125
+ fields: { type: "array", required: false, description: "Fields to include" },
126
+ },
127
+ async execute(input, ctx) {
128
+ const p = input;
129
+ const { baseUrl, token } = conn(ctx);
130
+ const qs = {};
131
+ if (p.query)
132
+ qs.query = p.query;
133
+ if (p.sort)
134
+ qs.sort = p.sort;
135
+ if (p.fields && Array.isArray(p.fields))
136
+ qs.fields = JSON.stringify(p.fields);
137
+ if (p.limit) {
138
+ qs.limit = p.limit;
139
+ const data = await apiRequest(baseUrl, token, "GET", `/api/v2/assets/${p.formId}/data/`, undefined, qs);
140
+ return data.results ?? data;
141
+ }
142
+ return paginate(baseUrl, token, `/api/v2/assets/${p.formId}/data/`, qs);
143
+ },
144
+ });
145
+ rl.registerAction("submission.delete", {
146
+ description: "Delete a submission",
147
+ inputSchema: { formId: { type: "string", required: true }, submissionId: { type: "string", required: true } },
148
+ async execute(input, ctx) {
149
+ const { formId, submissionId } = input;
150
+ const { baseUrl, token } = conn(ctx);
151
+ await apiRequest(baseUrl, token, "DELETE", `/api/v2/assets/${formId}/data/${submissionId}`);
152
+ return { success: true };
153
+ },
154
+ });
155
+ rl.registerAction("submission.getValidation", {
156
+ description: "Get the validation status of a submission",
157
+ inputSchema: { formId: { type: "string", required: true }, submissionId: { type: "string", required: true } },
158
+ async execute(input, ctx) {
159
+ const { formId, submissionId } = input;
160
+ const { baseUrl, token } = conn(ctx);
161
+ return apiRequest(baseUrl, token, "GET", `/api/v2/assets/${formId}/data/${submissionId}/validation_status/`);
162
+ },
163
+ });
164
+ rl.registerAction("submission.setValidation", {
165
+ description: "Set the validation status of a submission",
166
+ inputSchema: {
167
+ formId: { type: "string", required: true },
168
+ submissionId: { type: "string", required: true },
169
+ validationStatus: { type: "string", required: true, description: "validation_status_not_approved, validation_status_approved, validation_status_on_hold" },
170
+ },
171
+ async execute(input, ctx) {
172
+ const { formId, submissionId, validationStatus } = input;
173
+ const { baseUrl, token } = conn(ctx);
174
+ return apiRequest(baseUrl, token, "PATCH", `/api/v2/assets/${formId}/data/${submissionId}/validation_status/`, { "validation_status.uid": validationStatus });
175
+ },
176
+ });
177
+ // ── Hook ────────────────────────────────────────────
178
+ rl.registerAction("hook.get", {
179
+ description: "Get a hook by ID",
180
+ inputSchema: { formId: { type: "string", required: true }, hookId: { type: "string", required: true } },
181
+ async execute(input, ctx) {
182
+ const { formId, hookId } = input;
183
+ const { baseUrl, token } = conn(ctx);
184
+ return apiRequest(baseUrl, token, "GET", `/api/v2/assets/${formId}/hooks/${hookId}`);
185
+ },
186
+ });
187
+ rl.registerAction("hook.list", {
188
+ description: "List hooks for a form",
189
+ inputSchema: { formId: { type: "string", required: true }, limit: { type: "number", required: false } },
190
+ async execute(input, ctx) {
191
+ const { formId, limit } = input;
192
+ const { baseUrl, token } = conn(ctx);
193
+ if (limit)
194
+ return apiRequest(baseUrl, token, "GET", `/api/v2/assets/${formId}/hooks/`, undefined, { limit });
195
+ return paginate(baseUrl, token, `/api/v2/assets/${formId}/hooks/`);
196
+ },
197
+ });
198
+ rl.registerAction("hook.retryAll", {
199
+ description: "Retry all failed attempts for a hook",
200
+ inputSchema: { formId: { type: "string", required: true }, hookId: { type: "string", required: true } },
201
+ async execute(input, ctx) {
202
+ const { formId, hookId } = input;
203
+ const { baseUrl, token } = conn(ctx);
204
+ return apiRequest(baseUrl, token, "PATCH", `/api/v2/assets/${formId}/hooks/${hookId}/retry/`);
205
+ },
206
+ });
207
+ rl.registerAction("hook.getLogs", {
208
+ description: "Get logs for a hook",
209
+ inputSchema: {
210
+ formId: { type: "string", required: true },
211
+ hookId: { type: "string", required: true },
212
+ startDate: { type: "string", required: false, description: "Start date filter" },
213
+ endDate: { type: "string", required: false, description: "End date filter" },
214
+ status: { type: "number", required: false, description: "HTTP status code filter" },
215
+ },
216
+ async execute(input, ctx) {
217
+ const { formId, hookId, startDate, endDate, status } = input;
218
+ const { baseUrl, token } = conn(ctx);
219
+ const qs = {};
220
+ if (startDate)
221
+ qs.start = startDate;
222
+ if (endDate)
223
+ qs.end = endDate;
224
+ if (status)
225
+ qs.status = status;
226
+ return apiRequest(baseUrl, token, "GET", `/api/v2/assets/${formId}/hooks/${hookId}/logs/`, undefined, qs);
227
+ },
228
+ });
229
+ rl.registerAction("hook.retryOne", {
230
+ description: "Retry a single failed hook log entry",
231
+ inputSchema: { formId: { type: "string", required: true }, hookId: { type: "string", required: true }, logId: { type: "string", required: true } },
232
+ async execute(input, ctx) {
233
+ const { formId, hookId, logId } = input;
234
+ const { baseUrl, token } = conn(ctx);
235
+ return apiRequest(baseUrl, token, "PATCH", `/api/v2/assets/${formId}/hooks/${hookId}/logs/${logId}/retry/`);
236
+ },
237
+ });
238
+ // ── File ────────────────────────────────────────────
239
+ rl.registerAction("file.list", {
240
+ description: "List media files for a form",
241
+ inputSchema: { formId: { type: "string", required: true } },
242
+ async execute(input, ctx) {
243
+ const { baseUrl, token } = conn(ctx);
244
+ return paginate(baseUrl, token, `/api/v2/assets/${input.formId}/files`, { file_type: "form_media" });
245
+ },
246
+ });
247
+ rl.registerAction("file.get", {
248
+ description: "Get a file's metadata",
249
+ inputSchema: { formId: { type: "string", required: true }, fileId: { type: "string", required: true } },
250
+ async execute(input, ctx) {
251
+ const { formId, fileId } = input;
252
+ const { baseUrl, token } = conn(ctx);
253
+ return apiRequest(baseUrl, token, "GET", `/api/v2/assets/${formId}/files/${fileId}`);
254
+ },
255
+ });
256
+ rl.registerAction("file.delete", {
257
+ description: "Delete a file",
258
+ inputSchema: { formId: { type: "string", required: true }, fileId: { type: "string", required: true } },
259
+ async execute(input, ctx) {
260
+ const { formId, fileId } = input;
261
+ const { baseUrl, token } = conn(ctx);
262
+ return apiRequest(baseUrl, token, "DELETE", `/api/v2/assets/${formId}/files/${fileId}`);
263
+ },
264
+ });
265
+ rl.registerAction("file.createFromUrl", {
266
+ description: "Create a file from a URL (redirect-based media)",
267
+ inputSchema: {
268
+ formId: { type: "string", required: true },
269
+ redirectUrl: { type: "string", required: true, description: "URL of the file" },
270
+ },
271
+ async execute(input, ctx) {
272
+ const { formId, redirectUrl } = input;
273
+ const { baseUrl, token } = conn(ctx);
274
+ return apiRequest(baseUrl, token, "POST", `/api/v2/assets/${formId}/files/`, {
275
+ description: "Uploaded file",
276
+ file_type: "form_media",
277
+ metadata: { redirect_url: redirectUrl },
278
+ });
279
+ },
280
+ });
281
+ }
@@ -0,0 +1,231 @@
1
+ const BASE_URL = "https://api.lemlist.com/api";
2
+ async function apiRequest(apiKey, 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: `Basic ${btoa(`:${apiKey}`)}`, "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(`Lemlist API error ${res.status}: ${await res.text()}`);
19
+ if (res.status === 204)
20
+ return { success: true };
21
+ return res.json();
22
+ }
23
+ async function paginate(apiKey, method, endpoint, qs = {}) {
24
+ const all = [];
25
+ qs.limit = 100;
26
+ qs.offset = 0;
27
+ let data;
28
+ do {
29
+ data = (await apiRequest(apiKey, method, endpoint, undefined, qs));
30
+ all.push(...data);
31
+ qs.offset += qs.limit;
32
+ } while (data.length > 0);
33
+ return all;
34
+ }
35
+ export default function lemlist(rl) {
36
+ rl.setName("lemlist");
37
+ rl.setVersion("0.1.0");
38
+ rl.setConnectionSchema({
39
+ apiKey: { type: "string", required: true, description: "Lemlist API key", env: "LEMLIST_API_KEY" },
40
+ });
41
+ const key = (ctx) => ctx.connection.config.apiKey;
42
+ // ── Activity ────────────────────────────────────────
43
+ rl.registerAction("activity.list", {
44
+ description: "List activities",
45
+ inputSchema: {
46
+ limit: { type: "number", required: false, description: "Max results" },
47
+ campaignId: { type: "string", required: false, description: "Filter by campaign ID" },
48
+ type: { type: "string", required: false, description: "Filter by activity type" },
49
+ isFirst: { type: "boolean", required: false, description: "Filter first activities only" },
50
+ },
51
+ async execute(input, ctx) {
52
+ const p = (input ?? {});
53
+ const qs = {};
54
+ if (p.campaignId)
55
+ qs.campaignId = p.campaignId;
56
+ if (p.type)
57
+ qs.type = p.type;
58
+ if (p.isFirst !== undefined)
59
+ qs.isFirst = p.isFirst;
60
+ if (p.limit) {
61
+ qs.limit = p.limit;
62
+ return apiRequest(key(ctx), "GET", "/activities", undefined, qs);
63
+ }
64
+ return paginate(key(ctx), "GET", "/activities", qs);
65
+ },
66
+ });
67
+ // ── Campaign ────────────────────────────────────────
68
+ rl.registerAction("campaign.list", {
69
+ description: "List campaigns",
70
+ inputSchema: {
71
+ limit: { type: "number", required: false },
72
+ },
73
+ async execute(input, ctx) {
74
+ const p = (input ?? {});
75
+ if (p.limit)
76
+ return apiRequest(key(ctx), "GET", "/campaigns", undefined, { limit: p.limit });
77
+ return paginate(key(ctx), "GET", "/campaigns");
78
+ },
79
+ });
80
+ rl.registerAction("campaign.getStats", {
81
+ description: "Get campaign statistics",
82
+ inputSchema: {
83
+ campaignId: { type: "string", required: true },
84
+ startDate: { type: "string", required: true, description: "Start date (YYYY-MM-DD)" },
85
+ endDate: { type: "string", required: true, description: "End date (YYYY-MM-DD)" },
86
+ timezone: { type: "string", required: true, description: "Timezone (e.g. America/New_York)" },
87
+ },
88
+ async execute(input, ctx) {
89
+ const { campaignId, startDate, endDate, timezone } = input;
90
+ return apiRequest(key(ctx), "GET", `/campaigns/${campaignId}/stats`, undefined, { startDate, endDate, timezone });
91
+ },
92
+ });
93
+ // ── Lead ────────────────────────────────────────────
94
+ rl.registerAction("lead.create", {
95
+ description: "Add a lead to a campaign",
96
+ inputSchema: {
97
+ campaignId: { type: "string", required: true },
98
+ email: { type: "string", required: true },
99
+ deduplicate: { type: "boolean", required: false, description: "Deduplicate by email" },
100
+ firstName: { type: "string", required: false },
101
+ lastName: { type: "string", required: false },
102
+ companyName: { type: "string", required: false },
103
+ additionalFields: { type: "object", required: false, description: "Any extra fields" },
104
+ },
105
+ async execute(input, ctx) {
106
+ const { campaignId, email, deduplicate, firstName, lastName, companyName, additionalFields } = input;
107
+ const body = {};
108
+ if (firstName)
109
+ body.firstName = firstName;
110
+ if (lastName)
111
+ body.lastName = lastName;
112
+ if (companyName)
113
+ body.companyName = companyName;
114
+ if (additionalFields)
115
+ Object.assign(body, additionalFields);
116
+ const qs = {};
117
+ if (deduplicate !== undefined)
118
+ qs.deduplicate = deduplicate;
119
+ return apiRequest(key(ctx), "POST", `/campaigns/${campaignId}/leads/${encodeURIComponent(email)}`, body, Object.keys(qs).length > 0 ? qs : undefined);
120
+ },
121
+ });
122
+ rl.registerAction("lead.get", {
123
+ description: "Get a lead by email",
124
+ inputSchema: { email: { type: "string", required: true } },
125
+ async execute(input, ctx) {
126
+ return apiRequest(key(ctx), "GET", `/leads/${encodeURIComponent(input.email)}`);
127
+ },
128
+ });
129
+ rl.registerAction("lead.delete", {
130
+ description: "Remove a lead from a campaign (keeps lead in unsubscribe list)",
131
+ inputSchema: {
132
+ campaignId: { type: "string", required: true },
133
+ email: { type: "string", required: true },
134
+ },
135
+ async execute(input, ctx) {
136
+ const { campaignId, email } = input;
137
+ return apiRequest(key(ctx), "DELETE", `/campaigns/${campaignId}/leads/${encodeURIComponent(email)}`, undefined, { action: "remove" });
138
+ },
139
+ });
140
+ rl.registerAction("lead.unsubscribe", {
141
+ description: "Unsubscribe a lead from a campaign",
142
+ inputSchema: {
143
+ campaignId: { type: "string", required: true },
144
+ email: { type: "string", required: true },
145
+ },
146
+ async execute(input, ctx) {
147
+ const { campaignId, email } = input;
148
+ return apiRequest(key(ctx), "DELETE", `/campaigns/${campaignId}/leads/${encodeURIComponent(email)}`);
149
+ },
150
+ });
151
+ // ── Team ────────────────────────────────────────────
152
+ rl.registerAction("team.get", {
153
+ description: "Get team information",
154
+ async execute(_input, ctx) { return apiRequest(key(ctx), "GET", "/team"); },
155
+ });
156
+ rl.registerAction("team.getCredits", {
157
+ description: "Get team credits",
158
+ async execute(_input, ctx) { return apiRequest(key(ctx), "GET", "/team/credits"); },
159
+ });
160
+ // ── Unsubscribe ─────────────────────────────────────
161
+ rl.registerAction("unsubscribe.add", {
162
+ description: "Add an email to the unsubscribe list",
163
+ inputSchema: { email: { type: "string", required: true } },
164
+ async execute(input, ctx) {
165
+ return apiRequest(key(ctx), "POST", `/unsubscribes/${encodeURIComponent(input.email)}`);
166
+ },
167
+ });
168
+ rl.registerAction("unsubscribe.delete", {
169
+ description: "Remove an email from the unsubscribe list",
170
+ inputSchema: { email: { type: "string", required: true } },
171
+ async execute(input, ctx) {
172
+ return apiRequest(key(ctx), "DELETE", `/unsubscribes/${encodeURIComponent(input.email)}`);
173
+ },
174
+ });
175
+ rl.registerAction("unsubscribe.list", {
176
+ description: "List all unsubscribed emails",
177
+ inputSchema: { limit: { type: "number", required: false } },
178
+ async execute(input, ctx) {
179
+ const p = (input ?? {});
180
+ if (p.limit)
181
+ return apiRequest(key(ctx), "GET", "/unsubscribes", undefined, { limit: p.limit });
182
+ return paginate(key(ctx), "GET", "/unsubscribes");
183
+ },
184
+ });
185
+ // ── Enrichment ──────────────────────────────────────
186
+ rl.registerAction("enrich.get", {
187
+ description: "Get an enrichment result by ID",
188
+ inputSchema: { enrichId: { type: "string", required: true } },
189
+ async execute(input, ctx) {
190
+ return apiRequest(key(ctx), "GET", `/enrich/${input.enrichId}`);
191
+ },
192
+ });
193
+ rl.registerAction("enrich.lead", {
194
+ description: "Enrich a lead by ID",
195
+ inputSchema: {
196
+ leadId: { type: "string", required: true },
197
+ findEmail: { type: "boolean", required: true },
198
+ verifyEmail: { type: "boolean", required: true },
199
+ linkedinEnrichment: { type: "boolean", required: true },
200
+ findPhone: { type: "boolean", required: true },
201
+ },
202
+ async execute(input, ctx) {
203
+ const { leadId, findEmail, verifyEmail, linkedinEnrichment, findPhone } = input;
204
+ return apiRequest(key(ctx), "POST", `/leads/${leadId}/enrich/`, {}, { findEmail, verifyEmail, linkedinEnrichment, findPhone });
205
+ },
206
+ });
207
+ rl.registerAction("enrich.person", {
208
+ description: "Enrich a person (without existing lead)",
209
+ inputSchema: {
210
+ findEmail: { type: "boolean", required: true },
211
+ verifyEmail: { type: "boolean", required: true },
212
+ linkedinEnrichment: { type: "boolean", required: true },
213
+ findPhone: { type: "boolean", required: true },
214
+ email: { type: "string", required: false },
215
+ firstName: { type: "string", required: false },
216
+ lastName: { type: "string", required: false },
217
+ linkedinUrl: { type: "string", required: false },
218
+ companyName: { type: "string", required: false },
219
+ companyDomain: { type: "string", required: false },
220
+ },
221
+ async execute(input, ctx) {
222
+ const { findEmail, verifyEmail, linkedinEnrichment, findPhone, ...rest } = input;
223
+ const qs = { findEmail, verifyEmail, linkedinEnrichment, findPhone };
224
+ for (const [k, v] of Object.entries(rest)) {
225
+ if (v !== undefined && v !== null)
226
+ qs[k] = v;
227
+ }
228
+ return apiRequest(key(ctx), "POST", "/enrich/", {}, qs);
229
+ },
230
+ });
231
+ }
@@ -0,0 +1,133 @@
1
+ const GQL_URL = "https://api.linear.app/graphql";
2
+ async function gql(apiKey, query, variables) {
3
+ const body = { query };
4
+ if (variables)
5
+ body.variables = variables;
6
+ const res = await fetch(GQL_URL, {
7
+ method: "POST",
8
+ headers: { Authorization: apiKey, "Content-Type": "application/json" },
9
+ body: JSON.stringify(body),
10
+ });
11
+ if (!res.ok)
12
+ throw new Error(`Linear API error ${res.status}: ${await res.text()}`);
13
+ const data = (await res.json());
14
+ if (data.errors)
15
+ throw new Error(`Linear GraphQL error: ${JSON.stringify(data.errors)}`);
16
+ return data.data;
17
+ }
18
+ export default function linear(rl) {
19
+ rl.setName("linear");
20
+ rl.setVersion("0.1.0");
21
+ rl.setConnectionSchema({ apiKey: { type: "string", required: true, description: "Linear API key", env: "LINEAR_API_KEY" } });
22
+ const key = (ctx) => ctx.connection.config.apiKey;
23
+ rl.registerAction("issue.create", {
24
+ description: "Create an issue",
25
+ inputSchema: {
26
+ teamId: { type: "string", required: true, description: "Team ID" },
27
+ title: { type: "string", required: true, description: "Issue title" },
28
+ description: { type: "string", required: false, description: "Issue description (markdown)" },
29
+ assigneeId: { type: "string", required: false, description: "Assignee user ID" },
30
+ priority: { type: "number", required: false, description: "Priority (0=none, 1=urgent, 2=high, 3=medium, 4=low)" },
31
+ stateId: { type: "string", required: false, description: "Workflow state ID" },
32
+ labelIds: { type: "array", required: false, description: "Label IDs" },
33
+ parentId: { type: "string", required: false, description: "Parent issue ID (for sub-issues)" },
34
+ },
35
+ async execute(input, ctx) {
36
+ const { teamId, title, description: desc, assigneeId, priority, stateId, labelIds, parentId } = input;
37
+ const vars = { teamId, title };
38
+ if (desc)
39
+ vars.description = desc;
40
+ if (assigneeId)
41
+ vars.assigneeId = assigneeId;
42
+ if (priority !== undefined)
43
+ vars.priority = priority;
44
+ if (stateId)
45
+ vars.stateId = stateId;
46
+ if (labelIds)
47
+ vars.labelIds = labelIds;
48
+ if (parentId)
49
+ vars.parentId = parentId;
50
+ const data = await gql(key(ctx), `mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title url } } }`, { input: vars });
51
+ return data.issueCreate?.issue;
52
+ },
53
+ });
54
+ rl.registerAction("issue.get", {
55
+ description: "Get an issue by ID",
56
+ inputSchema: { issueId: { type: "string", required: true, description: "Issue ID" } },
57
+ async execute(input, ctx) {
58
+ const data = await gql(key(ctx), `query($id: String!) { issue(id: $id) { id identifier title description url priority state { id name } assignee { id name } labels { nodes { id name } } createdAt updatedAt } }`, { id: input.issueId });
59
+ return data.issue;
60
+ },
61
+ });
62
+ rl.registerAction("issue.list", {
63
+ description: "List issues",
64
+ inputSchema: {
65
+ limit: { type: "number", required: false, description: "Max results (default: 50)" },
66
+ teamId: { type: "string", required: false, description: "Filter by team" },
67
+ assigneeId: { type: "string", required: false, description: "Filter by assignee" },
68
+ },
69
+ async execute(input, ctx) {
70
+ const { limit = 50, teamId, assigneeId } = (input ?? {});
71
+ let filter = "";
72
+ const filterParts = [];
73
+ if (teamId)
74
+ filterParts.push(`team: { id: { eq: "${teamId}" } }`);
75
+ if (assigneeId)
76
+ filterParts.push(`assignee: { id: { eq: "${assigneeId}" } }`);
77
+ if (filterParts.length > 0)
78
+ filter = `, filter: { ${filterParts.join(", ")} }`;
79
+ const data = await gql(key(ctx), `query { issues(first: ${limit}${filter}) { nodes { id identifier title url priority state { name } assignee { name } createdAt } } }`);
80
+ return data.issues?.nodes;
81
+ },
82
+ });
83
+ rl.registerAction("issue.update", {
84
+ description: "Update an issue",
85
+ inputSchema: {
86
+ issueId: { type: "string", required: true, description: "Issue ID" },
87
+ title: { type: "string", required: false, description: "New title" },
88
+ description: { type: "string", required: false, description: "New description" },
89
+ assigneeId: { type: "string", required: false, description: "Assignee ID" },
90
+ stateId: { type: "string", required: false, description: "State ID" },
91
+ priority: { type: "number", required: false, description: "Priority" },
92
+ labelIds: { type: "array", required: false, description: "Label IDs" },
93
+ },
94
+ async execute(input, ctx) {
95
+ const { issueId, ...fields } = input;
96
+ const data = await gql(key(ctx), `mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success issue { id identifier title url } } }`, { id: issueId, input: fields });
97
+ return data.issueUpdate?.issue;
98
+ },
99
+ });
100
+ rl.registerAction("issue.delete", {
101
+ description: "Delete an issue",
102
+ inputSchema: { issueId: { type: "string", required: true, description: "Issue ID" } },
103
+ async execute(input, ctx) {
104
+ const data = await gql(key(ctx), `mutation($id: String!) { issueDelete(id: $id) { success } }`, { id: input.issueId });
105
+ return data.issueDelete;
106
+ },
107
+ });
108
+ rl.registerAction("issue.addComment", {
109
+ description: "Add a comment to an issue",
110
+ inputSchema: {
111
+ issueId: { type: "string", required: true, description: "Issue ID" },
112
+ body: { type: "string", required: true, description: "Comment body (markdown)" },
113
+ },
114
+ async execute(input, ctx) {
115
+ const { issueId, body: commentBody } = input;
116
+ const data = await gql(key(ctx), `mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { id body createdAt } } }`, { input: { issueId, body: commentBody } });
117
+ return data.commentCreate?.comment;
118
+ },
119
+ });
120
+ rl.registerAction("issue.addLink", {
121
+ description: "Add a link/relation between issues",
122
+ inputSchema: {
123
+ issueId: { type: "string", required: true, description: "Source issue ID" },
124
+ relatedIssueId: { type: "string", required: true, description: "Related issue ID" },
125
+ type: { type: "string", required: true, description: "Relation type: relates, blocks, duplicate" },
126
+ },
127
+ async execute(input, ctx) {
128
+ const { issueId, relatedIssueId, type } = input;
129
+ const data = await gql(key(ctx), `mutation($input: IssueRelationCreateInput!) { issueRelationCreate(input: $input) { success } }`, { input: { issueId, relatedIssueId, type } });
130
+ return data.issueRelationCreate;
131
+ },
132
+ });
133
+ }