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,353 @@
1
+ const BASE_URL = "https://actionnetwork.org/api/v2";
2
+ async function apiRequest(apiKey, method, endpoint, body) {
3
+ const opts = {
4
+ method,
5
+ headers: {
6
+ "Content-Type": "application/json",
7
+ "OSDI-API-Token": apiKey,
8
+ },
9
+ };
10
+ if (body && Object.keys(body).length > 0) {
11
+ opts.body = JSON.stringify(body);
12
+ }
13
+ const res = await fetch(`${BASE_URL}${endpoint}`, opts);
14
+ if (!res.ok) {
15
+ const text = await res.text();
16
+ throw new Error(`Action Network API error ${res.status}: ${text}`);
17
+ }
18
+ if (res.status === 204)
19
+ return { ok: true };
20
+ return res.json();
21
+ }
22
+ async function paginate(apiKey, endpoint, itemsKey, limit) {
23
+ const results = [];
24
+ let page = 1;
25
+ while (true) {
26
+ const data = (await apiRequest(apiKey, "GET", `${endpoint}?page=${page}&per_page=25`));
27
+ const items = data._embedded?.[itemsKey] ?? [];
28
+ results.push(...items);
29
+ if (limit && results.length >= limit)
30
+ return results.slice(0, limit);
31
+ if (!data._links?.next)
32
+ break;
33
+ page++;
34
+ }
35
+ return results;
36
+ }
37
+ function itemsKeyForEndpoint(endpoint) {
38
+ const segment = endpoint.split("/").pop();
39
+ return `osdi:${segment}`;
40
+ }
41
+ function extractId(item) {
42
+ return item._links?.self?.href?.split("/").pop() ?? "";
43
+ }
44
+ export default function actionNetwork(rl) {
45
+ rl.setName("actionNetwork");
46
+ rl.setVersion("0.1.0");
47
+ rl.setConnectionSchema({
48
+ apiKey: {
49
+ type: "string",
50
+ required: true,
51
+ description: "Action Network API key",
52
+ env: "ACTION_NETWORK_API_KEY",
53
+ },
54
+ });
55
+ // ── Attendance ──────────────────────────────────────
56
+ rl.registerAction("attendance.create", {
57
+ description: "Record a person's attendance at an event",
58
+ inputSchema: {
59
+ eventId: { type: "string", required: true, description: "Event ID" },
60
+ personId: { type: "string", required: true, description: "Person ID" },
61
+ },
62
+ async execute(input, ctx) {
63
+ const { eventId, personId } = input;
64
+ const body = {
65
+ _links: {
66
+ "osdi:person": {
67
+ href: `${BASE_URL}/people/${personId}`,
68
+ },
69
+ },
70
+ };
71
+ return apiRequest(ctx.connection.config.apiKey, "POST", `/events/${eventId}/attendances`, body);
72
+ },
73
+ });
74
+ rl.registerAction("attendance.get", {
75
+ description: "Get a specific attendance record",
76
+ inputSchema: {
77
+ eventId: { type: "string", required: true, description: "Event ID" },
78
+ attendanceId: { type: "string", required: true, description: "Attendance ID" },
79
+ },
80
+ async execute(input, ctx) {
81
+ const { eventId, attendanceId } = input;
82
+ return apiRequest(ctx.connection.config.apiKey, "GET", `/events/${eventId}/attendances/${attendanceId}`);
83
+ },
84
+ });
85
+ rl.registerAction("attendance.list", {
86
+ description: "List attendances for an event",
87
+ inputSchema: {
88
+ eventId: { type: "string", required: true, description: "Event ID" },
89
+ limit: { type: "number", required: false, description: "Max results to return" },
90
+ },
91
+ async execute(input, ctx) {
92
+ const { eventId, limit } = input;
93
+ return paginate(ctx.connection.config.apiKey, `/events/${eventId}/attendances`, "osdi:attendances", limit);
94
+ },
95
+ });
96
+ // ── Event ───────────────────────────────────────────
97
+ rl.registerAction("event.create", {
98
+ description: "Create a new event",
99
+ inputSchema: {
100
+ title: { type: "string", required: true, description: "Event title" },
101
+ originSystem: { type: "string", required: true, description: "Origin system identifier" },
102
+ description: { type: "string", required: false, description: "Event description" },
103
+ },
104
+ async execute(input, ctx) {
105
+ const { title, originSystem, description, ...rest } = input;
106
+ const body = { title, origin_system: originSystem };
107
+ if (description)
108
+ body.description = description;
109
+ Object.assign(body, rest);
110
+ return apiRequest(ctx.connection.config.apiKey, "POST", "/events", body);
111
+ },
112
+ });
113
+ rl.registerAction("event.get", {
114
+ description: "Get a specific event",
115
+ inputSchema: {
116
+ eventId: { type: "string", required: true, description: "Event ID" },
117
+ },
118
+ async execute(input, ctx) {
119
+ const { eventId } = input;
120
+ return apiRequest(ctx.connection.config.apiKey, "GET", `/events/${eventId}`);
121
+ },
122
+ });
123
+ rl.registerAction("event.list", {
124
+ description: "List all events",
125
+ inputSchema: {
126
+ limit: { type: "number", required: false, description: "Max results to return" },
127
+ },
128
+ async execute(input, ctx) {
129
+ const { limit } = input ?? {};
130
+ return paginate(ctx.connection.config.apiKey, "/events", "osdi:events", limit);
131
+ },
132
+ });
133
+ // ── Person ──────────────────────────────────────────
134
+ rl.registerAction("person.create", {
135
+ description: "Create a new person",
136
+ inputSchema: {
137
+ email: { type: "string", required: true, description: "Email address" },
138
+ givenName: { type: "string", required: false, description: "First name" },
139
+ familyName: { type: "string", required: false, description: "Last name" },
140
+ },
141
+ async execute(input, ctx) {
142
+ const { email, givenName, familyName, ...rest } = input;
143
+ const body = {
144
+ person: {
145
+ email_addresses: [{ address: email, primary: true, status: "subscribed" }],
146
+ ...(givenName ? { given_name: givenName } : {}),
147
+ ...(familyName ? { family_name: familyName } : {}),
148
+ ...rest,
149
+ },
150
+ };
151
+ return apiRequest(ctx.connection.config.apiKey, "POST", "/people", body);
152
+ },
153
+ });
154
+ rl.registerAction("person.get", {
155
+ description: "Get a specific person",
156
+ inputSchema: {
157
+ personId: { type: "string", required: true, description: "Person ID" },
158
+ },
159
+ async execute(input, ctx) {
160
+ const { personId } = input;
161
+ return apiRequest(ctx.connection.config.apiKey, "GET", `/people/${personId}`);
162
+ },
163
+ });
164
+ rl.registerAction("person.list", {
165
+ description: "List all people",
166
+ inputSchema: {
167
+ limit: { type: "number", required: false, description: "Max results to return" },
168
+ },
169
+ async execute(input, ctx) {
170
+ const { limit } = input ?? {};
171
+ return paginate(ctx.connection.config.apiKey, "/people", "osdi:people", limit);
172
+ },
173
+ });
174
+ rl.registerAction("person.update", {
175
+ description: "Update a person",
176
+ inputSchema: {
177
+ personId: { type: "string", required: true, description: "Person ID" },
178
+ givenName: { type: "string", required: false, description: "First name" },
179
+ familyName: { type: "string", required: false, description: "Last name" },
180
+ },
181
+ async execute(input, ctx) {
182
+ const { personId, givenName, familyName, ...rest } = input;
183
+ const body = { ...rest };
184
+ if (givenName !== undefined)
185
+ body.given_name = givenName;
186
+ if (familyName !== undefined)
187
+ body.family_name = familyName;
188
+ return apiRequest(ctx.connection.config.apiKey, "PUT", `/people/${personId}`, body);
189
+ },
190
+ });
191
+ // ── Petition ────────────────────────────────────────
192
+ rl.registerAction("petition.create", {
193
+ description: "Create a new petition",
194
+ inputSchema: {
195
+ title: { type: "string", required: true, description: "Petition title" },
196
+ originSystem: { type: "string", required: true, description: "Origin system identifier" },
197
+ target: { type: "string", required: false, description: "Comma-separated list of targets" },
198
+ },
199
+ async execute(input, ctx) {
200
+ const { title, originSystem, target, ...rest } = input;
201
+ const body = { title, origin_system: originSystem, ...rest };
202
+ if (target) {
203
+ body.target = target.split(",").map((t) => ({ name: t.trim() }));
204
+ }
205
+ return apiRequest(ctx.connection.config.apiKey, "POST", "/petitions", body);
206
+ },
207
+ });
208
+ rl.registerAction("petition.get", {
209
+ description: "Get a specific petition",
210
+ inputSchema: {
211
+ petitionId: { type: "string", required: true, description: "Petition ID" },
212
+ },
213
+ async execute(input, ctx) {
214
+ const { petitionId } = input;
215
+ return apiRequest(ctx.connection.config.apiKey, "GET", `/petitions/${petitionId}`);
216
+ },
217
+ });
218
+ rl.registerAction("petition.list", {
219
+ description: "List all petitions",
220
+ inputSchema: {
221
+ limit: { type: "number", required: false, description: "Max results to return" },
222
+ },
223
+ async execute(input, ctx) {
224
+ const { limit } = input ?? {};
225
+ return paginate(ctx.connection.config.apiKey, "/petitions", "osdi:petitions", limit);
226
+ },
227
+ });
228
+ rl.registerAction("petition.update", {
229
+ description: "Update a petition",
230
+ inputSchema: {
231
+ petitionId: { type: "string", required: true, description: "Petition ID" },
232
+ title: { type: "string", required: false, description: "Petition title" },
233
+ target: { type: "string", required: false, description: "Comma-separated list of targets" },
234
+ },
235
+ async execute(input, ctx) {
236
+ const { petitionId, target, ...rest } = input;
237
+ const body = { ...rest };
238
+ if (target) {
239
+ body.target = target.split(",").map((t) => ({ name: t.trim() }));
240
+ }
241
+ return apiRequest(ctx.connection.config.apiKey, "PUT", `/petitions/${petitionId}`, body);
242
+ },
243
+ });
244
+ // ── Signature ───────────────────────────────────────
245
+ rl.registerAction("signature.create", {
246
+ description: "Add a signature to a petition",
247
+ inputSchema: {
248
+ petitionId: { type: "string", required: true, description: "Petition ID" },
249
+ personId: { type: "string", required: true, description: "Person ID" },
250
+ },
251
+ async execute(input, ctx) {
252
+ const { petitionId, personId } = input;
253
+ const body = {
254
+ _links: {
255
+ "osdi:person": { href: `${BASE_URL}/people/${personId}` },
256
+ },
257
+ };
258
+ return apiRequest(ctx.connection.config.apiKey, "POST", `/petitions/${petitionId}/signatures`, body);
259
+ },
260
+ });
261
+ rl.registerAction("signature.get", {
262
+ description: "Get a specific signature",
263
+ inputSchema: {
264
+ petitionId: { type: "string", required: true, description: "Petition ID" },
265
+ signatureId: { type: "string", required: true, description: "Signature ID" },
266
+ },
267
+ async execute(input, ctx) {
268
+ const { petitionId, signatureId } = input;
269
+ return apiRequest(ctx.connection.config.apiKey, "GET", `/petitions/${petitionId}/signatures/${signatureId}`);
270
+ },
271
+ });
272
+ rl.registerAction("signature.list", {
273
+ description: "List signatures on a petition",
274
+ inputSchema: {
275
+ petitionId: { type: "string", required: true, description: "Petition ID" },
276
+ limit: { type: "number", required: false, description: "Max results to return" },
277
+ },
278
+ async execute(input, ctx) {
279
+ const { petitionId, limit } = input;
280
+ return paginate(ctx.connection.config.apiKey, `/petitions/${petitionId}/signatures`, "osdi:signatures", limit);
281
+ },
282
+ });
283
+ rl.registerAction("signature.update", {
284
+ description: "Update a signature",
285
+ inputSchema: {
286
+ petitionId: { type: "string", required: true, description: "Petition ID" },
287
+ signatureId: { type: "string", required: true, description: "Signature ID" },
288
+ },
289
+ async execute(input, ctx) {
290
+ const { petitionId, signatureId, ...rest } = input;
291
+ return apiRequest(ctx.connection.config.apiKey, "PUT", `/petitions/${petitionId}/signatures/${signatureId}`, rest);
292
+ },
293
+ });
294
+ // ── Tag ─────────────────────────────────────────────
295
+ rl.registerAction("tag.create", {
296
+ description: "Create a new tag",
297
+ inputSchema: {
298
+ name: { type: "string", required: true, description: "Tag name" },
299
+ },
300
+ async execute(input, ctx) {
301
+ const { name } = input;
302
+ return apiRequest(ctx.connection.config.apiKey, "POST", "/tags", { name });
303
+ },
304
+ });
305
+ rl.registerAction("tag.get", {
306
+ description: "Get a specific tag",
307
+ inputSchema: {
308
+ tagId: { type: "string", required: true, description: "Tag ID" },
309
+ },
310
+ async execute(input, ctx) {
311
+ const { tagId } = input;
312
+ return apiRequest(ctx.connection.config.apiKey, "GET", `/tags/${tagId}`);
313
+ },
314
+ });
315
+ rl.registerAction("tag.list", {
316
+ description: "List all tags",
317
+ inputSchema: {
318
+ limit: { type: "number", required: false, description: "Max results to return" },
319
+ },
320
+ async execute(input, ctx) {
321
+ const { limit } = input ?? {};
322
+ return paginate(ctx.connection.config.apiKey, "/tags", "osdi:tags", limit);
323
+ },
324
+ });
325
+ // ── Person Tag ──────────────────────────────────────
326
+ rl.registerAction("personTag.add", {
327
+ description: "Add a tag to a person",
328
+ inputSchema: {
329
+ tagId: { type: "string", required: true, description: "Tag ID" },
330
+ personId: { type: "string", required: true, description: "Person ID" },
331
+ },
332
+ async execute(input, ctx) {
333
+ const { tagId, personId } = input;
334
+ const body = {
335
+ _links: {
336
+ "osdi:person": { href: `${BASE_URL}/people/${personId}` },
337
+ },
338
+ };
339
+ return apiRequest(ctx.connection.config.apiKey, "POST", `/tags/${tagId}/taggings`, body);
340
+ },
341
+ });
342
+ rl.registerAction("personTag.remove", {
343
+ description: "Remove a tag from a person",
344
+ inputSchema: {
345
+ tagId: { type: "string", required: true, description: "Tag ID" },
346
+ taggingId: { type: "string", required: true, description: "Tagging ID" },
347
+ },
348
+ async execute(input, ctx) {
349
+ const { tagId, taggingId } = input;
350
+ return apiRequest(ctx.connection.config.apiKey, "DELETE", `/tags/${tagId}/taggings/${taggingId}`);
351
+ },
352
+ });
353
+ }