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,298 @@
1
+ const BASE_URL = "https://www.beeminder.com/api/v1";
2
+ async function apiRequest(token, method, endpoint, body, qs) {
3
+ const url = new URL(`${BASE_URL}${endpoint}`);
4
+ url.searchParams.set("auth_token", token);
5
+ if (qs) {
6
+ for (const [k, v] of Object.entries(qs)) {
7
+ if (v !== undefined && v !== null)
8
+ url.searchParams.set(k, String(v));
9
+ }
10
+ }
11
+ const opts = {
12
+ method,
13
+ headers: { "Content-Type": "application/json" },
14
+ };
15
+ if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE") {
16
+ opts.body = JSON.stringify(body);
17
+ }
18
+ const res = await fetch(url.toString(), opts);
19
+ if (!res.ok) {
20
+ const text = await res.text();
21
+ throw new Error(`Beeminder API error ${res.status}: ${text}`);
22
+ }
23
+ const ct = res.headers.get("content-type") ?? "";
24
+ if (ct.includes("application/json"))
25
+ return res.json();
26
+ return { success: true };
27
+ }
28
+ async function paginateAll(token, endpoint, qs) {
29
+ const results = [];
30
+ let page = 1;
31
+ while (true) {
32
+ const data = (await apiRequest(token, "GET", endpoint, undefined, { ...qs, page }));
33
+ if (!Array.isArray(data) || data.length === 0)
34
+ break;
35
+ results.push(...data);
36
+ page++;
37
+ }
38
+ return results;
39
+ }
40
+ function getToken(ctx) {
41
+ return ctx.connection.config.apiToken;
42
+ }
43
+ export default function beeminder(rl) {
44
+ rl.setName("beeminder");
45
+ rl.setVersion("0.1.0");
46
+ rl.setConnectionSchema({
47
+ apiToken: {
48
+ type: "string",
49
+ required: true,
50
+ description: "Beeminder API token",
51
+ env: "BEEMINDER_API_TOKEN",
52
+ },
53
+ });
54
+ // ── Datapoint ───────────────────────────────────────
55
+ rl.registerAction("datapoint.create", {
56
+ description: "Create a datapoint for a goal",
57
+ inputSchema: {
58
+ goalName: { type: "string", required: true, description: "Goal slug" },
59
+ value: { type: "number", required: true, description: "Datapoint value" },
60
+ timestamp: { type: "number", required: false, description: "Unix timestamp (default: now)" },
61
+ comment: { type: "string", required: false, description: "Comment" },
62
+ requestid: { type: "string", required: false, description: "Unique ID to prevent duplicates" },
63
+ },
64
+ async execute(input, ctx) {
65
+ const { goalName, ...body } = input;
66
+ return apiRequest(getToken(ctx), "POST", `/users/me/goals/${goalName}/datapoints.json`, body);
67
+ },
68
+ });
69
+ rl.registerAction("datapoint.createAll", {
70
+ description: "Create multiple datapoints at once",
71
+ inputSchema: {
72
+ goalName: { type: "string", required: true, description: "Goal slug" },
73
+ datapoints: { type: "array", required: true, description: "Array of {value, timestamp?, comment?, requestid?}" },
74
+ },
75
+ async execute(input, ctx) {
76
+ const { goalName, datapoints } = input;
77
+ return apiRequest(getToken(ctx), "POST", `/users/me/goals/${goalName}/datapoints/create_all.json`, { datapoints });
78
+ },
79
+ });
80
+ rl.registerAction("datapoint.get", {
81
+ description: "Get a single datapoint",
82
+ inputSchema: {
83
+ goalName: { type: "string", required: true, description: "Goal slug" },
84
+ datapointId: { type: "string", required: true, description: "Datapoint ID" },
85
+ },
86
+ async execute(input, ctx) {
87
+ const { goalName, datapointId } = input;
88
+ return apiRequest(getToken(ctx), "GET", `/users/me/goals/${goalName}/datapoints/${datapointId}.json`);
89
+ },
90
+ });
91
+ rl.registerAction("datapoint.list", {
92
+ description: "List datapoints for a goal",
93
+ inputSchema: {
94
+ goalName: { type: "string", required: true, description: "Goal slug" },
95
+ sort: { type: "string", required: false, description: "Sort attribute (default: id)" },
96
+ limit: { type: "number", required: false, description: "Max results (omit for all)" },
97
+ page: { type: "number", required: false, description: "Page number (1-indexed)" },
98
+ per: { type: "number", required: false, description: "Results per page" },
99
+ },
100
+ async execute(input, ctx) {
101
+ const { goalName, sort, limit, page, per } = (input ?? {});
102
+ const token = getToken(ctx);
103
+ const qs = {};
104
+ if (sort)
105
+ qs.sort = sort;
106
+ if (limit) {
107
+ qs.count = limit;
108
+ if (page)
109
+ qs.page = page;
110
+ if (per)
111
+ qs.per = per;
112
+ return apiRequest(token, "GET", `/users/me/goals/${goalName}/datapoints.json`, undefined, qs);
113
+ }
114
+ return paginateAll(token, `/users/me/goals/${goalName}/datapoints.json`, qs);
115
+ },
116
+ });
117
+ rl.registerAction("datapoint.update", {
118
+ description: "Update a datapoint",
119
+ inputSchema: {
120
+ goalName: { type: "string", required: true, description: "Goal slug" },
121
+ datapointId: { type: "string", required: true, description: "Datapoint ID" },
122
+ value: { type: "number", required: false, description: "New value" },
123
+ comment: { type: "string", required: false, description: "New comment" },
124
+ timestamp: { type: "number", required: false, description: "New unix timestamp" },
125
+ },
126
+ async execute(input, ctx) {
127
+ const { goalName, datapointId, ...body } = input;
128
+ return apiRequest(getToken(ctx), "PUT", `/users/me/goals/${goalName}/datapoints/${datapointId}.json`, body);
129
+ },
130
+ });
131
+ rl.registerAction("datapoint.delete", {
132
+ description: "Delete a datapoint",
133
+ inputSchema: {
134
+ goalName: { type: "string", required: true, description: "Goal slug" },
135
+ datapointId: { type: "string", required: true, description: "Datapoint ID" },
136
+ },
137
+ async execute(input, ctx) {
138
+ const { goalName, datapointId } = input;
139
+ return apiRequest(getToken(ctx), "DELETE", `/users/me/goals/${goalName}/datapoints/${datapointId}.json`);
140
+ },
141
+ });
142
+ // ── Charge ──────────────────────────────────────────
143
+ rl.registerAction("charge.create", {
144
+ description: "Create a charge (pay money to Beeminder)",
145
+ inputSchema: {
146
+ amount: { type: "number", required: true, description: "Amount in USD" },
147
+ note: { type: "string", required: false, description: "Charge explanation" },
148
+ dryrun: { type: "boolean", required: false, description: "Test without actually charging" },
149
+ },
150
+ async execute(input, ctx) {
151
+ const { amount, note, dryrun } = input;
152
+ const body = { user_id: "me", amount };
153
+ if (note)
154
+ body.note = note;
155
+ if (dryrun)
156
+ body.dryrun = dryrun;
157
+ return apiRequest(getToken(ctx), "POST", "/charges.json", body);
158
+ },
159
+ });
160
+ // ── Goal ────────────────────────────────────────────
161
+ rl.registerAction("goal.create", {
162
+ description: "Create a new goal",
163
+ inputSchema: {
164
+ slug: { type: "string", required: true, description: "Goal slug (unique identifier)" },
165
+ title: { type: "string", required: true, description: "Human-readable title" },
166
+ goal_type: { type: "string", required: true, description: "Goal type: hustler, biker, fatloser, gainer, inboxer, drinker, custom" },
167
+ gunits: { type: "string", required: true, description: "Units (e.g. hours, pages, pounds)" },
168
+ goaldate: { type: "number", required: false, description: "Target date (unix timestamp)" },
169
+ goalval: { type: "number", required: false, description: "Target value" },
170
+ rate: { type: "number", required: false, description: "Rate (units per day)" },
171
+ initval: { type: "number", required: false, description: "Initial value (default: 0)" },
172
+ secret: { type: "boolean", required: false, description: "Secret goal" },
173
+ datapublic: { type: "boolean", required: false, description: "Public data" },
174
+ datasource: { type: "string", required: false, description: "Data source: api, ifttt, zapier, manual" },
175
+ dryrun: { type: "boolean", required: false, description: "Test without creating" },
176
+ tags: { type: "array", required: false, description: "Array of tag strings" },
177
+ },
178
+ async execute(input, ctx) {
179
+ return apiRequest(getToken(ctx), "POST", "/users/me/goals.json", input);
180
+ },
181
+ });
182
+ rl.registerAction("goal.get", {
183
+ description: "Get a specific goal",
184
+ inputSchema: {
185
+ goalName: { type: "string", required: true, description: "Goal slug" },
186
+ datapoints: { type: "boolean", required: false, description: "Include datapoints" },
187
+ emaciated: { type: "boolean", required: false, description: "Strip road/roadall/fullroad attributes" },
188
+ },
189
+ async execute(input, ctx) {
190
+ const { goalName, ...qs } = input;
191
+ return apiRequest(getToken(ctx), "GET", `/users/me/goals/${goalName}.json`, undefined, qs);
192
+ },
193
+ });
194
+ rl.registerAction("goal.list", {
195
+ description: "List all goals",
196
+ inputSchema: {
197
+ emaciated: { type: "boolean", required: false, description: "Strip road attributes" },
198
+ },
199
+ async execute(input, ctx) {
200
+ const qs = (input ?? {});
201
+ return apiRequest(getToken(ctx), "GET", "/users/me/goals.json", undefined, qs);
202
+ },
203
+ });
204
+ rl.registerAction("goal.listArchived", {
205
+ description: "List archived goals",
206
+ inputSchema: {
207
+ emaciated: { type: "boolean", required: false, description: "Strip road attributes" },
208
+ },
209
+ async execute(input, ctx) {
210
+ const qs = (input ?? {});
211
+ return apiRequest(getToken(ctx), "GET", "/users/me/goals/archived.json", undefined, qs);
212
+ },
213
+ });
214
+ rl.registerAction("goal.update", {
215
+ description: "Update a goal",
216
+ inputSchema: {
217
+ goalName: { type: "string", required: true, description: "Goal slug" },
218
+ title: { type: "string", required: false, description: "New title" },
219
+ yaxis: { type: "string", required: false, description: "Y-axis label" },
220
+ tmin: { type: "string", required: false, description: "Min date (yyyy-mm-dd)" },
221
+ tmax: { type: "string", required: false, description: "Max date (yyyy-mm-dd)" },
222
+ secret: { type: "boolean", required: false, description: "Secret goal" },
223
+ datapublic: { type: "boolean", required: false, description: "Public data" },
224
+ roadall: { type: "array", required: false, description: "Road matrix [[date, value, rate], ...]" },
225
+ datasource: { type: "string", required: false, description: "Data source" },
226
+ tags: { type: "array", required: false, description: "Array of tag strings" },
227
+ },
228
+ async execute(input, ctx) {
229
+ const { goalName, ...body } = input;
230
+ return apiRequest(getToken(ctx), "PUT", `/users/me/goals/${goalName}.json`, body);
231
+ },
232
+ });
233
+ rl.registerAction("goal.refresh", {
234
+ description: "Refresh a goal's graph",
235
+ inputSchema: {
236
+ goalName: { type: "string", required: true, description: "Goal slug" },
237
+ },
238
+ async execute(input, ctx) {
239
+ const { goalName } = input;
240
+ return apiRequest(getToken(ctx), "GET", `/users/me/goals/${goalName}/refresh_graph.json`);
241
+ },
242
+ });
243
+ rl.registerAction("goal.shortCircuit", {
244
+ description: "Short-circuit a goal's pledge",
245
+ inputSchema: {
246
+ goalName: { type: "string", required: true, description: "Goal slug" },
247
+ },
248
+ async execute(input, ctx) {
249
+ const { goalName } = input;
250
+ return apiRequest(getToken(ctx), "POST", `/users/me/goals/${goalName}/shortcircuit.json`);
251
+ },
252
+ });
253
+ rl.registerAction("goal.stepDown", {
254
+ description: "Step down a goal's pledge",
255
+ inputSchema: {
256
+ goalName: { type: "string", required: true, description: "Goal slug" },
257
+ },
258
+ async execute(input, ctx) {
259
+ const { goalName } = input;
260
+ return apiRequest(getToken(ctx), "POST", `/users/me/goals/${goalName}/stepdown.json`);
261
+ },
262
+ });
263
+ rl.registerAction("goal.cancelStepDown", {
264
+ description: "Cancel a step-down on a goal's pledge",
265
+ inputSchema: {
266
+ goalName: { type: "string", required: true, description: "Goal slug" },
267
+ },
268
+ async execute(input, ctx) {
269
+ const { goalName } = input;
270
+ return apiRequest(getToken(ctx), "POST", `/users/me/goals/${goalName}/cancel_stepdown.json`);
271
+ },
272
+ });
273
+ rl.registerAction("goal.uncle", {
274
+ description: "Derail a goal and charge the pledge amount",
275
+ inputSchema: {
276
+ goalName: { type: "string", required: true, description: "Goal slug" },
277
+ },
278
+ async execute(input, ctx) {
279
+ const { goalName } = input;
280
+ return apiRequest(getToken(ctx), "POST", `/users/me/goals/${goalName}/uncleme.json`);
281
+ },
282
+ });
283
+ // ── User ────────────────────────────────────────────
284
+ rl.registerAction("user.get", {
285
+ description: "Get current user information",
286
+ inputSchema: {
287
+ associations: { type: "boolean", required: false, description: "Include associations" },
288
+ diff_since: { type: "number", required: false, description: "Unix timestamp — only return goals/datapoints changed since" },
289
+ skinny: { type: "boolean", required: false, description: "Minimal user data" },
290
+ emaciated: { type: "boolean", required: false, description: "Strip road attributes from goals" },
291
+ datapoints_count: { type: "number", required: false, description: "Number of datapoints to include" },
292
+ },
293
+ async execute(input, ctx) {
294
+ const qs = (input ?? {});
295
+ return apiRequest(getToken(ctx), "GET", "/users/me.json", undefined, qs);
296
+ },
297
+ });
298
+ }
@@ -0,0 +1,107 @@
1
+ const BASE_URL = "https://api-ssl.bitly.com/v4";
2
+ async function apiRequest(token, method, endpoint, body) {
3
+ const opts = {
4
+ method,
5
+ headers: {
6
+ "Content-Type": "application/json",
7
+ Authorization: `Bearer ${token}`,
8
+ },
9
+ };
10
+ if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE") {
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(`Bitly API error ${res.status}: ${text}`);
17
+ }
18
+ return res.json();
19
+ }
20
+ function getToken(ctx) {
21
+ return ctx.connection.config.accessToken;
22
+ }
23
+ export default function bitly(rl) {
24
+ rl.setName("bitly");
25
+ rl.setVersion("0.1.0");
26
+ rl.setConnectionSchema({
27
+ accessToken: {
28
+ type: "string",
29
+ required: true,
30
+ description: "Bitly access token",
31
+ env: "BITLY_ACCESS_TOKEN",
32
+ },
33
+ });
34
+ rl.registerAction("link.create", {
35
+ description: "Create a shortened link (bitlink)",
36
+ inputSchema: {
37
+ longUrl: { type: "string", required: true, description: "Long URL to shorten" },
38
+ title: { type: "string", required: false, description: "Title for the link" },
39
+ domain: { type: "string", required: false, description: "Custom domain (default: bit.ly)" },
40
+ group: { type: "string", required: false, description: "Group GUID" },
41
+ tags: { type: "array", required: false, description: "Array of tag strings" },
42
+ deeplinks: {
43
+ type: "array",
44
+ required: false,
45
+ description: "Array of {app_uri_path, install_type, install_url, app_id}",
46
+ },
47
+ },
48
+ async execute(input, ctx) {
49
+ const { longUrl, title, domain, group, tags, deeplinks } = (input ?? {});
50
+ const body = { long_url: longUrl };
51
+ if (title)
52
+ body.title = title;
53
+ if (domain)
54
+ body.domain = domain;
55
+ if (group)
56
+ body.group = group;
57
+ if (tags)
58
+ body.tags = tags;
59
+ if (deeplinks)
60
+ body.deeplinks = deeplinks;
61
+ return apiRequest(getToken(ctx), "POST", "/bitlinks", body);
62
+ },
63
+ });
64
+ rl.registerAction("link.get", {
65
+ description: "Get a bitlink by ID",
66
+ inputSchema: {
67
+ bitlink: { type: "string", required: true, description: "Bitlink (e.g. bit.ly/22u3ypK)" },
68
+ },
69
+ async execute(input, ctx) {
70
+ const { bitlink } = input;
71
+ return apiRequest(getToken(ctx), "GET", `/bitlinks/${bitlink}`);
72
+ },
73
+ });
74
+ rl.registerAction("link.update", {
75
+ description: "Update a bitlink",
76
+ inputSchema: {
77
+ bitlink: { type: "string", required: true, description: "Bitlink (e.g. bit.ly/22u3ypK)" },
78
+ longUrl: { type: "string", required: false, description: "New long URL" },
79
+ title: { type: "string", required: false, description: "New title" },
80
+ archived: { type: "boolean", required: false, description: "Archive the link" },
81
+ group: { type: "string", required: false, description: "Group GUID" },
82
+ tags: { type: "array", required: false, description: "Array of tag strings" },
83
+ deeplinks: {
84
+ type: "array",
85
+ required: false,
86
+ description: "Array of {app_uri_path, install_type, install_url, app_id}",
87
+ },
88
+ },
89
+ async execute(input, ctx) {
90
+ const { bitlink, longUrl, title, archived, group, tags, deeplinks } = (input ?? {});
91
+ const body = {};
92
+ if (longUrl)
93
+ body.long_url = longUrl;
94
+ if (title)
95
+ body.title = title;
96
+ if (archived !== undefined)
97
+ body.archived = archived;
98
+ if (group)
99
+ body.group = group;
100
+ if (tags)
101
+ body.tags = tags;
102
+ if (deeplinks)
103
+ body.deeplinks = deeplinks;
104
+ return apiRequest(getToken(ctx), "PATCH", `/bitlinks/${bitlink}`, body);
105
+ },
106
+ });
107
+ }