@credal/actions 0.2.74 → 0.2.76
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/actions/autogen/templates.js +198 -14
- package/dist/actions/autogen/types.d.ts +492 -64
- package/dist/actions/autogen/types.js +56 -2
- package/dist/actions/providers/google-oauth/utils/decodeMessage.d.ts +12 -12
- package/dist/actions/providers/google-oauth/utils/decodeMessage.js +47 -31
- package/dist/actions/providers/googlemail/searchGmailMessages.js +45 -26
- package/dist/actions/providers/jira/getJiraIssuesByQuery.js +45 -1
- package/package.json +4 -1
|
@@ -425,7 +425,34 @@ export const jiraGetJiraIssuesByQueryParamsSchema = z.object({
|
|
|
425
425
|
});
|
|
426
426
|
export const jiraGetJiraIssuesByQueryOutputSchema = z.object({
|
|
427
427
|
success: z.boolean().describe("Whether the records were successfully retrieved"),
|
|
428
|
-
records: z
|
|
428
|
+
records: z
|
|
429
|
+
.object({
|
|
430
|
+
issues: z
|
|
431
|
+
.array(z.object({
|
|
432
|
+
id: z.string().describe("Internal Jira issue ID"),
|
|
433
|
+
key: z.string().describe("Human-readable issue key (e.g. SSPR-123)"),
|
|
434
|
+
summary: z.string().describe("Summary of the issue"),
|
|
435
|
+
description: z.string().describe("Plain text description"),
|
|
436
|
+
project: z.object({ id: z.string().optional(), key: z.string().optional(), name: z.string().optional() }),
|
|
437
|
+
issueType: z.object({ id: z.string().optional(), name: z.string().optional() }),
|
|
438
|
+
status: z.object({
|
|
439
|
+
id: z.string().optional(),
|
|
440
|
+
name: z.string().optional(),
|
|
441
|
+
category: z.string().optional(),
|
|
442
|
+
}),
|
|
443
|
+
assignee: z.string().nullable().describe("Email of the assignee, if any").optional(),
|
|
444
|
+
reporter: z.string().nullable().describe("Email of the reporter, if any").optional(),
|
|
445
|
+
creator: z.string().nullable().describe("Email of the creator, if any").optional(),
|
|
446
|
+
created: z.string().datetime({ offset: true }),
|
|
447
|
+
updated: z.string().datetime({ offset: true }),
|
|
448
|
+
resolution: z.string().nullable().optional(),
|
|
449
|
+
dueDate: z.string().date().nullable().optional(),
|
|
450
|
+
}))
|
|
451
|
+
.describe("The retrieved Jira issues")
|
|
452
|
+
.optional(),
|
|
453
|
+
})
|
|
454
|
+
.describe("The result object containing issues")
|
|
455
|
+
.optional(),
|
|
429
456
|
error: z.string().describe("The error that occurred if the records were not successfully retrieved").optional(),
|
|
430
457
|
});
|
|
431
458
|
export const jiraOrgAssignJiraTicketParamsSchema = z.object({
|
|
@@ -573,7 +600,34 @@ export const jiraOrgGetJiraIssuesByQueryParamsSchema = z.object({
|
|
|
573
600
|
});
|
|
574
601
|
export const jiraOrgGetJiraIssuesByQueryOutputSchema = z.object({
|
|
575
602
|
success: z.boolean().describe("Whether the records were successfully retrieved"),
|
|
576
|
-
records: z
|
|
603
|
+
records: z
|
|
604
|
+
.object({
|
|
605
|
+
issues: z
|
|
606
|
+
.array(z.object({
|
|
607
|
+
id: z.string().describe("Internal Jira issue ID"),
|
|
608
|
+
key: z.string().describe("Human-readable issue key (e.g. SSPR-123)"),
|
|
609
|
+
summary: z.string().describe("Summary of the issue"),
|
|
610
|
+
description: z.string().describe("Plain text description"),
|
|
611
|
+
project: z.object({ id: z.string().optional(), key: z.string().optional(), name: z.string().optional() }),
|
|
612
|
+
issueType: z.object({ id: z.string().optional(), name: z.string().optional() }),
|
|
613
|
+
status: z.object({
|
|
614
|
+
id: z.string().optional(),
|
|
615
|
+
name: z.string().optional(),
|
|
616
|
+
category: z.string().optional(),
|
|
617
|
+
}),
|
|
618
|
+
assignee: z.string().nullable().describe("Email of the assignee, if any").optional(),
|
|
619
|
+
reporter: z.string().nullable().describe("Email of the reporter, if any").optional(),
|
|
620
|
+
creator: z.string().nullable().describe("Email of the creator, if any").optional(),
|
|
621
|
+
created: z.string().datetime({ offset: true }),
|
|
622
|
+
updated: z.string().datetime({ offset: true }),
|
|
623
|
+
resolution: z.string().nullable().optional(),
|
|
624
|
+
dueDate: z.string().date().nullable().optional(),
|
|
625
|
+
}))
|
|
626
|
+
.describe("The retrieved Jira issues")
|
|
627
|
+
.optional(),
|
|
628
|
+
})
|
|
629
|
+
.describe("The result object containing issues")
|
|
630
|
+
.optional(),
|
|
577
631
|
error: z.string().describe("The error that occurred if the records were not successfully retrieved").optional(),
|
|
578
632
|
});
|
|
579
633
|
export const kandjiGetFVRecoveryKeyForDeviceParamsSchema = z.object({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
interface GmailMessage {
|
|
2
2
|
payload: {
|
|
3
3
|
mimeType: string;
|
|
4
4
|
body?: {
|
|
@@ -6,22 +6,22 @@ export interface GmailMessage {
|
|
|
6
6
|
size: number;
|
|
7
7
|
};
|
|
8
8
|
parts?: Array<{
|
|
9
|
-
partId: string;
|
|
10
9
|
mimeType: string;
|
|
11
|
-
body
|
|
10
|
+
body?: {
|
|
12
11
|
data?: string;
|
|
13
12
|
size: number;
|
|
14
13
|
};
|
|
15
|
-
parts?:
|
|
16
|
-
partId: string;
|
|
17
|
-
mimeType: string;
|
|
18
|
-
body: {
|
|
19
|
-
data?: string;
|
|
20
|
-
size: number;
|
|
21
|
-
};
|
|
22
|
-
}>;
|
|
14
|
+
parts?: GmailMessagePart[];
|
|
23
15
|
}>;
|
|
24
16
|
};
|
|
25
17
|
}
|
|
26
|
-
|
|
18
|
+
interface GmailMessagePart {
|
|
19
|
+
mimeType: string;
|
|
20
|
+
body?: {
|
|
21
|
+
data?: string;
|
|
22
|
+
size: number;
|
|
23
|
+
};
|
|
24
|
+
parts?: GmailMessagePart[];
|
|
25
|
+
}
|
|
27
26
|
export declare function getEmailContent(message: GmailMessage): string | null;
|
|
27
|
+
export {};
|
|
@@ -1,37 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
// Gmail API uses URL-safe base64 encoding
|
|
3
|
-
const standardBase64 = base64String.replace(/-/g, "+").replace(/_/g, "/");
|
|
4
|
-
// Add padding if needed
|
|
5
|
-
const padded = standardBase64.padEnd(standardBase64.length + ((4 - (standardBase64.length % 4)) % 4), "=");
|
|
6
|
-
// Only works for Node.js environment
|
|
7
|
-
return Buffer.from(padded, "base64").toString("utf-8");
|
|
8
|
-
}
|
|
1
|
+
import { convert } from "html-to-text";
|
|
9
2
|
export function getEmailContent(message) {
|
|
10
3
|
var _a;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
4
|
+
const { mimeType, body, parts } = message.payload;
|
|
5
|
+
if (mimeType === "text/plain" && (body === null || body === void 0 ? void 0 : body.data)) {
|
|
6
|
+
return tryDecode(body.data);
|
|
7
|
+
}
|
|
8
|
+
if (mimeType === "text/html" && (body === null || body === void 0 ? void 0 : body.data)) {
|
|
9
|
+
const htmlRaw = tryDecode(body.data);
|
|
10
|
+
if (htmlRaw)
|
|
11
|
+
return convert(htmlRaw, { wordwrap: false });
|
|
12
|
+
}
|
|
13
|
+
const { plainText, htmlText } = searchParts(parts);
|
|
14
|
+
return (_a = plainText !== null && plainText !== void 0 ? plainText : htmlText) !== null && _a !== void 0 ? _a : null;
|
|
15
|
+
}
|
|
16
|
+
function tryDecode(data) {
|
|
17
|
+
if (!data)
|
|
18
|
+
return null;
|
|
19
|
+
try {
|
|
20
|
+
const base64 = data.replace(/-/g, "+").replace(/_/g, "/");
|
|
21
|
+
const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), "=");
|
|
22
|
+
return Buffer.from(padded, "base64").toString("utf-8");
|
|
26
23
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return decodeGmailBase64(message.payload.body.data);
|
|
24
|
+
catch (_a) {
|
|
25
|
+
return null;
|
|
30
26
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
}
|
|
28
|
+
function searchParts(parts) {
|
|
29
|
+
let plainText = null;
|
|
30
|
+
let htmlText = null;
|
|
31
|
+
if (!parts)
|
|
32
|
+
return { plainText, htmlText };
|
|
33
|
+
for (const part of parts) {
|
|
34
|
+
const { mimeType, body, parts: subParts } = part;
|
|
35
|
+
if (mimeType === "text/plain" && !plainText) {
|
|
36
|
+
plainText = tryDecode(body === null || body === void 0 ? void 0 : body.data);
|
|
37
|
+
}
|
|
38
|
+
else if (mimeType === "text/html" && !htmlText) {
|
|
39
|
+
const htmlRaw = tryDecode(body === null || body === void 0 ? void 0 : body.data);
|
|
40
|
+
if (htmlRaw) {
|
|
41
|
+
htmlText = convert(htmlRaw, { wordwrap: false });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (subParts === null || subParts === void 0 ? void 0 : subParts.length) {
|
|
45
|
+
const result = searchParts(subParts);
|
|
46
|
+
if (!plainText && result.plainText)
|
|
47
|
+
plainText = result.plainText;
|
|
48
|
+
if (!htmlText && result.htmlText)
|
|
49
|
+
htmlText = result.htmlText;
|
|
50
|
+
}
|
|
34
51
|
}
|
|
35
|
-
|
|
36
|
-
return textContent;
|
|
52
|
+
return { plainText, htmlText };
|
|
37
53
|
}
|
|
@@ -7,68 +7,87 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import { RateLimiter } from "limiter";
|
|
10
11
|
import { axiosClient } from "../../util/axiosClient.js";
|
|
11
12
|
import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
|
|
12
13
|
import { getEmailContent } from "../google-oauth/utils/decodeMessage.js";
|
|
14
|
+
const MAX_EMAIL_CONTENTS_FETCHED = 50;
|
|
15
|
+
const DEFAULT_EMAIL_CONTENTS_FETCHED = 25;
|
|
16
|
+
const MAX_RESULTS_PER_REQUEST = 100;
|
|
17
|
+
const MAX_EMAILS_FETCHED_CONCURRENTLY = 5;
|
|
18
|
+
const limiter = new RateLimiter({ tokensPerInterval: MAX_EMAILS_FETCHED_CONCURRENTLY, interval: "second" });
|
|
19
|
+
function cleanAndTruncateEmail(text, maxLength = 2000) {
|
|
20
|
+
if (!text)
|
|
21
|
+
return "";
|
|
22
|
+
// Remove quoted replies (naive)
|
|
23
|
+
text = text.replace(/^>.*$/gm, "");
|
|
24
|
+
// Remove signatures
|
|
25
|
+
const signatureMarkers = ["\nBest", "\nRegards", "\nThanks", "\nSincerely"];
|
|
26
|
+
for (const marker of signatureMarkers) {
|
|
27
|
+
const index = text.indexOf(marker);
|
|
28
|
+
if (index !== -1) {
|
|
29
|
+
text = text.slice(0, index).trim();
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Normalize whitespace
|
|
34
|
+
text = text
|
|
35
|
+
.replace(/\r\n|\r/g, "\n")
|
|
36
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
37
|
+
.trim();
|
|
38
|
+
return text.slice(0, maxLength).trim();
|
|
39
|
+
}
|
|
13
40
|
const searchGmailMessages = (_a) => __awaiter(void 0, [_a], void 0, function* ({ params, authParams, }) {
|
|
14
41
|
if (!authParams.authToken) {
|
|
15
42
|
return { success: false, error: MISSING_AUTH_TOKEN, messages: [] };
|
|
16
43
|
}
|
|
17
44
|
const { query, maxResults } = params;
|
|
45
|
+
const max = Math.min(maxResults !== null && maxResults !== void 0 ? maxResults : DEFAULT_EMAIL_CONTENTS_FETCHED, MAX_EMAIL_CONTENTS_FETCHED);
|
|
18
46
|
const allMessages = [];
|
|
19
|
-
const max = maxResults !== null && maxResults !== void 0 ? maxResults : 100;
|
|
20
47
|
const errorMessages = [];
|
|
21
|
-
let pageToken
|
|
48
|
+
let pageToken;
|
|
22
49
|
let fetched = 0;
|
|
23
50
|
try {
|
|
24
51
|
while (fetched < max) {
|
|
25
52
|
const url = `https://gmail.googleapis.com/gmail/v1/users/me/messages?q=${encodeURIComponent(query)}` +
|
|
26
53
|
(pageToken ? `&pageToken=${encodeURIComponent(pageToken)}` : "") +
|
|
27
|
-
`&maxResults=${Math.min(
|
|
54
|
+
`&maxResults=${Math.min(MAX_RESULTS_PER_REQUEST, max - fetched)}`;
|
|
28
55
|
const listRes = yield axiosClient.get(url, {
|
|
29
|
-
headers: {
|
|
30
|
-
Authorization: `Bearer ${authParams.authToken}`,
|
|
31
|
-
},
|
|
56
|
+
headers: { Authorization: `Bearer ${authParams.authToken}` },
|
|
32
57
|
});
|
|
33
58
|
const { messages: messageList = [], nextPageToken } = listRes.data;
|
|
34
59
|
if (!Array.isArray(messageList) || messageList.length === 0)
|
|
35
60
|
break;
|
|
36
|
-
const
|
|
37
|
-
const batch = messageList.slice(0, remaining);
|
|
61
|
+
const batch = messageList.slice(0, max - allMessages.length);
|
|
38
62
|
const results = yield Promise.all(batch.map((msg) => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
63
|
try {
|
|
64
|
+
yield limiter.removeTokens(1);
|
|
40
65
|
const msgRes = yield axiosClient.get(`https://gmail.googleapis.com/gmail/v1/users/me/messages/${msg.id}?format=full`, {
|
|
41
|
-
headers: {
|
|
42
|
-
|
|
43
|
-
},
|
|
66
|
+
headers: { Authorization: `Bearer ${authParams.authToken}` },
|
|
67
|
+
validateStatus: () => true,
|
|
44
68
|
});
|
|
45
69
|
const { id, threadId, snippet, labelIds, internalDate } = msgRes.data;
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
threadId,
|
|
50
|
-
snippet,
|
|
51
|
-
labelIds,
|
|
52
|
-
internalDate,
|
|
53
|
-
emailBody,
|
|
54
|
-
};
|
|
70
|
+
const rawBody = getEmailContent(msgRes.data) || "";
|
|
71
|
+
const emailBody = cleanAndTruncateEmail(rawBody);
|
|
72
|
+
return { id, threadId, snippet, labelIds, internalDate, emailBody };
|
|
55
73
|
}
|
|
56
74
|
catch (err) {
|
|
57
|
-
|
|
75
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to fetch message details";
|
|
76
|
+
errorMessages.push(errorMessage);
|
|
58
77
|
return {
|
|
59
78
|
id: msg.id,
|
|
60
79
|
threadId: "",
|
|
61
80
|
snippet: "",
|
|
62
81
|
labelIds: [],
|
|
63
82
|
internalDate: "",
|
|
64
|
-
|
|
65
|
-
error:
|
|
83
|
+
emailBody: "",
|
|
84
|
+
error: errorMessage,
|
|
66
85
|
};
|
|
67
86
|
}
|
|
68
87
|
})));
|
|
69
88
|
allMessages.push(...results);
|
|
70
89
|
fetched = allMessages.length;
|
|
71
|
-
if (!nextPageToken ||
|
|
90
|
+
if (!nextPageToken || fetched >= max)
|
|
72
91
|
break;
|
|
73
92
|
pageToken = nextPageToken;
|
|
74
93
|
}
|
|
@@ -78,10 +97,10 @@ const searchGmailMessages = (_a) => __awaiter(void 0, [_a], void 0, function* ({
|
|
|
78
97
|
error: errorMessages.join("; "),
|
|
79
98
|
};
|
|
80
99
|
}
|
|
81
|
-
catch (
|
|
100
|
+
catch (err) {
|
|
82
101
|
return {
|
|
83
102
|
success: false,
|
|
84
|
-
error:
|
|
103
|
+
error: err instanceof Error ? err.message : "Unknown error searching Gmail",
|
|
85
104
|
messages: [],
|
|
86
105
|
};
|
|
87
106
|
}
|
|
@@ -48,7 +48,38 @@ const getJiraIssuesByQuery = (_a) => __awaiter(void 0, [_a], void 0, function* (
|
|
|
48
48
|
});
|
|
49
49
|
return {
|
|
50
50
|
success: true,
|
|
51
|
-
records:
|
|
51
|
+
records: {
|
|
52
|
+
issues: response.data.issues.map(issue => {
|
|
53
|
+
var _a, _b, _c, _d;
|
|
54
|
+
return ({
|
|
55
|
+
id: issue.id,
|
|
56
|
+
key: issue.key,
|
|
57
|
+
summary: issue.fields.summary,
|
|
58
|
+
description: extractPlainText(issue.fields.description),
|
|
59
|
+
project: {
|
|
60
|
+
id: issue.fields.project.id,
|
|
61
|
+
key: issue.fields.project.key,
|
|
62
|
+
name: issue.fields.project.name,
|
|
63
|
+
},
|
|
64
|
+
issueType: {
|
|
65
|
+
id: issue.fields.issuetype.id,
|
|
66
|
+
name: issue.fields.issuetype.name,
|
|
67
|
+
},
|
|
68
|
+
status: {
|
|
69
|
+
id: issue.fields.status.id,
|
|
70
|
+
name: issue.fields.status.name,
|
|
71
|
+
category: issue.fields.status.statusCategory.name,
|
|
72
|
+
},
|
|
73
|
+
assignee: ((_a = issue.fields.assignee) === null || _a === void 0 ? void 0 : _a.emailAddress) || null,
|
|
74
|
+
reporter: ((_b = issue.fields.reporter) === null || _b === void 0 ? void 0 : _b.emailAddress) || null,
|
|
75
|
+
creator: ((_c = issue.fields.creator) === null || _c === void 0 ? void 0 : _c.emailAddress) || null,
|
|
76
|
+
created: issue.fields.created,
|
|
77
|
+
updated: issue.fields.updated,
|
|
78
|
+
resolution: ((_d = issue.fields.resolution) === null || _d === void 0 ? void 0 : _d.name) || null,
|
|
79
|
+
dueDate: issue.fields.duedate || null,
|
|
80
|
+
});
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
52
83
|
};
|
|
53
84
|
}
|
|
54
85
|
catch (error) {
|
|
@@ -63,4 +94,17 @@ const getJiraIssuesByQuery = (_a) => __awaiter(void 0, [_a], void 0, function* (
|
|
|
63
94
|
};
|
|
64
95
|
}
|
|
65
96
|
});
|
|
97
|
+
function extractPlainText(adf) {
|
|
98
|
+
if (!adf || adf.type !== "doc" || !Array.isArray(adf.content))
|
|
99
|
+
return "";
|
|
100
|
+
return adf.content
|
|
101
|
+
.map(block => {
|
|
102
|
+
if (block.type === "paragraph" && Array.isArray(block.content)) {
|
|
103
|
+
return block.content.map(inline => { var _a; return (_a = inline.text) !== null && _a !== void 0 ? _a : ""; }).join("");
|
|
104
|
+
}
|
|
105
|
+
return "";
|
|
106
|
+
})
|
|
107
|
+
.join("\n")
|
|
108
|
+
.trim();
|
|
109
|
+
}
|
|
66
110
|
export default getJiraIssuesByQuery;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@credal/actions",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.76",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI Actions by Credal AI",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@eslint/js": "^9.16.0",
|
|
30
30
|
"@jest/globals": "^29.7.0",
|
|
31
|
+
"@types/html-to-text": "^9.0.4",
|
|
31
32
|
"@types/js-yaml": "^4.0.9",
|
|
32
33
|
"@types/jsonwebtoken": "^9.0.9",
|
|
33
34
|
"@types/node": "^22.10.1",
|
|
@@ -58,8 +59,10 @@
|
|
|
58
59
|
"date-fns": "^4.1.0",
|
|
59
60
|
"docx": "^9.3.0",
|
|
60
61
|
"dotenv": "^16.4.7",
|
|
62
|
+
"html-to-text": "^9.0.5",
|
|
61
63
|
"json-schema-to-zod": "^2.5.0",
|
|
62
64
|
"jsonwebtoken": "^9.0.2",
|
|
65
|
+
"limiter": "^3.0.0",
|
|
63
66
|
"mammoth": "^1.4.27",
|
|
64
67
|
"mongodb": "^6.13.1",
|
|
65
68
|
"node-forge": "^1.3.1",
|