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,233 @@
1
+ function getConn(ctx) {
2
+ const c = ctx.connection.config;
3
+ const base = (c.url ?? "https://sentry.io").replace(/\/$/, "");
4
+ return { base, token: c.token };
5
+ }
6
+ async function apiRequest(conn, method, endpoint, body, qs) {
7
+ const url = new URL(`${conn.base}${endpoint}`);
8
+ if (qs) {
9
+ for (const [k, v] of Object.entries(qs)) {
10
+ if (v !== undefined && v !== null)
11
+ url.searchParams.set(k, String(v));
12
+ }
13
+ }
14
+ const init = { method, headers: { Authorization: `Bearer ${conn.token}`, "Content-Type": "application/json" } };
15
+ if (body && Object.keys(body).length > 0)
16
+ init.body = JSON.stringify(body);
17
+ const res = await fetch(url.toString(), init);
18
+ if (!res.ok)
19
+ throw new Error(`Sentry error ${res.status}: ${await res.text()}`);
20
+ const text = await res.text();
21
+ return text ? JSON.parse(text) : {};
22
+ }
23
+ function registerCrud(rl, resource, basePath, idField, extraCreateFields) {
24
+ rl.registerAction(`${resource}.get`, {
25
+ description: `Get a ${resource} by ${idField}`,
26
+ inputSchema: { [idField]: { type: "string", required: true }, org: { type: "string", required: true, description: "Organization slug" } },
27
+ async execute(input, ctx) {
28
+ const p = input;
29
+ return apiRequest(getConn(ctx), "GET", `${basePath(p)}${p[idField]}/`);
30
+ },
31
+ });
32
+ rl.registerAction(`${resource}.list`, {
33
+ description: `List ${resource}s`,
34
+ inputSchema: { org: { type: "string", required: true }, limit: { type: "number", required: false }, ...(resource === "event" || resource === "issue" ? { project: { type: "string", required: true, description: "Project slug" } } : {}) },
35
+ async execute(input, ctx) {
36
+ const p = (input ?? {});
37
+ const qs = {};
38
+ if (p.limit)
39
+ qs.limit = p.limit;
40
+ const data = (await apiRequest(getConn(ctx), "GET", basePath(p), undefined, qs));
41
+ return data;
42
+ },
43
+ });
44
+ rl.registerAction(`${resource}.delete`, {
45
+ description: `Delete a ${resource}`,
46
+ inputSchema: { [idField]: { type: "string", required: true }, org: { type: "string", required: true } },
47
+ async execute(input, ctx) {
48
+ const p = input;
49
+ await apiRequest(getConn(ctx), "DELETE", `${basePath(p)}${p[idField]}/`);
50
+ return { success: true };
51
+ },
52
+ });
53
+ }
54
+ export default function sentry(rl) {
55
+ rl.setName("sentry");
56
+ rl.setVersion("0.1.0");
57
+ rl.setConnectionSchema({
58
+ token: { type: "string", required: true, description: "Sentry auth token (Bearer)", env: "SENTRY_TOKEN" },
59
+ url: { type: "string", required: false, description: "Sentry base URL (default https://sentry.io)", env: "SENTRY_URL" },
60
+ });
61
+ // ── Event ───────────────────────────────────────────
62
+ rl.registerAction("event.get", {
63
+ description: "Get a project event by ID",
64
+ inputSchema: { org: { type: "string", required: true }, project: { type: "string", required: true }, eventId: { type: "string", required: true } },
65
+ async execute(input, ctx) {
66
+ const p = input;
67
+ return apiRequest(getConn(ctx), "GET", `/api/0/projects/${p.org}/${p.project}/events/${p.eventId}/`);
68
+ },
69
+ });
70
+ rl.registerAction("event.list", {
71
+ description: "List project events",
72
+ inputSchema: { org: { type: "string", required: true }, project: { type: "string", required: true }, full: { type: "boolean", required: false }, limit: { type: "number", required: false } },
73
+ async execute(input, ctx) {
74
+ const p = (input ?? {});
75
+ const qs = {};
76
+ if (p.full)
77
+ qs.full = "true";
78
+ if (p.limit)
79
+ qs.limit = p.limit;
80
+ return apiRequest(getConn(ctx), "GET", `/api/0/projects/${p.org}/${p.project}/events/`, undefined, qs);
81
+ },
82
+ });
83
+ // ── Issue ───────────────────────────────────────────
84
+ rl.registerAction("issue.get", {
85
+ description: "Get an issue by ID",
86
+ inputSchema: { issueId: { type: "string", required: true } },
87
+ async execute(input, ctx) { return apiRequest(getConn(ctx), "GET", `/api/0/issues/${input.issueId}/`); },
88
+ });
89
+ rl.registerAction("issue.list", {
90
+ description: "List issues for a project",
91
+ inputSchema: { org: { type: "string", required: true }, project: { type: "string", required: true }, query: { type: "string", required: false }, limit: { type: "number", required: false } },
92
+ async execute(input, ctx) {
93
+ const p = (input ?? {});
94
+ const qs = {};
95
+ if (p.query)
96
+ qs.query = p.query;
97
+ if (p.limit)
98
+ qs.limit = p.limit;
99
+ return apiRequest(getConn(ctx), "GET", `/api/0/projects/${p.org}/${p.project}/issues/`, undefined, qs);
100
+ },
101
+ });
102
+ rl.registerAction("issue.update", {
103
+ description: "Update an issue",
104
+ inputSchema: { issueId: { type: "string", required: true }, status: { type: "string", required: false }, assignedTo: { type: "string", required: false }, hasSeen: { type: "boolean", required: false }, isBookmarked: { type: "boolean", required: false } },
105
+ async execute(input, ctx) {
106
+ const { issueId, ...fields } = input;
107
+ return apiRequest(getConn(ctx), "PUT", `/api/0/issues/${issueId}/`, fields);
108
+ },
109
+ });
110
+ rl.registerAction("issue.delete", {
111
+ description: "Delete an issue",
112
+ inputSchema: { issueId: { type: "string", required: true } },
113
+ async execute(input, ctx) { await apiRequest(getConn(ctx), "DELETE", `/api/0/issues/${input.issueId}/`); return { success: true }; },
114
+ });
115
+ // ── Organization ────────────────────────────────────
116
+ rl.registerAction("organization.get", {
117
+ description: "Get an organization",
118
+ inputSchema: { org: { type: "string", required: true } },
119
+ async execute(input, ctx) { return apiRequest(getConn(ctx), "GET", `/api/0/organizations/${input.org}/`); },
120
+ });
121
+ rl.registerAction("organization.list", {
122
+ description: "List organizations",
123
+ inputSchema: { limit: { type: "number", required: false } },
124
+ async execute(input, ctx) {
125
+ const qs = {};
126
+ if (input?.limit)
127
+ qs.limit = input.limit;
128
+ return apiRequest(getConn(ctx), "GET", "/api/0/organizations/", undefined, qs);
129
+ },
130
+ });
131
+ rl.registerAction("organization.create", {
132
+ description: "Create an organization",
133
+ inputSchema: { name: { type: "string", required: true }, slug: { type: "string", required: false } },
134
+ async execute(input, ctx) {
135
+ const p = input;
136
+ return apiRequest(getConn(ctx), "POST", "/api/0/organizations/", { name: p.name, agreeTerms: true, slug: p.slug });
137
+ },
138
+ });
139
+ // ── Project ─────────────────────────────────────────
140
+ rl.registerAction("project.get", {
141
+ description: "Get a project",
142
+ inputSchema: { org: { type: "string", required: true }, project: { type: "string", required: true } },
143
+ async execute(input, ctx) { const p = input; return apiRequest(getConn(ctx), "GET", `/api/0/projects/${p.org}/${p.project}/`); },
144
+ });
145
+ rl.registerAction("project.list", {
146
+ description: "List all projects",
147
+ inputSchema: { limit: { type: "number", required: false } },
148
+ async execute(input, ctx) { return apiRequest(getConn(ctx), "GET", "/api/0/projects/", undefined, input?.limit ? { limit: input.limit } : undefined); },
149
+ });
150
+ rl.registerAction("project.create", {
151
+ description: "Create a project",
152
+ inputSchema: { org: { type: "string", required: true }, team: { type: "string", required: true }, name: { type: "string", required: true }, slug: { type: "string", required: false }, platform: { type: "string", required: false } },
153
+ async execute(input, ctx) {
154
+ const p = input;
155
+ const body = { name: p.name };
156
+ if (p.slug)
157
+ body.slug = p.slug;
158
+ if (p.platform)
159
+ body.platform = p.platform;
160
+ return apiRequest(getConn(ctx), "POST", `/api/0/teams/${p.org}/${p.team}/projects/`, body);
161
+ },
162
+ });
163
+ rl.registerAction("project.delete", {
164
+ description: "Delete a project",
165
+ inputSchema: { org: { type: "string", required: true }, project: { type: "string", required: true } },
166
+ async execute(input, ctx) { const p = input; await apiRequest(getConn(ctx), "DELETE", `/api/0/projects/${p.org}/${p.project}/`); return { success: true }; },
167
+ });
168
+ // ── Release ─────────────────────────────────────────
169
+ rl.registerAction("release.get", {
170
+ description: "Get a release",
171
+ inputSchema: { org: { type: "string", required: true }, version: { type: "string", required: true } },
172
+ async execute(input, ctx) { const p = input; return apiRequest(getConn(ctx), "GET", `/api/0/organizations/${p.org}/releases/${encodeURIComponent(p.version)}/`); },
173
+ });
174
+ rl.registerAction("release.list", {
175
+ description: "List releases",
176
+ inputSchema: { org: { type: "string", required: true }, query: { type: "string", required: false }, limit: { type: "number", required: false } },
177
+ async execute(input, ctx) {
178
+ const p = (input ?? {});
179
+ const qs = {};
180
+ if (p.query)
181
+ qs.query = p.query;
182
+ if (p.limit)
183
+ qs.limit = p.limit;
184
+ return apiRequest(getConn(ctx), "GET", `/api/0/organizations/${p.org}/releases/`, undefined, qs);
185
+ },
186
+ });
187
+ rl.registerAction("release.create", {
188
+ description: "Create a release",
189
+ inputSchema: { org: { type: "string", required: true }, version: { type: "string", required: true }, projects: { type: "object", required: true, description: "Array of project slugs" }, url: { type: "string", required: false } },
190
+ async execute(input, ctx) {
191
+ const p = input;
192
+ const body = { version: p.version, projects: p.projects };
193
+ if (p.url)
194
+ body.url = p.url;
195
+ return apiRequest(getConn(ctx), "POST", `/api/0/organizations/${p.org}/releases/`, body);
196
+ },
197
+ });
198
+ rl.registerAction("release.delete", {
199
+ description: "Delete a release",
200
+ inputSchema: { org: { type: "string", required: true }, version: { type: "string", required: true } },
201
+ async execute(input, ctx) { const p = input; await apiRequest(getConn(ctx), "DELETE", `/api/0/organizations/${p.org}/releases/${encodeURIComponent(p.version)}/`); return { success: true }; },
202
+ });
203
+ // ── Team ────────────────────────────────────────────
204
+ rl.registerAction("team.get", {
205
+ description: "Get a team",
206
+ inputSchema: { org: { type: "string", required: true }, team: { type: "string", required: true } },
207
+ async execute(input, ctx) { const p = input; return apiRequest(getConn(ctx), "GET", `/api/0/teams/${p.org}/${p.team}/`); },
208
+ });
209
+ rl.registerAction("team.list", {
210
+ description: "List teams in an organization",
211
+ inputSchema: { org: { type: "string", required: true }, limit: { type: "number", required: false } },
212
+ async execute(input, ctx) {
213
+ const p = (input ?? {});
214
+ return apiRequest(getConn(ctx), "GET", `/api/0/organizations/${p.org}/teams/`, undefined, p.limit ? { limit: p.limit } : undefined);
215
+ },
216
+ });
217
+ rl.registerAction("team.create", {
218
+ description: "Create a team",
219
+ inputSchema: { org: { type: "string", required: true }, name: { type: "string", required: true }, slug: { type: "string", required: false } },
220
+ async execute(input, ctx) {
221
+ const p = input;
222
+ const body = { name: p.name };
223
+ if (p.slug)
224
+ body.slug = p.slug;
225
+ return apiRequest(getConn(ctx), "POST", `/api/0/organizations/${p.org}/teams/`, body);
226
+ },
227
+ });
228
+ rl.registerAction("team.delete", {
229
+ description: "Delete a team",
230
+ inputSchema: { org: { type: "string", required: true }, team: { type: "string", required: true } },
231
+ async execute(input, ctx) { const p = input; await apiRequest(getConn(ctx), "DELETE", `/api/0/teams/${p.org}/${p.team}/`); return { success: true }; },
232
+ });
233
+ }
@@ -0,0 +1,108 @@
1
+ function getConn(ctx) {
2
+ const c = ctx.connection.config;
3
+ return { subdomain: c.subdomain, username: c.username, password: c.password };
4
+ }
5
+ async function api(conn, method, endpoint, body, qs) {
6
+ const url = new URL(`https://${conn.subdomain}.service-now.com/api${endpoint}`);
7
+ if (qs) {
8
+ for (const [k, v] of Object.entries(qs)) {
9
+ if (v !== undefined && v !== null)
10
+ url.searchParams.set(k, String(v));
11
+ }
12
+ }
13
+ const init = { method, headers: { Authorization: `Basic ${btoa(`${conn.username}:${conn.password}`)}`, "Content-Type": "application/json", Accept: "application/json" } };
14
+ if (body && Object.keys(body).length > 0)
15
+ init.body = JSON.stringify(body);
16
+ const res = await fetch(url.toString(), init);
17
+ if (!res.ok)
18
+ throw new Error(`ServiceNow error ${res.status}: ${await res.text()}`);
19
+ return res.json();
20
+ }
21
+ const TABLES = {
22
+ incident: "incident", user: "sys_user", userGroup: "sys_user_group", userRole: "sys_user_role",
23
+ businessService: "cmdb_ci_service", configurationItem: "cmdb_ci", department: "cmn_department",
24
+ };
25
+ function registerTableResource(rl, resource, table, conn) {
26
+ rl.registerAction(`${resource}.create`, { description: `Create a ${resource}`, inputSchema: { data: { type: "object", required: true } },
27
+ async execute(input, ctx) {
28
+ const data = (await api(conn(ctx), "POST", `/now/table/${table}`, input.data));
29
+ return data.result;
30
+ } });
31
+ rl.registerAction(`${resource}.get`, { description: `Get a ${resource} by sys_id`, inputSchema: { sysId: { type: "string", required: true } },
32
+ async execute(input, ctx) {
33
+ const data = (await api(conn(ctx), "GET", `/now/table/${table}/${input.sysId}`));
34
+ return data.result;
35
+ } });
36
+ rl.registerAction(`${resource}.list`, { description: `List ${resource}s`, inputSchema: { limit: { type: "number", required: false }, query: { type: "string", required: false, description: "Encoded query string" }, fields: { type: "string", required: false, description: "Comma-separated fields" } },
37
+ async execute(input, ctx) {
38
+ const p = (input ?? {});
39
+ const qs = {};
40
+ if (p.limit)
41
+ qs.sysparm_limit = p.limit;
42
+ if (p.query)
43
+ qs.sysparm_query = p.query;
44
+ if (p.fields)
45
+ qs.sysparm_fields = p.fields;
46
+ const data = (await api(conn(ctx), "GET", `/now/table/${table}`, undefined, qs));
47
+ return data.result;
48
+ } });
49
+ rl.registerAction(`${resource}.update`, { description: `Update a ${resource}`, inputSchema: { sysId: { type: "string", required: true }, data: { type: "object", required: true } },
50
+ async execute(input, ctx) {
51
+ const p = input;
52
+ const data = (await api(conn(ctx), "PATCH", `/now/table/${table}/${p.sysId}`, p.data));
53
+ return data.result;
54
+ } });
55
+ rl.registerAction(`${resource}.delete`, { description: `Delete a ${resource}`, inputSchema: { sysId: { type: "string", required: true } },
56
+ async execute(input, ctx) {
57
+ await api(conn(ctx), "DELETE", `/now/table/${table}/${input.sysId}`);
58
+ return { success: true };
59
+ } });
60
+ }
61
+ export default function servicenow(rl) {
62
+ rl.setName("servicenow");
63
+ rl.setVersion("0.1.0");
64
+ rl.setConnectionSchema({
65
+ subdomain: { type: "string", required: true, description: "ServiceNow instance subdomain", env: "SERVICENOW_SUBDOMAIN" },
66
+ username: { type: "string", required: true, description: "ServiceNow username", env: "SERVICENOW_USERNAME" },
67
+ password: { type: "string", required: true, description: "ServiceNow password", env: "SERVICENOW_PASSWORD" },
68
+ });
69
+ for (const [resource, table] of Object.entries(TABLES)) {
70
+ registerTableResource(rl, resource, table, getConn);
71
+ }
72
+ // ── Generic Table Record ────────────────────────────
73
+ rl.registerAction("tableRecord.create", { description: "Create a record in any table", inputSchema: { tableName: { type: "string", required: true }, data: { type: "object", required: true } },
74
+ async execute(input, ctx) {
75
+ const p = input;
76
+ const data = (await api(getConn(ctx), "POST", `/now/table/${p.tableName}`, p.data));
77
+ return data.result;
78
+ } });
79
+ rl.registerAction("tableRecord.get", { description: "Get a record from any table", inputSchema: { tableName: { type: "string", required: true }, sysId: { type: "string", required: true } },
80
+ async execute(input, ctx) {
81
+ const p = input;
82
+ const data = (await api(getConn(ctx), "GET", `/now/table/${p.tableName}/${p.sysId}`));
83
+ return data.result;
84
+ } });
85
+ rl.registerAction("tableRecord.list", { description: "List records from any table", inputSchema: { tableName: { type: "string", required: true }, limit: { type: "number", required: false }, query: { type: "string", required: false } },
86
+ async execute(input, ctx) {
87
+ const p = input;
88
+ const qs = {};
89
+ if (p.limit)
90
+ qs.sysparm_limit = p.limit;
91
+ if (p.query)
92
+ qs.sysparm_query = p.query;
93
+ const data = (await api(getConn(ctx), "GET", `/now/table/${p.tableName}`, undefined, qs));
94
+ return data.result;
95
+ } });
96
+ rl.registerAction("tableRecord.update", { description: "Update a record in any table", inputSchema: { tableName: { type: "string", required: true }, sysId: { type: "string", required: true }, data: { type: "object", required: true } },
97
+ async execute(input, ctx) {
98
+ const p = input;
99
+ const data = (await api(getConn(ctx), "PATCH", `/now/table/${p.tableName}/${p.sysId}`, p.data));
100
+ return data.result;
101
+ } });
102
+ rl.registerAction("tableRecord.delete", { description: "Delete a record from any table", inputSchema: { tableName: { type: "string", required: true }, sysId: { type: "string", required: true } },
103
+ async execute(input, ctx) {
104
+ const p = input;
105
+ await api(getConn(ctx), "DELETE", `/now/table/${p.tableName}/${p.sysId}`);
106
+ return { success: true };
107
+ } });
108
+ }
@@ -0,0 +1,222 @@
1
+ function getConn(ctx) {
2
+ const c = ctx.connection.config;
3
+ const subdomain = c.shopSubdomain;
4
+ const base = `https://${subdomain}.myshopify.com/admin/api/2024-07`;
5
+ return { base, accessToken: c.accessToken };
6
+ }
7
+ async function apiRequest(conn, method, endpoint, body, qs) {
8
+ const url = new URL(`${conn.base}${endpoint}`);
9
+ if (qs) {
10
+ for (const [k, v] of Object.entries(qs)) {
11
+ if (v !== undefined && v !== null)
12
+ url.searchParams.set(k, String(v));
13
+ }
14
+ }
15
+ const init = {
16
+ method,
17
+ headers: { "X-Shopify-Access-Token": conn.accessToken, "Content-Type": "application/json" },
18
+ };
19
+ if (body !== undefined)
20
+ init.body = JSON.stringify(body);
21
+ const res = await fetch(url.toString(), init);
22
+ if (!res.ok)
23
+ throw new Error(`Shopify error ${res.status}: ${await res.text()}`);
24
+ const text = await res.text();
25
+ return text ? JSON.parse(text) : {};
26
+ }
27
+ async function paginate(conn, propertyName, endpoint, qs = {}) {
28
+ const all = [];
29
+ let nextUrl;
30
+ do {
31
+ const url = nextUrl ?? `${conn.base}${endpoint}`;
32
+ const u = new URL(url);
33
+ if (!nextUrl && qs) {
34
+ for (const [k, v] of Object.entries(qs)) {
35
+ if (v !== undefined && v !== null)
36
+ u.searchParams.set(k, String(v));
37
+ }
38
+ }
39
+ const res = await fetch(u.toString(), {
40
+ headers: { "X-Shopify-Access-Token": conn.accessToken },
41
+ });
42
+ if (!res.ok)
43
+ throw new Error(`Shopify error ${res.status}: ${await res.text()}`);
44
+ const data = (await res.json());
45
+ all.push(...(data[propertyName] ?? []));
46
+ nextUrl = undefined;
47
+ const link = res.headers.get("link") ?? "";
48
+ if (link.includes('rel="next"')) {
49
+ const match = link.match(/<([^>]+)>;\s*rel="next"/);
50
+ if (match)
51
+ nextUrl = match[1];
52
+ }
53
+ } while (nextUrl);
54
+ return all;
55
+ }
56
+ export default function shopify(rl) {
57
+ rl.setName("shopify");
58
+ rl.setVersion("0.1.0");
59
+ rl.setConnectionSchema({
60
+ shopSubdomain: { type: "string", required: true, description: "Shopify store subdomain (e.g. mystore)", env: "SHOPIFY_SUBDOMAIN" },
61
+ accessToken: { type: "string", required: true, description: "Shopify Admin API access token", env: "SHOPIFY_ACCESS_TOKEN" },
62
+ });
63
+ // ── Order ───────────────────────────────────────────
64
+ rl.registerAction("order.create", {
65
+ description: "Create an order",
66
+ inputSchema: {
67
+ lineItems: { type: "object", required: true, description: "Array of line item objects [{variant_id, quantity}]" },
68
+ email: { type: "string", required: false },
69
+ note: { type: "string", required: false },
70
+ tags: { type: "string", required: false },
71
+ test: { type: "boolean", required: false, description: "Mark as test order (default true)" },
72
+ },
73
+ async execute(input, ctx) {
74
+ const p = input;
75
+ const order = { line_items: p.lineItems, test: p.test !== false };
76
+ if (p.email)
77
+ order.email = p.email;
78
+ if (p.note)
79
+ order.note = p.note;
80
+ if (p.tags)
81
+ order.tags = p.tags;
82
+ const data = (await apiRequest(getConn(ctx), "POST", "/orders.json", { order }));
83
+ return data.order;
84
+ },
85
+ });
86
+ rl.registerAction("order.get", {
87
+ description: "Get an order by ID",
88
+ inputSchema: { orderId: { type: "string", required: true }, fields: { type: "string", required: false } },
89
+ async execute(input, ctx) {
90
+ const p = input;
91
+ const qs = {};
92
+ if (p.fields)
93
+ qs.fields = p.fields;
94
+ const data = (await apiRequest(getConn(ctx), "GET", `/orders/${p.orderId}.json`, undefined, qs));
95
+ return data.order;
96
+ },
97
+ });
98
+ rl.registerAction("order.list", {
99
+ description: "List orders",
100
+ inputSchema: {
101
+ status: { type: "string", required: false, description: "open, closed, cancelled, any" },
102
+ limit: { type: "number", required: false },
103
+ createdAtMin: { type: "string", required: false },
104
+ createdAtMax: { type: "string", required: false },
105
+ },
106
+ async execute(input, ctx) {
107
+ const p = (input ?? {});
108
+ const conn = getConn(ctx);
109
+ const qs = {};
110
+ if (p.status)
111
+ qs.status = p.status;
112
+ if (p.createdAtMin)
113
+ qs.created_at_min = p.createdAtMin;
114
+ if (p.createdAtMax)
115
+ qs.created_at_max = p.createdAtMax;
116
+ if (p.limit) {
117
+ qs.limit = p.limit;
118
+ const d = (await apiRequest(conn, "GET", "/orders.json", undefined, qs));
119
+ return d.orders;
120
+ }
121
+ return paginate(conn, "orders", "/orders.json", qs);
122
+ },
123
+ });
124
+ rl.registerAction("order.update", {
125
+ description: "Update an order",
126
+ inputSchema: {
127
+ orderId: { type: "string", required: true },
128
+ note: { type: "string", required: false },
129
+ tags: { type: "string", required: false },
130
+ email: { type: "string", required: false },
131
+ },
132
+ async execute(input, ctx) {
133
+ const p = input;
134
+ const order = {};
135
+ if (p.note !== undefined)
136
+ order.note = p.note;
137
+ if (p.tags)
138
+ order.tags = p.tags;
139
+ if (p.email)
140
+ order.email = p.email;
141
+ const data = (await apiRequest(getConn(ctx), "PUT", `/orders/${p.orderId}.json`, { order }));
142
+ return data.order;
143
+ },
144
+ });
145
+ rl.registerAction("order.delete", {
146
+ description: "Delete an order",
147
+ inputSchema: { orderId: { type: "string", required: true } },
148
+ async execute(input, ctx) {
149
+ await apiRequest(getConn(ctx), "DELETE", `/orders/${input.orderId}.json`);
150
+ return { success: true };
151
+ },
152
+ });
153
+ // ── Product ─────────────────────────────────────────
154
+ rl.registerAction("product.create", {
155
+ description: "Create a product",
156
+ inputSchema: {
157
+ title: { type: "string", required: true },
158
+ body_html: { type: "string", required: false },
159
+ vendor: { type: "string", required: false },
160
+ product_type: { type: "string", required: false },
161
+ tags: { type: "string", required: false },
162
+ },
163
+ async execute(input, ctx) {
164
+ const p = input;
165
+ const data = (await apiRequest(getConn(ctx), "POST", "/products.json", { product: p }));
166
+ return data.product;
167
+ },
168
+ });
169
+ rl.registerAction("product.get", {
170
+ description: "Get a product by ID",
171
+ inputSchema: { productId: { type: "string", required: true }, fields: { type: "string", required: false } },
172
+ async execute(input, ctx) {
173
+ const p = input;
174
+ const qs = {};
175
+ if (p.fields)
176
+ qs.fields = p.fields;
177
+ const data = (await apiRequest(getConn(ctx), "GET", `/products/${p.productId}.json`, undefined, qs));
178
+ return data.product;
179
+ },
180
+ });
181
+ rl.registerAction("product.list", {
182
+ description: "List products",
183
+ inputSchema: { limit: { type: "number", required: false }, title: { type: "string", required: false } },
184
+ async execute(input, ctx) {
185
+ const p = (input ?? {});
186
+ const conn = getConn(ctx);
187
+ const qs = {};
188
+ if (p.title)
189
+ qs.title = p.title;
190
+ if (p.limit) {
191
+ qs.limit = p.limit;
192
+ const d = (await apiRequest(conn, "GET", "/products.json", undefined, qs));
193
+ return d.products;
194
+ }
195
+ return paginate(conn, "products", "/products.json", qs);
196
+ },
197
+ });
198
+ rl.registerAction("product.update", {
199
+ description: "Update a product",
200
+ inputSchema: {
201
+ productId: { type: "string", required: true },
202
+ title: { type: "string", required: false },
203
+ body_html: { type: "string", required: false },
204
+ vendor: { type: "string", required: false },
205
+ tags: { type: "string", required: false },
206
+ },
207
+ async execute(input, ctx) {
208
+ const p = input;
209
+ const { productId, ...fields } = p;
210
+ const data = (await apiRequest(getConn(ctx), "PUT", `/products/${productId}.json`, { product: fields }));
211
+ return data.product;
212
+ },
213
+ });
214
+ rl.registerAction("product.delete", {
215
+ description: "Delete a product",
216
+ inputSchema: { productId: { type: "string", required: true } },
217
+ async execute(input, ctx) {
218
+ await apiRequest(getConn(ctx), "DELETE", `/products/${input.productId}.json`);
219
+ return { success: true };
220
+ },
221
+ });
222
+ }
@@ -0,0 +1,61 @@
1
+ export default function signl4(rl) {
2
+ rl.setName("signl4");
3
+ rl.setVersion("0.1.0");
4
+ rl.setConnectionSchema({
5
+ teamSecret: { type: "string", required: true, description: "SIGNL4 team secret (webhook path)", env: "SIGNL4_TEAM_SECRET" },
6
+ });
7
+ const url = (ctx) => `https://connect.signl4.com/webhook/${ctx.connection.config.teamSecret}`;
8
+ rl.registerAction("alert.send", {
9
+ description: "Send a SIGNL4 alert",
10
+ inputSchema: {
11
+ message: { type: "string", required: true },
12
+ title: { type: "string", required: false },
13
+ service: { type: "string", required: false },
14
+ externalId: { type: "string", required: false, description: "External ID for correlation" },
15
+ alertingScenario: { type: "string", required: false, description: "single_ack or multi_ack" },
16
+ latitude: { type: "string", required: false },
17
+ longitude: { type: "string", required: false },
18
+ filtering: { type: "boolean", required: false },
19
+ },
20
+ async execute(input, ctx) {
21
+ const p = (input ?? {});
22
+ const form = new URLSearchParams();
23
+ form.set("message", p.message);
24
+ form.set("X-S4-Status", "new");
25
+ form.set("X-S4-SourceSystem", "runline");
26
+ if (p.title)
27
+ form.set("title", p.title);
28
+ if (p.service)
29
+ form.set("service", p.service);
30
+ if (p.externalId)
31
+ form.set("X-S4-ExternalID", p.externalId);
32
+ if (p.alertingScenario)
33
+ form.set("X-S4-AlertingScenario", p.alertingScenario);
34
+ if (p.latitude && p.longitude)
35
+ form.set("X-S4-Location", `${p.latitude},${p.longitude}`);
36
+ if (p.filtering !== undefined)
37
+ form.set("X-S4-Filtering", String(p.filtering));
38
+ const res = await fetch(url(ctx), { method: "POST", body: form });
39
+ if (!res.ok)
40
+ throw new Error(`SIGNL4 error ${res.status}: ${await res.text()}`);
41
+ return res.json();
42
+ },
43
+ });
44
+ rl.registerAction("alert.resolve", {
45
+ description: "Resolve a SIGNL4 alert by external ID",
46
+ inputSchema: {
47
+ externalId: { type: "string", required: true },
48
+ },
49
+ async execute(input, ctx) {
50
+ const { externalId } = input;
51
+ const form = new URLSearchParams();
52
+ form.set("X-S4-ExternalID", externalId);
53
+ form.set("X-S4-Status", "resolved");
54
+ form.set("X-S4-SourceSystem", "runline");
55
+ const res = await fetch(url(ctx), { method: "POST", body: form });
56
+ if (!res.ok)
57
+ throw new Error(`SIGNL4 error ${res.status}: ${await res.text()}`);
58
+ return res.json();
59
+ },
60
+ });
61
+ }