runline 0.2.1 → 0.2.2
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/dist/plugins/actionNetwork/src/index.js +118 -25
- package/dist/plugins/activeCampaign/src/index.js +184 -38
- package/dist/plugins/adalo/src/index.js +40 -8
- package/dist/plugins/affinity/src/index.js +100 -20
- package/dist/plugins/agileCrm/src/index.js +115 -27
- package/dist/plugins/airtable/src/index.js +94 -19
- package/dist/plugins/airtop/src/index.js +266 -62
- package/dist/plugins/apiTemplateIo/src/index.js +25 -5
- package/dist/plugins/asana/src/index.js +195 -41
- package/dist/plugins/autopilot/src/index.js +39 -8
- package/dist/plugins/bambooHr/src/index.js +109 -22
- package/dist/plugins/bannerbear/src/index.js +36 -8
- package/dist/plugins/baserow/src/index.js +35 -7
- package/dist/plugins/beeminder/src/index.js +198 -40
- package/dist/plugins/bitly/src/index.js +46 -10
- package/dist/plugins/bitwarden/src/index.js +167 -36
- package/dist/plugins/box/src/index.js +172 -37
- package/dist/plugins/brandfetch/src/index.js +5 -1
- package/dist/plugins/brevo/src/index.js +136 -29
- package/dist/plugins/bubble/src/index.js +76 -17
- package/dist/plugins/chargebee/src/index.js +35 -7
- package/dist/plugins/circleci/src/index.js +50 -10
- package/dist/plugins/ciscoWebex/src/index.js +131 -27
- package/dist/plugins/clearbit/src/index.js +76 -17
- package/dist/plugins/clickup/src/index.js +500 -107
- package/dist/plugins/clockify/src/index.js +229 -47
- package/dist/plugins/cloudflare/src/index.js +28 -6
- package/dist/plugins/cockpit/src/index.js +54 -12
- package/dist/plugins/coda/src/index.js +81 -19
- package/dist/plugins/coingecko/src/index.js +157 -33
- package/dist/plugins/contentful/src/index.js +85 -17
- package/dist/plugins/convertkit/src/index.js +74 -18
- package/dist/plugins/copper/src/index.js +59 -11
- package/dist/plugins/cortex/src/index.js +55 -13
- package/dist/plugins/currents/src/index.js +310 -71
- package/dist/plugins/customerIo/src/index.js +112 -23
- package/dist/plugins/databricks/src/index.js +549 -115
- package/dist/plugins/deepl/src/index.js +26 -6
- package/dist/plugins/demio/src/index.js +56 -11
- package/dist/plugins/dhl/src/index.js +16 -3
- package/dist/plugins/discord/src/index.js +141 -31
- package/dist/plugins/discourse/src/index.js +136 -31
- package/dist/plugins/disqus/src/index.js +96 -20
- package/dist/plugins/docker/src/index.js +20 -4
- package/dist/plugins/drift/src/index.js +19 -5
- package/dist/plugins/dropbox/src/index.js +139 -30
- package/dist/plugins/dropcontact/src/index.js +21 -4
- package/dist/plugins/egoi/src/index.js +61 -15
- package/dist/plugins/elasticsearch/src/index.js +59 -13
- package/dist/plugins/emelia/src/index.js +95 -19
- package/dist/plugins/erpnext/src/index.js +74 -15
- package/dist/plugins/facebookGraph/src/index.js +52 -11
- package/dist/plugins/freshdesk/src/index.js +220 -48
- package/dist/plugins/freshservice/src/index.js +39 -9
- package/dist/plugins/freshworksCrm/src/index.js +58 -12
- package/dist/plugins/getresponse/src/index.js +87 -18
- package/dist/plugins/ghost/src/index.js +114 -26
- package/dist/plugins/github/src/index.js +483 -109
- package/dist/plugins/gitlab/src/index.js +193 -45
- package/dist/plugins/gong/src/index.js +68 -14
- package/dist/plugins/gotify/src/index.js +43 -9
- package/dist/plugins/gotowebinar/src/index.js +233 -47
- package/dist/plugins/grafana/src/index.js +92 -21
- package/dist/plugins/graphql/src/index.js +38 -8
- package/dist/plugins/grist/src/index.js +52 -10
- package/dist/plugins/hackernews/src/index.js +32 -6
- package/dist/plugins/halopsa/src/index.js +131 -26
- package/dist/plugins/harvest/src/index.js +182 -42
- package/dist/plugins/helpscout/src/index.js +153 -31
- package/dist/plugins/highlevel/src/index.js +291 -58
- package/dist/plugins/homeAssistant/src/index.js +124 -26
- package/dist/plugins/hubspot/src/index.js +163 -29
- package/dist/plugins/humanticAi/src/index.js +54 -5
- package/dist/plugins/hunter/src/index.js +21 -4
- package/dist/plugins/intercom/src/index.js +95 -20
- package/dist/plugins/iterable/src/index.js +96 -20
- package/dist/plugins/jenkins/src/index.js +75 -17
- package/dist/plugins/jira/src/index.js +193 -43
- package/dist/plugins/keap/src/index.js +222 -56
- package/dist/plugins/kobotoolbox/src/index.js +113 -25
- package/dist/plugins/lemlist/src/index.js +79 -18
- package/dist/plugins/linear/src/index.js +86 -19
- package/dist/plugins/lingvanex/src/index.js +38 -8
- package/dist/plugins/linkedin/src/index.js +37 -8
- package/dist/plugins/lonescale/src/index.js +41 -9
- package/dist/plugins/magento/src/index.js +98 -27
- package/dist/plugins/mailcheck/src/index.js +11 -2
- package/dist/plugins/mailchimp/src/index.js +193 -42
- package/dist/plugins/mailerlite/src/index.js +61 -12
- package/dist/plugins/mailgun/src/index.js +39 -7
- package/dist/plugins/mailjet/src/index.js +141 -30
- package/dist/plugins/mandrill/src/index.js +67 -14
- package/dist/plugins/marketstack/src/index.js +56 -10
- package/dist/plugins/matrix/src/index.js +97 -20
- package/dist/plugins/mattermost/src/index.js +124 -26
- package/dist/plugins/mautic/src/index.js +129 -26
- package/dist/plugins/medium/src/index.js +64 -13
- package/dist/plugins/messagebird/src/index.js +80 -15
- package/dist/plugins/metabase/src/index.js +57 -12
- package/dist/plugins/misp/src/index.js +135 -33
- package/dist/plugins/mocean/src/index.js +33 -7
- package/dist/plugins/monday/src/index.js +97 -23
- package/dist/plugins/monicaCrm/src/index.js +112 -23
- package/dist/plugins/msg91/src/index.js +11 -2
- package/dist/plugins/nasa/src/index.js +108 -21
- package/dist/plugins/netlify/src/index.js +28 -6
- package/dist/plugins/netscalerAdc/src/index.js +144 -29
- package/dist/plugins/nextcloud/src/index.js +103 -20
- package/dist/plugins/nocodb/src/index.js +57 -11
- package/dist/plugins/notion/src/index.js +148 -37
- package/dist/plugins/npm/src/index.js +59 -12
- package/dist/plugins/odoo/src/index.js +102 -20
- package/dist/plugins/okta/src/index.js +50 -10
- package/dist/plugins/oneSimpleApi/src/index.js +84 -17
- package/dist/plugins/onfleet/src/index.js +139 -32
- package/dist/plugins/openThesaurus/src/index.js +46 -9
- package/dist/plugins/openweathermap/src/index.js +31 -6
- package/dist/plugins/oura/src/index.js +36 -7
- package/dist/plugins/paddle/src/index.js +80 -17
- package/dist/plugins/pagerduty/src/index.js +53 -12
- package/dist/plugins/paypal/src/index.js +51 -11
- package/dist/plugins/peekalink/src/index.js +12 -3
- package/dist/plugins/phantombuster/src/index.js +39 -8
- package/dist/plugins/philipsHue/src/index.js +64 -13
- package/dist/plugins/pipedrive/src/index.js +167 -45
- package/dist/plugins/plivo/src/index.js +43 -8
- package/dist/plugins/postbin/src/index.js +9 -2
- package/dist/plugins/posthog/src/index.js +50 -13
- package/dist/plugins/profitwell/src/index.js +24 -5
- package/dist/plugins/pushbullet/src/index.js +45 -9
- package/dist/plugins/pushcut/src/index.js +31 -6
- package/dist/plugins/pushover/src/index.js +51 -10
- package/dist/plugins/quickbase/src/index.js +66 -13
- package/dist/plugins/quickbooks/src/index.js +86 -19
- package/dist/plugins/quickchart/src/index.js +35 -7
- package/dist/plugins/raindrop/src/index.js +54 -13
- package/dist/plugins/reddit/src/index.js +73 -18
- package/dist/plugins/rocketchat/src/index.js +47 -9
- package/dist/plugins/rundeck/src/index.js +26 -5
- package/dist/plugins/salesforce/src/index.js +161 -31
- package/dist/plugins/salesmate/src/index.js +75 -16
- package/dist/plugins/securityScorecard/src/index.js +73 -15
- package/dist/plugins/segment/src/index.js +21 -4
- package/dist/plugins/sendgrid/src/index.js +102 -24
- package/dist/plugins/sendy/src/index.js +54 -11
- package/dist/plugins/sentry/src/index.js +174 -34
- package/dist/plugins/servicenow/src/index.js +120 -27
- package/dist/plugins/shopify/src/index.js +53 -12
- package/dist/plugins/signl4/src/index.js +16 -3
- package/dist/plugins/slack/src/index.js +407 -105
- package/dist/plugins/sms77/src/index.js +26 -5
- package/dist/plugins/splunk/src/index.js +186 -45
- package/dist/plugins/spotify/src/index.js +265 -66
- package/dist/plugins/stackby/src/index.js +18 -6
- package/dist/plugins/storyblok/src/index.js +57 -11
- package/dist/plugins/strapi/src/index.js +63 -12
- package/dist/plugins/strava/src/index.js +58 -13
- package/dist/plugins/stripe/src/index.js +143 -30
- package/dist/plugins/supabase/src/index.js +49 -10
- package/dist/plugins/syncromsp/src/index.js +245 -67
- package/dist/plugins/tapfiliate/src/index.js +57 -14
- package/dist/plugins/telegram/src/index.js +202 -39
- package/dist/plugins/thehive/src/index.js +271 -66
- package/dist/plugins/thehiveProject/src/index.js +457 -89
- package/dist/plugins/todoist/src/index.js +326 -77
- package/dist/plugins/travisci/src/index.js +35 -8
- package/dist/plugins/trello/src/index.js +204 -46
- package/dist/plugins/twake/src/index.js +10 -2
- package/dist/plugins/twilio/src/index.js +56 -11
- package/dist/plugins/twist/src/index.js +241 -48
- package/dist/plugins/twitter/src/index.js +128 -30
- package/dist/plugins/unleashedSoftware/src/index.js +30 -6
- package/dist/plugins/uplead/src/index.js +9 -2
- package/dist/plugins/uproc/src/index.js +31 -6
- package/dist/plugins/uptimerobot/src/index.js +119 -25
- package/dist/plugins/urlscanio/src/index.js +25 -6
- package/dist/plugins/vero/src/index.js +68 -13
- package/dist/plugins/vonage/src/index.js +32 -6
- package/dist/plugins/wekan/src/index.js +297 -52
- package/dist/plugins/woocommerce/src/index.js +57 -12
- package/dist/plugins/wordpress/src/index.js +94 -18
- package/dist/plugins/xero/src/index.js +79 -13
- package/dist/plugins/yourls/src/index.js +33 -6
- package/dist/plugins/zammad/src/index.js +139 -36
- package/dist/plugins/zendesk/src/index.js +201 -48
- package/dist/plugins/zoho/src/index.js +88 -21
- package/dist/plugins/zoom/src/index.js +41 -7
- package/dist/plugins/zulip/src/index.js +101 -20
- package/package.json +1 -1
|
@@ -9,9 +9,17 @@ async function apiRequest(token, accountId, method, endpoint, body, qs) {
|
|
|
9
9
|
}
|
|
10
10
|
const opts = {
|
|
11
11
|
method,
|
|
12
|
-
headers: {
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: `Bearer ${token}`,
|
|
14
|
+
"Harvest-Account-Id": accountId,
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
"User-Agent": "Runline",
|
|
17
|
+
},
|
|
13
18
|
};
|
|
14
|
-
if (body &&
|
|
19
|
+
if (body &&
|
|
20
|
+
Object.keys(body).length > 0 &&
|
|
21
|
+
method !== "GET" &&
|
|
22
|
+
method !== "DELETE")
|
|
15
23
|
opts.body = JSON.stringify(body);
|
|
16
24
|
const res = await fetch(url.toString(), opts);
|
|
17
25
|
if (!res.ok)
|
|
@@ -21,47 +29,103 @@ async function apiRequest(token, accountId, method, endpoint, body, qs) {
|
|
|
21
29
|
return res.json();
|
|
22
30
|
}
|
|
23
31
|
function getConn(ctx) {
|
|
24
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
token: ctx.connection.config.token,
|
|
34
|
+
accountId: ctx.connection.config.accountId,
|
|
35
|
+
};
|
|
25
36
|
}
|
|
26
37
|
function hv(ctx, method, endpoint, body, qs) {
|
|
27
38
|
const { token, accountId } = getConn(ctx);
|
|
28
39
|
return apiRequest(token, accountId, method, endpoint, body, qs);
|
|
29
40
|
}
|
|
30
41
|
function unwrapList(data, key) {
|
|
31
|
-
if (data &&
|
|
42
|
+
if (data &&
|
|
43
|
+
typeof data === "object" &&
|
|
44
|
+
key in data)
|
|
32
45
|
return data[key];
|
|
33
46
|
return data;
|
|
34
47
|
}
|
|
35
48
|
function registerCrud(rl, resource, apiPath, listKey) {
|
|
36
49
|
rl.registerAction(`${resource}.create`, {
|
|
37
|
-
description: `Create a ${resource}`,
|
|
38
|
-
|
|
50
|
+
description: `Create a ${resource}`,
|
|
51
|
+
inputSchema: {
|
|
52
|
+
properties: {
|
|
53
|
+
type: "object",
|
|
54
|
+
required: true,
|
|
55
|
+
description: `${resource} data`,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
async execute(input, ctx) {
|
|
59
|
+
return hv(ctx, "POST", apiPath, input.properties);
|
|
60
|
+
},
|
|
39
61
|
});
|
|
40
62
|
rl.registerAction(`${resource}.get`, {
|
|
41
|
-
description: `Get a ${resource}`,
|
|
42
|
-
|
|
63
|
+
description: `Get a ${resource}`,
|
|
64
|
+
inputSchema: {
|
|
65
|
+
id: { type: "number", required: true, description: `${resource} ID` },
|
|
66
|
+
},
|
|
67
|
+
async execute(input, ctx) {
|
|
68
|
+
return hv(ctx, "GET", `${apiPath}/${input.id}`);
|
|
69
|
+
},
|
|
43
70
|
});
|
|
44
71
|
rl.registerAction(`${resource}.list`, {
|
|
45
|
-
description: `List ${resource}s`,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
72
|
+
description: `List ${resource}s`,
|
|
73
|
+
inputSchema: {
|
|
74
|
+
limit: { type: "number", required: false, description: "Max results" },
|
|
75
|
+
page: { type: "number", required: false, description: "Page" },
|
|
76
|
+
},
|
|
77
|
+
async execute(input, ctx) {
|
|
78
|
+
const { limit, page } = (input ?? {});
|
|
79
|
+
const qs = {};
|
|
80
|
+
if (limit)
|
|
81
|
+
qs.per_page = limit;
|
|
82
|
+
if (page)
|
|
83
|
+
qs.page = page;
|
|
84
|
+
return unwrapList(await hv(ctx, "GET", apiPath, undefined, qs), listKey);
|
|
85
|
+
},
|
|
49
86
|
});
|
|
50
87
|
rl.registerAction(`${resource}.update`, {
|
|
51
|
-
description: `Update a ${resource}`,
|
|
52
|
-
|
|
88
|
+
description: `Update a ${resource}`,
|
|
89
|
+
inputSchema: {
|
|
90
|
+
id: { type: "number", required: true, description: `${resource} ID` },
|
|
91
|
+
properties: {
|
|
92
|
+
type: "object",
|
|
93
|
+
required: true,
|
|
94
|
+
description: "Fields to update",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
async execute(input, ctx) {
|
|
98
|
+
const { id, properties } = input;
|
|
99
|
+
return hv(ctx, "PATCH", `${apiPath}/${id}`, properties);
|
|
100
|
+
},
|
|
53
101
|
});
|
|
54
102
|
rl.registerAction(`${resource}.delete`, {
|
|
55
|
-
description: `Delete a ${resource}`,
|
|
56
|
-
|
|
103
|
+
description: `Delete a ${resource}`,
|
|
104
|
+
inputSchema: {
|
|
105
|
+
id: { type: "number", required: true, description: `${resource} ID` },
|
|
106
|
+
},
|
|
107
|
+
async execute(input, ctx) {
|
|
108
|
+
await hv(ctx, "DELETE", `${apiPath}/${input.id}`);
|
|
109
|
+
return { success: true };
|
|
110
|
+
},
|
|
57
111
|
});
|
|
58
112
|
}
|
|
59
113
|
export default function harvest(rl) {
|
|
60
114
|
rl.setName("harvest");
|
|
61
115
|
rl.setVersion("0.1.0");
|
|
62
116
|
rl.setConnectionSchema({
|
|
63
|
-
token: {
|
|
64
|
-
|
|
117
|
+
token: {
|
|
118
|
+
type: "string",
|
|
119
|
+
required: true,
|
|
120
|
+
description: "Harvest personal access token",
|
|
121
|
+
env: "HARVEST_TOKEN",
|
|
122
|
+
},
|
|
123
|
+
accountId: {
|
|
124
|
+
type: "string",
|
|
125
|
+
required: true,
|
|
126
|
+
description: "Harvest account ID",
|
|
127
|
+
env: "HARVEST_ACCOUNT_ID",
|
|
128
|
+
},
|
|
65
129
|
});
|
|
66
130
|
// Standard CRUD resources
|
|
67
131
|
registerCrud(rl, "client", "clients", "clients");
|
|
@@ -75,7 +139,9 @@ export default function harvest(rl) {
|
|
|
75
139
|
registerCrud(rl, "user", "users", "users");
|
|
76
140
|
rl.registerAction("user.me", {
|
|
77
141
|
description: "Get the currently authenticated user",
|
|
78
|
-
async execute(_input, ctx) {
|
|
142
|
+
async execute(_input, ctx) {
|
|
143
|
+
return hv(ctx, "GET", "users/me");
|
|
144
|
+
},
|
|
79
145
|
});
|
|
80
146
|
// Time entry (special operations)
|
|
81
147
|
rl.registerAction("timeEntry.create", {
|
|
@@ -83,16 +149,40 @@ export default function harvest(rl) {
|
|
|
83
149
|
inputSchema: {
|
|
84
150
|
projectId: { type: "number", required: true, description: "Project ID" },
|
|
85
151
|
taskId: { type: "number", required: true, description: "Task ID" },
|
|
86
|
-
spentDate: {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
152
|
+
spentDate: {
|
|
153
|
+
type: "string",
|
|
154
|
+
required: true,
|
|
155
|
+
description: "Date (YYYY-MM-DD)",
|
|
156
|
+
},
|
|
157
|
+
hours: {
|
|
158
|
+
type: "number",
|
|
159
|
+
required: false,
|
|
160
|
+
description: "Hours (for duration-based)",
|
|
161
|
+
},
|
|
162
|
+
startedTime: {
|
|
163
|
+
type: "string",
|
|
164
|
+
required: false,
|
|
165
|
+
description: "Start time HH:MM (for start/end)",
|
|
166
|
+
},
|
|
167
|
+
endedTime: {
|
|
168
|
+
type: "string",
|
|
169
|
+
required: false,
|
|
170
|
+
description: "End time HH:MM",
|
|
171
|
+
},
|
|
90
172
|
notes: { type: "string", required: false, description: "Notes" },
|
|
91
|
-
userId: {
|
|
173
|
+
userId: {
|
|
174
|
+
type: "number",
|
|
175
|
+
required: false,
|
|
176
|
+
description: "User ID (admin only)",
|
|
177
|
+
},
|
|
92
178
|
},
|
|
93
179
|
async execute(input, ctx) {
|
|
94
|
-
const { projectId, taskId, spentDate, hours, startedTime, endedTime, notes, userId } = input;
|
|
95
|
-
const body = {
|
|
180
|
+
const { projectId, taskId, spentDate, hours, startedTime, endedTime, notes, userId, } = input;
|
|
181
|
+
const body = {
|
|
182
|
+
project_id: projectId,
|
|
183
|
+
task_id: taskId,
|
|
184
|
+
spent_date: spentDate,
|
|
185
|
+
};
|
|
96
186
|
if (hours !== undefined)
|
|
97
187
|
body.hours = hours;
|
|
98
188
|
if (startedTime)
|
|
@@ -107,21 +197,43 @@ export default function harvest(rl) {
|
|
|
107
197
|
},
|
|
108
198
|
});
|
|
109
199
|
rl.registerAction("timeEntry.get", {
|
|
110
|
-
description: "Get a time entry",
|
|
111
|
-
|
|
200
|
+
description: "Get a time entry",
|
|
201
|
+
inputSchema: {
|
|
202
|
+
id: { type: "number", required: true, description: "Time entry ID" },
|
|
203
|
+
},
|
|
204
|
+
async execute(input, ctx) {
|
|
205
|
+
return hv(ctx, "GET", `time_entries/${input.id}`);
|
|
206
|
+
},
|
|
112
207
|
});
|
|
113
208
|
rl.registerAction("timeEntry.list", {
|
|
114
209
|
description: "List time entries",
|
|
115
210
|
inputSchema: {
|
|
116
211
|
limit: { type: "number", required: false, description: "Max results" },
|
|
117
212
|
page: { type: "number", required: false, description: "Page" },
|
|
118
|
-
from: {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
213
|
+
from: {
|
|
214
|
+
type: "string",
|
|
215
|
+
required: false,
|
|
216
|
+
description: "From date (YYYY-MM-DD)",
|
|
217
|
+
},
|
|
218
|
+
to: {
|
|
219
|
+
type: "string",
|
|
220
|
+
required: false,
|
|
221
|
+
description: "To date (YYYY-MM-DD)",
|
|
222
|
+
},
|
|
223
|
+
userId: {
|
|
224
|
+
type: "number",
|
|
225
|
+
required: false,
|
|
226
|
+
description: "Filter by user",
|
|
227
|
+
},
|
|
228
|
+
projectId: {
|
|
229
|
+
type: "number",
|
|
230
|
+
required: false,
|
|
231
|
+
description: "Filter by project",
|
|
232
|
+
},
|
|
122
233
|
},
|
|
123
234
|
async execute(input, ctx) {
|
|
124
|
-
const { limit, page, from, to, userId, projectId } = (input ??
|
|
235
|
+
const { limit, page, from, to, userId, projectId } = (input ??
|
|
236
|
+
{});
|
|
125
237
|
const qs = {};
|
|
126
238
|
if (limit)
|
|
127
239
|
qs.per_page = limit;
|
|
@@ -140,24 +252,52 @@ export default function harvest(rl) {
|
|
|
140
252
|
});
|
|
141
253
|
rl.registerAction("timeEntry.update", {
|
|
142
254
|
description: "Update a time entry",
|
|
143
|
-
inputSchema: {
|
|
144
|
-
|
|
255
|
+
inputSchema: {
|
|
256
|
+
id: { type: "number", required: true, description: "Time entry ID" },
|
|
257
|
+
properties: {
|
|
258
|
+
type: "object",
|
|
259
|
+
required: true,
|
|
260
|
+
description: "Fields to update",
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
async execute(input, ctx) {
|
|
264
|
+
const { id, properties } = input;
|
|
265
|
+
return hv(ctx, "PATCH", `time_entries/${id}`, properties);
|
|
266
|
+
},
|
|
145
267
|
});
|
|
146
268
|
rl.registerAction("timeEntry.delete", {
|
|
147
|
-
description: "Delete a time entry",
|
|
148
|
-
|
|
269
|
+
description: "Delete a time entry",
|
|
270
|
+
inputSchema: {
|
|
271
|
+
id: { type: "number", required: true, description: "Time entry ID" },
|
|
272
|
+
},
|
|
273
|
+
async execute(input, ctx) {
|
|
274
|
+
await hv(ctx, "DELETE", `time_entries/${input.id}`);
|
|
275
|
+
return { success: true };
|
|
276
|
+
},
|
|
149
277
|
});
|
|
150
278
|
rl.registerAction("timeEntry.restart", {
|
|
151
|
-
description: "Restart a stopped time entry",
|
|
152
|
-
|
|
279
|
+
description: "Restart a stopped time entry",
|
|
280
|
+
inputSchema: {
|
|
281
|
+
id: { type: "number", required: true, description: "Time entry ID" },
|
|
282
|
+
},
|
|
283
|
+
async execute(input, ctx) {
|
|
284
|
+
return hv(ctx, "PATCH", `time_entries/${input.id}/restart`);
|
|
285
|
+
},
|
|
153
286
|
});
|
|
154
287
|
rl.registerAction("timeEntry.stop", {
|
|
155
|
-
description: "Stop a running time entry",
|
|
156
|
-
|
|
288
|
+
description: "Stop a running time entry",
|
|
289
|
+
inputSchema: {
|
|
290
|
+
id: { type: "number", required: true, description: "Time entry ID" },
|
|
291
|
+
},
|
|
292
|
+
async execute(input, ctx) {
|
|
293
|
+
return hv(ctx, "PATCH", `time_entries/${input.id}/stop`);
|
|
294
|
+
},
|
|
157
295
|
});
|
|
158
296
|
// Company (read-only)
|
|
159
297
|
rl.registerAction("company.get", {
|
|
160
298
|
description: "Get company info",
|
|
161
|
-
async execute(_input, ctx) {
|
|
299
|
+
async execute(_input, ctx) {
|
|
300
|
+
return hv(ctx, "GET", "company");
|
|
301
|
+
},
|
|
162
302
|
});
|
|
163
303
|
}
|
|
@@ -7,8 +7,17 @@ async function apiRequest(token, method, endpoint, body, qs) {
|
|
|
7
7
|
url.searchParams.set(k, String(v));
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
-
const opts = {
|
|
11
|
-
|
|
10
|
+
const opts = {
|
|
11
|
+
method,
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: `Bearer ${token}`,
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
if (body &&
|
|
18
|
+
Object.keys(body).length > 0 &&
|
|
19
|
+
method !== "GET" &&
|
|
20
|
+
method !== "DELETE")
|
|
12
21
|
opts.body = JSON.stringify(body);
|
|
13
22
|
const res = await fetch(url.toString(), opts);
|
|
14
23
|
if (!res.ok)
|
|
@@ -18,7 +27,9 @@ async function apiRequest(token, method, endpoint, body, qs) {
|
|
|
18
27
|
return res.json();
|
|
19
28
|
}
|
|
20
29
|
function unwrapEmbedded(data, key) {
|
|
21
|
-
if (data &&
|
|
30
|
+
if (data &&
|
|
31
|
+
typeof data === "object" &&
|
|
32
|
+
"_embedded" in data) {
|
|
22
33
|
return data._embedded[key];
|
|
23
34
|
}
|
|
24
35
|
return data;
|
|
@@ -27,7 +38,12 @@ export default function helpscout(rl) {
|
|
|
27
38
|
rl.setName("helpscout");
|
|
28
39
|
rl.setVersion("0.1.0");
|
|
29
40
|
rl.setConnectionSchema({
|
|
30
|
-
accessToken: {
|
|
41
|
+
accessToken: {
|
|
42
|
+
type: "string",
|
|
43
|
+
required: true,
|
|
44
|
+
description: "HelpScout OAuth2 access token",
|
|
45
|
+
env: "HELPSCOUT_ACCESS_TOKEN",
|
|
46
|
+
},
|
|
31
47
|
});
|
|
32
48
|
const tok = (ctx) => ctx.connection.config.accessToken;
|
|
33
49
|
// ── Conversation ────────────────────────────────────
|
|
@@ -35,16 +51,38 @@ export default function helpscout(rl) {
|
|
|
35
51
|
description: "Create a conversation",
|
|
36
52
|
inputSchema: {
|
|
37
53
|
subject: { type: "string", required: true, description: "Subject" },
|
|
38
|
-
customer: {
|
|
54
|
+
customer: {
|
|
55
|
+
type: "object",
|
|
56
|
+
required: true,
|
|
57
|
+
description: "{email} or {id}",
|
|
58
|
+
},
|
|
39
59
|
mailboxId: { type: "number", required: true, description: "Mailbox ID" },
|
|
40
|
-
type: {
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
type: {
|
|
61
|
+
type: "string",
|
|
62
|
+
required: true,
|
|
63
|
+
description: "email, phone, chat",
|
|
64
|
+
},
|
|
65
|
+
threads: {
|
|
66
|
+
type: "array",
|
|
67
|
+
required: true,
|
|
68
|
+
description: "Array of thread objects [{type, text}]",
|
|
69
|
+
},
|
|
70
|
+
status: {
|
|
71
|
+
type: "string",
|
|
72
|
+
required: false,
|
|
73
|
+
description: "active, pending, closed, spam",
|
|
74
|
+
},
|
|
43
75
|
tags: { type: "array", required: false, description: "Tag names" },
|
|
44
76
|
},
|
|
45
77
|
async execute(input, ctx) {
|
|
46
78
|
const { subject, customer, mailboxId, type, threads, status, tags } = input;
|
|
47
|
-
const body = {
|
|
79
|
+
const body = {
|
|
80
|
+
subject,
|
|
81
|
+
customer,
|
|
82
|
+
mailboxId,
|
|
83
|
+
type,
|
|
84
|
+
threads,
|
|
85
|
+
};
|
|
48
86
|
if (status)
|
|
49
87
|
body.status = status;
|
|
50
88
|
if (tags)
|
|
@@ -54,14 +92,30 @@ export default function helpscout(rl) {
|
|
|
54
92
|
});
|
|
55
93
|
rl.registerAction("conversation.get", {
|
|
56
94
|
description: "Get a conversation",
|
|
57
|
-
inputSchema: {
|
|
58
|
-
|
|
95
|
+
inputSchema: {
|
|
96
|
+
conversationId: {
|
|
97
|
+
type: "number",
|
|
98
|
+
required: true,
|
|
99
|
+
description: "Conversation ID",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
async execute(input, ctx) {
|
|
103
|
+
return apiRequest(tok(ctx), "GET", `/conversations/${input.conversationId}`);
|
|
104
|
+
},
|
|
59
105
|
});
|
|
60
106
|
rl.registerAction("conversation.list", {
|
|
61
107
|
description: "List conversations",
|
|
62
108
|
inputSchema: {
|
|
63
|
-
mailboxId: {
|
|
64
|
-
|
|
109
|
+
mailboxId: {
|
|
110
|
+
type: "number",
|
|
111
|
+
required: false,
|
|
112
|
+
description: "Filter by mailbox",
|
|
113
|
+
},
|
|
114
|
+
status: {
|
|
115
|
+
type: "string",
|
|
116
|
+
required: false,
|
|
117
|
+
description: "active, pending, closed, spam, all",
|
|
118
|
+
},
|
|
65
119
|
limit: { type: "number", required: false, description: "Max results" },
|
|
66
120
|
page: { type: "number", required: false, description: "Page" },
|
|
67
121
|
},
|
|
@@ -81,8 +135,17 @@ export default function helpscout(rl) {
|
|
|
81
135
|
});
|
|
82
136
|
rl.registerAction("conversation.delete", {
|
|
83
137
|
description: "Delete a conversation",
|
|
84
|
-
inputSchema: {
|
|
85
|
-
|
|
138
|
+
inputSchema: {
|
|
139
|
+
conversationId: {
|
|
140
|
+
type: "number",
|
|
141
|
+
required: true,
|
|
142
|
+
description: "Conversation ID",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
async execute(input, ctx) {
|
|
146
|
+
await apiRequest(tok(ctx), "DELETE", `/conversations/${input.conversationId}`);
|
|
147
|
+
return { success: true };
|
|
148
|
+
},
|
|
86
149
|
});
|
|
87
150
|
// ── Customer ────────────────────────────────────────
|
|
88
151
|
rl.registerAction("customer.create", {
|
|
@@ -90,7 +153,11 @@ export default function helpscout(rl) {
|
|
|
90
153
|
inputSchema: {
|
|
91
154
|
firstName: { type: "string", required: true, description: "First name" },
|
|
92
155
|
lastName: { type: "string", required: false, description: "Last name" },
|
|
93
|
-
emails: {
|
|
156
|
+
emails: {
|
|
157
|
+
type: "array",
|
|
158
|
+
required: false,
|
|
159
|
+
description: "Array of {type, value} email objects",
|
|
160
|
+
},
|
|
94
161
|
phones: { type: "array", required: false, description: "Phone objects" },
|
|
95
162
|
},
|
|
96
163
|
async execute(input, ctx) {
|
|
@@ -107,12 +174,23 @@ export default function helpscout(rl) {
|
|
|
107
174
|
});
|
|
108
175
|
rl.registerAction("customer.get", {
|
|
109
176
|
description: "Get a customer",
|
|
110
|
-
inputSchema: {
|
|
111
|
-
|
|
177
|
+
inputSchema: {
|
|
178
|
+
customerId: {
|
|
179
|
+
type: "number",
|
|
180
|
+
required: true,
|
|
181
|
+
description: "Customer ID",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
async execute(input, ctx) {
|
|
185
|
+
return apiRequest(tok(ctx), "GET", `/customers/${input.customerId}`);
|
|
186
|
+
},
|
|
112
187
|
});
|
|
113
188
|
rl.registerAction("customer.list", {
|
|
114
189
|
description: "List customers",
|
|
115
|
-
inputSchema: {
|
|
190
|
+
inputSchema: {
|
|
191
|
+
limit: { type: "number", required: false, description: "Max results" },
|
|
192
|
+
page: { type: "number", required: false, description: "Page" },
|
|
193
|
+
},
|
|
116
194
|
async execute(input, ctx) {
|
|
117
195
|
const { limit, page } = (input ?? {});
|
|
118
196
|
const qs = {};
|
|
@@ -126,8 +204,16 @@ export default function helpscout(rl) {
|
|
|
126
204
|
rl.registerAction("customer.update", {
|
|
127
205
|
description: "Update a customer",
|
|
128
206
|
inputSchema: {
|
|
129
|
-
customerId: {
|
|
130
|
-
|
|
207
|
+
customerId: {
|
|
208
|
+
type: "number",
|
|
209
|
+
required: true,
|
|
210
|
+
description: "Customer ID",
|
|
211
|
+
},
|
|
212
|
+
properties: {
|
|
213
|
+
type: "object",
|
|
214
|
+
required: true,
|
|
215
|
+
description: "Fields to update",
|
|
216
|
+
},
|
|
131
217
|
},
|
|
132
218
|
async execute(input, ctx) {
|
|
133
219
|
const { customerId, properties } = input;
|
|
@@ -136,27 +222,57 @@ export default function helpscout(rl) {
|
|
|
136
222
|
});
|
|
137
223
|
rl.registerAction("customer.getProperties", {
|
|
138
224
|
description: "Get custom properties for a customer",
|
|
139
|
-
inputSchema: {
|
|
140
|
-
|
|
225
|
+
inputSchema: {
|
|
226
|
+
customerId: {
|
|
227
|
+
type: "number",
|
|
228
|
+
required: true,
|
|
229
|
+
description: "Customer ID",
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
async execute(input, ctx) {
|
|
233
|
+
return apiRequest(tok(ctx), "GET", `/customers/${input.customerId}/properties`);
|
|
234
|
+
},
|
|
141
235
|
});
|
|
142
236
|
// ── Mailbox ─────────────────────────────────────────
|
|
143
237
|
rl.registerAction("mailbox.get", {
|
|
144
238
|
description: "Get a mailbox",
|
|
145
|
-
inputSchema: {
|
|
146
|
-
|
|
239
|
+
inputSchema: {
|
|
240
|
+
mailboxId: { type: "number", required: true, description: "Mailbox ID" },
|
|
241
|
+
},
|
|
242
|
+
async execute(input, ctx) {
|
|
243
|
+
return apiRequest(tok(ctx), "GET", `/mailboxes/${input.mailboxId}`);
|
|
244
|
+
},
|
|
147
245
|
});
|
|
148
246
|
rl.registerAction("mailbox.list", {
|
|
149
247
|
description: "List mailboxes",
|
|
150
|
-
async execute(_input, ctx) {
|
|
248
|
+
async execute(_input, ctx) {
|
|
249
|
+
return unwrapEmbedded(await apiRequest(tok(ctx), "GET", "/mailboxes"), "mailboxes");
|
|
250
|
+
},
|
|
151
251
|
});
|
|
152
252
|
// ── Thread ──────────────────────────────────────────
|
|
153
253
|
rl.registerAction("thread.create", {
|
|
154
254
|
description: "Create a thread (reply/note) on a conversation",
|
|
155
255
|
inputSchema: {
|
|
156
|
-
conversationId: {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
256
|
+
conversationId: {
|
|
257
|
+
type: "number",
|
|
258
|
+
required: true,
|
|
259
|
+
description: "Conversation ID",
|
|
260
|
+
},
|
|
261
|
+
type: {
|
|
262
|
+
type: "string",
|
|
263
|
+
required: true,
|
|
264
|
+
description: "reply, note, phone, chat",
|
|
265
|
+
},
|
|
266
|
+
text: {
|
|
267
|
+
type: "string",
|
|
268
|
+
required: true,
|
|
269
|
+
description: "Thread body (HTML)",
|
|
270
|
+
},
|
|
271
|
+
customer: {
|
|
272
|
+
type: "object",
|
|
273
|
+
required: false,
|
|
274
|
+
description: "Customer {email} or {id}",
|
|
275
|
+
},
|
|
160
276
|
},
|
|
161
277
|
async execute(input, ctx) {
|
|
162
278
|
const { conversationId, type, text, customer } = input;
|
|
@@ -168,7 +284,13 @@ export default function helpscout(rl) {
|
|
|
168
284
|
});
|
|
169
285
|
rl.registerAction("thread.list", {
|
|
170
286
|
description: "List threads in a conversation",
|
|
171
|
-
inputSchema: {
|
|
287
|
+
inputSchema: {
|
|
288
|
+
conversationId: {
|
|
289
|
+
type: "number",
|
|
290
|
+
required: true,
|
|
291
|
+
description: "Conversation ID",
|
|
292
|
+
},
|
|
293
|
+
},
|
|
172
294
|
async execute(input, ctx) {
|
|
173
295
|
return unwrapEmbedded(await apiRequest(tok(ctx), "GET", `/conversations/${input.conversationId}/threads`), "threads");
|
|
174
296
|
},
|