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,443 @@
1
+ const BASE_URL = "https://api.clockify.me/api/v1";
2
+ async function apiRequest(apiKey, method, endpoint, body, qs) {
3
+ const url = new URL(`${BASE_URL}/${endpoint}`);
4
+ if (qs) {
5
+ for (const [k, v] of Object.entries(qs)) {
6
+ if (v !== undefined && v !== null)
7
+ url.searchParams.set(k, String(v));
8
+ }
9
+ }
10
+ const opts = {
11
+ method,
12
+ headers: { "Content-Type": "application/json", "X-Api-Key": apiKey },
13
+ };
14
+ if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE") {
15
+ opts.body = JSON.stringify(body);
16
+ }
17
+ const res = await fetch(url.toString(), opts);
18
+ if (!res.ok) {
19
+ const text = await res.text();
20
+ throw new Error(`Clockify API error ${res.status}: ${text}`);
21
+ }
22
+ if (res.status === 204)
23
+ return { success: true };
24
+ const ct = res.headers.get("content-type") ?? "";
25
+ if (ct.includes("application/json"))
26
+ return res.json();
27
+ return { success: true };
28
+ }
29
+ async function paginateAll(apiKey, endpoint, qs, limit) {
30
+ const results = [];
31
+ let page = 1;
32
+ const size = 50;
33
+ while (true) {
34
+ const data = (await apiRequest(apiKey, "GET", endpoint, undefined, { ...qs, page, "page-size": size }));
35
+ if (!Array.isArray(data))
36
+ break;
37
+ results.push(...data);
38
+ if (limit && results.length >= limit)
39
+ return results.slice(0, limit);
40
+ if (data.length < size)
41
+ break;
42
+ page++;
43
+ }
44
+ return results;
45
+ }
46
+ function getKey(ctx) {
47
+ return ctx.connection.config.apiKey;
48
+ }
49
+ export default function clockify(rl) {
50
+ rl.setName("clockify");
51
+ rl.setVersion("0.1.0");
52
+ rl.setConnectionSchema({
53
+ apiKey: {
54
+ type: "string",
55
+ required: true,
56
+ description: "Clockify API key",
57
+ env: "CLOCKIFY_API_KEY",
58
+ },
59
+ });
60
+ // ── Client ──────────────────────────────────────────
61
+ rl.registerAction("client.create", {
62
+ description: "Create a client",
63
+ inputSchema: {
64
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
65
+ name: { type: "string", required: true, description: "Client name" },
66
+ },
67
+ async execute(input, ctx) {
68
+ const { workspaceId, name } = input;
69
+ return apiRequest(getKey(ctx), "POST", `workspaces/${workspaceId}/clients`, { name });
70
+ },
71
+ });
72
+ rl.registerAction("client.get", {
73
+ description: "Get a client",
74
+ inputSchema: {
75
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
76
+ clientId: { type: "string", required: true, description: "Client ID" },
77
+ },
78
+ async execute(input, ctx) {
79
+ const { workspaceId, clientId } = input;
80
+ return apiRequest(getKey(ctx), "GET", `workspaces/${workspaceId}/clients/${clientId}`);
81
+ },
82
+ });
83
+ rl.registerAction("client.list", {
84
+ description: "List clients in a workspace",
85
+ inputSchema: {
86
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
87
+ name: { type: "string", required: false, description: "Filter by name" },
88
+ archived: { type: "boolean", required: false, description: "Include archived" },
89
+ limit: { type: "number", required: false, description: "Max results" },
90
+ },
91
+ async execute(input, ctx) {
92
+ const { workspaceId, name, archived, limit } = (input ?? {});
93
+ const qs = {};
94
+ if (name)
95
+ qs.name = name;
96
+ if (archived !== undefined)
97
+ qs.archived = archived;
98
+ return paginateAll(getKey(ctx), `workspaces/${workspaceId}/clients`, qs, limit);
99
+ },
100
+ });
101
+ rl.registerAction("client.update", {
102
+ description: "Update a client",
103
+ inputSchema: {
104
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
105
+ clientId: { type: "string", required: true, description: "Client ID" },
106
+ name: { type: "string", required: true, description: "Client name" },
107
+ archived: { type: "boolean", required: false, description: "Archived" },
108
+ },
109
+ async execute(input, ctx) {
110
+ const { workspaceId, clientId, ...body } = input;
111
+ return apiRequest(getKey(ctx), "PUT", `workspaces/${workspaceId}/clients/${clientId}`, body);
112
+ },
113
+ });
114
+ rl.registerAction("client.delete", {
115
+ description: "Delete a client",
116
+ inputSchema: {
117
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
118
+ clientId: { type: "string", required: true, description: "Client ID" },
119
+ },
120
+ async execute(input, ctx) {
121
+ const { workspaceId, clientId } = input;
122
+ await apiRequest(getKey(ctx), "DELETE", `workspaces/${workspaceId}/clients/${clientId}`);
123
+ return { success: true };
124
+ },
125
+ });
126
+ // ── Project ─────────────────────────────────────────
127
+ rl.registerAction("project.create", {
128
+ description: "Create a project",
129
+ inputSchema: {
130
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
131
+ name: { type: "string", required: true, description: "Project name" },
132
+ clientId: { type: "string", required: false, description: "Client ID" },
133
+ isPublic: { type: "boolean", required: false, description: "Public project" },
134
+ billable: { type: "boolean", required: false, description: "Billable" },
135
+ color: { type: "string", required: false, description: "Color hex" },
136
+ note: { type: "string", required: false, description: "Note" },
137
+ },
138
+ async execute(input, ctx) {
139
+ const { workspaceId, ...body } = input;
140
+ return apiRequest(getKey(ctx), "POST", `workspaces/${workspaceId}/projects`, body);
141
+ },
142
+ });
143
+ rl.registerAction("project.get", {
144
+ description: "Get a project",
145
+ inputSchema: {
146
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
147
+ projectId: { type: "string", required: true, description: "Project ID" },
148
+ },
149
+ async execute(input, ctx) {
150
+ const { workspaceId, projectId } = input;
151
+ return apiRequest(getKey(ctx), "GET", `workspaces/${workspaceId}/projects/${projectId}`);
152
+ },
153
+ });
154
+ rl.registerAction("project.list", {
155
+ description: "List projects in a workspace",
156
+ inputSchema: {
157
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
158
+ name: { type: "string", required: false, description: "Filter by name" },
159
+ archived: { type: "boolean", required: false, description: "Include archived" },
160
+ limit: { type: "number", required: false, description: "Max results" },
161
+ },
162
+ async execute(input, ctx) {
163
+ const { workspaceId, name, archived, limit } = (input ?? {});
164
+ const qs = {};
165
+ if (name)
166
+ qs.name = name;
167
+ if (archived !== undefined)
168
+ qs.archived = archived;
169
+ return paginateAll(getKey(ctx), `workspaces/${workspaceId}/projects`, qs, limit);
170
+ },
171
+ });
172
+ rl.registerAction("project.update", {
173
+ description: "Update a project",
174
+ inputSchema: {
175
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
176
+ projectId: { type: "string", required: true, description: "Project ID" },
177
+ name: { type: "string", required: false, description: "Name" },
178
+ clientId: { type: "string", required: false, description: "Client ID" },
179
+ isPublic: { type: "boolean", required: false, description: "Public" },
180
+ billable: { type: "boolean", required: false, description: "Billable" },
181
+ color: { type: "string", required: false, description: "Color" },
182
+ note: { type: "string", required: false, description: "Note" },
183
+ archived: { type: "boolean", required: false, description: "Archived" },
184
+ },
185
+ async execute(input, ctx) {
186
+ const { workspaceId, projectId, ...body } = input;
187
+ return apiRequest(getKey(ctx), "PUT", `workspaces/${workspaceId}/projects/${projectId}`, body);
188
+ },
189
+ });
190
+ rl.registerAction("project.delete", {
191
+ description: "Delete a project",
192
+ inputSchema: {
193
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
194
+ projectId: { type: "string", required: true, description: "Project ID" },
195
+ },
196
+ async execute(input, ctx) {
197
+ const { workspaceId, projectId } = input;
198
+ await apiRequest(getKey(ctx), "DELETE", `workspaces/${workspaceId}/projects/${projectId}`);
199
+ return { success: true };
200
+ },
201
+ });
202
+ // ── Tag ─────────────────────────────────────────────
203
+ rl.registerAction("tag.create", {
204
+ description: "Create a tag",
205
+ inputSchema: {
206
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
207
+ name: { type: "string", required: true, description: "Tag name" },
208
+ },
209
+ async execute(input, ctx) {
210
+ const { workspaceId, name } = input;
211
+ return apiRequest(getKey(ctx), "POST", `workspaces/${workspaceId}/tags`, { name });
212
+ },
213
+ });
214
+ rl.registerAction("tag.list", {
215
+ description: "List tags in a workspace",
216
+ inputSchema: {
217
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
218
+ name: { type: "string", required: false, description: "Filter by name" },
219
+ archived: { type: "boolean", required: false, description: "Include archived" },
220
+ limit: { type: "number", required: false, description: "Max results" },
221
+ },
222
+ async execute(input, ctx) {
223
+ const { workspaceId, name, archived, limit } = (input ?? {});
224
+ const qs = {};
225
+ if (name)
226
+ qs.name = name;
227
+ if (archived !== undefined)
228
+ qs.archived = archived;
229
+ return paginateAll(getKey(ctx), `workspaces/${workspaceId}/tags`, qs, limit);
230
+ },
231
+ });
232
+ rl.registerAction("tag.update", {
233
+ description: "Update a tag",
234
+ inputSchema: {
235
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
236
+ tagId: { type: "string", required: true, description: "Tag ID" },
237
+ name: { type: "string", required: false, description: "Name" },
238
+ archived: { type: "boolean", required: false, description: "Archived" },
239
+ },
240
+ async execute(input, ctx) {
241
+ const { workspaceId, tagId, ...body } = input;
242
+ return apiRequest(getKey(ctx), "PUT", `workspaces/${workspaceId}/tags/${tagId}`, body);
243
+ },
244
+ });
245
+ rl.registerAction("tag.delete", {
246
+ description: "Delete a tag",
247
+ inputSchema: {
248
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
249
+ tagId: { type: "string", required: true, description: "Tag ID" },
250
+ },
251
+ async execute(input, ctx) {
252
+ const { workspaceId, tagId } = input;
253
+ await apiRequest(getKey(ctx), "DELETE", `workspaces/${workspaceId}/tags/${tagId}`);
254
+ return { success: true };
255
+ },
256
+ });
257
+ // ── Task ────────────────────────────────────────────
258
+ rl.registerAction("task.create", {
259
+ description: "Create a task in a project",
260
+ inputSchema: {
261
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
262
+ projectId: { type: "string", required: true, description: "Project ID" },
263
+ name: { type: "string", required: true, description: "Task name" },
264
+ assigneeIds: { type: "array", required: false, description: "Assignee user IDs" },
265
+ estimate: { type: "string", required: false, description: "Estimate (HH:MM format)" },
266
+ status: { type: "string", required: false, description: "Status: ACTIVE or DONE" },
267
+ },
268
+ async execute(input, ctx) {
269
+ const { workspaceId, projectId, estimate, ...body } = input;
270
+ if (estimate) {
271
+ const [h, m] = estimate.split(":");
272
+ body.estimate = `PT${h}H${m}M`;
273
+ }
274
+ return apiRequest(getKey(ctx), "POST", `workspaces/${workspaceId}/projects/${projectId}/tasks`, body);
275
+ },
276
+ });
277
+ rl.registerAction("task.get", {
278
+ description: "Get a task",
279
+ inputSchema: {
280
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
281
+ projectId: { type: "string", required: true, description: "Project ID" },
282
+ taskId: { type: "string", required: true, description: "Task ID" },
283
+ },
284
+ async execute(input, ctx) {
285
+ const { workspaceId, projectId, taskId } = input;
286
+ return apiRequest(getKey(ctx), "GET", `workspaces/${workspaceId}/projects/${projectId}/tasks/${taskId}`);
287
+ },
288
+ });
289
+ rl.registerAction("task.list", {
290
+ description: "List tasks in a project",
291
+ inputSchema: {
292
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
293
+ projectId: { type: "string", required: true, description: "Project ID" },
294
+ isActive: { type: "boolean", required: false, description: "Filter active tasks" },
295
+ name: { type: "string", required: false, description: "Filter by name" },
296
+ limit: { type: "number", required: false, description: "Max results" },
297
+ },
298
+ async execute(input, ctx) {
299
+ const { workspaceId, projectId, isActive, name, limit } = (input ?? {});
300
+ const qs = {};
301
+ if (isActive !== undefined)
302
+ qs["is-active"] = isActive;
303
+ if (name)
304
+ qs.name = name;
305
+ if (limit) {
306
+ qs["page-size"] = limit;
307
+ return apiRequest(getKey(ctx), "GET", `workspaces/${workspaceId}/projects/${projectId}/tasks`, undefined, qs);
308
+ }
309
+ return paginateAll(getKey(ctx), `workspaces/${workspaceId}/projects/${projectId}/tasks`, qs);
310
+ },
311
+ });
312
+ rl.registerAction("task.update", {
313
+ description: "Update a task",
314
+ inputSchema: {
315
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
316
+ projectId: { type: "string", required: true, description: "Project ID" },
317
+ taskId: { type: "string", required: true, description: "Task ID" },
318
+ name: { type: "string", required: false, description: "Name" },
319
+ assigneeIds: { type: "array", required: false, description: "Assignee IDs" },
320
+ estimate: { type: "string", required: false, description: "Estimate (HH:MM)" },
321
+ status: { type: "string", required: false, description: "Status" },
322
+ },
323
+ async execute(input, ctx) {
324
+ const { workspaceId, projectId, taskId, estimate, ...body } = input;
325
+ if (estimate) {
326
+ const [h, m] = estimate.split(":");
327
+ body.estimate = `PT${h}H${m}M`;
328
+ }
329
+ return apiRequest(getKey(ctx), "PUT", `workspaces/${workspaceId}/projects/${projectId}/tasks/${taskId}`, body);
330
+ },
331
+ });
332
+ rl.registerAction("task.delete", {
333
+ description: "Delete a task",
334
+ inputSchema: {
335
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
336
+ projectId: { type: "string", required: true, description: "Project ID" },
337
+ taskId: { type: "string", required: true, description: "Task ID" },
338
+ },
339
+ async execute(input, ctx) {
340
+ const { workspaceId, projectId, taskId } = input;
341
+ await apiRequest(getKey(ctx), "DELETE", `workspaces/${workspaceId}/projects/${projectId}/tasks/${taskId}`);
342
+ return { success: true };
343
+ },
344
+ });
345
+ // ── Time Entry ──────────────────────────────────────
346
+ rl.registerAction("timeEntry.create", {
347
+ description: "Create a time entry",
348
+ inputSchema: {
349
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
350
+ start: { type: "string", required: true, description: "Start time (ISO 8601)" },
351
+ end: { type: "string", required: false, description: "End time (ISO 8601)" },
352
+ description: { type: "string", required: false, description: "Description" },
353
+ projectId: { type: "string", required: false, description: "Project ID" },
354
+ taskId: { type: "string", required: false, description: "Task ID" },
355
+ tagIds: { type: "array", required: false, description: "Tag IDs" },
356
+ billable: { type: "boolean", required: false, description: "Billable" },
357
+ },
358
+ async execute(input, ctx) {
359
+ const { workspaceId, ...body } = input;
360
+ return apiRequest(getKey(ctx), "POST", `workspaces/${workspaceId}/time-entries`, body);
361
+ },
362
+ });
363
+ rl.registerAction("timeEntry.get", {
364
+ description: "Get a time entry",
365
+ inputSchema: {
366
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
367
+ timeEntryId: { type: "string", required: true, description: "Time entry ID" },
368
+ },
369
+ async execute(input, ctx) {
370
+ const { workspaceId, timeEntryId } = input;
371
+ return apiRequest(getKey(ctx), "GET", `workspaces/${workspaceId}/time-entries/${timeEntryId}`);
372
+ },
373
+ });
374
+ rl.registerAction("timeEntry.update", {
375
+ description: "Update a time entry",
376
+ inputSchema: {
377
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
378
+ timeEntryId: { type: "string", required: true, description: "Time entry ID" },
379
+ start: { type: "string", required: false, description: "Start time" },
380
+ end: { type: "string", required: false, description: "End time" },
381
+ description: { type: "string", required: false, description: "Description" },
382
+ projectId: { type: "string", required: false, description: "Project ID" },
383
+ taskId: { type: "string", required: false, description: "Task ID" },
384
+ tagIds: { type: "array", required: false, description: "Tag IDs" },
385
+ billable: { type: "boolean", required: false, description: "Billable" },
386
+ },
387
+ async execute(input, ctx) {
388
+ const { workspaceId, timeEntryId, ...body } = input;
389
+ // start is required by API — fetch current if not set
390
+ if (!body.start) {
391
+ const current = (await apiRequest(getKey(ctx), "GET", `workspaces/${workspaceId}/time-entries/${timeEntryId}`));
392
+ const interval = current.timeInterval;
393
+ body.start = interval.start;
394
+ }
395
+ return apiRequest(getKey(ctx), "PUT", `workspaces/${workspaceId}/time-entries/${timeEntryId}`, body);
396
+ },
397
+ });
398
+ rl.registerAction("timeEntry.delete", {
399
+ description: "Delete a time entry",
400
+ inputSchema: {
401
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
402
+ timeEntryId: { type: "string", required: true, description: "Time entry ID" },
403
+ },
404
+ async execute(input, ctx) {
405
+ const { workspaceId, timeEntryId } = input;
406
+ await apiRequest(getKey(ctx), "DELETE", `workspaces/${workspaceId}/time-entries/${timeEntryId}`);
407
+ return { success: true };
408
+ },
409
+ });
410
+ // ── User ────────────────────────────────────────────
411
+ rl.registerAction("user.list", {
412
+ description: "List users in a workspace",
413
+ inputSchema: {
414
+ workspaceId: { type: "string", required: true, description: "Workspace ID" },
415
+ email: { type: "string", required: false, description: "Filter by email" },
416
+ status: { type: "string", required: false, description: "Filter by status: ACTIVE, PENDING, DECLINED" },
417
+ limit: { type: "number", required: false, description: "Max results" },
418
+ },
419
+ async execute(input, ctx) {
420
+ const { workspaceId, email, status, limit } = (input ?? {});
421
+ const qs = {};
422
+ if (email)
423
+ qs.email = email;
424
+ if (status)
425
+ qs.status = status;
426
+ return paginateAll(getKey(ctx), `workspaces/${workspaceId}/users`, qs, limit);
427
+ },
428
+ });
429
+ // ── Workspace ───────────────────────────────────────
430
+ rl.registerAction("workspace.list", {
431
+ description: "List all workspaces",
432
+ inputSchema: {
433
+ limit: { type: "number", required: false, description: "Max results" },
434
+ },
435
+ async execute(input, ctx) {
436
+ const { limit } = (input ?? {});
437
+ const data = (await apiRequest(getKey(ctx), "GET", "workspaces"));
438
+ if (limit)
439
+ return data.slice(0, limit);
440
+ return data;
441
+ },
442
+ });
443
+ }
@@ -0,0 +1,93 @@
1
+ const BASE_URL = "https://api.cloudflare.com/client/v4";
2
+ async function apiRequest(token, method, endpoint, body, qs) {
3
+ const url = new URL(`${BASE_URL}${endpoint}`);
4
+ if (qs) {
5
+ for (const [k, v] of Object.entries(qs)) {
6
+ if (v !== undefined && v !== null)
7
+ url.searchParams.set(k, String(v));
8
+ }
9
+ }
10
+ const opts = {
11
+ method,
12
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
13
+ };
14
+ if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE") {
15
+ opts.body = JSON.stringify(body);
16
+ }
17
+ const res = await fetch(url.toString(), opts);
18
+ if (!res.ok) {
19
+ const text = await res.text();
20
+ throw new Error(`Cloudflare API error ${res.status}: ${text}`);
21
+ }
22
+ return res.json();
23
+ }
24
+ function getToken(ctx) {
25
+ return ctx.connection.config.apiToken;
26
+ }
27
+ export default function cloudflare(rl) {
28
+ rl.setName("cloudflare");
29
+ rl.setVersion("0.1.0");
30
+ rl.setConnectionSchema({
31
+ apiToken: {
32
+ type: "string",
33
+ required: true,
34
+ description: "Cloudflare API token",
35
+ env: "CLOUDFLARE_API_TOKEN",
36
+ },
37
+ });
38
+ rl.registerAction("zoneCertificate.get", {
39
+ description: "Get a zone-level origin certificate",
40
+ inputSchema: {
41
+ zoneId: { type: "string", required: true, description: "Zone ID" },
42
+ certificateId: { type: "string", required: true, description: "Certificate ID" },
43
+ },
44
+ async execute(input, ctx) {
45
+ const { zoneId, certificateId } = input;
46
+ const data = (await apiRequest(getToken(ctx), "GET", `/zones/${zoneId}/origin_tls_client_auth/${certificateId}`));
47
+ return data.result;
48
+ },
49
+ });
50
+ rl.registerAction("zoneCertificate.list", {
51
+ description: "List zone-level origin certificates",
52
+ inputSchema: {
53
+ zoneId: { type: "string", required: true, description: "Zone ID" },
54
+ limit: { type: "number", required: false, description: "Max results" },
55
+ },
56
+ async execute(input, ctx) {
57
+ const { zoneId, limit } = input;
58
+ const qs = {};
59
+ if (limit)
60
+ qs.per_page = limit;
61
+ const data = (await apiRequest(getToken(ctx), "GET", `/zones/${zoneId}/origin_tls_client_auth`, undefined, qs));
62
+ return data.result;
63
+ },
64
+ });
65
+ rl.registerAction("zoneCertificate.upload", {
66
+ description: "Upload a zone-level origin certificate",
67
+ inputSchema: {
68
+ zoneId: { type: "string", required: true, description: "Zone ID" },
69
+ certificate: { type: "string", required: true, description: "PEM certificate" },
70
+ privateKey: { type: "string", required: true, description: "PEM private key" },
71
+ },
72
+ async execute(input, ctx) {
73
+ const { zoneId, certificate, privateKey } = input;
74
+ const data = (await apiRequest(getToken(ctx), "POST", `/zones/${zoneId}/origin_tls_client_auth`, {
75
+ certificate,
76
+ private_key: privateKey,
77
+ }));
78
+ return data.result;
79
+ },
80
+ });
81
+ rl.registerAction("zoneCertificate.delete", {
82
+ description: "Delete a zone-level origin certificate",
83
+ inputSchema: {
84
+ zoneId: { type: "string", required: true, description: "Zone ID" },
85
+ certificateId: { type: "string", required: true, description: "Certificate ID" },
86
+ },
87
+ async execute(input, ctx) {
88
+ const { zoneId, certificateId } = input;
89
+ const data = (await apiRequest(getToken(ctx), "DELETE", `/zones/${zoneId}/origin_tls_client_auth/${certificateId}`));
90
+ return data.result;
91
+ },
92
+ });
93
+ }
@@ -0,0 +1,131 @@
1
+ async function apiRequest(baseUrl, token, method, endpoint, body) {
2
+ const url = new URL(`${baseUrl}/api${endpoint}`);
3
+ url.searchParams.set("token", token);
4
+ const opts = {
5
+ method,
6
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
7
+ };
8
+ if (body && Object.keys(body).length > 0 && method !== "GET") {
9
+ opts.body = JSON.stringify(body);
10
+ }
11
+ const res = await fetch(url.toString(), opts);
12
+ if (!res.ok) {
13
+ const text = await res.text();
14
+ throw new Error(`Cockpit API error ${res.status}: ${text}`);
15
+ }
16
+ return res.json();
17
+ }
18
+ function getConn(ctx) {
19
+ return {
20
+ baseUrl: ctx.connection.config.url.replace(/\/$/, ""),
21
+ token: ctx.connection.config.accessToken,
22
+ };
23
+ }
24
+ export default function cockpit(rl) {
25
+ rl.setName("cockpit");
26
+ rl.setVersion("0.1.0");
27
+ rl.setConnectionSchema({
28
+ url: {
29
+ type: "string",
30
+ required: true,
31
+ description: "Cockpit CMS URL (e.g. https://cockpit.example.com)",
32
+ env: "COCKPIT_URL",
33
+ },
34
+ accessToken: {
35
+ type: "string",
36
+ required: true,
37
+ description: "Cockpit API token",
38
+ env: "COCKPIT_ACCESS_TOKEN",
39
+ },
40
+ });
41
+ // ── Collection ──────────────────────────────────────
42
+ rl.registerAction("collection.create", {
43
+ description: "Create an entry in a collection",
44
+ inputSchema: {
45
+ collection: { type: "string", required: true, description: "Collection name" },
46
+ data: { type: "object", required: true, description: "Entry data as key-value pairs" },
47
+ },
48
+ async execute(input, ctx) {
49
+ const { collection, data } = input;
50
+ const { baseUrl, token } = getConn(ctx);
51
+ return apiRequest(baseUrl, token, "POST", `/collections/save/${collection}`, { data });
52
+ },
53
+ });
54
+ rl.registerAction("collection.list", {
55
+ description: "List entries in a collection",
56
+ inputSchema: {
57
+ collection: { type: "string", required: true, description: "Collection name" },
58
+ filter: { type: "object", required: false, description: "Filter object" },
59
+ fields: { type: "array", required: false, description: "Array of field names to return" },
60
+ sort: { type: "object", required: false, description: "Sort object (e.g. {fieldName: 1})" },
61
+ limit: { type: "number", required: false, description: "Max results" },
62
+ skip: { type: "number", required: false, description: "Number to skip" },
63
+ populate: { type: "boolean", required: false, description: "Populate linked entries" },
64
+ language: { type: "string", required: false, description: "Language code" },
65
+ },
66
+ async execute(input, ctx) {
67
+ const { collection, filter, fields, sort, limit, skip, populate, language } = (input ?? {});
68
+ const { baseUrl, token } = getConn(ctx);
69
+ const body = { simple: true };
70
+ if (filter)
71
+ body.filter = filter;
72
+ if (fields) {
73
+ const f = { _id: false };
74
+ for (const name of fields)
75
+ f[name] = true;
76
+ body.fields = f;
77
+ }
78
+ if (sort)
79
+ body.sort = sort;
80
+ if (limit)
81
+ body.limit = limit;
82
+ if (skip)
83
+ body.skip = skip;
84
+ if (populate)
85
+ body.populate = populate;
86
+ if (language)
87
+ body.lang = language;
88
+ return apiRequest(baseUrl, token, "POST", `/collections/get/${collection}`, body);
89
+ },
90
+ });
91
+ rl.registerAction("collection.update", {
92
+ description: "Update an entry in a collection",
93
+ inputSchema: {
94
+ collection: { type: "string", required: true, description: "Collection name" },
95
+ id: { type: "string", required: true, description: "Entry _id" },
96
+ data: { type: "object", required: true, description: "Fields to update" },
97
+ },
98
+ async execute(input, ctx) {
99
+ const { collection, id, data } = input;
100
+ const { baseUrl, token } = getConn(ctx);
101
+ return apiRequest(baseUrl, token, "POST", `/collections/save/${collection}`, {
102
+ data: { _id: id, ...data },
103
+ });
104
+ },
105
+ });
106
+ // ── Form ────────────────────────────────────────────
107
+ rl.registerAction("form.submit", {
108
+ description: "Submit a form",
109
+ inputSchema: {
110
+ form: { type: "string", required: true, description: "Form name" },
111
+ data: { type: "object", required: true, description: "Form data as key-value pairs" },
112
+ },
113
+ async execute(input, ctx) {
114
+ const { form, data } = input;
115
+ const { baseUrl, token } = getConn(ctx);
116
+ return apiRequest(baseUrl, token, "POST", `/forms/submit/${form}`, { form: data });
117
+ },
118
+ });
119
+ // ── Singleton ───────────────────────────────────────
120
+ rl.registerAction("singleton.get", {
121
+ description: "Get a singleton's data",
122
+ inputSchema: {
123
+ singleton: { type: "string", required: true, description: "Singleton name" },
124
+ },
125
+ async execute(input, ctx) {
126
+ const { singleton } = input;
127
+ const { baseUrl, token } = getConn(ctx);
128
+ return apiRequest(baseUrl, token, "GET", `/singletons/get/${singleton}`);
129
+ },
130
+ });
131
+ }