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,413 @@
|
|
|
1
|
+
const BASE_URL = "https://app.asana.com/api/1.0";
|
|
2
|
+
async function apiRequest(token, method, endpoint, body, qs) {
|
|
3
|
+
const url = new URL(`${BASE_URL}${endpoint}`);
|
|
4
|
+
if (qs) {
|
|
5
|
+
for (const [k, v] of Object.entries(qs)) {
|
|
6
|
+
if (v !== undefined)
|
|
7
|
+
url.searchParams.set(k, String(v));
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const opts = {
|
|
11
|
+
method,
|
|
12
|
+
headers: {
|
|
13
|
+
"Content-Type": "application/json",
|
|
14
|
+
Authorization: `Bearer ${token}`,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE") {
|
|
18
|
+
opts.body = JSON.stringify({ data: body });
|
|
19
|
+
}
|
|
20
|
+
const res = await fetch(url.toString(), opts);
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
const text = await res.text();
|
|
23
|
+
throw new Error(`Asana API error ${res.status}: ${text}`);
|
|
24
|
+
}
|
|
25
|
+
if (res.status === 204)
|
|
26
|
+
return { success: true };
|
|
27
|
+
const json = (await res.json());
|
|
28
|
+
return json.data ?? json;
|
|
29
|
+
}
|
|
30
|
+
async function paginateAll(token, endpoint, qs, limit) {
|
|
31
|
+
const results = [];
|
|
32
|
+
const _qs = { limit: 100, ...qs };
|
|
33
|
+
let uri;
|
|
34
|
+
while (true) {
|
|
35
|
+
const fetchUrl = uri ?? `${BASE_URL}${endpoint}`;
|
|
36
|
+
const url = new URL(fetchUrl);
|
|
37
|
+
if (!uri) {
|
|
38
|
+
for (const [k, v] of Object.entries(_qs)) {
|
|
39
|
+
if (v !== undefined)
|
|
40
|
+
url.searchParams.set(k, String(v));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const res = await fetch(url.toString(), {
|
|
44
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok)
|
|
47
|
+
throw new Error(`Asana API error ${res.status}: ${await res.text()}`);
|
|
48
|
+
const json = (await res.json());
|
|
49
|
+
const data = json.data ?? [];
|
|
50
|
+
results.push(...data);
|
|
51
|
+
if (limit && results.length >= limit)
|
|
52
|
+
return results.slice(0, limit);
|
|
53
|
+
const nextPage = json.next_page;
|
|
54
|
+
if (!nextPage?.uri)
|
|
55
|
+
break;
|
|
56
|
+
uri = nextPage.uri;
|
|
57
|
+
}
|
|
58
|
+
return results;
|
|
59
|
+
}
|
|
60
|
+
function getToken(ctx) {
|
|
61
|
+
return ctx.connection.config.token;
|
|
62
|
+
}
|
|
63
|
+
export default function asana(rl) {
|
|
64
|
+
rl.setName("asana");
|
|
65
|
+
rl.setVersion("0.1.0");
|
|
66
|
+
rl.setConnectionSchema({
|
|
67
|
+
token: {
|
|
68
|
+
type: "string",
|
|
69
|
+
required: true,
|
|
70
|
+
description: "Asana personal access token",
|
|
71
|
+
env: "ASANA_TOKEN",
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
// ── Task ────────────────────────────────────────────
|
|
75
|
+
rl.registerAction("task.create", {
|
|
76
|
+
description: "Create a new task",
|
|
77
|
+
inputSchema: {
|
|
78
|
+
name: { type: "string", required: true, description: "Task name" },
|
|
79
|
+
workspace: { type: "string", required: true, description: "Workspace GID" },
|
|
80
|
+
assignee: { type: "string", required: false, description: "Assignee GID" },
|
|
81
|
+
projects: { type: "array", required: false, description: "Array of project GIDs" },
|
|
82
|
+
notes: { type: "string", required: false, description: "Task notes" },
|
|
83
|
+
dueOn: { type: "string", required: false, description: "Due date (YYYY-MM-DD)" },
|
|
84
|
+
completed: { type: "boolean", required: false, description: "Mark as completed" },
|
|
85
|
+
},
|
|
86
|
+
async execute(input, ctx) {
|
|
87
|
+
const { name, workspace, assignee, projects, notes, dueOn, completed, ...rest } = input;
|
|
88
|
+
const body = { name, workspace, ...rest };
|
|
89
|
+
if (assignee)
|
|
90
|
+
body.assignee = assignee;
|
|
91
|
+
if (projects)
|
|
92
|
+
body.projects = projects;
|
|
93
|
+
if (notes)
|
|
94
|
+
body.notes = notes;
|
|
95
|
+
if (dueOn)
|
|
96
|
+
body.due_on = dueOn;
|
|
97
|
+
if (completed !== undefined)
|
|
98
|
+
body.completed = completed;
|
|
99
|
+
return apiRequest(getToken(ctx), "POST", "/tasks", body);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
rl.registerAction("task.get", {
|
|
103
|
+
description: "Get a task by ID",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
taskId: { type: "string", required: true, description: "Task GID" },
|
|
106
|
+
},
|
|
107
|
+
async execute(input, ctx) {
|
|
108
|
+
const { taskId } = input;
|
|
109
|
+
return apiRequest(getToken(ctx), "GET", `/tasks/${taskId}`);
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
rl.registerAction("task.list", {
|
|
113
|
+
description: "List tasks (requires project, section, or workspace+assignee filter)",
|
|
114
|
+
inputSchema: {
|
|
115
|
+
project: { type: "string", required: false, description: "Project GID" },
|
|
116
|
+
section: { type: "string", required: false, description: "Section GID" },
|
|
117
|
+
workspace: { type: "string", required: false, description: "Workspace GID" },
|
|
118
|
+
assignee: { type: "string", required: false, description: "Assignee GID (required with workspace)" },
|
|
119
|
+
limit: { type: "number", required: false, description: "Max results to return" },
|
|
120
|
+
},
|
|
121
|
+
async execute(input, ctx) {
|
|
122
|
+
const { project, section, workspace, assignee, limit } = (input ?? {});
|
|
123
|
+
const qs = {};
|
|
124
|
+
if (project)
|
|
125
|
+
qs.project = project;
|
|
126
|
+
if (section)
|
|
127
|
+
qs.section = section;
|
|
128
|
+
if (workspace)
|
|
129
|
+
qs.workspace = workspace;
|
|
130
|
+
if (assignee)
|
|
131
|
+
qs.assignee = assignee;
|
|
132
|
+
return paginateAll(getToken(ctx), "/tasks", qs, limit);
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
rl.registerAction("task.update", {
|
|
136
|
+
description: "Update a task",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
taskId: { type: "string", required: true, description: "Task GID" },
|
|
139
|
+
name: { type: "string", required: false, description: "Task name" },
|
|
140
|
+
notes: { type: "string", required: false, description: "Task notes" },
|
|
141
|
+
assignee: { type: "string", required: false, description: "Assignee GID" },
|
|
142
|
+
dueOn: { type: "string", required: false, description: "Due date (YYYY-MM-DD)" },
|
|
143
|
+
completed: { type: "boolean", required: false, description: "Mark as completed" },
|
|
144
|
+
},
|
|
145
|
+
async execute(input, ctx) {
|
|
146
|
+
const { taskId, dueOn, ...fields } = input;
|
|
147
|
+
const body = { ...fields };
|
|
148
|
+
if (dueOn)
|
|
149
|
+
body.due_on = dueOn;
|
|
150
|
+
return apiRequest(getToken(ctx), "PUT", `/tasks/${taskId}`, body);
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
rl.registerAction("task.delete", {
|
|
154
|
+
description: "Delete a task",
|
|
155
|
+
inputSchema: {
|
|
156
|
+
taskId: { type: "string", required: true, description: "Task GID" },
|
|
157
|
+
},
|
|
158
|
+
async execute(input, ctx) {
|
|
159
|
+
const { taskId } = input;
|
|
160
|
+
await apiRequest(getToken(ctx), "DELETE", `/tasks/${taskId}`);
|
|
161
|
+
return { success: true };
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
rl.registerAction("task.move", {
|
|
165
|
+
description: "Move a task to a section",
|
|
166
|
+
inputSchema: {
|
|
167
|
+
taskId: { type: "string", required: true, description: "Task GID" },
|
|
168
|
+
sectionId: { type: "string", required: true, description: "Section GID to move into" },
|
|
169
|
+
},
|
|
170
|
+
async execute(input, ctx) {
|
|
171
|
+
const { taskId, sectionId } = input;
|
|
172
|
+
await apiRequest(getToken(ctx), "POST", `/sections/${sectionId}/addTask`, { task: taskId });
|
|
173
|
+
return { success: true };
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
rl.registerAction("task.search", {
|
|
177
|
+
description: "Search for tasks in a workspace",
|
|
178
|
+
inputSchema: {
|
|
179
|
+
workspace: { type: "string", required: true, description: "Workspace GID" },
|
|
180
|
+
text: { type: "string", required: false, description: "Text to search in name/notes" },
|
|
181
|
+
completed: { type: "boolean", required: false, description: "Filter by completed status" },
|
|
182
|
+
},
|
|
183
|
+
async execute(input, ctx) {
|
|
184
|
+
const { workspace, text, completed } = input;
|
|
185
|
+
const qs = {};
|
|
186
|
+
if (text)
|
|
187
|
+
qs.text = text;
|
|
188
|
+
if (completed !== undefined)
|
|
189
|
+
qs.completed = completed;
|
|
190
|
+
return apiRequest(getToken(ctx), "GET", `/workspaces/${workspace}/tasks/search`, undefined, qs);
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
// ── Subtask ─────────────────────────────────────────
|
|
194
|
+
rl.registerAction("subtask.create", {
|
|
195
|
+
description: "Create a subtask on a task",
|
|
196
|
+
inputSchema: {
|
|
197
|
+
taskId: { type: "string", required: true, description: "Parent task GID" },
|
|
198
|
+
name: { type: "string", required: true, description: "Subtask name" },
|
|
199
|
+
assignee: { type: "string", required: false, description: "Assignee GID" },
|
|
200
|
+
notes: { type: "string", required: false, description: "Subtask notes" },
|
|
201
|
+
dueOn: { type: "string", required: false, description: "Due date (YYYY-MM-DD)" },
|
|
202
|
+
completed: { type: "boolean", required: false, description: "Mark as completed" },
|
|
203
|
+
},
|
|
204
|
+
async execute(input, ctx) {
|
|
205
|
+
const { taskId, dueOn, ...fields } = input;
|
|
206
|
+
const body = { ...fields };
|
|
207
|
+
if (dueOn)
|
|
208
|
+
body.due_on = dueOn;
|
|
209
|
+
return apiRequest(getToken(ctx), "POST", `/tasks/${taskId}/subtasks`, body);
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
rl.registerAction("subtask.list", {
|
|
213
|
+
description: "List subtasks of a task",
|
|
214
|
+
inputSchema: {
|
|
215
|
+
taskId: { type: "string", required: true, description: "Parent task GID" },
|
|
216
|
+
limit: { type: "number", required: false, description: "Max results to return" },
|
|
217
|
+
},
|
|
218
|
+
async execute(input, ctx) {
|
|
219
|
+
const { taskId, limit } = input;
|
|
220
|
+
const data = (await apiRequest(getToken(ctx), "GET", `/tasks/${taskId}/subtasks`));
|
|
221
|
+
if (limit)
|
|
222
|
+
return data.slice(0, limit);
|
|
223
|
+
return data;
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
// ── Task Comment ────────────────────────────────────
|
|
227
|
+
rl.registerAction("taskComment.add", {
|
|
228
|
+
description: "Add a comment to a task",
|
|
229
|
+
inputSchema: {
|
|
230
|
+
taskId: { type: "string", required: true, description: "Task GID" },
|
|
231
|
+
text: { type: "string", required: true, description: "Comment text" },
|
|
232
|
+
isHtml: { type: "boolean", required: false, description: "Whether text is HTML" },
|
|
233
|
+
isPinned: { type: "boolean", required: false, description: "Pin the comment" },
|
|
234
|
+
},
|
|
235
|
+
async execute(input, ctx) {
|
|
236
|
+
const { taskId, text, isHtml, isPinned } = input;
|
|
237
|
+
const body = isHtml ? { html_text: text } : { text };
|
|
238
|
+
if (isPinned)
|
|
239
|
+
body.is_pinned = true;
|
|
240
|
+
return apiRequest(getToken(ctx), "POST", `/tasks/${taskId}/stories`, body);
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
rl.registerAction("taskComment.remove", {
|
|
244
|
+
description: "Remove a comment (story) from a task",
|
|
245
|
+
inputSchema: {
|
|
246
|
+
commentId: { type: "string", required: true, description: "Comment/story GID" },
|
|
247
|
+
},
|
|
248
|
+
async execute(input, ctx) {
|
|
249
|
+
const { commentId } = input;
|
|
250
|
+
await apiRequest(getToken(ctx), "DELETE", `/stories/${commentId}`);
|
|
251
|
+
return { success: true };
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
// ── Task Tag ────────────────────────────────────────
|
|
255
|
+
rl.registerAction("taskTag.add", {
|
|
256
|
+
description: "Add a tag to a task",
|
|
257
|
+
inputSchema: {
|
|
258
|
+
taskId: { type: "string", required: true, description: "Task GID" },
|
|
259
|
+
tagId: { type: "string", required: true, description: "Tag GID" },
|
|
260
|
+
},
|
|
261
|
+
async execute(input, ctx) {
|
|
262
|
+
const { taskId, tagId } = input;
|
|
263
|
+
await apiRequest(getToken(ctx), "POST", `/tasks/${taskId}/addTag`, { tag: tagId });
|
|
264
|
+
return { success: true };
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
rl.registerAction("taskTag.remove", {
|
|
268
|
+
description: "Remove a tag from a task",
|
|
269
|
+
inputSchema: {
|
|
270
|
+
taskId: { type: "string", required: true, description: "Task GID" },
|
|
271
|
+
tagId: { type: "string", required: true, description: "Tag GID" },
|
|
272
|
+
},
|
|
273
|
+
async execute(input, ctx) {
|
|
274
|
+
const { taskId, tagId } = input;
|
|
275
|
+
await apiRequest(getToken(ctx), "POST", `/tasks/${taskId}/removeTag`, { tag: tagId });
|
|
276
|
+
return { success: true };
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
// ── Task Project ────────────────────────────────────
|
|
280
|
+
rl.registerAction("taskProject.add", {
|
|
281
|
+
description: "Add a task to a project",
|
|
282
|
+
inputSchema: {
|
|
283
|
+
taskId: { type: "string", required: true, description: "Task GID" },
|
|
284
|
+
projectId: { type: "string", required: true, description: "Project GID" },
|
|
285
|
+
section: { type: "string", required: false, description: "Section GID to insert into" },
|
|
286
|
+
},
|
|
287
|
+
async execute(input, ctx) {
|
|
288
|
+
const { taskId, projectId, section } = input;
|
|
289
|
+
const body = { project: projectId };
|
|
290
|
+
if (section)
|
|
291
|
+
body.section = section;
|
|
292
|
+
await apiRequest(getToken(ctx), "POST", `/tasks/${taskId}/addProject`, body);
|
|
293
|
+
return { success: true };
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
rl.registerAction("taskProject.remove", {
|
|
297
|
+
description: "Remove a task from a project",
|
|
298
|
+
inputSchema: {
|
|
299
|
+
taskId: { type: "string", required: true, description: "Task GID" },
|
|
300
|
+
projectId: { type: "string", required: true, description: "Project GID" },
|
|
301
|
+
},
|
|
302
|
+
async execute(input, ctx) {
|
|
303
|
+
const { taskId, projectId } = input;
|
|
304
|
+
await apiRequest(getToken(ctx), "POST", `/tasks/${taskId}/removeProject`, { project: projectId });
|
|
305
|
+
return { success: true };
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
// ── User ────────────────────────────────────────────
|
|
309
|
+
rl.registerAction("user.get", {
|
|
310
|
+
description: "Get a user by ID (or 'me' for current user)",
|
|
311
|
+
inputSchema: {
|
|
312
|
+
userId: { type: "string", required: true, description: "User GID or 'me'" },
|
|
313
|
+
},
|
|
314
|
+
async execute(input, ctx) {
|
|
315
|
+
const { userId } = input;
|
|
316
|
+
return apiRequest(getToken(ctx), "GET", `/users/${userId}`);
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
rl.registerAction("user.list", {
|
|
320
|
+
description: "List users in a workspace",
|
|
321
|
+
inputSchema: {
|
|
322
|
+
workspace: { type: "string", required: true, description: "Workspace GID" },
|
|
323
|
+
},
|
|
324
|
+
async execute(input, ctx) {
|
|
325
|
+
const { workspace } = input;
|
|
326
|
+
return apiRequest(getToken(ctx), "GET", `/workspaces/${workspace}/users`);
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
// ── Project ─────────────────────────────────────────
|
|
330
|
+
rl.registerAction("project.create", {
|
|
331
|
+
description: "Create a new project",
|
|
332
|
+
inputSchema: {
|
|
333
|
+
name: { type: "string", required: true, description: "Project name" },
|
|
334
|
+
workspace: { type: "string", required: true, description: "Workspace GID" },
|
|
335
|
+
team: { type: "string", required: true, description: "Team GID" },
|
|
336
|
+
notes: { type: "string", required: false, description: "Project description" },
|
|
337
|
+
color: { type: "string", required: false, description: "Project color" },
|
|
338
|
+
dueOn: { type: "string", required: false, description: "Due date (YYYY-MM-DD)" },
|
|
339
|
+
},
|
|
340
|
+
async execute(input, ctx) {
|
|
341
|
+
const { name, workspace, team, notes, color, dueOn } = input;
|
|
342
|
+
const body = { name, workspace };
|
|
343
|
+
if (notes)
|
|
344
|
+
body.notes = notes;
|
|
345
|
+
if (color)
|
|
346
|
+
body.color = color;
|
|
347
|
+
if (dueOn)
|
|
348
|
+
body.due_on = dueOn;
|
|
349
|
+
return apiRequest(getToken(ctx), "POST", `/teams/${team}/projects`, body);
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
rl.registerAction("project.get", {
|
|
353
|
+
description: "Get a project by ID",
|
|
354
|
+
inputSchema: {
|
|
355
|
+
projectId: { type: "string", required: true, description: "Project GID" },
|
|
356
|
+
},
|
|
357
|
+
async execute(input, ctx) {
|
|
358
|
+
const { projectId } = input;
|
|
359
|
+
return apiRequest(getToken(ctx), "GET", `/projects/${projectId}`);
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
rl.registerAction("project.list", {
|
|
363
|
+
description: "List projects in a workspace",
|
|
364
|
+
inputSchema: {
|
|
365
|
+
workspace: { type: "string", required: true, description: "Workspace GID" },
|
|
366
|
+
team: { type: "string", required: false, description: "Filter by team GID" },
|
|
367
|
+
archived: { type: "boolean", required: false, description: "Filter by archived status" },
|
|
368
|
+
limit: { type: "number", required: false, description: "Max results to return" },
|
|
369
|
+
},
|
|
370
|
+
async execute(input, ctx) {
|
|
371
|
+
const { workspace, team, archived, limit } = (input ?? {});
|
|
372
|
+
const qs = {};
|
|
373
|
+
if (team) {
|
|
374
|
+
qs.team = team;
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
qs.workspace = workspace;
|
|
378
|
+
}
|
|
379
|
+
if (archived !== undefined)
|
|
380
|
+
qs.archived = archived;
|
|
381
|
+
return paginateAll(getToken(ctx), "/projects", qs, limit);
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
rl.registerAction("project.update", {
|
|
385
|
+
description: "Update a project",
|
|
386
|
+
inputSchema: {
|
|
387
|
+
projectId: { type: "string", required: true, description: "Project GID" },
|
|
388
|
+
name: { type: "string", required: false, description: "Project name" },
|
|
389
|
+
notes: { type: "string", required: false, description: "Project description" },
|
|
390
|
+
color: { type: "string", required: false, description: "Project color" },
|
|
391
|
+
owner: { type: "string", required: false, description: "Owner GID" },
|
|
392
|
+
dueOn: { type: "string", required: false, description: "Due date (YYYY-MM-DD)" },
|
|
393
|
+
},
|
|
394
|
+
async execute(input, ctx) {
|
|
395
|
+
const { projectId, dueOn, ...fields } = input;
|
|
396
|
+
const body = { ...fields };
|
|
397
|
+
if (dueOn)
|
|
398
|
+
body.due_on = dueOn;
|
|
399
|
+
return apiRequest(getToken(ctx), "PUT", `/projects/${projectId}`, body);
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
rl.registerAction("project.delete", {
|
|
403
|
+
description: "Delete a project",
|
|
404
|
+
inputSchema: {
|
|
405
|
+
projectId: { type: "string", required: true, description: "Project GID" },
|
|
406
|
+
},
|
|
407
|
+
async execute(input, ctx) {
|
|
408
|
+
const { projectId } = input;
|
|
409
|
+
await apiRequest(getToken(ctx), "DELETE", `/projects/${projectId}`);
|
|
410
|
+
return { success: true };
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const BASE_URL = "https://api2.autopilothq.com/v1";
|
|
2
|
+
async function apiRequest(apiKey, method, endpoint, body) {
|
|
3
|
+
const opts = {
|
|
4
|
+
method,
|
|
5
|
+
headers: {
|
|
6
|
+
"Content-Type": "application/json",
|
|
7
|
+
autopilotapikey: apiKey,
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
if (body && Object.keys(body).length > 0 && method !== "GET" && method !== "DELETE") {
|
|
11
|
+
opts.body = JSON.stringify(body);
|
|
12
|
+
}
|
|
13
|
+
const res = await fetch(`${BASE_URL}${endpoint}`, opts);
|
|
14
|
+
if (!res.ok) {
|
|
15
|
+
const text = await res.text();
|
|
16
|
+
throw new Error(`Autopilot API error ${res.status}: ${text}`);
|
|
17
|
+
}
|
|
18
|
+
if (res.status === 204 || res.headers.get("content-length") === "0")
|
|
19
|
+
return { success: true };
|
|
20
|
+
return res.json();
|
|
21
|
+
}
|
|
22
|
+
async function paginateAll(apiKey, endpoint, dataKey, limit) {
|
|
23
|
+
const results = [];
|
|
24
|
+
let currentEndpoint = endpoint;
|
|
25
|
+
while (true) {
|
|
26
|
+
const data = (await apiRequest(apiKey, "GET", currentEndpoint));
|
|
27
|
+
const items = data[dataKey] ?? [];
|
|
28
|
+
results.push(...items);
|
|
29
|
+
if (limit && results.length >= limit)
|
|
30
|
+
return results.slice(0, limit);
|
|
31
|
+
const bookmark = data.bookmark;
|
|
32
|
+
if (!bookmark)
|
|
33
|
+
break;
|
|
34
|
+
currentEndpoint = `${endpoint}/${bookmark}`;
|
|
35
|
+
}
|
|
36
|
+
return results;
|
|
37
|
+
}
|
|
38
|
+
function getKey(ctx) {
|
|
39
|
+
return ctx.connection.config.apiKey;
|
|
40
|
+
}
|
|
41
|
+
export default function autopilot(rl) {
|
|
42
|
+
rl.setName("autopilot");
|
|
43
|
+
rl.setVersion("0.1.0");
|
|
44
|
+
rl.setConnectionSchema({
|
|
45
|
+
apiKey: {
|
|
46
|
+
type: "string",
|
|
47
|
+
required: true,
|
|
48
|
+
description: "Autopilot API key",
|
|
49
|
+
env: "AUTOPILOT_API_KEY",
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
// ── Contact ─────────────────────────────────────────
|
|
53
|
+
rl.registerAction("contact.upsert", {
|
|
54
|
+
description: "Create or update a contact",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
email: { type: "string", required: true, description: "Contact email" },
|
|
57
|
+
firstName: { type: "string", required: false, description: "First name" },
|
|
58
|
+
lastName: { type: "string", required: false, description: "Last name" },
|
|
59
|
+
company: { type: "string", required: false, description: "Company" },
|
|
60
|
+
phone: { type: "string", required: false, description: "Phone" },
|
|
61
|
+
listId: { type: "string", required: false, description: "Add to this list" },
|
|
62
|
+
newEmail: { type: "string", required: false, description: "Change email address" },
|
|
63
|
+
},
|
|
64
|
+
async execute(input, ctx) {
|
|
65
|
+
const { email, firstName, lastName, company, phone, listId, newEmail, ...rest } = input;
|
|
66
|
+
const contact = { Email: email, ...rest };
|
|
67
|
+
if (firstName)
|
|
68
|
+
contact.FirstName = firstName;
|
|
69
|
+
if (lastName)
|
|
70
|
+
contact.LastName = lastName;
|
|
71
|
+
if (company)
|
|
72
|
+
contact.Company = company;
|
|
73
|
+
if (phone)
|
|
74
|
+
contact.Phone = phone;
|
|
75
|
+
if (listId)
|
|
76
|
+
contact._autopilot_list = listId;
|
|
77
|
+
if (newEmail)
|
|
78
|
+
contact._NewEmail = newEmail;
|
|
79
|
+
return apiRequest(getKey(ctx), "POST", "/contact", { contact });
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
rl.registerAction("contact.get", {
|
|
83
|
+
description: "Get a contact by ID or email",
|
|
84
|
+
inputSchema: {
|
|
85
|
+
contactId: { type: "string", required: true, description: "Contact ID or email" },
|
|
86
|
+
},
|
|
87
|
+
async execute(input, ctx) {
|
|
88
|
+
const { contactId } = input;
|
|
89
|
+
return apiRequest(getKey(ctx), "GET", `/contact/${contactId}`);
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
rl.registerAction("contact.list", {
|
|
93
|
+
description: "List all contacts",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
limit: { type: "number", required: false, description: "Max results to return" },
|
|
96
|
+
},
|
|
97
|
+
async execute(input, ctx) {
|
|
98
|
+
const { limit } = (input ?? {});
|
|
99
|
+
return paginateAll(getKey(ctx), "/contacts", "contacts", limit);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
rl.registerAction("contact.delete", {
|
|
103
|
+
description: "Delete a contact",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
contactId: { type: "string", required: true, description: "Contact ID" },
|
|
106
|
+
},
|
|
107
|
+
async execute(input, ctx) {
|
|
108
|
+
const { contactId } = input;
|
|
109
|
+
await apiRequest(getKey(ctx), "DELETE", `/contact/${contactId}`);
|
|
110
|
+
return { success: true };
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
// ── Contact Journey ─────────────────────────────────
|
|
114
|
+
rl.registerAction("contactJourney.add", {
|
|
115
|
+
description: "Add a contact to a journey/trigger",
|
|
116
|
+
inputSchema: {
|
|
117
|
+
triggerId: { type: "string", required: true, description: "Trigger/journey ID" },
|
|
118
|
+
contactId: { type: "string", required: true, description: "Contact ID" },
|
|
119
|
+
},
|
|
120
|
+
async execute(input, ctx) {
|
|
121
|
+
const { triggerId, contactId } = input;
|
|
122
|
+
await apiRequest(getKey(ctx), "POST", `/trigger/${triggerId}/contact/${contactId}`);
|
|
123
|
+
return { success: true };
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
// ── Contact List ────────────────────────────────────
|
|
127
|
+
rl.registerAction("contactList.add", {
|
|
128
|
+
description: "Add a contact to a list",
|
|
129
|
+
inputSchema: {
|
|
130
|
+
listId: { type: "string", required: true, description: "List ID" },
|
|
131
|
+
contactId: { type: "string", required: true, description: "Contact ID" },
|
|
132
|
+
},
|
|
133
|
+
async execute(input, ctx) {
|
|
134
|
+
const { listId, contactId } = input;
|
|
135
|
+
await apiRequest(getKey(ctx), "POST", `/list/${listId}/contact/${contactId}`);
|
|
136
|
+
return { success: true };
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
rl.registerAction("contactList.remove", {
|
|
140
|
+
description: "Remove a contact from a list",
|
|
141
|
+
inputSchema: {
|
|
142
|
+
listId: { type: "string", required: true, description: "List ID" },
|
|
143
|
+
contactId: { type: "string", required: true, description: "Contact ID" },
|
|
144
|
+
},
|
|
145
|
+
async execute(input, ctx) {
|
|
146
|
+
const { listId, contactId } = input;
|
|
147
|
+
await apiRequest(getKey(ctx), "DELETE", `/list/${listId}/contact/${contactId}`);
|
|
148
|
+
return { success: true };
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
rl.registerAction("contactList.exists", {
|
|
152
|
+
description: "Check if a contact is in a list",
|
|
153
|
+
inputSchema: {
|
|
154
|
+
listId: { type: "string", required: true, description: "List ID" },
|
|
155
|
+
contactId: { type: "string", required: true, description: "Contact ID" },
|
|
156
|
+
},
|
|
157
|
+
async execute(input, ctx) {
|
|
158
|
+
const { listId, contactId } = input;
|
|
159
|
+
try {
|
|
160
|
+
await apiRequest(getKey(ctx), "GET", `/list/${listId}/contact/${contactId}`);
|
|
161
|
+
return { exists: true };
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return { exists: false };
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
rl.registerAction("contactList.list", {
|
|
169
|
+
description: "List all contacts in a list",
|
|
170
|
+
inputSchema: {
|
|
171
|
+
listId: { type: "string", required: true, description: "List ID" },
|
|
172
|
+
limit: { type: "number", required: false, description: "Max results to return" },
|
|
173
|
+
},
|
|
174
|
+
async execute(input, ctx) {
|
|
175
|
+
const { listId, limit } = input;
|
|
176
|
+
return paginateAll(getKey(ctx), `/list/${listId}/contacts`, "contacts", limit);
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
// ── List ────────────────────────────────────────────
|
|
180
|
+
rl.registerAction("list.create", {
|
|
181
|
+
description: "Create a new list",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
name: { type: "string", required: true, description: "List name" },
|
|
184
|
+
},
|
|
185
|
+
async execute(input, ctx) {
|
|
186
|
+
const { name } = input;
|
|
187
|
+
return apiRequest(getKey(ctx), "POST", "/list", { name });
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
rl.registerAction("list.list", {
|
|
191
|
+
description: "List all lists",
|
|
192
|
+
inputSchema: {
|
|
193
|
+
limit: { type: "number", required: false, description: "Max results to return" },
|
|
194
|
+
},
|
|
195
|
+
async execute(input, ctx) {
|
|
196
|
+
const { limit } = (input ?? {});
|
|
197
|
+
const data = (await apiRequest(getKey(ctx), "GET", "/lists"));
|
|
198
|
+
if (limit)
|
|
199
|
+
return data.lists.slice(0, limit);
|
|
200
|
+
return data.lists;
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|