runline 0.8.0 → 0.9.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/dist/commands/auth.js +4 -2
- package/dist/config/loader.d.ts +2 -4
- package/dist/config/loader.js +2 -1
- package/dist/core/engine.js +18 -13
- package/dist/index.d.ts +2 -2
- package/dist/plugin/api.d.ts +2 -9
- package/dist/plugin/api.js +1 -4
- package/dist/plugin/loader.js +41 -25
- package/dist/plugin/schema.d.ts +19 -0
- package/dist/plugin/schema.js +168 -0
- package/dist/plugin/types.d.ts +22 -8
- package/dist/plugins/gmail/src/index.js +14 -2
- package/dist/plugins/linear/src/attachments.js +87 -0
- package/dist/plugins/linear/src/comments.js +64 -0
- package/dist/plugins/linear/src/cycles.js +42 -0
- package/dist/plugins/linear/src/index.js +35 -1153
- package/dist/plugins/linear/src/initiatives.js +84 -0
- package/dist/plugins/linear/src/issues.js +267 -0
- package/dist/plugins/linear/src/labels.js +74 -0
- package/dist/plugins/linear/src/organization.js +13 -0
- package/dist/plugins/linear/src/projects.js +200 -0
- package/dist/plugins/linear/src/shared.js +234 -0
- package/dist/plugins/linear/src/states.js +41 -0
- package/dist/plugins/linear/src/teams.js +77 -0
- package/dist/plugins/linear/src/users.js +37 -0
- package/dist/plugins/linear/src/views.js +105 -0
- package/dist/plugins/linear/src/webhooks.js +61 -0
- package/dist/plugins/vercel/src/account.js +11 -0
- package/dist/plugins/vercel/src/deployments.js +79 -0
- package/dist/plugins/vercel/src/env.js +101 -0
- package/dist/plugins/vercel/src/index.js +27 -0
- package/dist/plugins/vercel/src/projects.js +29 -0
- package/dist/plugins/vercel/src/shared.js +73 -0
- package/package.json +3 -2
|
@@ -1,1159 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
throw new Error(`Linear GraphQL error: ${JSON.stringify(data.errors)}`);
|
|
16
|
-
return data.data;
|
|
17
|
-
}
|
|
18
|
-
// ---------- selection sets (kept compact, repeated by name) ----------
|
|
19
|
-
const ISSUE_FIELDS = `id identifier title description url priority estimate dueDate
|
|
20
|
-
state { id name type } assignee { id name email } creator { id name }
|
|
21
|
-
team { id key name } project { id name } cycle { id number name }
|
|
22
|
-
projectMilestone { id name } parent { id identifier }
|
|
23
|
-
labels { nodes { id name color } }
|
|
24
|
-
createdAt updatedAt completedAt canceledAt archivedAt`;
|
|
25
|
-
const ISSUE_LITE = `id identifier title url priority state { id name type } assignee { id name } team { key } updatedAt`;
|
|
26
|
-
const COMMENT_FIELDS = `id body url issue { id identifier } user { id name } parent { id } createdAt updatedAt editedAt resolvedAt`;
|
|
27
|
-
const STATE_FIELDS = `id name type color position description team { id key }`;
|
|
28
|
-
const LABEL_FIELDS = `id name color description isGroup parent { id name } team { id key } createdAt`;
|
|
29
|
-
const PROJECT_FIELDS = `id name description url icon color priority progress health
|
|
30
|
-
state status { id name type } lead { id name } startDate targetDate
|
|
31
|
-
teams { nodes { id key } } createdAt updatedAt completedAt canceledAt`;
|
|
32
|
-
const MILESTONE_FIELDS = `id name description targetDate sortOrder project { id name } createdAt updatedAt`;
|
|
33
|
-
const PROJECT_UPDATE_FIELDS = `id body health url user { id name } project { id name } createdAt`;
|
|
34
|
-
const FEED_ITEM_FIELDS = `id createdAt updatedAt archivedAt team { id key name } user { id name }
|
|
35
|
-
projectUpdate { ${PROJECT_UPDATE_FIELDS} }
|
|
36
|
-
initiativeUpdate { id body health url user { id name } initiative { id name } createdAt }
|
|
37
|
-
post { id title body slugId type creator { id name } createdAt updatedAt }`;
|
|
38
|
-
const CUSTOM_VIEW_FIELDS = `id name description icon color shared slugId modelName
|
|
39
|
-
filterData projectFilterData initiativeFilterData feedItemFilterData
|
|
40
|
-
team { id key name } owner { id name } creator { id name }
|
|
41
|
-
createdAt updatedAt archivedAt`;
|
|
42
|
-
const CYCLE_FIELDS = `id number name description startsAt endsAt completedAt progress team { id key } createdAt`;
|
|
43
|
-
const INITIATIVE_FIELDS = `id name description url icon color status targetDate owner { id name }
|
|
44
|
-
projects { nodes { id name } } createdAt updatedAt completedAt`;
|
|
45
|
-
const TEAM_FIELDS = `id key name description icon color private timezone
|
|
46
|
-
cyclesEnabled cycleDuration issueEstimationType triageEnabled
|
|
47
|
-
parent { id key } createdAt`;
|
|
48
|
-
const USER_FIELDS = `id name displayName email avatarUrl active admin guest
|
|
49
|
-
isMe statusEmoji statusLabel createdAt`;
|
|
50
|
-
const ATTACHMENT_FIELDS = `id title subtitle url sourceType groupBySource metadata
|
|
51
|
-
issue { id identifier } creator { id name } createdAt updatedAt`;
|
|
52
|
-
const ORG_FIELDS = `id name urlKey logoUrl userCount createdIssueCount
|
|
53
|
-
periodUploadVolume samlEnabled scimEnabled createdAt`;
|
|
54
|
-
const WEBHOOK_FIELDS = `id label url enabled resourceTypes secret
|
|
55
|
-
team { id key } allPublicTeams createdAt`;
|
|
56
|
-
function buildConnArgs(opts, filterTypeName) {
|
|
57
|
-
const declParts = [];
|
|
58
|
-
const callParts = [`first: $first`];
|
|
59
|
-
const vars = { first: opts.limit ?? 50 };
|
|
60
|
-
declParts.push(`$first: Int`);
|
|
61
|
-
if (filterTypeName && opts.filter !== undefined) {
|
|
62
|
-
declParts.push(`$filter: ${filterTypeName}`);
|
|
63
|
-
callParts.push(`filter: $filter`);
|
|
64
|
-
vars.filter = opts.filter;
|
|
65
|
-
}
|
|
66
|
-
if (opts.includeArchived !== undefined) {
|
|
67
|
-
declParts.push(`$includeArchived: Boolean`);
|
|
68
|
-
callParts.push(`includeArchived: $includeArchived`);
|
|
69
|
-
vars.includeArchived = opts.includeArchived;
|
|
70
|
-
}
|
|
71
|
-
if (opts.orderBy !== undefined) {
|
|
72
|
-
declParts.push(`$orderBy: PaginationOrderBy`);
|
|
73
|
-
callParts.push(`orderBy: $orderBy`);
|
|
74
|
-
vars.orderBy = opts.orderBy;
|
|
75
|
-
}
|
|
76
|
-
if (opts.after !== undefined) {
|
|
77
|
-
declParts.push(`$after: String`);
|
|
78
|
-
callParts.push(`after: $after`);
|
|
79
|
-
vars.after = opts.after;
|
|
80
|
-
}
|
|
81
|
-
if (opts.before !== undefined) {
|
|
82
|
-
declParts.push(`$before: String`);
|
|
83
|
-
callParts.push(`before: $before`);
|
|
84
|
-
vars.before = opts.before;
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
argsDecl: `(${declParts.join(", ")})`,
|
|
88
|
-
argsCall: `(${callParts.join(", ")})`,
|
|
89
|
-
vars,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
// Common list-input schema reused across resources
|
|
93
|
-
const LIST_INPUT_SCHEMA = {
|
|
94
|
-
limit: { type: "number", required: false, description: "Max results (default 50, max 250)" },
|
|
95
|
-
filter: { type: "object", required: false, description: "Linear filter object (see schema for the resource)" },
|
|
96
|
-
includeArchived: { type: "boolean", required: false, description: "Include archived items" },
|
|
97
|
-
orderBy: { type: "string", required: false, description: "createdAt | updatedAt" },
|
|
98
|
-
after: { type: "string", required: false, description: "Cursor for forward pagination" },
|
|
99
|
-
before: { type: "string", required: false, description: "Cursor for backward pagination" },
|
|
100
|
-
};
|
|
101
|
-
// ---------- plugin ----------
|
|
1
|
+
import * as t from "typebox";
|
|
2
|
+
import { registerAttachmentActions } from "./attachments.js";
|
|
3
|
+
import { registerCommentActions } from "./comments.js";
|
|
4
|
+
import { registerCycleActions } from "./cycles.js";
|
|
5
|
+
import { registerInitiativeActions } from "./initiatives.js";
|
|
6
|
+
import { registerIssueActions } from "./issues.js";
|
|
7
|
+
import { registerLabelActions } from "./labels.js";
|
|
8
|
+
import { registerOrganizationActions } from "./organization.js";
|
|
9
|
+
import { registerProjectActions } from "./projects.js";
|
|
10
|
+
import { registerStateActions } from "./states.js";
|
|
11
|
+
import { registerTeamActions } from "./teams.js";
|
|
12
|
+
import { registerUserActions } from "./users.js";
|
|
13
|
+
import { registerViewActions } from "./views.js";
|
|
14
|
+
import { registerWebhookActions } from "./webhooks.js";
|
|
102
15
|
export default function linear(rl) {
|
|
103
16
|
rl.setName("linear");
|
|
104
17
|
rl.setVersion("0.4.0");
|
|
105
|
-
rl.setConnectionSchema({
|
|
106
|
-
apiKey: {
|
|
107
|
-
type: "string",
|
|
108
|
-
required: true,
|
|
18
|
+
rl.setConnectionSchema(t.Object({
|
|
19
|
+
apiKey: t.String({
|
|
109
20
|
description: "Linear API key (https://linear.app/settings/account/security)",
|
|
110
21
|
env: "LINEAR_API_KEY",
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
description,
|
|
131
|
-
inputSchema: { id: { type: "string", required: true, description: "Identifier or slug" } },
|
|
132
|
-
async execute(input, ctx) {
|
|
133
|
-
const data = await gql(key(ctx), `query($id: String!) { ${rootField}(id: $id) { ${selection} } }`, { id: input.id });
|
|
134
|
-
return data[rootField];
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
function customViewConnectionAction(name, description, connectionField, filterTypeName, selection, includeSubTeamsDescription) {
|
|
139
|
-
rl.registerAction(name, {
|
|
140
|
-
description,
|
|
141
|
-
inputSchema: {
|
|
142
|
-
viewId: { type: "string", required: true, description: "The custom view ID or slug" },
|
|
143
|
-
...LIST_INPUT_SCHEMA,
|
|
144
|
-
...(includeSubTeamsDescription
|
|
145
|
-
? { includeSubTeams: { type: "boolean", required: false, description: includeSubTeamsDescription } }
|
|
146
|
-
: {}),
|
|
147
|
-
},
|
|
148
|
-
async execute(input, ctx) {
|
|
149
|
-
const opts = (input ?? {});
|
|
150
|
-
const { argsDecl, argsCall, vars } = buildConnArgs(opts, filterTypeName);
|
|
151
|
-
const declParts = ["$id: String!", argsDecl.slice(1, -1)];
|
|
152
|
-
const callParts = [argsCall.slice(1, -1)];
|
|
153
|
-
const includeSubTeamsSet = includeSubTeamsDescription !== undefined && opts.includeSubTeams !== undefined;
|
|
154
|
-
if (includeSubTeamsSet) {
|
|
155
|
-
declParts.push("$includeSubTeams: Boolean");
|
|
156
|
-
callParts.push("includeSubTeams: $includeSubTeams");
|
|
157
|
-
vars.includeSubTeams = opts.includeSubTeams;
|
|
158
|
-
}
|
|
159
|
-
const data = await gql(key(ctx), `query(${declParts.join(", ")}) {
|
|
160
|
-
customView(id: $id) { ${connectionField}(${callParts.join(", ")}) { nodes { ${selection} } pageInfo { hasNextPage endCursor } } }
|
|
161
|
-
}`, { id: opts.viewId, ...vars });
|
|
162
|
-
const conn = (data.customView?.[connectionField] ?? {});
|
|
163
|
-
return { nodes: conn.nodes, pageInfo: conn.pageInfo };
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
// =========================================================
|
|
168
|
-
// Issues
|
|
169
|
-
// =========================================================
|
|
170
|
-
rl.registerAction("issue.create", {
|
|
171
|
-
description: "Create an issue. teamId is required; title is required unless a template is applied.",
|
|
172
|
-
inputSchema: {
|
|
173
|
-
teamId: { type: "string", required: true, description: "The identifier of the team associated with the issue" },
|
|
174
|
-
title: { type: "string", required: true, description: "The title of the issue" },
|
|
175
|
-
description: { type: "string", required: false, description: "The issue description in markdown format" },
|
|
176
|
-
assigneeId: { type: "string", required: false, description: "The identifier of the user to assign the issue to" },
|
|
177
|
-
priority: { type: "number", required: false, description: "Priority. 0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
|
|
178
|
-
stateId: { type: "string", required: false, description: "The team workflow state of the issue" },
|
|
179
|
-
labelIds: { type: "array", required: false, description: "The identifiers of the issue labels associated with this ticket" },
|
|
180
|
-
parentId: { type: "string", required: false, description: "The identifier of the parent issue. UUID or issue identifier (e.g., 'LIN-123')" },
|
|
181
|
-
projectId: { type: "string", required: false, description: "The project associated with the issue" },
|
|
182
|
-
projectMilestoneId: { type: "string", required: false, description: "The project milestone associated with the issue" },
|
|
183
|
-
cycleId: { type: "string", required: false, description: "The cycle associated with the issue" },
|
|
184
|
-
estimate: { type: "number", required: false, description: "The estimated complexity of the issue (Int)" },
|
|
185
|
-
dueDate: { type: "string", required: false, description: "The date at which the issue is due (TimelessDate, YYYY-MM-DD)" },
|
|
186
|
-
subscriberIds: { type: "array", required: false, description: "The identifiers of the users subscribing to this ticket" },
|
|
187
|
-
templateId: { type: "string", required: false, description: "The identifier of a template the issue should be created from" },
|
|
188
|
-
useDefaultTemplate: { type: "boolean", required: false, description: "Apply the team's default template based on the user's membership" },
|
|
189
|
-
sortOrder: { type: "number", required: false, description: "The position of the issue related to other issues (Float)" },
|
|
190
|
-
subIssueSortOrder: { type: "number", required: false, description: "The position of the issue in its parent's sub-issue list (Float)" },
|
|
191
|
-
releaseIds: { type: "array", required: false, description: "The identifiers of the releases to associate with this issue" },
|
|
192
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
193
|
-
},
|
|
194
|
-
async execute(input, ctx) {
|
|
195
|
-
const fields = input;
|
|
196
|
-
const data = await gql(key(ctx), `mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { ${ISSUE_FIELDS} } } }`, { input: fields });
|
|
197
|
-
return data.issueCreate?.issue;
|
|
198
|
-
},
|
|
199
|
-
});
|
|
200
|
-
rl.registerAction("issue.get", {
|
|
201
|
-
description: "Get an issue by ID or identifier (e.g. 'THE-154')",
|
|
202
|
-
inputSchema: { issueId: { type: "string", required: true } },
|
|
203
|
-
async execute(input, ctx) {
|
|
204
|
-
const data = await gql(key(ctx), `query($id: String!) { issue(id: $id) { ${ISSUE_FIELDS} } }`, { id: input.issueId });
|
|
205
|
-
return data.issue;
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
rl.registerAction("issue.list", {
|
|
209
|
-
description: "List issues. Pass `filter` for state/label/project/etc. Default hides archived.",
|
|
210
|
-
inputSchema: {
|
|
211
|
-
...LIST_INPUT_SCHEMA,
|
|
212
|
-
teamId: { type: "string", required: false, description: "Convenience: filter by team" },
|
|
213
|
-
assigneeId: { type: "string", required: false, description: "Convenience: filter by assignee" },
|
|
214
|
-
},
|
|
215
|
-
async execute(input, ctx) {
|
|
216
|
-
const opts = (input ?? {});
|
|
217
|
-
// Merge convenience filters into `filter`
|
|
218
|
-
const merged = { ...(opts.filter ?? {}) };
|
|
219
|
-
if (opts.teamId)
|
|
220
|
-
merged.team = { id: { eq: opts.teamId } };
|
|
221
|
-
if (opts.assigneeId)
|
|
222
|
-
merged.assignee = { id: { eq: opts.assigneeId } };
|
|
223
|
-
const filter = Object.keys(merged).length > 0 ? merged : undefined;
|
|
224
|
-
const { argsDecl, argsCall, vars } = buildConnArgs({ ...opts, filter }, "IssueFilter");
|
|
225
|
-
const data = await gql(key(ctx), `query${argsDecl} { issues${argsCall} { nodes { ${ISSUE_LITE} } pageInfo { hasNextPage endCursor } } }`, vars);
|
|
226
|
-
const conn = data.issues;
|
|
227
|
-
return { nodes: conn.nodes, pageInfo: conn.pageInfo };
|
|
228
|
-
},
|
|
229
|
-
});
|
|
230
|
-
rl.registerAction("issue.update", {
|
|
231
|
-
description: "Update an issue. All fields optional; only provided fields are updated.",
|
|
232
|
-
inputSchema: {
|
|
233
|
-
issueId: { type: "string", required: true, description: "The identifier of the issue to update" },
|
|
234
|
-
title: { type: "string", required: false, description: "The issue title" },
|
|
235
|
-
description: { type: "string", required: false, description: "The issue description in markdown format" },
|
|
236
|
-
assigneeId: { type: "string", required: false, description: "The identifier of the user to assign the issue to" },
|
|
237
|
-
stateId: { type: "string", required: false, description: "The team workflow state of the issue" },
|
|
238
|
-
priority: { type: "number", required: false, description: "Priority. 0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
|
|
239
|
-
labelIds: { type: "array", required: false, description: "The identifiers of the issue labels associated with this ticket (replaces all)" },
|
|
240
|
-
addedLabelIds: { type: "array", required: false, description: "The identifiers of issue labels to be added to this issue" },
|
|
241
|
-
removedLabelIds: { type: "array", required: false, description: "The identifiers of issue labels to be removed from this issue" },
|
|
242
|
-
projectId: { type: "string", required: false, description: "The project associated with the issue" },
|
|
243
|
-
projectMilestoneId: { type: "string", required: false, description: "The project milestone associated with the issue" },
|
|
244
|
-
cycleId: { type: "string", required: false, description: "The cycle associated with the issue" },
|
|
245
|
-
parentId: { type: "string", required: false, description: "The identifier of the parent issue. UUID or issue identifier (e.g., 'LIN-123')" },
|
|
246
|
-
teamId: { type: "string", required: false, description: "The identifier of the team associated with the issue (move issue to a different team)" },
|
|
247
|
-
estimate: { type: "number", required: false, description: "The estimated complexity of the issue (Int)" },
|
|
248
|
-
dueDate: { type: "string", required: false, description: "The date at which the issue is due (TimelessDate, YYYY-MM-DD)" },
|
|
249
|
-
subscriberIds: { type: "array", required: false, description: "The identifiers of the users subscribing to this ticket" },
|
|
250
|
-
sortOrder: { type: "number", required: false, description: "The position of the issue related to other issues (Float)" },
|
|
251
|
-
subIssueSortOrder: { type: "number", required: false, description: "The position of the issue in its parent's sub-issue list (Float)" },
|
|
252
|
-
snoozedUntilAt: { type: "string", required: false, description: "The time until which the issue will be snoozed in Triage view (DateTime)" },
|
|
253
|
-
releaseIds: { type: "array", required: false, description: "The identifiers of the releases associated with this issue (replaces all)" },
|
|
254
|
-
addedReleaseIds: { type: "array", required: false, description: "The identifiers of releases to be added to this issue" },
|
|
255
|
-
removedReleaseIds: { type: "array", required: false, description: "The identifiers of releases to be removed from this issue" },
|
|
256
|
-
trashed: { type: "boolean", required: false, description: "Whether the issue has been trashed" },
|
|
257
|
-
},
|
|
258
|
-
async execute(input, ctx) {
|
|
259
|
-
const { issueId, ...fields } = input;
|
|
260
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success issue { ${ISSUE_FIELDS} } } }`, { id: issueId, input: fields });
|
|
261
|
-
return data.issueUpdate?.issue;
|
|
262
|
-
},
|
|
263
|
-
});
|
|
264
|
-
rl.registerAction("issue.delete", {
|
|
265
|
-
description: "Trash (soft-delete) an issue. Pass permanentlyDelete=true to bypass 30d grace period (admin only).",
|
|
266
|
-
inputSchema: {
|
|
267
|
-
issueId: { type: "string", required: true },
|
|
268
|
-
permanentlyDelete: { type: "boolean", required: false },
|
|
269
|
-
},
|
|
270
|
-
async execute(input, ctx) {
|
|
271
|
-
const { issueId, permanentlyDelete } = input;
|
|
272
|
-
const data = await gql(key(ctx), `mutation($id: String!, $perm: Boolean) { issueDelete(id: $id, permanentlyDelete: $perm) { success } }`, { id: issueId, perm: permanentlyDelete ?? null });
|
|
273
|
-
return data.issueDelete;
|
|
274
|
-
},
|
|
275
|
-
});
|
|
276
|
-
rl.registerAction("issue.archive", {
|
|
277
|
-
description: "Archive an issue.",
|
|
278
|
-
inputSchema: {
|
|
279
|
-
issueId: { type: "string", required: true },
|
|
280
|
-
trash: { type: "boolean", required: false },
|
|
281
|
-
},
|
|
282
|
-
async execute(input, ctx) {
|
|
283
|
-
const { issueId, trash } = input;
|
|
284
|
-
const data = await gql(key(ctx), `mutation($id: String!, $trash: Boolean) { issueArchive(id: $id, trash: $trash) { success } }`, { id: issueId, trash: trash ?? null });
|
|
285
|
-
return data.issueArchive;
|
|
286
|
-
},
|
|
287
|
-
});
|
|
288
|
-
rl.registerAction("issue.unarchive", {
|
|
289
|
-
description: "Unarchive an issue.",
|
|
290
|
-
inputSchema: { issueId: { type: "string", required: true } },
|
|
291
|
-
async execute(input, ctx) {
|
|
292
|
-
const data = await gql(key(ctx), `mutation($id: String!) { issueUnarchive(id: $id) { success } }`, { id: input.issueId });
|
|
293
|
-
return data.issueUnarchive;
|
|
294
|
-
},
|
|
295
|
-
});
|
|
296
|
-
rl.registerAction("issue.search", {
|
|
297
|
-
description: "Search issues by text query using full-text and vector search. Rate-limited to 30 req/min.",
|
|
298
|
-
inputSchema: {
|
|
299
|
-
term: { type: "string", required: true, description: "Search string to look for" },
|
|
300
|
-
limit: { type: "number", required: false, description: "Max results (forward pagination, default 50)" },
|
|
301
|
-
filter: { type: "object", required: false, description: "Optional IssueFilter" },
|
|
302
|
-
includeComments: { type: "boolean", required: false, description: "Should associated comments be searched (default false)" },
|
|
303
|
-
includeArchived: { type: "boolean", required: false, description: "Should archived resources be included (default false)" },
|
|
304
|
-
teamId: { type: "string", required: false, description: "UUID of a team to boost in search results" },
|
|
305
|
-
orderBy: { type: "string", required: false, description: "PaginationOrderBy: createdAt | updatedAt" },
|
|
306
|
-
after: { type: "string", required: false, description: "Cursor for forward pagination" },
|
|
307
|
-
before: { type: "string", required: false, description: "Cursor for backward pagination" },
|
|
308
|
-
},
|
|
309
|
-
async execute(input, ctx) {
|
|
310
|
-
const opts = input;
|
|
311
|
-
const data = await gql(key(ctx), `query($term: String!, $first: Int, $filter: IssueFilter, $includeComments: Boolean, $includeArchived: Boolean, $teamId: String, $orderBy: PaginationOrderBy, $after: String, $before: String) {
|
|
312
|
-
searchIssues(term: $term, first: $first, filter: $filter, includeComments: $includeComments, includeArchived: $includeArchived, teamId: $teamId, orderBy: $orderBy, after: $after, before: $before) {
|
|
313
|
-
nodes { ${ISSUE_LITE} }
|
|
314
|
-
totalCount
|
|
315
|
-
pageInfo { hasNextPage endCursor }
|
|
316
|
-
}
|
|
317
|
-
}`, {
|
|
318
|
-
term: opts.term,
|
|
319
|
-
first: opts.limit ?? 50,
|
|
320
|
-
filter: opts.filter ?? null,
|
|
321
|
-
includeComments: opts.includeComments ?? null,
|
|
322
|
-
includeArchived: opts.includeArchived ?? null,
|
|
323
|
-
teamId: opts.teamId ?? null,
|
|
324
|
-
orderBy: opts.orderBy ?? null,
|
|
325
|
-
after: opts.after ?? null,
|
|
326
|
-
before: opts.before ?? null,
|
|
327
|
-
});
|
|
328
|
-
return data.searchIssues;
|
|
329
|
-
},
|
|
330
|
-
});
|
|
331
|
-
rl.registerAction("issue.addLabel", {
|
|
332
|
-
description: "Add a single label to an issue.",
|
|
333
|
-
inputSchema: {
|
|
334
|
-
issueId: { type: "string", required: true },
|
|
335
|
-
labelId: { type: "string", required: true },
|
|
336
|
-
},
|
|
337
|
-
async execute(input, ctx) {
|
|
338
|
-
const { issueId, labelId } = input;
|
|
339
|
-
const data = await gql(key(ctx), `mutation($id: String!, $labelId: String!) { issueAddLabel(id: $id, labelId: $labelId) { success } }`, { id: issueId, labelId });
|
|
340
|
-
return data.issueAddLabel;
|
|
341
|
-
},
|
|
342
|
-
});
|
|
343
|
-
rl.registerAction("issue.removeLabel", {
|
|
344
|
-
description: "Remove a single label from an issue.",
|
|
345
|
-
inputSchema: {
|
|
346
|
-
issueId: { type: "string", required: true },
|
|
347
|
-
labelId: { type: "string", required: true },
|
|
348
|
-
},
|
|
349
|
-
async execute(input, ctx) {
|
|
350
|
-
const { issueId, labelId } = input;
|
|
351
|
-
const data = await gql(key(ctx), `mutation($id: String!, $labelId: String!) { issueRemoveLabel(id: $id, labelId: $labelId) { success } }`, { id: issueId, labelId });
|
|
352
|
-
return data.issueRemoveLabel;
|
|
353
|
-
},
|
|
354
|
-
});
|
|
355
|
-
rl.registerAction("issue.subscribe", {
|
|
356
|
-
description: "Subscribe a user to issue notifications (defaults to current user).",
|
|
357
|
-
inputSchema: {
|
|
358
|
-
issueId: { type: "string", required: true },
|
|
359
|
-
userId: { type: "string", required: false },
|
|
360
|
-
userEmail: { type: "string", required: false },
|
|
361
|
-
},
|
|
362
|
-
async execute(input, ctx) {
|
|
363
|
-
const { issueId, userId, userEmail } = input;
|
|
364
|
-
const data = await gql(key(ctx), `mutation($id: String!, $userId: String, $userEmail: String) {
|
|
365
|
-
issueSubscribe(id: $id, userId: $userId, userEmail: $userEmail) { success }
|
|
366
|
-
}`, { id: issueId, userId: userId ?? null, userEmail: userEmail ?? null });
|
|
367
|
-
return data.issueSubscribe;
|
|
368
|
-
},
|
|
369
|
-
});
|
|
370
|
-
rl.registerAction("issue.unsubscribe", {
|
|
371
|
-
description: "Unsubscribe a user from issue notifications (defaults to current user).",
|
|
372
|
-
inputSchema: {
|
|
373
|
-
issueId: { type: "string", required: true },
|
|
374
|
-
userId: { type: "string", required: false },
|
|
375
|
-
userEmail: { type: "string", required: false },
|
|
376
|
-
},
|
|
377
|
-
async execute(input, ctx) {
|
|
378
|
-
const { issueId, userId, userEmail } = input;
|
|
379
|
-
const data = await gql(key(ctx), `mutation($id: String!, $userId: String, $userEmail: String) {
|
|
380
|
-
issueUnsubscribe(id: $id, userId: $userId, userEmail: $userEmail) { success }
|
|
381
|
-
}`, { id: issueId, userId: userId ?? null, userEmail: userEmail ?? null });
|
|
382
|
-
return data.issueUnsubscribe;
|
|
383
|
-
},
|
|
384
|
-
});
|
|
385
|
-
rl.registerAction("issue.addLink", {
|
|
386
|
-
description: "Create a relation between two issues.",
|
|
387
|
-
inputSchema: {
|
|
388
|
-
issueId: { type: "string", required: true, description: "The identifier of the issue that is related to another issue. UUID or issue identifier (e.g., 'LIN-123')" },
|
|
389
|
-
relatedIssueId: { type: "string", required: true, description: "The identifier of the related issue. UUID or issue identifier (e.g., 'LIN-123')" },
|
|
390
|
-
type: { type: "string", required: true, description: "IssueRelationType: blocks | duplicate | related | similar" },
|
|
391
|
-
},
|
|
392
|
-
async execute(input, ctx) {
|
|
393
|
-
const data = await gql(key(ctx), `mutation($input: IssueRelationCreateInput!) { issueRelationCreate(input: $input) { success issueRelation { id type } } }`, { input: input });
|
|
394
|
-
return data.issueRelationCreate;
|
|
395
|
-
},
|
|
396
|
-
});
|
|
397
|
-
rl.registerAction("issue.listComments", {
|
|
398
|
-
description: "List comments on an issue.",
|
|
399
|
-
inputSchema: {
|
|
400
|
-
issueId: { type: "string", required: true },
|
|
401
|
-
limit: { type: "number", required: false },
|
|
402
|
-
},
|
|
403
|
-
async execute(input, ctx) {
|
|
404
|
-
const { issueId, limit } = input;
|
|
405
|
-
const data = await gql(key(ctx), `query($id: String!, $first: Int) {
|
|
406
|
-
issue(id: $id) { comments(first: $first) { nodes { ${COMMENT_FIELDS} } } }
|
|
407
|
-
}`, { id: issueId, first: limit ?? 50 });
|
|
408
|
-
return data.issue?.comments?.nodes;
|
|
409
|
-
},
|
|
410
|
-
});
|
|
411
|
-
// =========================================================
|
|
412
|
-
// Comments
|
|
413
|
-
// =========================================================
|
|
414
|
-
rl.registerAction("issue.addComment", {
|
|
415
|
-
description: "Add a comment to an issue. Pass parentId to nest as a reply.",
|
|
416
|
-
inputSchema: {
|
|
417
|
-
issueId: { type: "string", required: true, description: "The issue to associate the comment with. UUID or issue identifier (e.g., 'LIN-123')" },
|
|
418
|
-
body: { type: "string", required: true, description: "The comment content in markdown format" },
|
|
419
|
-
parentId: { type: "string", required: false, description: "The parent comment under which to nest this comment" },
|
|
420
|
-
doNotSubscribeToIssue: { type: "boolean", required: false, description: "Prevent auto-subscription to the issue the comment is created on" },
|
|
421
|
-
quotedText: { type: "string", required: false, description: "The text that this comment references (inline comments)" },
|
|
422
|
-
},
|
|
423
|
-
async execute(input, ctx) {
|
|
424
|
-
const fields = input;
|
|
425
|
-
const data = await gql(key(ctx), `mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { ${COMMENT_FIELDS} } } }`, { input: fields });
|
|
426
|
-
return data.commentCreate?.comment;
|
|
427
|
-
},
|
|
428
|
-
});
|
|
429
|
-
listAction("comment.list", "List comments across the workspace.", "comments", "CommentFilter", COMMENT_FIELDS);
|
|
430
|
-
rl.registerAction("comment.get", {
|
|
431
|
-
description: "Get a comment by ID.",
|
|
432
|
-
inputSchema: { id: { type: "string", required: true } },
|
|
433
|
-
async execute(input, ctx) {
|
|
434
|
-
const data = await gql(key(ctx), `query($id: String!) { comment(id: $id) { ${COMMENT_FIELDS} } }`, { id: input.id });
|
|
435
|
-
return data.comment;
|
|
436
|
-
},
|
|
437
|
-
});
|
|
438
|
-
rl.registerAction("comment.update", {
|
|
439
|
-
description: "Update a comment.",
|
|
440
|
-
inputSchema: {
|
|
441
|
-
id: { type: "string", required: true, description: "The identifier of the comment to update" },
|
|
442
|
-
body: { type: "string", required: false, description: "The comment content in markdown format" },
|
|
443
|
-
quotedText: { type: "string", required: false, description: "The text that this comment references (inline comments)" },
|
|
444
|
-
},
|
|
445
|
-
async execute(input, ctx) {
|
|
446
|
-
const { id, ...fields } = input;
|
|
447
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: CommentUpdateInput!) { commentUpdate(id: $id, input: $input) { success comment { ${COMMENT_FIELDS} } } }`, { id, input: fields });
|
|
448
|
-
return data.commentUpdate?.comment;
|
|
449
|
-
},
|
|
450
|
-
});
|
|
451
|
-
rl.registerAction("comment.delete", {
|
|
452
|
-
description: "Delete a comment.",
|
|
453
|
-
inputSchema: { id: { type: "string", required: true } },
|
|
454
|
-
async execute(input, ctx) {
|
|
455
|
-
const data = await gql(key(ctx), `mutation($id: String!) { commentDelete(id: $id) { success } }`, { id: input.id });
|
|
456
|
-
return data.commentDelete;
|
|
457
|
-
},
|
|
458
|
-
});
|
|
459
|
-
// =========================================================
|
|
460
|
-
// Workflow States
|
|
461
|
-
// =========================================================
|
|
462
|
-
listAction("state.list", "List workflow states. Filter by team for team-scoped states.", "workflowStates", "WorkflowStateFilter", STATE_FIELDS);
|
|
463
|
-
getAction("state.get", "Get a workflow state by ID.", "workflowState", STATE_FIELDS);
|
|
464
|
-
rl.registerAction("state.create", {
|
|
465
|
-
description: "Create a workflow state in a team.",
|
|
466
|
-
inputSchema: {
|
|
467
|
-
teamId: { type: "string", required: true, description: "The team associated with the state" },
|
|
468
|
-
name: { type: "string", required: true, description: "The name of the state" },
|
|
469
|
-
type: { type: "string", required: true, description: "The workflow state type which categorizes the state. Valid values: backlog, unstarted, started, completed, canceled" },
|
|
470
|
-
color: { type: "string", required: true, description: "The color of the state (hex, e.g. #6B7280)" },
|
|
471
|
-
description: { type: "string", required: false, description: "The description of the state" },
|
|
472
|
-
position: { type: "number", required: false, description: "The position of the state (Float)" },
|
|
473
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
474
|
-
},
|
|
475
|
-
async execute(input, ctx) {
|
|
476
|
-
const data = await gql(key(ctx), `mutation($input: WorkflowStateCreateInput!) { workflowStateCreate(input: $input) { success workflowState { ${STATE_FIELDS} } } }`, { input: input });
|
|
477
|
-
return data.workflowStateCreate?.workflowState;
|
|
478
|
-
},
|
|
479
|
-
});
|
|
480
|
-
rl.registerAction("state.update", {
|
|
481
|
-
description: "Update a workflow state. Type cannot be changed after creation.",
|
|
482
|
-
inputSchema: {
|
|
483
|
-
id: { type: "string", required: true, description: "The identifier of the state to update" },
|
|
484
|
-
name: { type: "string", required: false, description: "The name of the state" },
|
|
485
|
-
color: { type: "string", required: false, description: "The color of the state (hex)" },
|
|
486
|
-
description: { type: "string", required: false, description: "The description of the state" },
|
|
487
|
-
position: { type: "number", required: false, description: "The position of the state (Float)" },
|
|
488
|
-
},
|
|
489
|
-
async execute(input, ctx) {
|
|
490
|
-
const { id, ...fields } = input;
|
|
491
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: WorkflowStateUpdateInput!) { workflowStateUpdate(id: $id, input: $input) { success workflowState { ${STATE_FIELDS} } } }`, { id, input: fields });
|
|
492
|
-
return data.workflowStateUpdate?.workflowState;
|
|
493
|
-
},
|
|
494
|
-
});
|
|
495
|
-
// =========================================================
|
|
496
|
-
// Labels
|
|
497
|
-
// =========================================================
|
|
498
|
-
listAction("label.list", "List labels (workspace + team-scoped).", "issueLabels", "IssueLabelFilter", LABEL_FIELDS);
|
|
499
|
-
getAction("label.get", "Get a label by ID.", "issueLabel", LABEL_FIELDS);
|
|
500
|
-
rl.registerAction("label.create", {
|
|
501
|
-
description: "Create a label. Omit teamId for a workspace-level label.",
|
|
502
|
-
inputSchema: {
|
|
503
|
-
name: { type: "string", required: true, description: "The name of the label" },
|
|
504
|
-
teamId: { type: "string", required: false, description: "The team associated with the label. If omitted, the label is workspace-scoped" },
|
|
505
|
-
color: { type: "string", required: false, description: "The color of the label (hex)" },
|
|
506
|
-
description: { type: "string", required: false, description: "The description of the label" },
|
|
507
|
-
parentId: { type: "string", required: false, description: "The identifier of the parent label (group label)" },
|
|
508
|
-
isGroup: { type: "boolean", required: false, description: "Whether the label is a group" },
|
|
509
|
-
retiredAt: { type: "string", required: false, description: "The time at which the label was retired (DateTime). Set to null to restore a retired label" },
|
|
510
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
511
|
-
replaceTeamLabels: { type: "boolean", required: false, description: "Replace all team-specific labels with the same name with this newly created workspace label (default false)" },
|
|
512
|
-
},
|
|
513
|
-
async execute(input, ctx) {
|
|
514
|
-
const { replaceTeamLabels, ...fields } = input;
|
|
515
|
-
const data = await gql(key(ctx), `mutation($input: IssueLabelCreateInput!, $replaceTeamLabels: Boolean) { issueLabelCreate(input: $input, replaceTeamLabels: $replaceTeamLabels) { success issueLabel { ${LABEL_FIELDS} } } }`, { input: fields, replaceTeamLabels: replaceTeamLabels ?? null });
|
|
516
|
-
return data.issueLabelCreate?.issueLabel;
|
|
517
|
-
},
|
|
518
|
-
});
|
|
519
|
-
rl.registerAction("label.update", {
|
|
520
|
-
description: "Update a label.",
|
|
521
|
-
inputSchema: {
|
|
522
|
-
id: { type: "string", required: true, description: "The identifier of the label to update" },
|
|
523
|
-
name: { type: "string", required: false, description: "The name of the label" },
|
|
524
|
-
color: { type: "string", required: false, description: "The color of the label (hex)" },
|
|
525
|
-
description: { type: "string", required: false, description: "The description of the label" },
|
|
526
|
-
parentId: { type: "string", required: false, description: "The identifier of the parent label" },
|
|
527
|
-
isGroup: { type: "boolean", required: false, description: "Whether the label is a group" },
|
|
528
|
-
retiredAt: { type: "string", required: false, description: "The time at which the label was retired (DateTime). Set to null to restore a retired label" },
|
|
529
|
-
replaceTeamLabels: { type: "boolean", required: false, description: "Replace all team-specific labels with the same name with this updated workspace label (default false)" },
|
|
530
|
-
},
|
|
531
|
-
async execute(input, ctx) {
|
|
532
|
-
const { id, replaceTeamLabels, ...fields } = input;
|
|
533
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: IssueLabelUpdateInput!, $replaceTeamLabels: Boolean) { issueLabelUpdate(id: $id, input: $input, replaceTeamLabels: $replaceTeamLabels) { success issueLabel { ${LABEL_FIELDS} } } }`, { id, input: fields, replaceTeamLabels: replaceTeamLabels ?? null });
|
|
534
|
-
return data.issueLabelUpdate?.issueLabel;
|
|
535
|
-
},
|
|
536
|
-
});
|
|
537
|
-
rl.registerAction("label.delete", {
|
|
538
|
-
description: "Delete a label.",
|
|
539
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the label to delete" } },
|
|
540
|
-
async execute(input, ctx) {
|
|
541
|
-
const data = await gql(key(ctx), `mutation($id: String!) { issueLabelDelete(id: $id) { success } }`, { id: input.id });
|
|
542
|
-
return data.issueLabelDelete;
|
|
543
|
-
},
|
|
544
|
-
});
|
|
545
|
-
rl.registerAction("label.retire", {
|
|
546
|
-
description: "Retire a label. Retired labels remain visible but cannot be applied to new issues.",
|
|
547
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the label to retire" } },
|
|
548
|
-
async execute(input, ctx) {
|
|
549
|
-
const data = await gql(key(ctx), `mutation($id: String!) { issueLabelRetire(id: $id) { success issueLabel { ${LABEL_FIELDS} } } }`, { id: input.id });
|
|
550
|
-
return data.issueLabelRetire?.issueLabel;
|
|
551
|
-
},
|
|
552
|
-
});
|
|
553
|
-
rl.registerAction("label.restore", {
|
|
554
|
-
description: "Restore a previously retired label.",
|
|
555
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the label to restore" } },
|
|
556
|
-
async execute(input, ctx) {
|
|
557
|
-
const data = await gql(key(ctx), `mutation($id: String!) { issueLabelRestore(id: $id) { success issueLabel { ${LABEL_FIELDS} } } }`, { id: input.id });
|
|
558
|
-
return data.issueLabelRestore?.issueLabel;
|
|
559
|
-
},
|
|
560
|
-
});
|
|
561
|
-
// =========================================================
|
|
562
|
-
// Projects
|
|
563
|
-
// =========================================================
|
|
564
|
-
listAction("project.list", "List projects.", "projects", "ProjectFilter", PROJECT_FIELDS);
|
|
565
|
-
getAction("project.get", "Get a project by ID or slug.", "project", PROJECT_FIELDS);
|
|
566
|
-
rl.registerAction("project.create", {
|
|
567
|
-
description: "Create a project. teamIds is required.",
|
|
568
|
-
inputSchema: {
|
|
569
|
-
name: { type: "string", required: true, description: "The name of the project" },
|
|
570
|
-
teamIds: { type: "array", required: true, description: "The identifiers of the teams this project is associated with" },
|
|
571
|
-
description: { type: "string", required: false, description: "The description for the project" },
|
|
572
|
-
content: { type: "string", required: false, description: "The project content as markdown" },
|
|
573
|
-
icon: { type: "string", required: false, description: "The icon of the project" },
|
|
574
|
-
color: { type: "string", required: false, description: "The color of the project (hex)" },
|
|
575
|
-
priority: { type: "number", required: false, description: "The priority of the project. 0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
|
|
576
|
-
leadId: { type: "string", required: false, description: "The identifier of the project lead" },
|
|
577
|
-
memberIds: { type: "array", required: false, description: "The identifiers of the members of this project" },
|
|
578
|
-
startDate: { type: "string", required: false, description: "The planned start date of the project (TimelessDate, YYYY-MM-DD)" },
|
|
579
|
-
startDateResolution: { type: "string", required: false, description: "The resolution of the project's start date (DateResolutionType)" },
|
|
580
|
-
targetDate: { type: "string", required: false, description: "The planned target date of the project (TimelessDate, YYYY-MM-DD)" },
|
|
581
|
-
targetDateResolution: { type: "string", required: false, description: "The resolution of the project's estimated completion date (DateResolutionType)" },
|
|
582
|
-
statusId: { type: "string", required: false, description: "The ID of the project status" },
|
|
583
|
-
labelIds: { type: "array", required: false, description: "The identifiers of the project labels associated with this project" },
|
|
584
|
-
sortOrder: { type: "number", required: false, description: "The sort order for the project in shared views (Float)" },
|
|
585
|
-
templateId: { type: "string", required: false, description: "The ID of a project template to apply when creating the project" },
|
|
586
|
-
useDefaultTemplate: { type: "boolean", required: false, description: "Apply the default project template of the first team provided. Ignored if templateId is set" },
|
|
587
|
-
convertedFromIssueId: { type: "string", required: false, description: "The ID of the issue that was converted into this project" },
|
|
588
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
589
|
-
slackChannelName: { type: "string", required: false, description: "The full name for the Slack channel to create (including prefix). Creates and connects a Slack channel if provided" },
|
|
590
|
-
},
|
|
591
|
-
async execute(input, ctx) {
|
|
592
|
-
const { slackChannelName, ...fields } = input;
|
|
593
|
-
const data = await gql(key(ctx), `mutation($input: ProjectCreateInput!, $slackChannelName: String) { projectCreate(input: $input, slackChannelName: $slackChannelName) { success project { ${PROJECT_FIELDS} } } }`, { input: fields, slackChannelName: slackChannelName ?? null });
|
|
594
|
-
return data.projectCreate?.project;
|
|
595
|
-
},
|
|
596
|
-
});
|
|
597
|
-
rl.registerAction("project.update", {
|
|
598
|
-
description: "Update a project.",
|
|
599
|
-
inputSchema: {
|
|
600
|
-
id: { type: "string", required: true, description: "The identifier of the project to update (UUID or slug)" },
|
|
601
|
-
name: { type: "string", required: false, description: "The name of the project" },
|
|
602
|
-
description: { type: "string", required: false, description: "The description for the project" },
|
|
603
|
-
content: { type: "string", required: false, description: "The project content as markdown" },
|
|
604
|
-
icon: { type: "string", required: false, description: "The icon of the project" },
|
|
605
|
-
color: { type: "string", required: false, description: "The color of the project (hex)" },
|
|
606
|
-
priority: { type: "number", required: false, description: "The priority of the project. 0=No, 1=Urgent, 2=High, 3=Medium, 4=Low" },
|
|
607
|
-
leadId: { type: "string", required: false, description: "The identifier of the project lead" },
|
|
608
|
-
memberIds: { type: "array", required: false, description: "The identifiers of the members of this project" },
|
|
609
|
-
startDate: { type: "string", required: false, description: "The planned start date (TimelessDate, YYYY-MM-DD)" },
|
|
610
|
-
startDateResolution: { type: "string", required: false, description: "The resolution of the project's start date (DateResolutionType)" },
|
|
611
|
-
targetDate: { type: "string", required: false, description: "The planned target date (TimelessDate, YYYY-MM-DD)" },
|
|
612
|
-
targetDateResolution: { type: "string", required: false, description: "The resolution of the project's estimated completion date (DateResolutionType)" },
|
|
613
|
-
statusId: { type: "string", required: false, description: "The ID of the project status" },
|
|
614
|
-
labelIds: { type: "array", required: false, description: "The identifiers of the project labels associated with this project" },
|
|
615
|
-
teamIds: { type: "array", required: false, description: "The identifiers of the teams this project is associated with" },
|
|
616
|
-
sortOrder: { type: "number", required: false, description: "The sort order for the project in shared views (Float)" },
|
|
617
|
-
completedAt: { type: "string", required: false, description: "The time at which the project was completed (DateTime)" },
|
|
618
|
-
canceledAt: { type: "string", required: false, description: "The time at which the project was canceled (DateTime)" },
|
|
619
|
-
trashed: { type: "boolean", required: false, description: "Whether the project has been trashed. Set to true to trash, or null to restore" },
|
|
620
|
-
},
|
|
621
|
-
async execute(input, ctx) {
|
|
622
|
-
const { id, ...fields } = input;
|
|
623
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: ProjectUpdateInput!) { projectUpdate(id: $id, input: $input) { success project { ${PROJECT_FIELDS} } } }`, { id, input: fields });
|
|
624
|
-
return data.projectUpdate?.project;
|
|
625
|
-
},
|
|
626
|
-
});
|
|
627
|
-
rl.registerAction("project.delete", {
|
|
628
|
-
description: "Trash (soft-delete) a project. Restorable via project.unarchive.",
|
|
629
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the project to delete" } },
|
|
630
|
-
async execute(input, ctx) {
|
|
631
|
-
const data = await gql(key(ctx), `mutation($id: String!) { projectDelete(id: $id) { success } }`, { id: input.id });
|
|
632
|
-
return data.projectDelete;
|
|
633
|
-
},
|
|
634
|
-
});
|
|
635
|
-
rl.registerAction("project.unarchive", {
|
|
636
|
-
description: "Restore a previously trashed or archived project.",
|
|
637
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the project to restore (UUID or slug)" } },
|
|
638
|
-
async execute(input, ctx) {
|
|
639
|
-
const data = await gql(key(ctx), `mutation($id: String!) { projectUnarchive(id: $id) { success } }`, { id: input.id });
|
|
640
|
-
return data.projectUnarchive;
|
|
641
|
-
},
|
|
642
|
-
});
|
|
643
|
-
rl.registerAction("project.search", {
|
|
644
|
-
description: "Search projects by text. Rate-limited to 30 req/min.",
|
|
645
|
-
inputSchema: {
|
|
646
|
-
term: { type: "string", required: true, description: "Search string to look for" },
|
|
647
|
-
limit: { type: "number", required: false, description: "Max results (forward pagination, default 50)" },
|
|
648
|
-
includeComments: { type: "boolean", required: false, description: "Should associated comments be searched (default false)" },
|
|
649
|
-
teamId: { type: "string", required: false, description: "UUID of a team to boost in search results" },
|
|
650
|
-
},
|
|
651
|
-
async execute(input, ctx) {
|
|
652
|
-
const opts = input;
|
|
653
|
-
const data = await gql(key(ctx), `query($term: String!, $first: Int, $includeComments: Boolean, $teamId: String) {
|
|
654
|
-
searchProjects(term: $term, first: $first, includeComments: $includeComments, teamId: $teamId) {
|
|
655
|
-
nodes { ${PROJECT_FIELDS} }
|
|
656
|
-
totalCount
|
|
657
|
-
}
|
|
658
|
-
}`, {
|
|
659
|
-
term: opts.term,
|
|
660
|
-
first: opts.limit ?? 50,
|
|
661
|
-
includeComments: opts.includeComments ?? null,
|
|
662
|
-
teamId: opts.teamId ?? null,
|
|
663
|
-
});
|
|
664
|
-
return data.searchProjects;
|
|
665
|
-
},
|
|
666
|
-
});
|
|
667
|
-
// =========================================================
|
|
668
|
-
// Project Milestones
|
|
669
|
-
// =========================================================
|
|
670
|
-
listAction("milestone.list", "List project milestones.", "projectMilestones", "ProjectMilestoneFilter", MILESTONE_FIELDS);
|
|
671
|
-
getAction("milestone.get", "Get a project milestone by ID.", "projectMilestone", MILESTONE_FIELDS);
|
|
672
|
-
rl.registerAction("milestone.create", {
|
|
673
|
-
description: "Create a project milestone.",
|
|
674
|
-
inputSchema: {
|
|
675
|
-
projectId: { type: "string", required: true, description: "Related project for the project milestone" },
|
|
676
|
-
name: { type: "string", required: true, description: "The name of the project milestone" },
|
|
677
|
-
description: { type: "string", required: false, description: "The description of the project milestone in markdown format" },
|
|
678
|
-
targetDate: { type: "string", required: false, description: "The planned target date of the project milestone (TimelessDate, YYYY-MM-DD)" },
|
|
679
|
-
sortOrder: { type: "number", required: false, description: "The sort order for the project milestone within a project (Float)" },
|
|
680
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
681
|
-
},
|
|
682
|
-
async execute(input, ctx) {
|
|
683
|
-
const data = await gql(key(ctx), `mutation($input: ProjectMilestoneCreateInput!) { projectMilestoneCreate(input: $input) { success projectMilestone { ${MILESTONE_FIELDS} } } }`, { input: input });
|
|
684
|
-
return data.projectMilestoneCreate?.projectMilestone;
|
|
685
|
-
},
|
|
686
|
-
});
|
|
687
|
-
rl.registerAction("milestone.update", {
|
|
688
|
-
description: "Update a project milestone.",
|
|
689
|
-
inputSchema: {
|
|
690
|
-
id: { type: "string", required: true, description: "The identifier of the project milestone to update" },
|
|
691
|
-
name: { type: "string", required: false, description: "The name of the project milestone" },
|
|
692
|
-
description: { type: "string", required: false, description: "The description of the project milestone in markdown format" },
|
|
693
|
-
targetDate: { type: "string", required: false, description: "The planned target date (TimelessDate, YYYY-MM-DD)" },
|
|
694
|
-
projectId: { type: "string", required: false, description: "Related project for the project milestone (move to another project)" },
|
|
695
|
-
sortOrder: { type: "number", required: false, description: "The sort order for the project milestone within a project (Float)" },
|
|
696
|
-
},
|
|
697
|
-
async execute(input, ctx) {
|
|
698
|
-
const { id, ...fields } = input;
|
|
699
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: ProjectMilestoneUpdateInput!) { projectMilestoneUpdate(id: $id, input: $input) { success projectMilestone { ${MILESTONE_FIELDS} } } }`, { id, input: fields });
|
|
700
|
-
return data.projectMilestoneUpdate?.projectMilestone;
|
|
701
|
-
},
|
|
702
|
-
});
|
|
703
|
-
rl.registerAction("milestone.delete", {
|
|
704
|
-
description: "Delete a project milestone.",
|
|
705
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the project milestone to delete" } },
|
|
706
|
-
async execute(input, ctx) {
|
|
707
|
-
const data = await gql(key(ctx), `mutation($id: String!) { projectMilestoneDelete(id: $id) { success } }`, { id: input.id });
|
|
708
|
-
return data.projectMilestoneDelete;
|
|
709
|
-
},
|
|
710
|
-
});
|
|
711
|
-
// =========================================================
|
|
712
|
-
// Project Updates (status posts)
|
|
713
|
-
// =========================================================
|
|
714
|
-
listAction("projectUpdate.list", "List project updates.", "projectUpdates", "ProjectUpdateFilter", PROJECT_UPDATE_FIELDS);
|
|
715
|
-
rl.registerAction("projectUpdate.create", {
|
|
716
|
-
description: "Post a status update on a project.",
|
|
717
|
-
inputSchema: {
|
|
718
|
-
projectId: { type: "string", required: true, description: "The project to associate the project update with" },
|
|
719
|
-
body: { type: "string", required: false, description: "The content of the project update in markdown format" },
|
|
720
|
-
health: { type: "string", required: false, description: "The health of the project at the time of the update (ProjectUpdateHealthType: onTrack | atRisk | offTrack)" },
|
|
721
|
-
isDiffHidden: { type: "boolean", required: false, description: "Whether the diff between the current update and the previous one should be hidden" },
|
|
722
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
723
|
-
},
|
|
724
|
-
async execute(input, ctx) {
|
|
725
|
-
const data = await gql(key(ctx), `mutation($input: ProjectUpdateCreateInput!) { projectUpdateCreate(input: $input) { success projectUpdate { ${PROJECT_UPDATE_FIELDS} } } }`, { input: input });
|
|
726
|
-
return data.projectUpdateCreate?.projectUpdate;
|
|
727
|
-
},
|
|
728
|
-
});
|
|
729
|
-
rl.registerAction("projectUpdate.update", {
|
|
730
|
-
description: "Update a project status update.",
|
|
731
|
-
inputSchema: {
|
|
732
|
-
id: { type: "string", required: true, description: "The identifier of the project update to update" },
|
|
733
|
-
body: { type: "string", required: false, description: "The content of the project update in markdown format" },
|
|
734
|
-
health: { type: "string", required: false, description: "The health of the project at the time of the update (ProjectUpdateHealthType: onTrack | atRisk | offTrack)" },
|
|
735
|
-
isDiffHidden: { type: "boolean", required: false, description: "Whether the diff between the current update and the previous one should be hidden" },
|
|
736
|
-
},
|
|
737
|
-
async execute(input, ctx) {
|
|
738
|
-
const { id, ...fields } = input;
|
|
739
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: ProjectUpdateUpdateInput!) { projectUpdateUpdate(id: $id, input: $input) { success projectUpdate { ${PROJECT_UPDATE_FIELDS} } } }`, { id, input: fields });
|
|
740
|
-
return data.projectUpdateUpdate?.projectUpdate;
|
|
741
|
-
},
|
|
742
|
-
});
|
|
743
|
-
rl.registerAction("projectUpdate.archive", {
|
|
744
|
-
description: "Archive a project status update.",
|
|
745
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the project update to archive" } },
|
|
746
|
-
async execute(input, ctx) {
|
|
747
|
-
const data = await gql(key(ctx), `mutation($id: String!) { projectUpdateArchive(id: $id) { success } }`, { id: input.id });
|
|
748
|
-
return data.projectUpdateArchive;
|
|
749
|
-
},
|
|
750
|
-
});
|
|
751
|
-
// =========================================================
|
|
752
|
-
// Custom Views
|
|
753
|
-
// =========================================================
|
|
754
|
-
listAction("view.list", "List custom views accessible to the user, including personal and shared workspace views. Linear excludes views scoped to a specific project or initiative from this root query.", "customViews", "CustomViewFilter", CUSTOM_VIEW_FIELDS);
|
|
755
|
-
getAction("view.get", "Get a custom view by ID or slug.", "customView", CUSTOM_VIEW_FIELDS);
|
|
756
|
-
rl.registerAction("view.create", {
|
|
757
|
-
description: "Create a custom view. Set filterData for issue views; projectFilterData, initiativeFilterData, or feedItemFilterData for other view types.",
|
|
758
|
-
inputSchema: {
|
|
759
|
-
name: { type: "string", required: true, description: "The name of the custom view" },
|
|
760
|
-
description: { type: "string", required: false, description: "The description of the custom view" },
|
|
761
|
-
icon: { type: "string", required: false, description: "The icon of the custom view" },
|
|
762
|
-
color: { type: "string", required: false, description: "The color of the custom view icon (hex)" },
|
|
763
|
-
shared: { type: "boolean", required: false, description: "Whether the custom view is shared with everyone in the workspace" },
|
|
764
|
-
filterData: { type: "object", required: false, description: "IssueFilter for issue views" },
|
|
765
|
-
projectFilterData: { type: "object", required: false, description: "ProjectFilter for project views" },
|
|
766
|
-
initiativeFilterData: { type: "object", required: false, description: "InitiativeFilter for initiative views" },
|
|
767
|
-
feedItemFilterData: { type: "object", required: false, description: "FeedItemFilter for update/feed item views" },
|
|
768
|
-
teamId: { type: "string", required: false, description: "The team associated with the custom view" },
|
|
769
|
-
projectId: { type: "string", required: false, description: "The project associated with the custom view" },
|
|
770
|
-
initiativeId: { type: "string", required: false, description: "The initiative associated with the custom view" },
|
|
771
|
-
ownerId: { type: "string", required: false, description: "The owner of the custom view" },
|
|
772
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
773
|
-
},
|
|
774
|
-
async execute(input, ctx) {
|
|
775
|
-
const data = await gql(key(ctx), `mutation($input: CustomViewCreateInput!) { customViewCreate(input: $input) { success customView { ${CUSTOM_VIEW_FIELDS} } } }`, { input: input });
|
|
776
|
-
return data.customViewCreate?.customView;
|
|
777
|
-
},
|
|
778
|
-
});
|
|
779
|
-
rl.registerAction("view.update", {
|
|
780
|
-
description: "Update a custom view. All fields optional; only provided fields are updated.",
|
|
781
|
-
inputSchema: {
|
|
782
|
-
id: { type: "string", required: true, description: "The identifier of the custom view to update" },
|
|
783
|
-
name: { type: "string", required: false, description: "The name of the custom view" },
|
|
784
|
-
description: { type: "string", required: false, description: "The description of the custom view" },
|
|
785
|
-
icon: { type: "string", required: false, description: "The icon of the custom view" },
|
|
786
|
-
color: { type: "string", required: false, description: "The color of the custom view icon (hex)" },
|
|
787
|
-
shared: { type: "boolean", required: false, description: "Whether the custom view is shared with everyone in the workspace" },
|
|
788
|
-
filterData: { type: "object", required: false, description: "IssueFilter for issue views" },
|
|
789
|
-
projectFilterData: { type: "object", required: false, description: "ProjectFilter for project views" },
|
|
790
|
-
initiativeFilterData: { type: "object", required: false, description: "InitiativeFilter for initiative views" },
|
|
791
|
-
feedItemFilterData: { type: "object", required: false, description: "FeedItemFilter for update/feed item views" },
|
|
792
|
-
teamId: { type: "string", required: false, description: "The team associated with the custom view" },
|
|
793
|
-
projectId: { type: "string", required: false, description: "The project associated with the custom view" },
|
|
794
|
-
initiativeId: { type: "string", required: false, description: "The initiative associated with the custom view" },
|
|
795
|
-
ownerId: { type: "string", required: false, description: "The owner of the custom view" },
|
|
796
|
-
},
|
|
797
|
-
async execute(input, ctx) {
|
|
798
|
-
const { id, ...fields } = input;
|
|
799
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: CustomViewUpdateInput!) { customViewUpdate(id: $id, input: $input) { success customView { ${CUSTOM_VIEW_FIELDS} } } }`, { id, input: fields });
|
|
800
|
-
return data.customViewUpdate?.customView;
|
|
801
|
-
},
|
|
802
|
-
});
|
|
803
|
-
rl.registerAction("view.delete", {
|
|
804
|
-
description: "Delete a custom view.",
|
|
805
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the custom view to delete" } },
|
|
806
|
-
async execute(input, ctx) {
|
|
807
|
-
const data = await gql(key(ctx), `mutation($id: String!) { customViewDelete(id: $id) { success } }`, { id: input.id });
|
|
808
|
-
return data.customViewDelete;
|
|
809
|
-
},
|
|
810
|
-
});
|
|
811
|
-
customViewConnectionAction("view.issues", "List issues matching a custom view's issue filter. Returns an empty connection when the view's modelName is not Issue.", "issues", "IssueFilter", ISSUE_LITE, "Include issues from sub-teams when the custom view is associated with a team");
|
|
812
|
-
customViewConnectionAction("view.projects", "List projects matching a custom view's project filter. Returns an empty connection when the view's modelName is not Project.", "projects", "ProjectFilter", PROJECT_FIELDS, "Include projects from sub-teams when the custom view is associated with a team");
|
|
813
|
-
customViewConnectionAction("view.initiatives", "List initiatives matching a custom view's initiative filter. Returns an empty connection when the view's modelName is not Initiative.", "initiatives", "InitiativeFilter", INITIATIVE_FIELDS);
|
|
814
|
-
customViewConnectionAction("view.updates", "List feed items matching a custom view's feed item filter. Returns an empty connection when the view's modelName is not FeedItem.", "updates", "FeedItemFilter", FEED_ITEM_FIELDS, "Include updates from sub-teams when the custom view is associated with a team");
|
|
815
|
-
// =========================================================
|
|
816
|
-
// Cycles
|
|
817
|
-
// =========================================================
|
|
818
|
-
listAction("cycle.list", "List cycles. Use filter for isActive/isNext/isPrevious.", "cycles", "CycleFilter", CYCLE_FIELDS);
|
|
819
|
-
getAction("cycle.get", "Get a cycle by ID.", "cycle", CYCLE_FIELDS);
|
|
820
|
-
rl.registerAction("cycle.create", {
|
|
821
|
-
description: "Create a cycle for a team.",
|
|
822
|
-
inputSchema: {
|
|
823
|
-
teamId: { type: "string", required: true, description: "The team to associate the cycle with" },
|
|
824
|
-
startsAt: { type: "string", required: true, description: "The start time of the cycle (DateTime, ISO 8601)" },
|
|
825
|
-
endsAt: { type: "string", required: true, description: "The end time of the cycle (DateTime, ISO 8601)" },
|
|
826
|
-
name: { type: "string", required: false, description: "The custom name of the cycle" },
|
|
827
|
-
description: { type: "string", required: false, description: "The description of the cycle" },
|
|
828
|
-
completedAt: { type: "string", required: false, description: "The completion time of the cycle (DateTime). If null, the cycle hasn't been completed" },
|
|
829
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
830
|
-
},
|
|
831
|
-
async execute(input, ctx) {
|
|
832
|
-
const data = await gql(key(ctx), `mutation($input: CycleCreateInput!) { cycleCreate(input: $input) { success cycle { ${CYCLE_FIELDS} } } }`, { input: input });
|
|
833
|
-
return data.cycleCreate?.cycle;
|
|
834
|
-
},
|
|
835
|
-
});
|
|
836
|
-
rl.registerAction("cycle.update", {
|
|
837
|
-
description: "Update a cycle.",
|
|
838
|
-
inputSchema: {
|
|
839
|
-
id: { type: "string", required: true, description: "The identifier of the cycle to update" },
|
|
840
|
-
name: { type: "string", required: false, description: "The custom name of the cycle" },
|
|
841
|
-
description: { type: "string", required: false, description: "The description of the cycle" },
|
|
842
|
-
startsAt: { type: "string", required: false, description: "The start time of the cycle (DateTime, ISO 8601)" },
|
|
843
|
-
endsAt: { type: "string", required: false, description: "The end time of the cycle (DateTime, ISO 8601)" },
|
|
844
|
-
completedAt: { type: "string", required: false, description: "The completion time of the cycle (DateTime). If null, the cycle hasn't been completed" },
|
|
845
|
-
},
|
|
846
|
-
async execute(input, ctx) {
|
|
847
|
-
const { id, ...fields } = input;
|
|
848
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: CycleUpdateInput!) { cycleUpdate(id: $id, input: $input) { success cycle { ${CYCLE_FIELDS} } } }`, { id, input: fields });
|
|
849
|
-
return data.cycleUpdate?.cycle;
|
|
850
|
-
},
|
|
851
|
-
});
|
|
852
|
-
// =========================================================
|
|
853
|
-
// Initiatives
|
|
854
|
-
// =========================================================
|
|
855
|
-
listAction("initiative.list", "List initiatives.", "initiatives", "InitiativeFilter", INITIATIVE_FIELDS);
|
|
856
|
-
getAction("initiative.get", "Get an initiative by ID or slug.", "initiative", INITIATIVE_FIELDS);
|
|
857
|
-
rl.registerAction("initiative.create", {
|
|
858
|
-
description: "Create an initiative. Status: Planned | Active | Completed.",
|
|
859
|
-
inputSchema: {
|
|
860
|
-
name: { type: "string", required: true, description: "The name of the initiative" },
|
|
861
|
-
description: { type: "string", required: false, description: "The description of the initiative" },
|
|
862
|
-
content: { type: "string", required: false, description: "The initiative's content in markdown format" },
|
|
863
|
-
icon: { type: "string", required: false, description: "The initiative's icon" },
|
|
864
|
-
color: { type: "string", required: false, description: "The initiative's color (hex)" },
|
|
865
|
-
ownerId: { type: "string", required: false, description: "The owner of the initiative" },
|
|
866
|
-
status: { type: "string", required: false, description: "The initiative's status (InitiativeStatus: Planned | Active | Completed)" },
|
|
867
|
-
targetDate: { type: "string", required: false, description: "The estimated completion date of the initiative (TimelessDate, YYYY-MM-DD)" },
|
|
868
|
-
targetDateResolution: { type: "string", required: false, description: "The resolution of the initiative's estimated completion date (DateResolutionType)" },
|
|
869
|
-
sortOrder: { type: "number", required: false, description: "The sort order of the initiative within the workspace (Float)" },
|
|
870
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
871
|
-
},
|
|
872
|
-
async execute(input, ctx) {
|
|
873
|
-
const data = await gql(key(ctx), `mutation($input: InitiativeCreateInput!) { initiativeCreate(input: $input) { success initiative { ${INITIATIVE_FIELDS} } } }`, { input: input });
|
|
874
|
-
return data.initiativeCreate?.initiative;
|
|
875
|
-
},
|
|
876
|
-
});
|
|
877
|
-
rl.registerAction("initiative.update", {
|
|
878
|
-
description: "Update an initiative.",
|
|
879
|
-
inputSchema: {
|
|
880
|
-
id: { type: "string", required: true, description: "The identifier of the initiative to update" },
|
|
881
|
-
name: { type: "string", required: false, description: "The name of the initiative" },
|
|
882
|
-
description: { type: "string", required: false, description: "The description of the initiative" },
|
|
883
|
-
content: { type: "string", required: false, description: "The initiative's content in markdown format" },
|
|
884
|
-
icon: { type: "string", required: false, description: "The initiative's icon" },
|
|
885
|
-
color: { type: "string", required: false, description: "The initiative's color (hex)" },
|
|
886
|
-
ownerId: { type: "string", required: false, description: "The owner of the initiative" },
|
|
887
|
-
status: { type: "string", required: false, description: "The initiative's status (InitiativeStatus: Planned | Active | Completed)" },
|
|
888
|
-
targetDate: { type: "string", required: false, description: "The estimated completion date (TimelessDate, YYYY-MM-DD). Set to null to clear" },
|
|
889
|
-
targetDateResolution: { type: "string", required: false, description: "The resolution of the initiative's estimated completion date (DateResolutionType)" },
|
|
890
|
-
sortOrder: { type: "number", required: false, description: "The sort order of the initiative within the workspace (Float)" },
|
|
891
|
-
trashed: { type: "boolean", required: false, description: "Whether the initiative has been trashed. Set to true to trash, or null to restore" },
|
|
892
|
-
},
|
|
893
|
-
async execute(input, ctx) {
|
|
894
|
-
const { id, ...fields } = input;
|
|
895
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: InitiativeUpdateInput!) { initiativeUpdate(id: $id, input: $input) { success initiative { ${INITIATIVE_FIELDS} } } }`, { id, input: fields });
|
|
896
|
-
return data.initiativeUpdate?.initiative;
|
|
897
|
-
},
|
|
898
|
-
});
|
|
899
|
-
rl.registerAction("initiative.delete", {
|
|
900
|
-
description: "Trash an initiative.",
|
|
901
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the initiative to delete" } },
|
|
902
|
-
async execute(input, ctx) {
|
|
903
|
-
const data = await gql(key(ctx), `mutation($id: String!) { initiativeDelete(id: $id) { success } }`, { id: input.id });
|
|
904
|
-
return data.initiativeDelete;
|
|
905
|
-
},
|
|
906
|
-
});
|
|
907
|
-
rl.registerAction("initiative.addProject", {
|
|
908
|
-
description: "Associate a project with an initiative. A project can only appear once in an initiative hierarchy.",
|
|
909
|
-
inputSchema: {
|
|
910
|
-
initiativeId: { type: "string", required: true, description: "The identifier of the initiative" },
|
|
911
|
-
projectId: { type: "string", required: true, description: "The identifier of the project" },
|
|
912
|
-
sortOrder: { type: "number", required: false, description: "The sort order for the project within the initiative (Float)" },
|
|
913
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
914
|
-
},
|
|
915
|
-
async execute(input, ctx) {
|
|
916
|
-
const data = await gql(key(ctx), `mutation($input: InitiativeToProjectCreateInput!) { initiativeToProjectCreate(input: $input) { success initiativeToProject { id } } }`, { input: input });
|
|
917
|
-
return data.initiativeToProjectCreate;
|
|
918
|
-
},
|
|
919
|
-
});
|
|
920
|
-
rl.registerAction("initiative.removeProject", {
|
|
921
|
-
description: "Remove a project from an initiative. Pass the link id returned by initiative.addProject.",
|
|
922
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the initiativeToProject to delete" } },
|
|
923
|
-
async execute(input, ctx) {
|
|
924
|
-
const data = await gql(key(ctx), `mutation($id: String!) { initiativeToProjectDelete(id: $id) { success } }`, { id: input.id });
|
|
925
|
-
return data.initiativeToProjectDelete;
|
|
926
|
-
},
|
|
927
|
-
});
|
|
928
|
-
// =========================================================
|
|
929
|
-
// Teams
|
|
930
|
-
// =========================================================
|
|
931
|
-
listAction("team.list", "List teams whose issues you can access.", "teams", "TeamFilter", TEAM_FIELDS);
|
|
932
|
-
getAction("team.get", "Get a team by ID or key.", "team", TEAM_FIELDS);
|
|
933
|
-
rl.registerAction("team.create", {
|
|
934
|
-
description: "Create a team. Most settings have sensible defaults.",
|
|
935
|
-
inputSchema: {
|
|
936
|
-
name: { type: "string", required: true, description: "The name of the team" },
|
|
937
|
-
key: { type: "string", required: false, description: "The key of the team. If not given, the key will be generated based on the name" },
|
|
938
|
-
description: { type: "string", required: false, description: "The description of the team" },
|
|
939
|
-
icon: { type: "string", required: false, description: "The icon of the team" },
|
|
940
|
-
color: { type: "string", required: false, description: "The color of the team (hex)" },
|
|
941
|
-
private: { type: "boolean", required: false, description: "Whether the team is private" },
|
|
942
|
-
timezone: { type: "string", required: false, description: "The timezone of the team" },
|
|
943
|
-
cyclesEnabled: { type: "boolean", required: false, description: "Whether the team uses cycles" },
|
|
944
|
-
cycleDuration: { type: "number", required: false, description: "The duration of each cycle in weeks (Int)" },
|
|
945
|
-
cycleCooldownTime: { type: "number", required: false, description: "The cooldown time after each cycle in weeks (Int)" },
|
|
946
|
-
cycleStartDay: { type: "number", required: false, description: "The day of the week that a new cycle starts. 0=Sun..6=Sat (Float)" },
|
|
947
|
-
upcomingCycleCount: { type: "number", required: false, description: "How many upcoming cycles to create (Float)" },
|
|
948
|
-
issueEstimationType: { type: "string", required: false, description: "The issue estimation type: notUsed | exponential | fibonacci | linear | tShirt" },
|
|
949
|
-
triageEnabled: { type: "boolean", required: false, description: "Whether triage mode is enabled for the team" },
|
|
950
|
-
parentId: { type: "string", required: false, description: "The parent team ID (for sub-teams)" },
|
|
951
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
952
|
-
copySettingsFromTeamId: { type: "string", required: false, description: "The team id to copy settings from, if any" },
|
|
953
|
-
},
|
|
954
|
-
async execute(input, ctx) {
|
|
955
|
-
const { copySettingsFromTeamId, ...fields } = input;
|
|
956
|
-
const data = await gql(key(ctx), `mutation($input: TeamCreateInput!, $copySettingsFromTeamId: String) { teamCreate(input: $input, copySettingsFromTeamId: $copySettingsFromTeamId) { success team { ${TEAM_FIELDS} } } }`, { input: fields, copySettingsFromTeamId: copySettingsFromTeamId ?? null });
|
|
957
|
-
return data.teamCreate?.team;
|
|
958
|
-
},
|
|
959
|
-
});
|
|
960
|
-
rl.registerAction("team.update", {
|
|
961
|
-
description: "Update a team. Requires team owner or workspace admin permissions.",
|
|
962
|
-
inputSchema: {
|
|
963
|
-
id: { type: "string", required: true, description: "The identifier of the team to update" },
|
|
964
|
-
name: { type: "string", required: false, description: "The name of the team" },
|
|
965
|
-
key: { type: "string", required: false, description: "The key of the team" },
|
|
966
|
-
description: { type: "string", required: false, description: "The description of the team" },
|
|
967
|
-
icon: { type: "string", required: false, description: "The icon of the team" },
|
|
968
|
-
color: { type: "string", required: false, description: "The color of the team (hex)" },
|
|
969
|
-
private: { type: "boolean", required: false, description: "Whether the team is private" },
|
|
970
|
-
timezone: { type: "string", required: false, description: "The timezone of the team" },
|
|
971
|
-
cyclesEnabled: { type: "boolean", required: false, description: "Whether the team uses cycles" },
|
|
972
|
-
cycleDuration: { type: "number", required: false, description: "The duration of each cycle in weeks (Int)" },
|
|
973
|
-
cycleCooldownTime: { type: "number", required: false, description: "The cooldown time after each cycle in weeks (Int)" },
|
|
974
|
-
cycleStartDay: { type: "number", required: false, description: "The day of the week that a new cycle starts. 0=Sun..6=Sat (Float)" },
|
|
975
|
-
upcomingCycleCount: { type: "number", required: false, description: "How many upcoming cycles to create (Float)" },
|
|
976
|
-
issueEstimationType: { type: "string", required: false, description: "The issue estimation type: notUsed | exponential | fibonacci | linear | tShirt" },
|
|
977
|
-
triageEnabled: { type: "boolean", required: false, description: "Whether triage mode is enabled for the team" },
|
|
978
|
-
},
|
|
979
|
-
async execute(input, ctx) {
|
|
980
|
-
const { id, ...fields } = input;
|
|
981
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: TeamUpdateInput!) { teamUpdate(id: $id, input: $input) { success team { ${TEAM_FIELDS} } } }`, { id, input: fields });
|
|
982
|
-
return data.teamUpdate?.team;
|
|
983
|
-
},
|
|
984
|
-
});
|
|
985
|
-
rl.registerAction("team.members", {
|
|
986
|
-
description: "List members of a team.",
|
|
987
|
-
inputSchema: {
|
|
988
|
-
teamId: { type: "string", required: true, description: "The identifier of the team" },
|
|
989
|
-
limit: { type: "number", required: false, description: "Max members to return (default 50)" },
|
|
990
|
-
},
|
|
991
|
-
async execute(input, ctx) {
|
|
992
|
-
const { teamId, limit } = input;
|
|
993
|
-
const data = await gql(key(ctx), `query($id: String!, $first: Int) {
|
|
994
|
-
team(id: $id) { members(first: $first) { nodes { ${USER_FIELDS} } } }
|
|
995
|
-
}`, { id: teamId, first: limit ?? 50 });
|
|
996
|
-
return data.team?.members?.nodes;
|
|
997
|
-
},
|
|
998
|
-
});
|
|
999
|
-
// =========================================================
|
|
1000
|
-
// Users
|
|
1001
|
-
// =========================================================
|
|
1002
|
-
listAction("user.list", "List users in the workspace.", "users", "UserFilter", USER_FIELDS);
|
|
1003
|
-
getAction("user.get", "Get a user by ID. Use 'me' to reference the authenticated user.", "user", USER_FIELDS);
|
|
1004
|
-
rl.registerAction("user.me", {
|
|
1005
|
-
description: "Get the authenticated user.",
|
|
1006
|
-
inputSchema: {},
|
|
1007
|
-
async execute(_input, ctx) {
|
|
1008
|
-
const data = await gql(key(ctx), `query { viewer { ${USER_FIELDS} } }`);
|
|
1009
|
-
return data.viewer;
|
|
1010
|
-
},
|
|
1011
|
-
});
|
|
1012
|
-
rl.registerAction("user.update", {
|
|
1013
|
-
description: "Update a user. Use id='me' to update the authenticated user.",
|
|
1014
|
-
inputSchema: {
|
|
1015
|
-
id: { type: "string", required: true, description: "The identifier of the user to update. Use 'me' to reference the currently authenticated user" },
|
|
1016
|
-
name: { type: "string", required: false, description: "The name of the user" },
|
|
1017
|
-
displayName: { type: "string", required: false, description: "The display name of the user" },
|
|
1018
|
-
description: { type: "string", required: false, description: "The user description or short bio" },
|
|
1019
|
-
avatarUrl: { type: "string", required: false, description: "The avatar image URL of the user" },
|
|
1020
|
-
timezone: { type: "string", required: false, description: "The local timezone of the user" },
|
|
1021
|
-
title: { type: "string", required: false, description: "The user's job title" },
|
|
1022
|
-
statusEmoji: { type: "string", required: false, description: "The emoji part of the user status" },
|
|
1023
|
-
statusLabel: { type: "string", required: false, description: "The label part of the user status" },
|
|
1024
|
-
statusUntilAt: { type: "string", required: false, description: "When the user status should be cleared (DateTime)" },
|
|
1025
|
-
},
|
|
1026
|
-
async execute(input, ctx) {
|
|
1027
|
-
const { id, ...fields } = input;
|
|
1028
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: UserUpdateInput!) { userUpdate(id: $id, input: $input) { success user { ${USER_FIELDS} } } }`, { id, input: fields });
|
|
1029
|
-
return data.userUpdate?.user;
|
|
1030
|
-
},
|
|
1031
|
-
});
|
|
1032
|
-
// =========================================================
|
|
1033
|
-
// Attachments
|
|
1034
|
-
// =========================================================
|
|
1035
|
-
listAction("attachment.list", "List issue attachments.", "attachments", "AttachmentFilter", ATTACHMENT_FIELDS);
|
|
1036
|
-
getAction("attachment.get", "Get an attachment by ID.", "attachment", ATTACHMENT_FIELDS);
|
|
1037
|
-
rl.registerAction("attachment.create", {
|
|
1038
|
-
description: "Create an attachment on an issue.",
|
|
1039
|
-
inputSchema: {
|
|
1040
|
-
issueId: { type: "string", required: true, description: "The issue to associate the attachment with. UUID or issue identifier (e.g., 'LIN-123')" },
|
|
1041
|
-
title: { type: "string", required: true, description: "The attachment title" },
|
|
1042
|
-
url: { type: "string", required: true, description: "Attachment location, also used as a unique identifier. Re-creating with the same url updates the existing record" },
|
|
1043
|
-
subtitle: { type: "string", required: false, description: "The attachment subtitle" },
|
|
1044
|
-
iconUrl: { type: "string", required: false, description: "An icon url to display with the attachment (jpg or png, max 1MB, ideally 20x20px)" },
|
|
1045
|
-
commentBody: { type: "string", required: false, description: "Create a linked comment with markdown body" },
|
|
1046
|
-
groupBySource: { type: "boolean", required: false, description: "Whether attachments for the same source application should be grouped in the Linear UI" },
|
|
1047
|
-
metadata: { type: "object", required: false, description: "Attachment metadata object with string and number values (JSONObject)" },
|
|
1048
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
1049
|
-
},
|
|
1050
|
-
async execute(input, ctx) {
|
|
1051
|
-
const data = await gql(key(ctx), `mutation($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { ${ATTACHMENT_FIELDS} } } }`, { input: input });
|
|
1052
|
-
return data.attachmentCreate?.attachment;
|
|
1053
|
-
},
|
|
1054
|
-
});
|
|
1055
|
-
rl.registerAction("attachment.update", {
|
|
1056
|
-
description: "Update an attachment. title is required.",
|
|
1057
|
-
inputSchema: {
|
|
1058
|
-
id: { type: "string", required: true, description: "The identifier of the attachment to update" },
|
|
1059
|
-
title: { type: "string", required: true, description: "The attachment title" },
|
|
1060
|
-
subtitle: { type: "string", required: false, description: "The attachment subtitle" },
|
|
1061
|
-
iconUrl: { type: "string", required: false, description: "An icon url to display with the attachment" },
|
|
1062
|
-
metadata: { type: "object", required: false, description: "Attachment metadata object with string and number values (JSONObject)" },
|
|
1063
|
-
},
|
|
1064
|
-
async execute(input, ctx) {
|
|
1065
|
-
const { id, ...fields } = input;
|
|
1066
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: AttachmentUpdateInput!) { attachmentUpdate(id: $id, input: $input) { success attachment { ${ATTACHMENT_FIELDS} } } }`, { id, input: fields });
|
|
1067
|
-
return data.attachmentUpdate?.attachment;
|
|
1068
|
-
},
|
|
1069
|
-
});
|
|
1070
|
-
rl.registerAction("attachment.linkURL", {
|
|
1071
|
-
description: "Link any URL to an issue. If a workspace integration matches the URL (Zendesk, GitHub, Slack, etc.) a rich attachment is created; otherwise a basic one.",
|
|
1072
|
-
inputSchema: {
|
|
1073
|
-
issueId: { type: "string", required: true, description: "The issue for which to link the url. UUID or issue identifier (e.g., 'LIN-123')" },
|
|
1074
|
-
url: { type: "string", required: true, description: "The url to link" },
|
|
1075
|
-
title: { type: "string", required: false, description: "The title to use for the attachment" },
|
|
1076
|
-
id: { type: "string", required: false, description: "The id for the attachment (optional UUID override)" },
|
|
1077
|
-
},
|
|
1078
|
-
async execute(input, ctx) {
|
|
1079
|
-
const { issueId, url, title, id } = input;
|
|
1080
|
-
const data = await gql(key(ctx), `mutation($issueId: String!, $url: String!, $title: String, $id: String) {
|
|
1081
|
-
attachmentLinkURL(issueId: $issueId, url: $url, title: $title, id: $id) { success attachment { ${ATTACHMENT_FIELDS} } }
|
|
1082
|
-
}`, { issueId, url, title: title ?? null, id: id ?? null });
|
|
1083
|
-
return data.attachmentLinkURL?.attachment;
|
|
1084
|
-
},
|
|
1085
|
-
});
|
|
1086
|
-
rl.registerAction("attachment.delete", {
|
|
1087
|
-
description: "Delete an attachment.",
|
|
1088
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the attachment to delete" } },
|
|
1089
|
-
async execute(input, ctx) {
|
|
1090
|
-
const data = await gql(key(ctx), `mutation($id: String!) { attachmentDelete(id: $id) { success } }`, { id: input.id });
|
|
1091
|
-
return data.attachmentDelete;
|
|
1092
|
-
},
|
|
1093
|
-
});
|
|
1094
|
-
// =========================================================
|
|
1095
|
-
// Organization
|
|
1096
|
-
// =========================================================
|
|
1097
|
-
rl.registerAction("org.get", {
|
|
1098
|
-
description: "Get the authenticated workspace.",
|
|
1099
|
-
inputSchema: {},
|
|
1100
|
-
async execute(_input, ctx) {
|
|
1101
|
-
const data = await gql(key(ctx), `query { organization { ${ORG_FIELDS} } }`);
|
|
1102
|
-
return data.organization;
|
|
1103
|
-
},
|
|
1104
|
-
});
|
|
1105
|
-
// =========================================================
|
|
1106
|
-
// Webhooks
|
|
1107
|
-
// =========================================================
|
|
1108
|
-
listAction("webhook.list", "List webhooks for the current workspace.", "webhooks", null, WEBHOOK_FIELDS);
|
|
1109
|
-
getAction("webhook.get", "Get a webhook by ID.", "webhook", WEBHOOK_FIELDS);
|
|
1110
|
-
rl.registerAction("webhook.create", {
|
|
1111
|
-
description: "Create a webhook. resourceTypes example: ['Issue','Comment','Project'].",
|
|
1112
|
-
inputSchema: {
|
|
1113
|
-
url: { type: "string", required: true, description: "The URL that will be called on data changes" },
|
|
1114
|
-
resourceTypes: { type: "array", required: true, description: "List of resources the webhook should subscribe to (e.g. ['Issue','Comment'])" },
|
|
1115
|
-
label: { type: "string", required: false, description: "Label for the webhook" },
|
|
1116
|
-
teamId: { type: "string", required: false, description: "The identifier or key of the team associated with the webhook. Omit and set allPublicTeams=true for workspace-wide" },
|
|
1117
|
-
allPublicTeams: { type: "boolean", required: false, description: "Whether this webhook is enabled for all public teams" },
|
|
1118
|
-
enabled: { type: "boolean", required: false, description: "Whether this webhook is enabled (default true)" },
|
|
1119
|
-
secret: { type: "string", required: false, description: "A secret token used to sign the webhook payload" },
|
|
1120
|
-
id: { type: "string", required: false, description: "The identifier in UUID v4 format. If none is provided, the backend will generate one" },
|
|
1121
|
-
},
|
|
1122
|
-
async execute(input, ctx) {
|
|
1123
|
-
const data = await gql(key(ctx), `mutation($input: WebhookCreateInput!) { webhookCreate(input: $input) { success webhook { ${WEBHOOK_FIELDS} } } }`, { input: input });
|
|
1124
|
-
return data.webhookCreate?.webhook;
|
|
1125
|
-
},
|
|
1126
|
-
});
|
|
1127
|
-
rl.registerAction("webhook.update", {
|
|
1128
|
-
description: "Update a webhook. teamId and allPublicTeams cannot be changed after creation.",
|
|
1129
|
-
inputSchema: {
|
|
1130
|
-
id: { type: "string", required: true, description: "The identifier of the webhook to update" },
|
|
1131
|
-
url: { type: "string", required: false, description: "The URL that will be called on data changes" },
|
|
1132
|
-
resourceTypes: { type: "array", required: false, description: "List of resources the webhook should subscribe to" },
|
|
1133
|
-
label: { type: "string", required: false, description: "Label for the webhook" },
|
|
1134
|
-
enabled: { type: "boolean", required: false, description: "Whether this webhook is enabled" },
|
|
1135
|
-
secret: { type: "string", required: false, description: "A secret token used to sign the webhook payload" },
|
|
1136
|
-
},
|
|
1137
|
-
async execute(input, ctx) {
|
|
1138
|
-
const { id, ...fields } = input;
|
|
1139
|
-
const data = await gql(key(ctx), `mutation($id: String!, $input: WebhookUpdateInput!) { webhookUpdate(id: $id, input: $input) { success webhook { ${WEBHOOK_FIELDS} } } }`, { id, input: fields });
|
|
1140
|
-
return data.webhookUpdate?.webhook;
|
|
1141
|
-
},
|
|
1142
|
-
});
|
|
1143
|
-
rl.registerAction("webhook.delete", {
|
|
1144
|
-
description: "Delete a webhook.",
|
|
1145
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the webhook to delete" } },
|
|
1146
|
-
async execute(input, ctx) {
|
|
1147
|
-
const data = await gql(key(ctx), `mutation($id: String!) { webhookDelete(id: $id) { success } }`, { id: input.id });
|
|
1148
|
-
return data.webhookDelete;
|
|
1149
|
-
},
|
|
1150
|
-
});
|
|
1151
|
-
rl.registerAction("webhook.rotateSecret", {
|
|
1152
|
-
description: "Rotate a webhook's signing secret. Returns the new secret.",
|
|
1153
|
-
inputSchema: { id: { type: "string", required: true, description: "The identifier of the webhook to rotate the secret for" } },
|
|
1154
|
-
async execute(input, ctx) {
|
|
1155
|
-
const data = await gql(key(ctx), `mutation($id: String!) { webhookRotateSecret(id: $id) { success secret } }`, { id: input.id });
|
|
1156
|
-
return data.webhookRotateSecret;
|
|
1157
|
-
},
|
|
1158
|
-
});
|
|
22
|
+
}),
|
|
23
|
+
scopeLabelIds: t.Optional(t.String({
|
|
24
|
+
description: "Comma-separated Linear issue label IDs. When set, issue/comment/attachment access is restricted to issues with one of these labels.",
|
|
25
|
+
env: "LINEAR_SCOPE_LABEL_IDS",
|
|
26
|
+
})),
|
|
27
|
+
}));
|
|
28
|
+
registerIssueActions(rl);
|
|
29
|
+
registerCommentActions(rl);
|
|
30
|
+
registerStateActions(rl);
|
|
31
|
+
registerLabelActions(rl);
|
|
32
|
+
registerProjectActions(rl);
|
|
33
|
+
registerViewActions(rl);
|
|
34
|
+
registerCycleActions(rl);
|
|
35
|
+
registerInitiativeActions(rl);
|
|
36
|
+
registerTeamActions(rl);
|
|
37
|
+
registerUserActions(rl);
|
|
38
|
+
registerAttachmentActions(rl);
|
|
39
|
+
registerOrganizationActions(rl);
|
|
40
|
+
registerWebhookActions(rl);
|
|
1159
41
|
}
|