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,147 @@
|
|
|
1
|
+
async function apiRequest(host, apiKey, method, endpoint, body, qs) {
|
|
2
|
+
const url = new URL(`${host}/api${endpoint}`);
|
|
3
|
+
if (qs) {
|
|
4
|
+
for (const [k, v] of Object.entries(qs)) {
|
|
5
|
+
if (v !== undefined && v !== null)
|
|
6
|
+
url.searchParams.set(k, String(v));
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const opts = {
|
|
10
|
+
method,
|
|
11
|
+
headers: {
|
|
12
|
+
"Content-Type": "application/json",
|
|
13
|
+
Authorization: `Bearer ${apiKey}`,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE") {
|
|
17
|
+
opts.body = JSON.stringify(body);
|
|
18
|
+
}
|
|
19
|
+
const res = await fetch(url.toString(), opts);
|
|
20
|
+
if (!res.ok)
|
|
21
|
+
throw new Error(`Cortex API error ${res.status}: ${await res.text()}`);
|
|
22
|
+
return res.json();
|
|
23
|
+
}
|
|
24
|
+
function getConn(ctx) {
|
|
25
|
+
return {
|
|
26
|
+
host: ctx.connection.config.host.replace(/\/$/, ""),
|
|
27
|
+
apiKey: ctx.connection.config.apiKey,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export default function cortex(rl) {
|
|
31
|
+
rl.setName("cortex");
|
|
32
|
+
rl.setVersion("0.1.0");
|
|
33
|
+
rl.setConnectionSchema({
|
|
34
|
+
host: {
|
|
35
|
+
type: "string",
|
|
36
|
+
required: true,
|
|
37
|
+
description: "Cortex instance URL (e.g. https://cortex.example.com)",
|
|
38
|
+
env: "CORTEX_HOST",
|
|
39
|
+
},
|
|
40
|
+
apiKey: {
|
|
41
|
+
type: "string",
|
|
42
|
+
required: true,
|
|
43
|
+
description: "Cortex API key",
|
|
44
|
+
env: "CORTEX_API_KEY",
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
// ── Analyzer ────────────────────────────────────────
|
|
48
|
+
rl.registerAction("analyzer.execute", {
|
|
49
|
+
description: "Run an analyzer on an observable",
|
|
50
|
+
inputSchema: {
|
|
51
|
+
analyzerId: { type: "string", required: true, description: "Analyzer ID" },
|
|
52
|
+
dataType: { type: "string", required: true, description: "Observable type (domain, ip, url, mail, hash, filename, fqdn, uri_path, user-agent, regexp, registry, mail_subject, other)" },
|
|
53
|
+
data: { type: "string", required: true, description: "Observable value" },
|
|
54
|
+
tlp: { type: "number", required: false, description: "TLP level: 0=white, 1=green, 2=amber (default), 3=red" },
|
|
55
|
+
force: { type: "boolean", required: false, description: "Bypass cache" },
|
|
56
|
+
timeout: { type: "number", required: false, description: "Wait for report (seconds). If set, blocks until report is ready." },
|
|
57
|
+
},
|
|
58
|
+
async execute(input, ctx) {
|
|
59
|
+
const { analyzerId, dataType, data, tlp = 2, force, timeout } = input;
|
|
60
|
+
const { host, apiKey } = getConn(ctx);
|
|
61
|
+
const qs = {};
|
|
62
|
+
if (force)
|
|
63
|
+
qs.force = true;
|
|
64
|
+
const result = (await apiRequest(host, apiKey, "POST", `/analyzer/${analyzerId}/run`, {
|
|
65
|
+
dataType,
|
|
66
|
+
data,
|
|
67
|
+
tlp,
|
|
68
|
+
}, qs));
|
|
69
|
+
if (timeout && result.id) {
|
|
70
|
+
return apiRequest(host, apiKey, "GET", `/job/${result.id}/waitreport`, undefined, {
|
|
71
|
+
atMost: `${timeout}second`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
// ── Job ─────────────────────────────────────────────
|
|
78
|
+
rl.registerAction("job.get", {
|
|
79
|
+
description: "Get job details",
|
|
80
|
+
inputSchema: {
|
|
81
|
+
jobId: { type: "string", required: true, description: "Job ID" },
|
|
82
|
+
},
|
|
83
|
+
async execute(input, ctx) {
|
|
84
|
+
const { jobId } = input;
|
|
85
|
+
const { host, apiKey } = getConn(ctx);
|
|
86
|
+
return apiRequest(host, apiKey, "GET", `/job/${jobId}`);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
rl.registerAction("job.getReport", {
|
|
90
|
+
description: "Get job report",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
jobId: { type: "string", required: true, description: "Job ID" },
|
|
93
|
+
},
|
|
94
|
+
async execute(input, ctx) {
|
|
95
|
+
const { jobId } = input;
|
|
96
|
+
const { host, apiKey } = getConn(ctx);
|
|
97
|
+
return apiRequest(host, apiKey, "GET", `/job/${jobId}/report`);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
// ── Responder ───────────────────────────────────────
|
|
101
|
+
rl.registerAction("responder.execute", {
|
|
102
|
+
description: "Run a responder on an entity",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
responderId: { type: "string", required: true, description: "Responder ID" },
|
|
105
|
+
entityType: { type: "string", required: true, description: "Entity type: case, alert, case_artifact, case_task, case_task_log" },
|
|
106
|
+
data: { type: "object", required: true, description: "Entity data object (must include _type matching entityType)" },
|
|
107
|
+
tlp: { type: "number", required: false, description: "TLP level (default: 2)" },
|
|
108
|
+
pap: { type: "number", required: false, description: "PAP level (default: 2)" },
|
|
109
|
+
message: { type: "string", required: false, description: "Message" },
|
|
110
|
+
},
|
|
111
|
+
async execute(input, ctx) {
|
|
112
|
+
const { responderId, entityType, data, tlp = 2, pap = 2, message } = input;
|
|
113
|
+
const { host, apiKey } = getConn(ctx);
|
|
114
|
+
const entityData = { _type: entityType, ...data };
|
|
115
|
+
const body = {
|
|
116
|
+
responderId,
|
|
117
|
+
dataType: `thehive:${entityType}`,
|
|
118
|
+
data: entityData,
|
|
119
|
+
tlp,
|
|
120
|
+
pap,
|
|
121
|
+
message: message ?? "",
|
|
122
|
+
parameters: [],
|
|
123
|
+
};
|
|
124
|
+
// Generate label based on entity type
|
|
125
|
+
let label = "";
|
|
126
|
+
switch (entityType) {
|
|
127
|
+
case "case":
|
|
128
|
+
label = `#${entityData.caseId} ${entityData.title}`;
|
|
129
|
+
break;
|
|
130
|
+
case "alert":
|
|
131
|
+
label = `[${entityData.source}:${entityData.sourceRef}] ${entityData.title}`;
|
|
132
|
+
break;
|
|
133
|
+
case "case_artifact":
|
|
134
|
+
label = `[${entityData.dataType}] ${entityData.data ?? ""}`;
|
|
135
|
+
break;
|
|
136
|
+
case "case_task":
|
|
137
|
+
label = `${entityData.title} (${entityData.status})`;
|
|
138
|
+
break;
|
|
139
|
+
case "case_task_log":
|
|
140
|
+
label = `${entityData.message} from ${entityData.createdBy}`;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
body.label = label;
|
|
144
|
+
return apiRequest(host, apiKey, "POST", `/responder/${responderId}/run`, body);
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
const BASE_URL = "https://api.currents.dev/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
|
+
if (Array.isArray(v)) {
|
|
8
|
+
for (const item of v)
|
|
9
|
+
url.searchParams.append(`${k}[]`, String(item));
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
url.searchParams.set(k, String(v));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const opts = {
|
|
18
|
+
method,
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
Accept: "application/json",
|
|
22
|
+
Authorization: `Bearer ${apiKey}`,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE") {
|
|
26
|
+
opts.body = JSON.stringify(body);
|
|
27
|
+
}
|
|
28
|
+
const res = await fetch(url.toString(), opts);
|
|
29
|
+
if (!res.ok)
|
|
30
|
+
throw new Error(`Currents API error ${res.status}: ${await res.text()}`);
|
|
31
|
+
if (res.status === 204)
|
|
32
|
+
return { success: true };
|
|
33
|
+
return res.json();
|
|
34
|
+
}
|
|
35
|
+
function unwrapData(response) {
|
|
36
|
+
if (response && typeof response === "object" && "data" in response) {
|
|
37
|
+
return response.data;
|
|
38
|
+
}
|
|
39
|
+
return response;
|
|
40
|
+
}
|
|
41
|
+
function getKey(ctx) {
|
|
42
|
+
return ctx.connection.config.apiKey;
|
|
43
|
+
}
|
|
44
|
+
export default function currents(rl) {
|
|
45
|
+
rl.setName("currents");
|
|
46
|
+
rl.setVersion("0.1.0");
|
|
47
|
+
rl.setConnectionSchema({
|
|
48
|
+
apiKey: {
|
|
49
|
+
type: "string",
|
|
50
|
+
required: true,
|
|
51
|
+
description: "Currents API key",
|
|
52
|
+
env: "CURRENTS_API_KEY",
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
// ── Action ──────────────────────────────────────────
|
|
56
|
+
rl.registerAction("action.create", {
|
|
57
|
+
description: "Create a new action (skip, quarantine, or tag rule)",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
projectId: { type: "string", required: true, description: "Project ID" },
|
|
60
|
+
name: { type: "string", required: true, description: "Action name" },
|
|
61
|
+
actionType: { type: "string", required: true, description: "Type: skip, quarantine, or tag" },
|
|
62
|
+
matcherType: { type: "string", required: true, description: "Matcher: titleContains, titleEquals, specContains, specEquals, signature" },
|
|
63
|
+
matcherValue: { type: "string", required: true, description: "Value to match against" },
|
|
64
|
+
tags: { type: "array", required: false, description: "Tags to apply (for tag action type)" },
|
|
65
|
+
description: { type: "string", required: false, description: "Description" },
|
|
66
|
+
expiresAfter: { type: "string", required: false, description: "Expiration date (ISO 8601)" },
|
|
67
|
+
},
|
|
68
|
+
async execute(input, ctx) {
|
|
69
|
+
const { projectId, name, actionType, matcherType, matcherValue, tags, description: desc, expiresAfter } = input;
|
|
70
|
+
const typeMap = {
|
|
71
|
+
titleContains: "title", titleEquals: "title",
|
|
72
|
+
specContains: "file", specEquals: "file",
|
|
73
|
+
signature: "testId",
|
|
74
|
+
};
|
|
75
|
+
const opMap = {
|
|
76
|
+
titleContains: "inc", titleEquals: "eq",
|
|
77
|
+
specContains: "inc", specEquals: "eq",
|
|
78
|
+
signature: "eq",
|
|
79
|
+
};
|
|
80
|
+
const action = actionType === "tag" && tags
|
|
81
|
+
? [{ op: "tag", details: { tags } }]
|
|
82
|
+
: [{ op: actionType }];
|
|
83
|
+
const body = {
|
|
84
|
+
name,
|
|
85
|
+
action,
|
|
86
|
+
matcher: {
|
|
87
|
+
op: "AND",
|
|
88
|
+
cond: [{ type: typeMap[matcherType], op: opMap[matcherType], value: matcherValue }],
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
if (desc)
|
|
92
|
+
body.description = desc;
|
|
93
|
+
if (expiresAfter)
|
|
94
|
+
body.expiresAfter = expiresAfter;
|
|
95
|
+
return apiRequest(getKey(ctx), "POST", "/actions", body, { projectId });
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
rl.registerAction("action.get", {
|
|
99
|
+
description: "Get an action by ID",
|
|
100
|
+
inputSchema: { actionId: { type: "string", required: true, description: "Action ID" } },
|
|
101
|
+
async execute(input, ctx) {
|
|
102
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", `/actions/${input.actionId}`));
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
rl.registerAction("action.list", {
|
|
106
|
+
description: "List actions for a project",
|
|
107
|
+
inputSchema: {
|
|
108
|
+
projectId: { type: "string", required: true, description: "Project ID" },
|
|
109
|
+
search: { type: "string", required: false, description: "Search by name" },
|
|
110
|
+
status: { type: "array", required: false, description: "Filter by status: active, disabled, archived, expired" },
|
|
111
|
+
},
|
|
112
|
+
async execute(input, ctx) {
|
|
113
|
+
const { projectId, search, status } = (input ?? {});
|
|
114
|
+
const qs = { projectId };
|
|
115
|
+
if (search)
|
|
116
|
+
qs.search = search;
|
|
117
|
+
if (status)
|
|
118
|
+
qs.status = status;
|
|
119
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", "/actions", undefined, qs));
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
rl.registerAction("action.update", {
|
|
123
|
+
description: "Update an action",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
actionId: { type: "string", required: true, description: "Action ID" },
|
|
126
|
+
name: { type: "string", required: false, description: "New name" },
|
|
127
|
+
description: { type: "string", required: false, description: "New description" },
|
|
128
|
+
expiresAfter: { type: "string", required: false, description: "Expiration date" },
|
|
129
|
+
},
|
|
130
|
+
async execute(input, ctx) {
|
|
131
|
+
const { actionId, ...body } = input;
|
|
132
|
+
return apiRequest(getKey(ctx), "PUT", `/actions/${actionId}`, body);
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
rl.registerAction("action.enable", {
|
|
136
|
+
description: "Enable a disabled action",
|
|
137
|
+
inputSchema: { actionId: { type: "string", required: true, description: "Action ID" } },
|
|
138
|
+
async execute(input, ctx) {
|
|
139
|
+
return apiRequest(getKey(ctx), "PUT", `/actions/${input.actionId}/enable`);
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
rl.registerAction("action.disable", {
|
|
143
|
+
description: "Disable an active action",
|
|
144
|
+
inputSchema: { actionId: { type: "string", required: true, description: "Action ID" } },
|
|
145
|
+
async execute(input, ctx) {
|
|
146
|
+
return apiRequest(getKey(ctx), "PUT", `/actions/${input.actionId}/disable`);
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
rl.registerAction("action.delete", {
|
|
150
|
+
description: "Archive (soft delete) an action",
|
|
151
|
+
inputSchema: { actionId: { type: "string", required: true, description: "Action ID" } },
|
|
152
|
+
async execute(input, ctx) {
|
|
153
|
+
await apiRequest(getKey(ctx), "DELETE", `/actions/${input.actionId}`);
|
|
154
|
+
return { success: true };
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
// ── Instance ────────────────────────────────────────
|
|
158
|
+
rl.registerAction("instance.get", {
|
|
159
|
+
description: "Get a spec file execution instance with full test results",
|
|
160
|
+
inputSchema: { instanceId: { type: "string", required: true, description: "Instance ID" } },
|
|
161
|
+
async execute(input, ctx) {
|
|
162
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", `/instances/${input.instanceId}`));
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
// ── Project ─────────────────────────────────────────
|
|
166
|
+
rl.registerAction("project.get", {
|
|
167
|
+
description: "Get a project by ID",
|
|
168
|
+
inputSchema: { projectId: { type: "string", required: true, description: "Project ID" } },
|
|
169
|
+
async execute(input, ctx) {
|
|
170
|
+
return apiRequest(getKey(ctx), "GET", `/projects/${input.projectId}`);
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
rl.registerAction("project.list", {
|
|
174
|
+
description: "List projects",
|
|
175
|
+
inputSchema: { limit: { type: "number", required: false, description: "Max results" } },
|
|
176
|
+
async execute(input, ctx) {
|
|
177
|
+
const qs = {};
|
|
178
|
+
const { limit } = (input ?? {});
|
|
179
|
+
if (limit)
|
|
180
|
+
qs.limit = limit;
|
|
181
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", "/projects", undefined, qs));
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
rl.registerAction("project.getInsights", {
|
|
185
|
+
description: "Get project insights and metrics",
|
|
186
|
+
inputSchema: {
|
|
187
|
+
projectId: { type: "string", required: true, description: "Project ID" },
|
|
188
|
+
dateStart: { type: "string", required: true, description: "Start date (ISO 8601)" },
|
|
189
|
+
dateEnd: { type: "string", required: true, description: "End date (ISO 8601)" },
|
|
190
|
+
resolution: { type: "string", required: false, description: "Resolution: 1h, 1d (default), 1w" },
|
|
191
|
+
branches: { type: "array", required: false, description: "Filter by branches" },
|
|
192
|
+
authors: { type: "array", required: false, description: "Filter by authors" },
|
|
193
|
+
tags: { type: "array", required: false, description: "Filter by tags" },
|
|
194
|
+
},
|
|
195
|
+
async execute(input, ctx) {
|
|
196
|
+
const { projectId, dateStart, dateEnd, resolution, branches, authors, tags } = input;
|
|
197
|
+
const qs = { date_start: dateStart, date_end: dateEnd };
|
|
198
|
+
if (resolution)
|
|
199
|
+
qs.resolution = resolution;
|
|
200
|
+
if (branches)
|
|
201
|
+
qs.branches = branches;
|
|
202
|
+
if (authors)
|
|
203
|
+
qs.authors = authors;
|
|
204
|
+
if (tags)
|
|
205
|
+
qs.tags = tags;
|
|
206
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", `/projects/${projectId}/insights`, undefined, qs));
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
// ── Run ─────────────────────────────────────────────
|
|
210
|
+
rl.registerAction("run.get", {
|
|
211
|
+
description: "Get a run by ID",
|
|
212
|
+
inputSchema: { runId: { type: "string", required: true, description: "Run ID" } },
|
|
213
|
+
async execute(input, ctx) {
|
|
214
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", `/runs/${input.runId}`));
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
rl.registerAction("run.list", {
|
|
218
|
+
description: "List runs for a project",
|
|
219
|
+
inputSchema: {
|
|
220
|
+
projectId: { type: "string", required: true, description: "Project ID" },
|
|
221
|
+
limit: { type: "number", required: false, description: "Max results (default: 10, max: 50)" },
|
|
222
|
+
search: { type: "string", required: false, description: "Search by ciBuildId or commit message" },
|
|
223
|
+
status: { type: "array", required: false, description: "Filter by status" },
|
|
224
|
+
completionState: { type: "array", required: false, description: "Filter by completion state" },
|
|
225
|
+
branches: { type: "array", required: false, description: "Filter by branches" },
|
|
226
|
+
authors: { type: "array", required: false, description: "Filter by authors" },
|
|
227
|
+
tags: { type: "array", required: false, description: "Filter by tags" },
|
|
228
|
+
dateStart: { type: "string", required: false, description: "Start date" },
|
|
229
|
+
dateEnd: { type: "string", required: false, description: "End date" },
|
|
230
|
+
},
|
|
231
|
+
async execute(input, ctx) {
|
|
232
|
+
const { projectId, limit, search, status, completionState, branches, authors, tags, dateStart, dateEnd } = (input ?? {});
|
|
233
|
+
const qs = {};
|
|
234
|
+
if (limit)
|
|
235
|
+
qs.limit = limit;
|
|
236
|
+
if (search)
|
|
237
|
+
qs.search = search;
|
|
238
|
+
if (status)
|
|
239
|
+
qs.status = status;
|
|
240
|
+
if (completionState)
|
|
241
|
+
qs.completion_state = completionState;
|
|
242
|
+
if (branches)
|
|
243
|
+
qs.branches = branches;
|
|
244
|
+
if (authors)
|
|
245
|
+
qs.authors = authors;
|
|
246
|
+
if (tags)
|
|
247
|
+
qs.tags = tags;
|
|
248
|
+
if (dateStart)
|
|
249
|
+
qs.date_start = dateStart;
|
|
250
|
+
if (dateEnd)
|
|
251
|
+
qs.date_end = dateEnd;
|
|
252
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", `/projects/${projectId}/runs`, undefined, qs));
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
rl.registerAction("run.find", {
|
|
256
|
+
description: "Find a run by project and filters",
|
|
257
|
+
inputSchema: {
|
|
258
|
+
projectId: { type: "string", required: true, description: "Project ID" },
|
|
259
|
+
branch: { type: "string", required: false, description: "Filter by branch" },
|
|
260
|
+
ciBuildId: { type: "string", required: false, description: "Filter by CI build ID" },
|
|
261
|
+
tags: { type: "array", required: false, description: "Filter by tags" },
|
|
262
|
+
},
|
|
263
|
+
async execute(input, ctx) {
|
|
264
|
+
const { projectId, branch, ciBuildId, tags } = (input ?? {});
|
|
265
|
+
const qs = { projectId };
|
|
266
|
+
if (branch)
|
|
267
|
+
qs.branch = branch;
|
|
268
|
+
if (ciBuildId)
|
|
269
|
+
qs.ciBuildId = ciBuildId;
|
|
270
|
+
if (tags)
|
|
271
|
+
qs.tags = tags;
|
|
272
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", "/runs/find", undefined, qs));
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
rl.registerAction("run.cancel", {
|
|
276
|
+
description: "Cancel a run in progress",
|
|
277
|
+
inputSchema: { runId: { type: "string", required: true, description: "Run ID" } },
|
|
278
|
+
async execute(input, ctx) {
|
|
279
|
+
return apiRequest(getKey(ctx), "PUT", `/runs/${input.runId}/cancel`);
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
rl.registerAction("run.cancelGithub", {
|
|
283
|
+
description: "Cancel a run by GitHub Actions workflow run ID",
|
|
284
|
+
inputSchema: {
|
|
285
|
+
githubRunId: { type: "string", required: true, description: "GitHub Actions workflow run ID" },
|
|
286
|
+
githubRunAttempt: { type: "number", required: true, description: "Workflow attempt number" },
|
|
287
|
+
projectId: { type: "string", required: false, description: "Limit to specific project" },
|
|
288
|
+
ciBuildId: { type: "string", required: false, description: "Limit to specific CI build" },
|
|
289
|
+
},
|
|
290
|
+
async execute(input, ctx) {
|
|
291
|
+
const body = input;
|
|
292
|
+
return apiRequest(getKey(ctx), "PUT", "/runs/cancel-ci/github", body);
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
rl.registerAction("run.reset", {
|
|
296
|
+
description: "Reset failed specs for re-execution on specified machines",
|
|
297
|
+
inputSchema: {
|
|
298
|
+
runId: { type: "string", required: true, description: "Run ID" },
|
|
299
|
+
machineIds: { type: "array", required: true, description: "Array of machine identifiers to reset" },
|
|
300
|
+
isBatchedOr8n: { type: "boolean", required: false, description: "Enable batched orchestration" },
|
|
301
|
+
},
|
|
302
|
+
async execute(input, ctx) {
|
|
303
|
+
const { runId, machineIds, isBatchedOr8n } = input;
|
|
304
|
+
const body = { machineId: machineIds };
|
|
305
|
+
if (isBatchedOr8n)
|
|
306
|
+
body.isBatchedOr8n = true;
|
|
307
|
+
return apiRequest(getKey(ctx), "PUT", `/runs/${runId}/reset`, body);
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
rl.registerAction("run.delete", {
|
|
311
|
+
description: "Delete a run and all associated data",
|
|
312
|
+
inputSchema: { runId: { type: "string", required: true, description: "Run ID" } },
|
|
313
|
+
async execute(input, ctx) {
|
|
314
|
+
await apiRequest(getKey(ctx), "DELETE", `/runs/${input.runId}`);
|
|
315
|
+
return { success: true };
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
// ── Signature ───────────────────────────────────────
|
|
319
|
+
rl.registerAction("signature.generate", {
|
|
320
|
+
description: "Generate a unique test signature",
|
|
321
|
+
inputSchema: {
|
|
322
|
+
projectId: { type: "string", required: true, description: "Project ID" },
|
|
323
|
+
specFilePath: { type: "string", required: true, description: "Full spec file path" },
|
|
324
|
+
testTitle: { type: "string", required: true, description: "Test title (use ' > ' for nested describes)" },
|
|
325
|
+
},
|
|
326
|
+
async execute(input, ctx) {
|
|
327
|
+
return unwrapData(await apiRequest(getKey(ctx), "POST", "/signature/test", input));
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
// ── Spec File ───────────────────────────────────────
|
|
331
|
+
rl.registerAction("specFile.list", {
|
|
332
|
+
description: "Get aggregated spec file metrics for a project",
|
|
333
|
+
inputSchema: {
|
|
334
|
+
projectId: { type: "string", required: true, description: "Project ID" },
|
|
335
|
+
dateStart: { type: "string", required: true, description: "Start date (ISO 8601)" },
|
|
336
|
+
dateEnd: { type: "string", required: true, description: "End date (ISO 8601)" },
|
|
337
|
+
limit: { type: "number", required: false, description: "Max results (default: 50)" },
|
|
338
|
+
order: { type: "string", required: false, description: "Order by field" },
|
|
339
|
+
dir: { type: "string", required: false, description: "Sort direction: asc or desc" },
|
|
340
|
+
},
|
|
341
|
+
async execute(input, ctx) {
|
|
342
|
+
const { projectId, dateStart, dateEnd, limit, order, dir } = input;
|
|
343
|
+
const qs = { date_start: dateStart, date_end: dateEnd };
|
|
344
|
+
if (limit)
|
|
345
|
+
qs.limit = limit;
|
|
346
|
+
if (order)
|
|
347
|
+
qs.order = order;
|
|
348
|
+
if (dir)
|
|
349
|
+
qs.dir = dir;
|
|
350
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", `/spec-files/${projectId}`, undefined, qs));
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
// ── Test ────────────────────────────────────────────
|
|
354
|
+
rl.registerAction("test.list", {
|
|
355
|
+
description: "Get aggregated test metrics for a project",
|
|
356
|
+
inputSchema: {
|
|
357
|
+
projectId: { type: "string", required: true, description: "Project ID" },
|
|
358
|
+
dateStart: { type: "string", required: true, description: "Start date (ISO 8601)" },
|
|
359
|
+
dateEnd: { type: "string", required: true, description: "End date (ISO 8601)" },
|
|
360
|
+
limit: { type: "number", required: false, description: "Max results (default: 50)" },
|
|
361
|
+
order: { type: "string", required: false, description: "Order by field" },
|
|
362
|
+
dir: { type: "string", required: false, description: "Sort direction: asc or desc" },
|
|
363
|
+
title: { type: "string", required: false, description: "Filter by test title" },
|
|
364
|
+
spec: { type: "string", required: false, description: "Filter by spec file" },
|
|
365
|
+
},
|
|
366
|
+
async execute(input, ctx) {
|
|
367
|
+
const { projectId, dateStart, dateEnd, limit, order, dir, title, spec } = (input ?? {});
|
|
368
|
+
const qs = { date_start: dateStart, date_end: dateEnd };
|
|
369
|
+
if (limit)
|
|
370
|
+
qs.limit = limit;
|
|
371
|
+
if (order)
|
|
372
|
+
qs.order = order;
|
|
373
|
+
if (dir)
|
|
374
|
+
qs.dir = dir;
|
|
375
|
+
if (title)
|
|
376
|
+
qs.title = title;
|
|
377
|
+
if (spec)
|
|
378
|
+
qs.spec = spec;
|
|
379
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", `/tests/${projectId}`, undefined, qs));
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
// ── Test Result ─────────────────────────────────────
|
|
383
|
+
rl.registerAction("testResult.list", {
|
|
384
|
+
description: "Get historical test execution results for a test signature",
|
|
385
|
+
inputSchema: {
|
|
386
|
+
signature: { type: "string", required: true, description: "Test signature" },
|
|
387
|
+
dateStart: { type: "string", required: true, description: "Start date (ISO 8601)" },
|
|
388
|
+
dateEnd: { type: "string", required: true, description: "End date (ISO 8601)" },
|
|
389
|
+
limit: { type: "number", required: false, description: "Max results (default: 10, max: 100)" },
|
|
390
|
+
status: { type: "array", required: false, description: "Filter by status: passed, failed, pending, skipped" },
|
|
391
|
+
branches: { type: "array", required: false, description: "Filter by branches" },
|
|
392
|
+
},
|
|
393
|
+
async execute(input, ctx) {
|
|
394
|
+
const { signature, dateStart, dateEnd, limit, status, branches } = input;
|
|
395
|
+
const qs = { date_start: dateStart, date_end: dateEnd };
|
|
396
|
+
if (limit)
|
|
397
|
+
qs.limit = limit;
|
|
398
|
+
if (status)
|
|
399
|
+
qs.status = status;
|
|
400
|
+
if (branches)
|
|
401
|
+
qs.branches = branches;
|
|
402
|
+
return unwrapData(await apiRequest(getKey(ctx), "GET", `/test-results/${signature}`, undefined, qs));
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
}
|