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.
- package/.pi/extensions/runline-context/index.ts +135 -0
- package/.pi/extensions/runline-context/package.json +17 -0
- package/README.md +273 -0
- package/dist/commands/actions.d.ts +3 -0
- package/dist/commands/actions.js +43 -0
- package/dist/commands/connection.d.ts +11 -0
- package/dist/commands/connection.js +56 -0
- package/dist/commands/exec.d.ts +5 -0
- package/dist/commands/exec.js +46 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +26 -0
- package/dist/commands/plugin.d.ts +10 -0
- package/dist/commands/plugin.js +57 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +2 -0
- package/dist/config/loader.d.ts +11 -0
- package/dist/config/loader.js +82 -0
- package/dist/config/types.d.ts +9 -0
- package/dist/config/types.js +5 -0
- package/dist/core/engine.d.ts +21 -0
- package/dist/core/engine.js +280 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +9 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +127 -0
- package/dist/plugin/api.d.ts +32 -0
- package/dist/plugin/api.js +68 -0
- package/dist/plugin/installer.d.ts +27 -0
- package/dist/plugin/installer.js +181 -0
- package/dist/plugin/loader.d.ts +13 -0
- package/dist/plugin/loader.js +164 -0
- package/dist/plugin/registry.d.ts +18 -0
- package/dist/plugin/registry.js +43 -0
- package/dist/plugin/types.d.ts +40 -0
- package/dist/plugin/types.js +1 -0
- package/dist/plugins/actionNetwork/src/index.js +353 -0
- package/dist/plugins/activeCampaign/src/index.js +711 -0
- package/dist/plugins/adalo/src/index.js +131 -0
- package/dist/plugins/affinity/src/index.js +279 -0
- package/dist/plugins/agileCrm/src/index.js +415 -0
- package/dist/plugins/airtable/src/index.js +280 -0
- package/dist/plugins/airtop/src/index.js +527 -0
- package/dist/plugins/apiTemplateIo/src/index.js +86 -0
- package/dist/plugins/asana/src/index.js +413 -0
- package/dist/plugins/autopilot/src/index.js +203 -0
- package/dist/plugins/bambooHr/src/index.js +252 -0
- package/dist/plugins/bannerbear/src/index.js +100 -0
- package/dist/plugins/baserow/src/index.js +180 -0
- package/dist/plugins/beeminder/src/index.js +298 -0
- package/dist/plugins/bitly/src/index.js +107 -0
- package/dist/plugins/bitwarden/src/index.js +383 -0
- package/dist/plugins/box/src/index.js +300 -0
- package/dist/plugins/brandfetch/src/index.js +80 -0
- package/dist/plugins/brevo/src/index.js +305 -0
- package/dist/plugins/bubble/src/index.js +181 -0
- package/dist/plugins/chargebee/src/index.js +126 -0
- package/dist/plugins/circleci/src/index.js +111 -0
- package/dist/plugins/ciscoWebex/src/index.js +245 -0
- package/dist/plugins/clearbit/src/index.js +103 -0
- package/dist/plugins/clickup/src/index.js +1043 -0
- package/dist/plugins/clockify/src/index.js +443 -0
- package/dist/plugins/cloudflare/src/index.js +93 -0
- package/dist/plugins/cockpit/src/index.js +131 -0
- package/dist/plugins/coda/src/index.js +327 -0
- package/dist/plugins/coingecko/src/index.js +244 -0
- package/dist/plugins/contentful/src/index.js +146 -0
- package/dist/plugins/convertkit/src/index.js +270 -0
- package/dist/plugins/copper/src/index.js +140 -0
- package/dist/plugins/cortex/src/index.js +147 -0
- package/dist/plugins/currents/src/index.js +405 -0
- package/dist/plugins/customerIo/src/index.js +184 -0
- package/dist/plugins/databricks/src/index.js +342 -0
- package/dist/plugins/deepl/src/index.js +87 -0
- package/dist/plugins/demio/src/index.js +111 -0
- package/dist/plugins/dhl/src/index.js +40 -0
- package/dist/plugins/discord/src/index.js +275 -0
- package/dist/plugins/discourse/src/index.js +273 -0
- package/dist/plugins/disqus/src/index.js +145 -0
- package/dist/plugins/docker/src/index.js +76 -0
- package/dist/plugins/drift/src/index.js +89 -0
- package/dist/plugins/dropbox/src/index.js +159 -0
- package/dist/plugins/dropcontact/src/index.js +59 -0
- package/dist/plugins/egoi/src/index.js +151 -0
- package/dist/plugins/elasticsearch/src/index.js +157 -0
- package/dist/plugins/emelia/src/index.js +174 -0
- package/dist/plugins/erpnext/src/index.js +121 -0
- package/dist/plugins/facebookGraph/src/index.js +57 -0
- package/dist/plugins/freshdesk/src/index.js +320 -0
- package/dist/plugins/freshservice/src/index.js +146 -0
- package/dist/plugins/freshworksCrm/src/index.js +149 -0
- package/dist/plugins/getresponse/src/index.js +140 -0
- package/dist/plugins/ghost/src/index.js +192 -0
- package/dist/plugins/github/src/index.js +630 -0
- package/dist/plugins/gitlab/src/index.js +358 -0
- package/dist/plugins/gong/src/index.js +126 -0
- package/dist/plugins/gotify/src/index.js +77 -0
- package/dist/plugins/gotowebinar/src/index.js +316 -0
- package/dist/plugins/grafana/src/index.js +250 -0
- package/dist/plugins/graphql/src/index.js +78 -0
- package/dist/plugins/grist/src/index.js +106 -0
- package/dist/plugins/hackernews/src/index.js +89 -0
- package/dist/plugins/halopsa/src/index.js +79 -0
- package/dist/plugins/harvest/src/index.js +163 -0
- package/dist/plugins/helpscout/src/index.js +176 -0
- package/dist/plugins/highlevel/src/index.js +172 -0
- package/dist/plugins/homeAssistant/src/index.js +148 -0
- package/dist/plugins/hubspot/src/index.js +176 -0
- package/dist/plugins/humanticAi/src/index.js +60 -0
- package/dist/plugins/hunter/src/index.js +59 -0
- package/dist/plugins/intercom/src/index.js +156 -0
- package/dist/plugins/iterable/src/index.js +139 -0
- package/dist/plugins/jenkins/src/index.js +132 -0
- package/dist/plugins/jira/src/index.js +229 -0
- package/dist/plugins/keap/src/index.js +502 -0
- package/dist/plugins/kobotoolbox/src/index.js +281 -0
- package/dist/plugins/lemlist/src/index.js +231 -0
- package/dist/plugins/linear/src/index.js +133 -0
- package/dist/plugins/lingvanex/src/index.js +31 -0
- package/dist/plugins/linkedin/src/index.js +80 -0
- package/dist/plugins/lonescale/src/index.js +119 -0
- package/dist/plugins/magento/src/index.js +300 -0
- package/dist/plugins/mailcheck/src/index.js +27 -0
- package/dist/plugins/mailchimp/src/index.js +321 -0
- package/dist/plugins/mailerlite/src/index.js +123 -0
- package/dist/plugins/mailgun/src/index.js +48 -0
- package/dist/plugins/mailjet/src/index.js +155 -0
- package/dist/plugins/mandrill/src/index.js +145 -0
- package/dist/plugins/marketstack/src/index.js +97 -0
- package/dist/plugins/matrix/src/index.js +194 -0
- package/dist/plugins/mattermost/src/index.js +331 -0
- package/dist/plugins/mautic/src/index.js +311 -0
- package/dist/plugins/medium/src/index.js +77 -0
- package/dist/plugins/messagebird/src/index.js +57 -0
- package/dist/plugins/metabase/src/index.js +130 -0
- package/dist/plugins/misp/src/index.js +476 -0
- package/dist/plugins/mocean/src/index.js +67 -0
- package/dist/plugins/monday/src/index.js +231 -0
- package/dist/plugins/monicaCrm/src/index.js +52 -0
- package/dist/plugins/msg91/src/index.js +31 -0
- package/dist/plugins/nasa/src/index.js +146 -0
- package/dist/plugins/netlify/src/index.js +151 -0
- package/dist/plugins/netscalerAdc/src/index.js +131 -0
- package/dist/plugins/nextcloud/src/index.js +263 -0
- package/dist/plugins/nocodb/src/index.js +130 -0
- package/dist/plugins/notion/src/index.js +112 -0
- package/dist/plugins/npm/src/index.js +104 -0
- package/dist/plugins/odoo/src/index.js +157 -0
- package/dist/plugins/okta/src/index.js +141 -0
- package/dist/plugins/oneSimpleApi/src/index.js +155 -0
- package/dist/plugins/onfleet/src/index.js +254 -0
- package/dist/plugins/openThesaurus/src/index.js +32 -0
- package/dist/plugins/openweathermap/src/index.js +60 -0
- package/dist/plugins/oura/src/index.js +62 -0
- package/dist/plugins/paddle/src/index.js +247 -0
- package/dist/plugins/pagerduty/src/index.js +201 -0
- package/dist/plugins/paypal/src/index.js +106 -0
- package/dist/plugins/peekalink/src/index.js +35 -0
- package/dist/plugins/phantombuster/src/index.js +94 -0
- package/dist/plugins/philipsHue/src/index.js +98 -0
- package/dist/plugins/pipedrive/src/index.js +169 -0
- package/dist/plugins/plivo/src/index.js +66 -0
- package/dist/plugins/postbin/src/index.js +93 -0
- package/dist/plugins/posthog/src/index.js +113 -0
- package/dist/plugins/profitwell/src/index.js +50 -0
- package/dist/plugins/pushbullet/src/index.js +102 -0
- package/dist/plugins/pushcut/src/index.js +39 -0
- package/dist/plugins/pushover/src/index.js +65 -0
- package/dist/plugins/quickbase/src/index.js +153 -0
- package/dist/plugins/quickbooks/src/index.js +73 -0
- package/dist/plugins/quickchart/src/index.js +36 -0
- package/dist/plugins/raindrop/src/index.js +209 -0
- package/dist/plugins/reddit/src/index.js +185 -0
- package/dist/plugins/rocketchat/src/index.js +53 -0
- package/dist/plugins/rundeck/src/index.js +62 -0
- package/dist/plugins/salesforce/src/index.js +94 -0
- package/dist/plugins/salesmate/src/index.js +83 -0
- package/dist/plugins/securityScorecard/src/index.js +200 -0
- package/dist/plugins/segment/src/index.js +125 -0
- package/dist/plugins/sendgrid/src/index.js +187 -0
- package/dist/plugins/sendy/src/index.js +138 -0
- package/dist/plugins/sentry/src/index.js +233 -0
- package/dist/plugins/servicenow/src/index.js +108 -0
- package/dist/plugins/shopify/src/index.js +222 -0
- package/dist/plugins/signl4/src/index.js +61 -0
- package/dist/plugins/slack/src/index.js +236 -0
- package/dist/plugins/sms77/src/index.js +63 -0
- package/dist/plugins/splunk/src/index.js +207 -0
- package/dist/plugins/spotify/src/index.js +188 -0
- package/dist/plugins/stackby/src/index.js +82 -0
- package/dist/plugins/storyblok/src/index.js +141 -0
- package/dist/plugins/strapi/src/index.js +152 -0
- package/dist/plugins/strava/src/index.js +137 -0
- package/dist/plugins/stripe/src/index.js +222 -0
- package/dist/plugins/supabase/src/index.js +121 -0
- package/dist/plugins/syncromsp/src/index.js +255 -0
- package/dist/plugins/tapfiliate/src/index.js +125 -0
- package/dist/plugins/telegram/src/index.js +233 -0
- package/dist/plugins/thehive/src/index.js +142 -0
- package/dist/plugins/thehiveProject/src/index.js +194 -0
- package/dist/plugins/todoist/src/index.js +244 -0
- package/dist/plugins/travisci/src/index.js +71 -0
- package/dist/plugins/trello/src/index.js +341 -0
- package/dist/plugins/twake/src/index.js +40 -0
- package/dist/plugins/twilio/src/index.js +75 -0
- package/dist/plugins/twist/src/index.js +90 -0
- package/dist/plugins/twitter/src/index.js +123 -0
- package/dist/plugins/unleashedSoftware/src/index.js +84 -0
- package/dist/plugins/uplead/src/index.js +59 -0
- package/dist/plugins/uproc/src/index.js +34 -0
- package/dist/plugins/uptimerobot/src/index.js +264 -0
- package/dist/plugins/urlscanio/src/index.js +64 -0
- package/dist/plugins/vero/src/index.js +80 -0
- package/dist/plugins/vonage/src/index.js +42 -0
- package/dist/plugins/wekan/src/index.js +91 -0
- package/dist/plugins/woocommerce/src/index.js +92 -0
- package/dist/plugins/wordpress/src/index.js +121 -0
- package/dist/plugins/xero/src/index.js +136 -0
- package/dist/plugins/yourls/src/index.js +56 -0
- package/dist/plugins/zammad/src/index.js +91 -0
- package/dist/plugins/zendesk/src/index.js +137 -0
- package/dist/plugins/zoho/src/index.js +85 -0
- package/dist/plugins/zoom/src/index.js +122 -0
- package/dist/plugins/zulip/src/index.js +170 -0
- package/dist/sdk.d.ts +38 -0
- package/dist/sdk.js +105 -0
- package/dist/utils/cli.d.ts +13 -0
- package/dist/utils/cli.js +32 -0
- package/dist/utils/output.d.ts +4 -0
- package/dist/utils/output.js +13 -0
- package/package.json +57 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const BASE = "https://api.twitter.com/2";
|
|
2
|
+
async function api(token, method, endpoint, body, qs, fullOutput = false) {
|
|
3
|
+
const url = new URL(`${BASE}${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 init = { method, headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" } };
|
|
11
|
+
if (body && Object.keys(body).length > 0)
|
|
12
|
+
init.body = JSON.stringify(body);
|
|
13
|
+
const res = await fetch(url.toString(), init);
|
|
14
|
+
if (!res.ok)
|
|
15
|
+
throw new Error(`Twitter error ${res.status}: ${await res.text()}`);
|
|
16
|
+
const json = await res.json();
|
|
17
|
+
return fullOutput ? json : json.data;
|
|
18
|
+
}
|
|
19
|
+
async function paginate(token, endpoint, qs = {}, limit) {
|
|
20
|
+
const results = [];
|
|
21
|
+
let nextToken;
|
|
22
|
+
qs.max_results = limit && limit < 100 ? limit : 10;
|
|
23
|
+
do {
|
|
24
|
+
if (nextToken)
|
|
25
|
+
qs.next_token = nextToken;
|
|
26
|
+
const res = await api(token, "GET", endpoint, undefined, { ...qs }, true);
|
|
27
|
+
const data = res.data;
|
|
28
|
+
if (data)
|
|
29
|
+
results.push(...data);
|
|
30
|
+
nextToken = res.meta?.next_token;
|
|
31
|
+
if (limit && results.length >= limit) {
|
|
32
|
+
return results.slice(0, limit);
|
|
33
|
+
}
|
|
34
|
+
} while (nextToken);
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
export default function twitter(rl) {
|
|
38
|
+
rl.setName("twitter");
|
|
39
|
+
rl.setVersion("0.1.0");
|
|
40
|
+
rl.setConnectionSchema({ bearerToken: { type: "string", required: true, description: "OAuth2 Bearer token for Twitter/X API v2", env: "TWITTER_BEARER_TOKEN" } });
|
|
41
|
+
const t = (ctx) => ctx.connection.config.bearerToken;
|
|
42
|
+
// ── Tweet ───────────────────────────────────────────
|
|
43
|
+
rl.registerAction("tweet.create", { description: "Create a tweet (post, quote, or reply)",
|
|
44
|
+
inputSchema: { text: { type: "string", required: true }, quoteTweetId: { type: "string", required: false, description: "Tweet ID to quote" }, replyToTweetId: { type: "string", required: false, description: "Tweet ID to reply to" }, mediaId: { type: "string", required: false }, placeId: { type: "string", required: false } },
|
|
45
|
+
async execute(input, ctx) {
|
|
46
|
+
const p = input;
|
|
47
|
+
const body = { text: p.text };
|
|
48
|
+
if (p.quoteTweetId)
|
|
49
|
+
body.quote_tweet_id = p.quoteTweetId;
|
|
50
|
+
if (p.replyToTweetId)
|
|
51
|
+
body.reply = { in_reply_to_tweet_id: p.replyToTweetId };
|
|
52
|
+
if (p.mediaId)
|
|
53
|
+
body.media = { media_ids: [p.mediaId] };
|
|
54
|
+
if (p.placeId)
|
|
55
|
+
body.geo = { place_id: p.placeId };
|
|
56
|
+
return api(t(ctx), "POST", "/tweets", body);
|
|
57
|
+
} });
|
|
58
|
+
rl.registerAction("tweet.delete", { description: "Delete a tweet", inputSchema: { id: { type: "string", required: true } },
|
|
59
|
+
async execute(input, ctx) { return api(t(ctx), "DELETE", `/tweets/${input.id}`); } });
|
|
60
|
+
rl.registerAction("tweet.like", { description: "Like a tweet (requires user context)",
|
|
61
|
+
inputSchema: { tweetId: { type: "string", required: true } },
|
|
62
|
+
async execute(input, ctx) {
|
|
63
|
+
const user = await api(t(ctx), "GET", "/users/me");
|
|
64
|
+
return api(t(ctx), "POST", `/users/${user.id}/likes`, { tweet_id: input.tweetId });
|
|
65
|
+
} });
|
|
66
|
+
rl.registerAction("tweet.retweet", { description: "Retweet a tweet (requires user context)",
|
|
67
|
+
inputSchema: { tweetId: { type: "string", required: true } },
|
|
68
|
+
async execute(input, ctx) {
|
|
69
|
+
const user = await api(t(ctx), "GET", "/users/me");
|
|
70
|
+
return api(t(ctx), "POST", `/users/${user.id}/retweets`, { tweet_id: input.tweetId });
|
|
71
|
+
} });
|
|
72
|
+
rl.registerAction("tweet.search", { description: "Search recent tweets (last 7 days)",
|
|
73
|
+
inputSchema: { query: { type: "string", required: true }, limit: { type: "number", required: false, description: "Max results (default all)" }, sortOrder: { type: "string", required: false, description: "recency or relevancy" }, startTime: { type: "string", required: false, description: "ISO datetime" }, endTime: { type: "string", required: false, description: "ISO datetime" }, tweetFields: { type: "string", required: false, description: "Comma-separated fields like author_id,created_at,public_metrics" } },
|
|
74
|
+
async execute(input, ctx) {
|
|
75
|
+
const p = input;
|
|
76
|
+
const qs = { query: p.query };
|
|
77
|
+
if (p.sortOrder)
|
|
78
|
+
qs.sort_order = p.sortOrder;
|
|
79
|
+
if (p.startTime)
|
|
80
|
+
qs.start_time = p.startTime;
|
|
81
|
+
if (p.endTime)
|
|
82
|
+
qs.end_time = p.endTime;
|
|
83
|
+
if (p.tweetFields)
|
|
84
|
+
qs["tweet.fields"] = p.tweetFields;
|
|
85
|
+
const limit = p.limit;
|
|
86
|
+
if (limit) {
|
|
87
|
+
return paginate(t(ctx), "/tweets/search/recent", qs, limit);
|
|
88
|
+
}
|
|
89
|
+
return paginate(t(ctx), "/tweets/search/recent", qs);
|
|
90
|
+
} });
|
|
91
|
+
// ── User ────────────────────────────────────────────
|
|
92
|
+
rl.registerAction("user.get", { description: "Get a user by username or ID",
|
|
93
|
+
inputSchema: { username: { type: "string", required: false, description: "Username (without @)" }, id: { type: "string", required: false, description: "User ID" }, me: { type: "boolean", required: false, description: "Get the authenticated user" } },
|
|
94
|
+
async execute(input, ctx) {
|
|
95
|
+
const p = input;
|
|
96
|
+
if (p.me)
|
|
97
|
+
return api(t(ctx), "GET", "/users/me");
|
|
98
|
+
if (p.username) {
|
|
99
|
+
const name = p.username.replace(/^@/, "");
|
|
100
|
+
return api(t(ctx), "GET", `/users/by/username/${name}`);
|
|
101
|
+
}
|
|
102
|
+
if (p.id)
|
|
103
|
+
return api(t(ctx), "GET", `/users/${p.id}`);
|
|
104
|
+
throw new Error("Provide username, id, or set me=true");
|
|
105
|
+
} });
|
|
106
|
+
// ── List ────────────────────────────────────────────
|
|
107
|
+
rl.registerAction("list.addMember", { description: "Add a user to a list",
|
|
108
|
+
inputSchema: { listId: { type: "string", required: true }, userId: { type: "string", required: true, description: "User ID to add" } },
|
|
109
|
+
async execute(input, ctx) {
|
|
110
|
+
const p = input;
|
|
111
|
+
return api(t(ctx), "POST", `/lists/${p.listId}/members`, { user_id: p.userId });
|
|
112
|
+
} });
|
|
113
|
+
// ── Direct Message ──────────────────────────────────
|
|
114
|
+
rl.registerAction("dm.create", { description: "Send a direct message to a user",
|
|
115
|
+
inputSchema: { userId: { type: "string", required: true, description: "Recipient user ID" }, text: { type: "string", required: true }, mediaId: { type: "string", required: false } },
|
|
116
|
+
async execute(input, ctx) {
|
|
117
|
+
const p = input;
|
|
118
|
+
const body = { text: p.text };
|
|
119
|
+
if (p.mediaId)
|
|
120
|
+
body.attachments = [{ media_id: p.mediaId }];
|
|
121
|
+
return api(t(ctx), "POST", `/dm_conversations/with/${p.userId}/messages`, body);
|
|
122
|
+
} });
|
|
123
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createHmac } from "node:crypto";
|
|
2
|
+
const BASE = "https://api.unleashedsoftware.com";
|
|
3
|
+
function getConn(ctx) {
|
|
4
|
+
return { apiId: ctx.connection.config.apiId, apiKey: ctx.connection.config.apiKey };
|
|
5
|
+
}
|
|
6
|
+
function buildQs(params) {
|
|
7
|
+
const parts = [];
|
|
8
|
+
for (const [k, v] of Object.entries(params)) {
|
|
9
|
+
if (v !== undefined && v !== null)
|
|
10
|
+
parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
|
|
11
|
+
}
|
|
12
|
+
return parts.join("&");
|
|
13
|
+
}
|
|
14
|
+
async function apiRequest(conn, method, endpoint, qs, page) {
|
|
15
|
+
const path = page ? `${endpoint}/${page}` : endpoint;
|
|
16
|
+
const qsString = qs ? buildQs(qs) : "";
|
|
17
|
+
const signature = createHmac("sha256", conn.apiKey).update(qsString).digest("base64");
|
|
18
|
+
const url = qsString ? `${BASE}${path}?${qsString}` : `${BASE}${path}`;
|
|
19
|
+
const res = await fetch(url, {
|
|
20
|
+
method,
|
|
21
|
+
headers: { Accept: "application/json", "Content-Type": "application/json", "api-auth-id": conn.apiId, "api-auth-signature": signature },
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok)
|
|
24
|
+
throw new Error(`Unleashed error ${res.status}: ${await res.text()}`);
|
|
25
|
+
return res.json();
|
|
26
|
+
}
|
|
27
|
+
export default function unleashedSoftware(rl) {
|
|
28
|
+
rl.setName("unleashedSoftware");
|
|
29
|
+
rl.setVersion("0.1.0");
|
|
30
|
+
rl.setConnectionSchema({
|
|
31
|
+
apiId: { type: "string", required: true, description: "Unleashed API ID", env: "UNLEASHED_API_ID" },
|
|
32
|
+
apiKey: { type: "string", required: true, description: "Unleashed API Key", env: "UNLEASHED_API_KEY" },
|
|
33
|
+
});
|
|
34
|
+
rl.registerAction("salesOrder.list", {
|
|
35
|
+
description: "List sales orders",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
limit: { type: "number", required: false },
|
|
38
|
+
startDate: { type: "string", required: false, description: "YYYY-MM-DD" },
|
|
39
|
+
endDate: { type: "string", required: false, description: "YYYY-MM-DD" },
|
|
40
|
+
orderStatus: { type: "string", required: false, description: "Comma-separated statuses" },
|
|
41
|
+
},
|
|
42
|
+
async execute(input, ctx) {
|
|
43
|
+
const p = (input ?? {});
|
|
44
|
+
const qs = {};
|
|
45
|
+
if (p.startDate)
|
|
46
|
+
qs.startDate = p.startDate;
|
|
47
|
+
if (p.endDate)
|
|
48
|
+
qs.endDate = p.endDate;
|
|
49
|
+
if (p.orderStatus)
|
|
50
|
+
qs.orderStatus = p.orderStatus;
|
|
51
|
+
if (p.limit)
|
|
52
|
+
qs.pageSize = p.limit;
|
|
53
|
+
const data = (await apiRequest(getConn(ctx), "GET", "/SalesOrders", qs, 1));
|
|
54
|
+
return data.Items;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
rl.registerAction("stockOnHand.list", {
|
|
58
|
+
description: "List stock on hand",
|
|
59
|
+
inputSchema: {
|
|
60
|
+
limit: { type: "number", required: false },
|
|
61
|
+
asAtDate: { type: "string", required: false, description: "YYYY-MM-DD" },
|
|
62
|
+
productCode: { type: "string", required: false },
|
|
63
|
+
},
|
|
64
|
+
async execute(input, ctx) {
|
|
65
|
+
const p = (input ?? {});
|
|
66
|
+
const qs = {};
|
|
67
|
+
if (p.asAtDate)
|
|
68
|
+
qs.asAtDate = p.asAtDate;
|
|
69
|
+
if (p.productCode)
|
|
70
|
+
qs.productCode = p.productCode;
|
|
71
|
+
if (p.limit)
|
|
72
|
+
qs.pageSize = p.limit;
|
|
73
|
+
const data = (await apiRequest(getConn(ctx), "GET", "/StockOnHand", qs, 1));
|
|
74
|
+
return data.Items;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
rl.registerAction("stockOnHand.get", {
|
|
78
|
+
description: "Get stock on hand for a product",
|
|
79
|
+
inputSchema: { productId: { type: "string", required: true } },
|
|
80
|
+
async execute(input, ctx) {
|
|
81
|
+
return apiRequest(getConn(ctx), "GET", `/StockOnHand/${input.productId}`);
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const BASE = "https://api.uplead.com/v2";
|
|
2
|
+
async function apiRequest(apiKey, endpoint, qs) {
|
|
3
|
+
const url = new URL(`${BASE}${endpoint}`);
|
|
4
|
+
for (const [k, v] of Object.entries(qs)) {
|
|
5
|
+
if (v !== undefined && v !== null)
|
|
6
|
+
url.searchParams.set(k, String(v));
|
|
7
|
+
}
|
|
8
|
+
const res = await fetch(url.toString(), { headers: { Authorization: apiKey } });
|
|
9
|
+
if (!res.ok)
|
|
10
|
+
throw new Error(`Uplead error ${res.status}: ${await res.text()}`);
|
|
11
|
+
const data = (await res.json());
|
|
12
|
+
return data.data;
|
|
13
|
+
}
|
|
14
|
+
export default function uplead(rl) {
|
|
15
|
+
rl.setName("uplead");
|
|
16
|
+
rl.setVersion("0.1.0");
|
|
17
|
+
rl.setConnectionSchema({
|
|
18
|
+
apiKey: { type: "string", required: true, description: "Uplead API key", env: "UPLEAD_API_KEY" },
|
|
19
|
+
});
|
|
20
|
+
const key = (ctx) => ctx.connection.config.apiKey;
|
|
21
|
+
rl.registerAction("person.enrich", {
|
|
22
|
+
description: "Enrich a person by email or name+domain",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
email: { type: "string", required: false },
|
|
25
|
+
firstName: { type: "string", required: false },
|
|
26
|
+
lastName: { type: "string", required: false },
|
|
27
|
+
domain: { type: "string", required: false },
|
|
28
|
+
},
|
|
29
|
+
async execute(input, ctx) {
|
|
30
|
+
const p = (input ?? {});
|
|
31
|
+
const qs = {};
|
|
32
|
+
if (p.email)
|
|
33
|
+
qs.email = p.email;
|
|
34
|
+
if (p.firstName)
|
|
35
|
+
qs.first_name = p.firstName;
|
|
36
|
+
if (p.lastName)
|
|
37
|
+
qs.last_name = p.lastName;
|
|
38
|
+
if (p.domain)
|
|
39
|
+
qs.domain = p.domain;
|
|
40
|
+
return apiRequest(key(ctx), "/person-search", qs);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
rl.registerAction("company.enrich", {
|
|
44
|
+
description: "Enrich a company by domain or name",
|
|
45
|
+
inputSchema: {
|
|
46
|
+
domain: { type: "string", required: false },
|
|
47
|
+
company: { type: "string", required: false },
|
|
48
|
+
},
|
|
49
|
+
async execute(input, ctx) {
|
|
50
|
+
const p = (input ?? {});
|
|
51
|
+
const qs = {};
|
|
52
|
+
if (p.domain)
|
|
53
|
+
qs.domain = p.domain;
|
|
54
|
+
if (p.company)
|
|
55
|
+
qs.company = p.company;
|
|
56
|
+
return apiRequest(key(ctx), "/company-search", qs);
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export default function uproc(rl) {
|
|
2
|
+
rl.setName("uproc");
|
|
3
|
+
rl.setVersion("0.1.0");
|
|
4
|
+
rl.setConnectionSchema({
|
|
5
|
+
email: { type: "string", required: true, description: "uProc account email", env: "UPROC_EMAIL" },
|
|
6
|
+
apiKey: { type: "string", required: true, description: "uProc API key", env: "UPROC_API_KEY" },
|
|
7
|
+
});
|
|
8
|
+
rl.registerAction("process.run", {
|
|
9
|
+
description: "Run a uProc data processor tool",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
processor: { type: "string", required: true, description: "Processor key (e.g. get-email-from-name-and-domain)" },
|
|
12
|
+
params: { type: "object", required: true, description: "Processor parameters as key-value pairs" },
|
|
13
|
+
dataWebhook: { type: "string", required: false, description: "URL for async callback" },
|
|
14
|
+
},
|
|
15
|
+
async execute(input, ctx) {
|
|
16
|
+
const c = ctx.connection.config;
|
|
17
|
+
const p = input;
|
|
18
|
+
const body = { processor: p.processor, params: p.params };
|
|
19
|
+
if (p.dataWebhook)
|
|
20
|
+
body.callback = { data: p.dataWebhook };
|
|
21
|
+
const res = await fetch("https://api.uproc.io/api/v2/process", {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Basic ${btoa(`${c.email}:${c.apiKey}`)}`,
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify(body),
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok)
|
|
30
|
+
throw new Error(`uProc error ${res.status}: ${await res.text()}`);
|
|
31
|
+
return res.json();
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
const BASE = "https://api.uptimerobot.com/v2";
|
|
2
|
+
async function apiRequest(apiKey, endpoint, body = {}) {
|
|
3
|
+
const form = new URLSearchParams();
|
|
4
|
+
form.set("api_key", apiKey);
|
|
5
|
+
for (const [k, v] of Object.entries(body)) {
|
|
6
|
+
if (v !== undefined && v !== null)
|
|
7
|
+
form.set(k, String(v));
|
|
8
|
+
}
|
|
9
|
+
const res = await fetch(`${BASE}${endpoint}`, {
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
12
|
+
body: form,
|
|
13
|
+
});
|
|
14
|
+
if (!res.ok)
|
|
15
|
+
throw new Error(`UptimeRobot error ${res.status}: ${await res.text()}`);
|
|
16
|
+
const data = (await res.json());
|
|
17
|
+
if (data.stat !== "ok")
|
|
18
|
+
throw new Error(`UptimeRobot error: ${JSON.stringify(data)}`);
|
|
19
|
+
return data;
|
|
20
|
+
}
|
|
21
|
+
export default function uptimerobot(rl) {
|
|
22
|
+
rl.setName("uptimerobot");
|
|
23
|
+
rl.setVersion("0.1.0");
|
|
24
|
+
rl.setConnectionSchema({ apiKey: { type: "string", required: true, description: "UptimeRobot API key", env: "UPTIMEROBOT_API_KEY" } });
|
|
25
|
+
const key = (ctx) => ctx.connection.config.apiKey;
|
|
26
|
+
// ── Account ─────────────────────────────────────────
|
|
27
|
+
rl.registerAction("account.get", {
|
|
28
|
+
description: "Get account details",
|
|
29
|
+
inputSchema: {},
|
|
30
|
+
async execute(_input, ctx) {
|
|
31
|
+
const data = (await apiRequest(key(ctx), "/getAccountDetails"));
|
|
32
|
+
return data.account;
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
// ── Monitor ─────────────────────────────────────────
|
|
36
|
+
rl.registerAction("monitor.create", {
|
|
37
|
+
description: "Create a monitor",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
friendlyName: { type: "string", required: true },
|
|
40
|
+
url: { type: "string", required: true },
|
|
41
|
+
type: { type: "number", required: true, description: "1=HTTP(s), 2=Keyword, 3=Ping, 4=Port, 5=Heartbeat" },
|
|
42
|
+
},
|
|
43
|
+
async execute(input, ctx) {
|
|
44
|
+
const p = input;
|
|
45
|
+
const data = (await apiRequest(key(ctx), "/newMonitor", { friendly_name: p.friendlyName, url: p.url, type: p.type }));
|
|
46
|
+
return data.monitor;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
rl.registerAction("monitor.get", {
|
|
50
|
+
description: "Get a monitor by ID",
|
|
51
|
+
inputSchema: { id: { type: "string", required: true } },
|
|
52
|
+
async execute(input, ctx) {
|
|
53
|
+
const data = (await apiRequest(key(ctx), "/getMonitors", { monitors: input.id }));
|
|
54
|
+
return data.monitors;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
rl.registerAction("monitor.list", {
|
|
58
|
+
description: "List monitors",
|
|
59
|
+
inputSchema: { limit: { type: "number", required: false } },
|
|
60
|
+
async execute(input, ctx) {
|
|
61
|
+
const p = (input ?? {});
|
|
62
|
+
const body = {};
|
|
63
|
+
if (p.limit)
|
|
64
|
+
body.limit = p.limit;
|
|
65
|
+
const data = (await apiRequest(key(ctx), "/getMonitors", body));
|
|
66
|
+
return data.monitors;
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
rl.registerAction("monitor.update", {
|
|
70
|
+
description: "Update a monitor",
|
|
71
|
+
inputSchema: { id: { type: "string", required: true }, friendlyName: { type: "string", required: false }, url: { type: "string", required: false }, type: { type: "number", required: false } },
|
|
72
|
+
async execute(input, ctx) {
|
|
73
|
+
const { id, ...fields } = input;
|
|
74
|
+
const body = { id };
|
|
75
|
+
if (fields.friendlyName)
|
|
76
|
+
body.friendly_name = fields.friendlyName;
|
|
77
|
+
if (fields.url)
|
|
78
|
+
body.url = fields.url;
|
|
79
|
+
if (fields.type)
|
|
80
|
+
body.type = fields.type;
|
|
81
|
+
const data = (await apiRequest(key(ctx), "/editMonitor", body));
|
|
82
|
+
return data.monitor;
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
rl.registerAction("monitor.delete", {
|
|
86
|
+
description: "Delete a monitor",
|
|
87
|
+
inputSchema: { id: { type: "string", required: true } },
|
|
88
|
+
async execute(input, ctx) {
|
|
89
|
+
const data = (await apiRequest(key(ctx), "/deleteMonitor", { id: input.id }));
|
|
90
|
+
return data.monitor;
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
rl.registerAction("monitor.reset", {
|
|
94
|
+
description: "Reset a monitor's stats",
|
|
95
|
+
inputSchema: { id: { type: "string", required: true } },
|
|
96
|
+
async execute(input, ctx) {
|
|
97
|
+
const data = (await apiRequest(key(ctx), "/resetMonitor", { id: input.id }));
|
|
98
|
+
return data.monitor;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
// ── Alert Contact ───────────────────────────────────
|
|
102
|
+
rl.registerAction("alertContact.create", {
|
|
103
|
+
description: "Create an alert contact",
|
|
104
|
+
inputSchema: { friendlyName: { type: "string", required: true }, value: { type: "string", required: true, description: "Email or phone" }, type: { type: "number", required: true, description: "1=SMS, 2=Email, 3=Twitter, 5=Pushbullet, 11=Slack" } },
|
|
105
|
+
async execute(input, ctx) {
|
|
106
|
+
const p = input;
|
|
107
|
+
const data = (await apiRequest(key(ctx), "/newAlertContact", { friendly_name: p.friendlyName, value: p.value, type: p.type }));
|
|
108
|
+
return data.alertcontact;
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
rl.registerAction("alertContact.get", {
|
|
112
|
+
description: "Get an alert contact by ID",
|
|
113
|
+
inputSchema: { id: { type: "string", required: true } },
|
|
114
|
+
async execute(input, ctx) {
|
|
115
|
+
const data = (await apiRequest(key(ctx), "/getAlertContacts", { alert_contacts: input.id }));
|
|
116
|
+
return data.alert_contacts;
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
rl.registerAction("alertContact.list", {
|
|
120
|
+
description: "List alert contacts",
|
|
121
|
+
inputSchema: { limit: { type: "number", required: false } },
|
|
122
|
+
async execute(input, ctx) {
|
|
123
|
+
const body = {};
|
|
124
|
+
if (input?.limit)
|
|
125
|
+
body.limit = input.limit;
|
|
126
|
+
const data = (await apiRequest(key(ctx), "/getAlertContacts", body));
|
|
127
|
+
return data.alert_contacts;
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
rl.registerAction("alertContact.update", {
|
|
131
|
+
description: "Update an alert contact",
|
|
132
|
+
inputSchema: { id: { type: "string", required: true }, friendlyName: { type: "string", required: false }, value: { type: "string", required: false } },
|
|
133
|
+
async execute(input, ctx) {
|
|
134
|
+
const { id, ...fields } = input;
|
|
135
|
+
const body = { id };
|
|
136
|
+
if (fields.friendlyName)
|
|
137
|
+
body.friendly_name = fields.friendlyName;
|
|
138
|
+
if (fields.value)
|
|
139
|
+
body.value = fields.value;
|
|
140
|
+
const data = (await apiRequest(key(ctx), "/editAlertContact", body));
|
|
141
|
+
return data.alert_contact;
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
rl.registerAction("alertContact.delete", {
|
|
145
|
+
description: "Delete an alert contact",
|
|
146
|
+
inputSchema: { id: { type: "string", required: true } },
|
|
147
|
+
async execute(input, ctx) {
|
|
148
|
+
const data = (await apiRequest(key(ctx), "/deleteAlertContact", { id: input.id }));
|
|
149
|
+
return data.alert_contact;
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
// ── Maintenance Window ──────────────────────────────
|
|
153
|
+
rl.registerAction("maintenanceWindow.create", {
|
|
154
|
+
description: "Create a maintenance window",
|
|
155
|
+
inputSchema: {
|
|
156
|
+
friendlyName: { type: "string", required: true },
|
|
157
|
+
type: { type: "number", required: true, description: "1=Once, 2=Daily, 3=Weekly, 4=Monthly" },
|
|
158
|
+
startTime: { type: "string", required: true, description: "ISO datetime for once, HH:mm for recurring" },
|
|
159
|
+
duration: { type: "number", required: true, description: "Duration in minutes" },
|
|
160
|
+
value: { type: "number", required: false, description: "Day of week (1-7) or month (1-28) for weekly/monthly" },
|
|
161
|
+
},
|
|
162
|
+
async execute(input, ctx) {
|
|
163
|
+
const p = input;
|
|
164
|
+
const body = { friendly_name: p.friendlyName, type: p.type, start_time: p.startTime, duration: p.duration };
|
|
165
|
+
if (p.value)
|
|
166
|
+
body.value = p.value;
|
|
167
|
+
const data = (await apiRequest(key(ctx), "/newMWindow", body));
|
|
168
|
+
return data.mwindow;
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
rl.registerAction("maintenanceWindow.get", {
|
|
172
|
+
description: "Get a maintenance window",
|
|
173
|
+
inputSchema: { id: { type: "string", required: true } },
|
|
174
|
+
async execute(input, ctx) {
|
|
175
|
+
const data = (await apiRequest(key(ctx), "/getMWindows", { mwindows: input.id }));
|
|
176
|
+
return data.mwindows;
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
rl.registerAction("maintenanceWindow.list", {
|
|
180
|
+
description: "List maintenance windows",
|
|
181
|
+
inputSchema: { limit: { type: "number", required: false } },
|
|
182
|
+
async execute(input, ctx) {
|
|
183
|
+
const body = {};
|
|
184
|
+
if (input?.limit)
|
|
185
|
+
body.limit = input.limit;
|
|
186
|
+
const data = (await apiRequest(key(ctx), "/getMWindows", body));
|
|
187
|
+
return data.mwindows;
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
rl.registerAction("maintenanceWindow.update", {
|
|
191
|
+
description: "Update a maintenance window",
|
|
192
|
+
inputSchema: { id: { type: "string", required: true }, friendlyName: { type: "string", required: false }, duration: { type: "number", required: false }, startTime: { type: "string", required: false } },
|
|
193
|
+
async execute(input, ctx) {
|
|
194
|
+
const { id, ...fields } = input;
|
|
195
|
+
const body = { id };
|
|
196
|
+
if (fields.friendlyName)
|
|
197
|
+
body.friendly_name = fields.friendlyName;
|
|
198
|
+
if (fields.duration)
|
|
199
|
+
body.duration = fields.duration;
|
|
200
|
+
if (fields.startTime)
|
|
201
|
+
body.start_time = fields.startTime;
|
|
202
|
+
const data = (await apiRequest(key(ctx), "/editMWindow", body));
|
|
203
|
+
return data.mwindow;
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
rl.registerAction("maintenanceWindow.delete", {
|
|
207
|
+
description: "Delete a maintenance window",
|
|
208
|
+
inputSchema: { id: { type: "string", required: true } },
|
|
209
|
+
async execute(input, ctx) {
|
|
210
|
+
return apiRequest(key(ctx), "/deleteMWindow", { id: input.id });
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
// ── Public Status Page ──────────────────────────────
|
|
214
|
+
rl.registerAction("publicStatusPage.create", {
|
|
215
|
+
description: "Create a public status page",
|
|
216
|
+
inputSchema: { friendlyName: { type: "string", required: true }, monitors: { type: "string", required: true, description: "Monitor IDs (comma-separated or 0 for all)" } },
|
|
217
|
+
async execute(input, ctx) {
|
|
218
|
+
const p = input;
|
|
219
|
+
const data = (await apiRequest(key(ctx), "/newPSP", { friendly_name: p.friendlyName, monitors: p.monitors }));
|
|
220
|
+
return data.psp;
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
rl.registerAction("publicStatusPage.get", {
|
|
224
|
+
description: "Get a public status page",
|
|
225
|
+
inputSchema: { id: { type: "string", required: true } },
|
|
226
|
+
async execute(input, ctx) {
|
|
227
|
+
const data = (await apiRequest(key(ctx), "/getPSPs", { psps: input.id }));
|
|
228
|
+
return data.psps;
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
rl.registerAction("publicStatusPage.list", {
|
|
232
|
+
description: "List public status pages",
|
|
233
|
+
inputSchema: { limit: { type: "number", required: false } },
|
|
234
|
+
async execute(input, ctx) {
|
|
235
|
+
const body = {};
|
|
236
|
+
if (input?.limit)
|
|
237
|
+
body.limit = input.limit;
|
|
238
|
+
const data = (await apiRequest(key(ctx), "/getPSPs", body));
|
|
239
|
+
return data.psps;
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
rl.registerAction("publicStatusPage.update", {
|
|
243
|
+
description: "Update a public status page",
|
|
244
|
+
inputSchema: { id: { type: "string", required: true }, friendlyName: { type: "string", required: false }, monitors: { type: "string", required: false } },
|
|
245
|
+
async execute(input, ctx) {
|
|
246
|
+
const { id, ...fields } = input;
|
|
247
|
+
const body = { id };
|
|
248
|
+
if (fields.friendlyName)
|
|
249
|
+
body.friendly_name = fields.friendlyName;
|
|
250
|
+
if (fields.monitors)
|
|
251
|
+
body.monitors = fields.monitors;
|
|
252
|
+
const data = (await apiRequest(key(ctx), "/editPSP", body));
|
|
253
|
+
return data.psp;
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
rl.registerAction("publicStatusPage.delete", {
|
|
257
|
+
description: "Delete a public status page",
|
|
258
|
+
inputSchema: { id: { type: "string", required: true } },
|
|
259
|
+
async execute(input, ctx) {
|
|
260
|
+
const data = (await apiRequest(key(ctx), "/deletePSP", { id: input.id }));
|
|
261
|
+
return data.psp;
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const BASE = "https://urlscan.io/api/v1";
|
|
2
|
+
async function apiRequest(apiKey, method, endpoint, body, qs) {
|
|
3
|
+
const url = new URL(`${BASE}${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 init = { method, headers: { "API-Key": apiKey, "Content-Type": "application/json" } };
|
|
11
|
+
if (body && Object.keys(body).length > 0)
|
|
12
|
+
init.body = JSON.stringify(body);
|
|
13
|
+
const res = await fetch(url.toString(), init);
|
|
14
|
+
if (!res.ok)
|
|
15
|
+
throw new Error(`urlscan.io error ${res.status}: ${await res.text()}`);
|
|
16
|
+
return res.json();
|
|
17
|
+
}
|
|
18
|
+
export default function urlscanio(rl) {
|
|
19
|
+
rl.setName("urlscanio");
|
|
20
|
+
rl.setVersion("0.1.0");
|
|
21
|
+
rl.setConnectionSchema({
|
|
22
|
+
apiKey: { type: "string", required: true, description: "urlscan.io API key", env: "URLSCANIO_API_KEY" },
|
|
23
|
+
});
|
|
24
|
+
const key = (ctx) => ctx.connection.config.apiKey;
|
|
25
|
+
rl.registerAction("scan.perform", {
|
|
26
|
+
description: "Submit a URL for scanning",
|
|
27
|
+
inputSchema: {
|
|
28
|
+
url: { type: "string", required: true },
|
|
29
|
+
visibility: { type: "string", required: false, description: "public, private, or unlisted" },
|
|
30
|
+
tags: { type: "string", required: false, description: "Comma-separated tags (max 10)" },
|
|
31
|
+
customAgent: { type: "string", required: false },
|
|
32
|
+
},
|
|
33
|
+
async execute(input, ctx) {
|
|
34
|
+
const p = input;
|
|
35
|
+
const body = { url: p.url };
|
|
36
|
+
if (p.visibility)
|
|
37
|
+
body.visibility = p.visibility;
|
|
38
|
+
if (p.tags)
|
|
39
|
+
body.tags = p.tags.split(",").map(t => t.trim());
|
|
40
|
+
if (p.customAgent)
|
|
41
|
+
body.customAgent = p.customAgent;
|
|
42
|
+
return apiRequest(key(ctx), "POST", "/scan", body);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
rl.registerAction("scan.get", {
|
|
46
|
+
description: "Get scan results by ID",
|
|
47
|
+
inputSchema: { scanId: { type: "string", required: true } },
|
|
48
|
+
async execute(input, ctx) {
|
|
49
|
+
return apiRequest(key(ctx), "GET", `/result/${input.scanId}`);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
rl.registerAction("scan.search", {
|
|
53
|
+
description: "Search scan results",
|
|
54
|
+
inputSchema: { query: { type: "string", required: false, description: "Search query" }, limit: { type: "number", required: false } },
|
|
55
|
+
async execute(input, ctx) {
|
|
56
|
+
const p = (input ?? {});
|
|
57
|
+
const qs = { size: p.limit ?? 100 };
|
|
58
|
+
if (p.query)
|
|
59
|
+
qs.q = p.query;
|
|
60
|
+
const data = (await apiRequest(key(ctx), "GET", "/search", undefined, qs));
|
|
61
|
+
return data.results;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|