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,104 @@
1
+ function getConn(ctx) {
2
+ const c = ctx.connection.config;
3
+ const registryUrl = (c.registryUrl ?? "https://registry.npmjs.org").replace(/\/$/, "");
4
+ const token = c.token;
5
+ return { registryUrl, token };
6
+ }
7
+ async function apiRequest(conn, method, path, body, contentType) {
8
+ const headers = {};
9
+ if (conn.token)
10
+ headers.Authorization = `Bearer ${conn.token}`;
11
+ if (contentType)
12
+ headers["Content-Type"] = contentType;
13
+ else
14
+ headers["Content-Type"] = "application/json";
15
+ const init = { method, headers };
16
+ if (body !== undefined)
17
+ init.body = body;
18
+ const res = await fetch(`${conn.registryUrl}${path}`, init);
19
+ if (!res.ok)
20
+ throw new Error(`npm registry error ${res.status}: ${await res.text()}`);
21
+ const text = await res.text();
22
+ return text ? JSON.parse(text) : {};
23
+ }
24
+ export default function npm(rl) {
25
+ rl.setName("npm");
26
+ rl.setVersion("0.1.0");
27
+ rl.setConnectionSchema({
28
+ registryUrl: { type: "string", required: false, description: "NPM registry URL (default: https://registry.npmjs.org)", env: "NPM_REGISTRY_URL" },
29
+ token: { type: "string", required: false, description: "NPM auth token (optional, needed for private packages and dist-tag updates)", env: "NPM_TOKEN" },
30
+ });
31
+ rl.registerAction("package.getMetadata", {
32
+ description: "Get metadata for a package at a specific version",
33
+ inputSchema: {
34
+ packageName: { type: "string", required: true, description: "Package name (e.g. lodash)" },
35
+ version: { type: "string", required: false, description: "Version or tag (default: latest)" },
36
+ },
37
+ async execute(input, ctx) {
38
+ const { packageName, version } = input;
39
+ const v = version || "latest";
40
+ return apiRequest(getConn(ctx), "GET", `/${encodeURIComponent(packageName)}/${v}`);
41
+ },
42
+ });
43
+ rl.registerAction("package.getVersions", {
44
+ description: "Get all versions for a package with publish dates",
45
+ inputSchema: {
46
+ packageName: { type: "string", required: true, description: "Package name" },
47
+ },
48
+ async execute(input, ctx) {
49
+ const { packageName } = input;
50
+ const data = (await apiRequest(getConn(ctx), "GET", `/${encodeURIComponent(packageName)}`));
51
+ const time = (data.time ?? {});
52
+ const versions = Object.entries(time)
53
+ .filter(([v]) => /^\d+\.\d+\.\d+/.test(v))
54
+ .map(([version, published_at]) => ({ version, published_at }))
55
+ .sort((a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime());
56
+ return versions;
57
+ },
58
+ });
59
+ rl.registerAction("package.search", {
60
+ description: "Search for packages on the npm registry",
61
+ inputSchema: {
62
+ query: { type: "string", required: true, description: "Search query" },
63
+ limit: { type: "number", required: false, description: "Max results (default 10, max 100)" },
64
+ offset: { type: "number", required: false, description: "Offset for pagination (default 0)" },
65
+ },
66
+ async execute(input, ctx) {
67
+ const p = (input ?? {});
68
+ const params = new URLSearchParams();
69
+ params.set("text", p.query);
70
+ params.set("size", String(p.limit ?? 10));
71
+ params.set("from", String(p.offset ?? 0));
72
+ params.set("popularity", "0.99");
73
+ const data = (await apiRequest(getConn(ctx), "GET", `/-/v1/search?${params.toString()}`));
74
+ const objects = (data.objects ?? []);
75
+ return objects.map(({ package: pkg }) => ({
76
+ name: pkg.name,
77
+ version: pkg.version,
78
+ description: pkg.description,
79
+ }));
80
+ },
81
+ });
82
+ rl.registerAction("distTag.list", {
83
+ description: "Get all dist-tags for a package",
84
+ inputSchema: {
85
+ packageName: { type: "string", required: true, description: "Package name" },
86
+ },
87
+ async execute(input, ctx) {
88
+ const { packageName } = input;
89
+ return apiRequest(getConn(ctx), "GET", `/-/package/${encodeURIComponent(packageName)}/dist-tags`);
90
+ },
91
+ });
92
+ rl.registerAction("distTag.update", {
93
+ description: "Update a dist-tag for a package (requires auth)",
94
+ inputSchema: {
95
+ packageName: { type: "string", required: true, description: "Package name" },
96
+ tagName: { type: "string", required: true, description: "Dist-tag name (e.g. latest)" },
97
+ version: { type: "string", required: true, description: "Version to point the tag to" },
98
+ },
99
+ async execute(input, ctx) {
100
+ const { packageName, tagName, version } = input;
101
+ return apiRequest(getConn(ctx), "PUT", `/-/package/${encodeURIComponent(packageName)}/dist-tags/${encodeURIComponent(tagName)}`, version, "application/x-www-form-urlencoded");
102
+ },
103
+ });
104
+ }
@@ -0,0 +1,157 @@
1
+ function getConn(ctx) {
2
+ const c = ctx.connection.config;
3
+ return {
4
+ url: c.url.replace(/\/$/, ""),
5
+ db: c.db,
6
+ username: c.username,
7
+ password: c.password,
8
+ };
9
+ }
10
+ function getDBName(db, url) {
11
+ if (db)
12
+ return db;
13
+ try {
14
+ return new URL(url).hostname.split(".")[0];
15
+ }
16
+ catch {
17
+ return "";
18
+ }
19
+ }
20
+ async function jsonRpc(url, params) {
21
+ const body = { jsonrpc: "2.0", method: "call", params, id: Math.floor(Math.random() * 1000) };
22
+ const res = await fetch(`${url}/jsonrpc`, {
23
+ method: "POST",
24
+ headers: { "Content-Type": "application/json" },
25
+ body: JSON.stringify(body),
26
+ });
27
+ if (!res.ok)
28
+ throw new Error(`Odoo HTTP error ${res.status}: ${await res.text()}`);
29
+ const json = (await res.json());
30
+ if (json.error) {
31
+ const err = json.error;
32
+ const data = (err.data ?? err);
33
+ throw new Error(`Odoo RPC error: ${data.message ?? JSON.stringify(err)}`);
34
+ }
35
+ return json.result;
36
+ }
37
+ async function login(url, db, username, password) {
38
+ const uid = (await jsonRpc(url, { service: "common", method: "login", args: [db, username, password] }));
39
+ if (!uid)
40
+ throw new Error("Odoo login failed — check credentials");
41
+ return uid;
42
+ }
43
+ async function execute(url, db, uid, password, model, method, ...args) {
44
+ return jsonRpc(url, {
45
+ service: "object", method: "execute",
46
+ args: [db, uid, password, model, method, ...args],
47
+ });
48
+ }
49
+ const MODEL_MAP = {
50
+ contact: "res.partner",
51
+ opportunity: "crm.lead",
52
+ note: "note.note",
53
+ };
54
+ function resolveModel(resource) {
55
+ return MODEL_MAP[resource] ?? resource;
56
+ }
57
+ export default function odoo(rl) {
58
+ rl.setName("odoo");
59
+ rl.setVersion("0.1.0");
60
+ rl.setConnectionSchema({
61
+ url: { type: "string", required: true, description: "Odoo instance URL (e.g. https://mycompany.odoo.com)", env: "ODOO_URL" },
62
+ db: { type: "string", required: false, description: "Database name (auto-detected from URL if omitted)", env: "ODOO_DB" },
63
+ username: { type: "string", required: true, description: "Odoo username (email)", env: "ODOO_USERNAME" },
64
+ password: { type: "string", required: true, description: "Odoo password or API key", env: "ODOO_PASSWORD" },
65
+ });
66
+ // Helper to get authenticated session
67
+ async function getSession(ctx) {
68
+ const c = getConn(ctx);
69
+ const db = getDBName(c.db, c.url);
70
+ const uid = await login(c.url, db, c.username, c.password);
71
+ return { url: c.url, db, uid, password: c.password };
72
+ }
73
+ rl.registerAction("record.create", {
74
+ description: "Create a record in any Odoo model (contact, opportunity, note, or custom model name)",
75
+ inputSchema: {
76
+ model: { type: "string", required: true, description: "Model: contact, opportunity, note, or Odoo model name (e.g. res.partner)" },
77
+ fields: { type: "object", required: true, description: "Fields to set on the new record" },
78
+ },
79
+ async execute(input, ctx) {
80
+ const { model, fields } = input;
81
+ const s = await getSession(ctx);
82
+ const id = await execute(s.url, s.db, s.uid, s.password, resolveModel(model), "create", fields);
83
+ return { id };
84
+ },
85
+ });
86
+ rl.registerAction("record.get", {
87
+ description: "Read a record by ID",
88
+ inputSchema: {
89
+ model: { type: "string", required: true, description: "Model: contact, opportunity, note, or Odoo model name" },
90
+ id: { type: "number", required: true, description: "Record ID" },
91
+ fields: { type: "object", required: false, description: "Array of field names to return (default: all)" },
92
+ },
93
+ async execute(input, ctx) {
94
+ const p = input;
95
+ const s = await getSession(ctx);
96
+ const fieldsToRead = p.fields ?? [];
97
+ return execute(s.url, s.db, s.uid, s.password, resolveModel(p.model), "read", [p.id], fieldsToRead);
98
+ },
99
+ });
100
+ rl.registerAction("record.list", {
101
+ description: "Search and read records from any Odoo model",
102
+ inputSchema: {
103
+ model: { type: "string", required: true, description: "Model: contact, opportunity, note, or Odoo model name" },
104
+ filters: { type: "object", required: false, description: "Array of filter tuples, e.g. [[\"name\", \"like\", \"test\"]]" },
105
+ fields: { type: "object", required: false, description: "Array of field names to return" },
106
+ limit: { type: "number", required: false, description: "Max records (0 = no limit)" },
107
+ offset: { type: "number", required: false, description: "Offset for pagination" },
108
+ },
109
+ async execute(input, ctx) {
110
+ const p = input;
111
+ const s = await getSession(ctx);
112
+ const filters = p.filters ?? [];
113
+ const fields = p.fields ?? [];
114
+ const offset = p.offset ?? 0;
115
+ const limit = p.limit ?? 0;
116
+ return execute(s.url, s.db, s.uid, s.password, resolveModel(p.model), "search_read", filters, fields, offset, limit);
117
+ },
118
+ });
119
+ rl.registerAction("record.update", {
120
+ description: "Update a record by ID",
121
+ inputSchema: {
122
+ model: { type: "string", required: true, description: "Model: contact, opportunity, note, or Odoo model name" },
123
+ id: { type: "number", required: true, description: "Record ID" },
124
+ fields: { type: "object", required: true, description: "Fields to update" },
125
+ },
126
+ async execute(input, ctx) {
127
+ const p = input;
128
+ const s = await getSession(ctx);
129
+ await execute(s.url, s.db, s.uid, s.password, resolveModel(p.model), "write", [p.id], p.fields);
130
+ return { id: p.id };
131
+ },
132
+ });
133
+ rl.registerAction("record.delete", {
134
+ description: "Delete a record by ID",
135
+ inputSchema: {
136
+ model: { type: "string", required: true, description: "Model: contact, opportunity, note, or Odoo model name" },
137
+ id: { type: "number", required: true, description: "Record ID" },
138
+ },
139
+ async execute(input, ctx) {
140
+ const p = input;
141
+ const s = await getSession(ctx);
142
+ await execute(s.url, s.db, s.uid, s.password, resolveModel(p.model), "unlink", [p.id]);
143
+ return { success: true };
144
+ },
145
+ });
146
+ rl.registerAction("model.getFields", {
147
+ description: "Get field definitions for an Odoo model",
148
+ inputSchema: {
149
+ model: { type: "string", required: true, description: "Model: contact, opportunity, note, or Odoo model name" },
150
+ },
151
+ async execute(input, ctx) {
152
+ const { model } = input;
153
+ const s = await getSession(ctx);
154
+ return execute(s.url, s.db, s.uid, s.password, resolveModel(model), "fields_get", [], ["string", "type", "help", "required", "name"]);
155
+ },
156
+ });
157
+ }
@@ -0,0 +1,141 @@
1
+ function getConn(ctx) {
2
+ const c = ctx.connection.config;
3
+ const url = c.url.replace(/\/$/, "");
4
+ return { url, apiToken: c.apiToken };
5
+ }
6
+ async function apiRequest(conn, method, endpoint, body, qs) {
7
+ const u = new URL(`${conn.url}${endpoint}`);
8
+ if (qs) {
9
+ for (const [k, v] of Object.entries(qs)) {
10
+ if (v !== undefined && v !== null)
11
+ u.searchParams.set(k, String(v));
12
+ }
13
+ }
14
+ const init = {
15
+ method,
16
+ headers: {
17
+ Authorization: `SSWS ${conn.apiToken}`,
18
+ "Content-Type": "application/json",
19
+ },
20
+ };
21
+ if (body && Object.keys(body).length > 0)
22
+ init.body = JSON.stringify(body);
23
+ const res = await fetch(u.toString(), init);
24
+ if (!res.ok)
25
+ throw new Error(`Okta API error ${res.status}: ${await res.text()}`);
26
+ const text = await res.text();
27
+ const data = text ? JSON.parse(text) : {};
28
+ return { data, linkHeader: res.headers.get("link") ?? undefined };
29
+ }
30
+ async function paginate(conn, endpoint, qs = {}) {
31
+ const all = [];
32
+ let after;
33
+ do {
34
+ if (after)
35
+ qs.after = after;
36
+ qs.limit = 200;
37
+ const { data, linkHeader } = await apiRequest(conn, "GET", endpoint, undefined, qs);
38
+ const items = Array.isArray(data) ? data : [];
39
+ all.push(...items);
40
+ after = undefined;
41
+ if (linkHeader) {
42
+ const match = linkHeader.match(/after=([^&>]+)/);
43
+ if (match)
44
+ after = match[1];
45
+ }
46
+ } while (after);
47
+ return all;
48
+ }
49
+ export default function okta(rl) {
50
+ rl.setName("okta");
51
+ rl.setVersion("0.1.0");
52
+ rl.setConnectionSchema({
53
+ url: { type: "string", required: true, description: "Okta org URL (e.g. https://yourorg.okta.com)", env: "OKTA_URL" },
54
+ apiToken: { type: "string", required: true, description: "Okta API token (SSWS)", env: "OKTA_API_TOKEN" },
55
+ });
56
+ rl.registerAction("user.create", {
57
+ description: "Create a new user in Okta",
58
+ inputSchema: {
59
+ firstName: { type: "string", required: true },
60
+ lastName: { type: "string", required: true },
61
+ login: { type: "string", required: true, description: "Username (must be email)" },
62
+ email: { type: "string", required: true },
63
+ activate: { type: "boolean", required: false, description: "Activate user immediately (default true)" },
64
+ password: { type: "string", required: false },
65
+ profile: { type: "object", required: false, description: "Additional profile fields (city, department, displayName, etc.)" },
66
+ },
67
+ async execute(input, ctx) {
68
+ const p = input;
69
+ const conn = getConn(ctx);
70
+ const body = {
71
+ profile: {
72
+ firstName: p.firstName,
73
+ lastName: p.lastName,
74
+ login: p.login,
75
+ email: p.email,
76
+ ...(p.profile ?? {}),
77
+ },
78
+ };
79
+ if (p.password) {
80
+ body.credentials = { password: { value: p.password } };
81
+ }
82
+ const qs = { activate: p.activate !== false ? "true" : "false" };
83
+ const { data } = await apiRequest(conn, "POST", "/api/v1/users/", body, qs);
84
+ return data;
85
+ },
86
+ });
87
+ rl.registerAction("user.get", {
88
+ description: "Get user details by ID or login",
89
+ inputSchema: {
90
+ userId: { type: "string", required: true, description: "User ID or login (email)" },
91
+ },
92
+ async execute(input, ctx) {
93
+ const { userId } = input;
94
+ const { data } = await apiRequest(getConn(ctx), "GET", `/api/v1/users/${userId}`);
95
+ return data;
96
+ },
97
+ });
98
+ rl.registerAction("user.list", {
99
+ description: "List users (with optional search query)",
100
+ inputSchema: {
101
+ search: { type: "string", required: false, description: "Search/filter query, e.g. profile.lastName sw \"Smi\"" },
102
+ limit: { type: "number", required: false, description: "Max results (default all)" },
103
+ },
104
+ async execute(input, ctx) {
105
+ const p = (input ?? {});
106
+ const conn = getConn(ctx);
107
+ const qs = {};
108
+ if (p.search)
109
+ qs.search = p.search;
110
+ if (p.limit) {
111
+ qs.limit = p.limit;
112
+ const { data } = await apiRequest(conn, "GET", "/api/v1/users/", undefined, qs);
113
+ return data;
114
+ }
115
+ return paginate(conn, "/api/v1/users/", qs);
116
+ },
117
+ });
118
+ rl.registerAction("user.update", {
119
+ description: "Update a user's profile",
120
+ inputSchema: {
121
+ userId: { type: "string", required: true, description: "User ID" },
122
+ profile: { type: "object", required: true, description: "Profile fields to update (firstName, lastName, email, login, city, department, etc.)" },
123
+ },
124
+ async execute(input, ctx) {
125
+ const { userId, profile } = input;
126
+ const { data } = await apiRequest(getConn(ctx), "POST", `/api/v1/users/${userId}`, { profile });
127
+ return data;
128
+ },
129
+ });
130
+ rl.registerAction("user.delete", {
131
+ description: "Delete (deactivate and then delete) a user",
132
+ inputSchema: {
133
+ userId: { type: "string", required: true, description: "User ID" },
134
+ },
135
+ async execute(input, ctx) {
136
+ const { userId } = input;
137
+ await apiRequest(getConn(ctx), "DELETE", `/api/v1/users/${userId}`);
138
+ return { success: true };
139
+ },
140
+ });
141
+ }
@@ -0,0 +1,155 @@
1
+ const BASE = "https://onesimpleapi.com/api";
2
+ async function apiRequest(token, endpoint, qs = {}) {
3
+ const url = new URL(`${BASE}${endpoint}`);
4
+ url.searchParams.set("token", token);
5
+ url.searchParams.set("output", "json");
6
+ for (const [k, v] of Object.entries(qs)) {
7
+ if (v !== undefined && v !== null)
8
+ url.searchParams.set(k, String(v));
9
+ }
10
+ const res = await fetch(url.toString());
11
+ if (!res.ok)
12
+ throw new Error(`OneSimpleAPI error ${res.status}: ${await res.text()}`);
13
+ return res.json();
14
+ }
15
+ export default function oneSimpleApi(rl) {
16
+ rl.setName("oneSimpleApi");
17
+ rl.setVersion("0.1.0");
18
+ rl.setConnectionSchema({
19
+ apiToken: { type: "string", required: true, description: "OneSimpleAPI token", env: "ONE_SIMPLE_API_TOKEN" },
20
+ });
21
+ const key = (ctx) => ctx.connection.config.apiToken;
22
+ // ── Website ─────────────────────────────────────────
23
+ rl.registerAction("website.pdf", {
24
+ description: "Generate a PDF from a webpage (returns URL to the PDF)",
25
+ inputSchema: {
26
+ url: { type: "string", required: true, description: "Webpage URL" },
27
+ page: { type: "string", required: false, description: "Page size: A0-A6, Letter, Legal, Tabloid, Ledger" },
28
+ force: { type: "boolean", required: false, description: "Force refresh (default false)" },
29
+ },
30
+ async execute(input, ctx) {
31
+ const p = (input ?? {});
32
+ const qs = { url: p.url };
33
+ if (p.page)
34
+ qs.page = p.page;
35
+ qs.force = p.force ? "yes" : "no";
36
+ return apiRequest(key(ctx), "/pdf", qs);
37
+ },
38
+ });
39
+ rl.registerAction("website.screenshot", {
40
+ description: "Take a screenshot of a webpage (returns URL to the image)",
41
+ inputSchema: {
42
+ url: { type: "string", required: true, description: "Webpage URL" },
43
+ screen: { type: "string", required: false, description: "Screen size: phone, phone-landscape, tablet, tablet-landscape, retina" },
44
+ fullpage: { type: "boolean", required: false, description: "Capture full page (default false)" },
45
+ force: { type: "boolean", required: false, description: "Force refresh (default false)" },
46
+ },
47
+ async execute(input, ctx) {
48
+ const p = (input ?? {});
49
+ const qs = { url: p.url };
50
+ if (p.screen)
51
+ qs.screen = p.screen;
52
+ qs.fullpage = p.fullpage ? "yes" : "no";
53
+ qs.force = p.force ? "yes" : "no";
54
+ return apiRequest(key(ctx), "/screenshot", qs);
55
+ },
56
+ });
57
+ rl.registerAction("website.seo", {
58
+ description: "Get SEO information from a webpage",
59
+ inputSchema: {
60
+ url: { type: "string", required: true, description: "Webpage URL" },
61
+ headers: { type: "boolean", required: false, description: "Include response headers (default false)" },
62
+ },
63
+ async execute(input, ctx) {
64
+ const p = (input ?? {});
65
+ const qs = { url: p.url };
66
+ if (p.headers)
67
+ qs.headers = "yes";
68
+ return apiRequest(key(ctx), "/page_info", qs);
69
+ },
70
+ });
71
+ // ── Social Profile ──────────────────────────────────
72
+ rl.registerAction("socialProfile.instagram", {
73
+ description: "Get details about an Instagram profile",
74
+ inputSchema: {
75
+ profile: { type: "string", required: true, description: "Instagram profile name" },
76
+ },
77
+ async execute(input, ctx) {
78
+ const { profile } = input;
79
+ return apiRequest(key(ctx), "/instagram_profile", { profile });
80
+ },
81
+ });
82
+ rl.registerAction("socialProfile.spotify", {
83
+ description: "Get details about a Spotify artist",
84
+ inputSchema: {
85
+ profile: { type: "string", required: true, description: "Spotify artist name" },
86
+ },
87
+ async execute(input, ctx) {
88
+ const { profile } = input;
89
+ return apiRequest(key(ctx), "/spotify_profile", { profile });
90
+ },
91
+ });
92
+ // ── Information ─────────────────────────────────────
93
+ rl.registerAction("information.exchangeRate", {
94
+ description: "Convert a value between currencies",
95
+ inputSchema: {
96
+ value: { type: "string", required: true, description: "Value to convert" },
97
+ fromCurrency: { type: "string", required: true, description: "Source currency (e.g. USD)" },
98
+ toCurrency: { type: "string", required: true, description: "Target currency (e.g. EUR)" },
99
+ },
100
+ async execute(input, ctx) {
101
+ const { value, fromCurrency, toCurrency } = input;
102
+ return apiRequest(key(ctx), "/exchange_rate", {
103
+ from_value: value, from_currency: fromCurrency, to_currency: toCurrency,
104
+ });
105
+ },
106
+ });
107
+ rl.registerAction("information.imageMetadata", {
108
+ description: "Get metadata from an image URL",
109
+ inputSchema: {
110
+ url: { type: "string", required: true, description: "Image URL" },
111
+ },
112
+ async execute(input, ctx) {
113
+ const { url } = input;
114
+ return apiRequest(key(ctx), "/image_info", { url, raw: true });
115
+ },
116
+ });
117
+ // ── Utility ─────────────────────────────────────────
118
+ rl.registerAction("utility.validateEmail", {
119
+ description: "Validate an email address",
120
+ inputSchema: {
121
+ email: { type: "string", required: true, description: "Email address to validate" },
122
+ },
123
+ async execute(input, ctx) {
124
+ const { email } = input;
125
+ return apiRequest(key(ctx), "/email", { email });
126
+ },
127
+ });
128
+ rl.registerAction("utility.expandUrl", {
129
+ description: "Expand a shortened URL",
130
+ inputSchema: {
131
+ url: { type: "string", required: true, description: "Shortened URL" },
132
+ },
133
+ async execute(input, ctx) {
134
+ const { url } = input;
135
+ return apiRequest(key(ctx), "/unshorten", { url });
136
+ },
137
+ });
138
+ rl.registerAction("utility.qrCode", {
139
+ description: "Generate a QR code (returns URL to the image)",
140
+ inputSchema: {
141
+ message: { type: "string", required: true, description: "Content for the QR code (URL, text, etc.)" },
142
+ size: { type: "string", required: false, description: "Size: Small, Medium, Large" },
143
+ format: { type: "string", required: false, description: "Format: PNG or SVG" },
144
+ },
145
+ async execute(input, ctx) {
146
+ const p = (input ?? {});
147
+ const qs = { message: p.message };
148
+ if (p.size)
149
+ qs.size = p.size;
150
+ if (p.format)
151
+ qs.format = p.format;
152
+ return apiRequest(key(ctx), "/qr_code", qs);
153
+ },
154
+ });
155
+ }