@credal/actions 0.2.75 → 0.2.77

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.
@@ -1299,7 +1299,18 @@ export const jiraGetJiraIssuesByQueryDefinition = {
1299
1299
  description: "The retrieved Jira issues",
1300
1300
  items: {
1301
1301
  type: "object",
1302
- required: ["id", "key", "summary", "description", "project", "issueType", "status", "created", "updated"],
1302
+ required: [
1303
+ "id",
1304
+ "key",
1305
+ "summary",
1306
+ "description",
1307
+ "project",
1308
+ "issueType",
1309
+ "status",
1310
+ "created",
1311
+ "updated",
1312
+ "url",
1313
+ ],
1303
1314
  properties: {
1304
1315
  id: {
1305
1316
  type: "string",
@@ -1317,6 +1328,10 @@ export const jiraGetJiraIssuesByQueryDefinition = {
1317
1328
  type: "string",
1318
1329
  description: "Plain text description",
1319
1330
  },
1331
+ url: {
1332
+ type: "string",
1333
+ description: "The web url of the Jira ticket",
1334
+ },
1320
1335
  project: {
1321
1336
  type: "object",
1322
1337
  properties: {
@@ -1925,7 +1940,18 @@ export const jiraOrgGetJiraIssuesByQueryDefinition = {
1925
1940
  description: "The retrieved Jira issues",
1926
1941
  items: {
1927
1942
  type: "object",
1928
- required: ["id", "key", "summary", "description", "project", "issueType", "status", "created", "updated"],
1943
+ required: [
1944
+ "id",
1945
+ "key",
1946
+ "summary",
1947
+ "description",
1948
+ "project",
1949
+ "issueType",
1950
+ "status",
1951
+ "created",
1952
+ "updated",
1953
+ "url",
1954
+ ],
1929
1955
  properties: {
1930
1956
  id: {
1931
1957
  type: "string",
@@ -1943,6 +1969,10 @@ export const jiraOrgGetJiraIssuesByQueryDefinition = {
1943
1969
  type: "string",
1944
1970
  description: "Plain text description",
1945
1971
  },
1972
+ url: {
1973
+ type: "string",
1974
+ description: "The web url of the Jira ticket",
1975
+ },
1946
1976
  project: {
1947
1977
  type: "object",
1948
1978
  properties: {
@@ -1298,6 +1298,7 @@ export declare const jiraGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
1298
1298
  key: z.ZodString;
1299
1299
  summary: z.ZodString;
1300
1300
  description: z.ZodString;
1301
+ url: z.ZodString;
1301
1302
  project: z.ZodObject<{
1302
1303
  id: z.ZodOptional<z.ZodString>;
1303
1304
  key: z.ZodOptional<z.ZodString>;
@@ -1362,6 +1363,7 @@ export declare const jiraGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
1362
1363
  };
1363
1364
  created: string;
1364
1365
  updated: string;
1366
+ url: string;
1365
1367
  assignee?: string | null | undefined;
1366
1368
  reporter?: string | null | undefined;
1367
1369
  creator?: string | null | undefined;
@@ -1388,6 +1390,7 @@ export declare const jiraGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
1388
1390
  };
1389
1391
  created: string;
1390
1392
  updated: string;
1393
+ url: string;
1391
1394
  assignee?: string | null | undefined;
1392
1395
  reporter?: string | null | undefined;
1393
1396
  creator?: string | null | undefined;
@@ -1416,6 +1419,7 @@ export declare const jiraGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
1416
1419
  };
1417
1420
  created: string;
1418
1421
  updated: string;
1422
+ url: string;
1419
1423
  assignee?: string | null | undefined;
1420
1424
  reporter?: string | null | undefined;
1421
1425
  creator?: string | null | undefined;
@@ -1444,6 +1448,7 @@ export declare const jiraGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
1444
1448
  };
1445
1449
  created: string;
1446
1450
  updated: string;
1451
+ url: string;
1447
1452
  assignee?: string | null | undefined;
1448
1453
  reporter?: string | null | undefined;
1449
1454
  creator?: string | null | undefined;
@@ -1477,6 +1482,7 @@ export declare const jiraGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
1477
1482
  };
1478
1483
  created: string;
1479
1484
  updated: string;
1485
+ url: string;
1480
1486
  assignee?: string | null | undefined;
1481
1487
  reporter?: string | null | undefined;
1482
1488
  creator?: string | null | undefined;
@@ -1509,6 +1515,7 @@ export declare const jiraGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
1509
1515
  };
1510
1516
  created: string;
1511
1517
  updated: string;
1518
+ url: string;
1512
1519
  assignee?: string | null | undefined;
1513
1520
  reporter?: string | null | undefined;
1514
1521
  creator?: string | null | undefined;
@@ -1921,6 +1928,7 @@ export declare const jiraOrgGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
1921
1928
  key: z.ZodString;
1922
1929
  summary: z.ZodString;
1923
1930
  description: z.ZodString;
1931
+ url: z.ZodString;
1924
1932
  project: z.ZodObject<{
1925
1933
  id: z.ZodOptional<z.ZodString>;
1926
1934
  key: z.ZodOptional<z.ZodString>;
@@ -1985,6 +1993,7 @@ export declare const jiraOrgGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
1985
1993
  };
1986
1994
  created: string;
1987
1995
  updated: string;
1996
+ url: string;
1988
1997
  assignee?: string | null | undefined;
1989
1998
  reporter?: string | null | undefined;
1990
1999
  creator?: string | null | undefined;
@@ -2011,6 +2020,7 @@ export declare const jiraOrgGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
2011
2020
  };
2012
2021
  created: string;
2013
2022
  updated: string;
2023
+ url: string;
2014
2024
  assignee?: string | null | undefined;
2015
2025
  reporter?: string | null | undefined;
2016
2026
  creator?: string | null | undefined;
@@ -2039,6 +2049,7 @@ export declare const jiraOrgGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
2039
2049
  };
2040
2050
  created: string;
2041
2051
  updated: string;
2052
+ url: string;
2042
2053
  assignee?: string | null | undefined;
2043
2054
  reporter?: string | null | undefined;
2044
2055
  creator?: string | null | undefined;
@@ -2067,6 +2078,7 @@ export declare const jiraOrgGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
2067
2078
  };
2068
2079
  created: string;
2069
2080
  updated: string;
2081
+ url: string;
2070
2082
  assignee?: string | null | undefined;
2071
2083
  reporter?: string | null | undefined;
2072
2084
  creator?: string | null | undefined;
@@ -2100,6 +2112,7 @@ export declare const jiraOrgGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
2100
2112
  };
2101
2113
  created: string;
2102
2114
  updated: string;
2115
+ url: string;
2103
2116
  assignee?: string | null | undefined;
2104
2117
  reporter?: string | null | undefined;
2105
2118
  creator?: string | null | undefined;
@@ -2132,6 +2145,7 @@ export declare const jiraOrgGetJiraIssuesByQueryOutputSchema: z.ZodObject<{
2132
2145
  };
2133
2146
  created: string;
2134
2147
  updated: string;
2148
+ url: string;
2135
2149
  assignee?: string | null | undefined;
2136
2150
  reporter?: string | null | undefined;
2137
2151
  creator?: string | null | undefined;
@@ -433,6 +433,7 @@ export const jiraGetJiraIssuesByQueryOutputSchema = z.object({
433
433
  key: z.string().describe("Human-readable issue key (e.g. SSPR-123)"),
434
434
  summary: z.string().describe("Summary of the issue"),
435
435
  description: z.string().describe("Plain text description"),
436
+ url: z.string().describe("The web url of the Jira ticket"),
436
437
  project: z.object({ id: z.string().optional(), key: z.string().optional(), name: z.string().optional() }),
437
438
  issueType: z.object({ id: z.string().optional(), name: z.string().optional() }),
438
439
  status: z.object({
@@ -608,6 +609,7 @@ export const jiraOrgGetJiraIssuesByQueryOutputSchema = z.object({
608
609
  key: z.string().describe("Human-readable issue key (e.g. SSPR-123)"),
609
610
  summary: z.string().describe("Summary of the issue"),
610
611
  description: z.string().describe("Plain text description"),
612
+ url: z.string().describe("The web url of the Jira ticket"),
611
613
  project: z.object({ id: z.string().optional(), key: z.string().optional(), name: z.string().optional() }),
612
614
  issueType: z.object({ id: z.string().optional(), name: z.string().optional() }),
613
615
  status: z.object({
@@ -1,4 +1,4 @@
1
- export interface GmailMessage {
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?: Array<{
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
- export declare function decodeGmailBase64(base64String: string): string;
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
- export function decodeGmailBase64(base64String) {
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
- let textContent = null;
12
- // Function to recursively search for plain text content in parts
13
- function searchParts(parts) {
14
- var _a;
15
- if (!parts)
16
- return;
17
- for (const part of parts) {
18
- if (part.mimeType === "text/plain" && ((_a = part.body) === null || _a === void 0 ? void 0 : _a.data) && !textContent) {
19
- textContent = decodeGmailBase64(part.body.data);
20
- }
21
- else if (part.parts) {
22
- // Recursively search nested parts
23
- searchParts(part.parts);
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
- // 1. Check if content is directly in the payload body (simple emails)
28
- if (((_a = message.payload.body) === null || _a === void 0 ? void 0 : _a.data) && message.payload.mimeType === "text/plain") {
29
- return decodeGmailBase64(message.payload.body.data);
24
+ catch (_a) {
25
+ return null;
30
26
  }
31
- // 2. Search through parts for plain text content
32
- if (message.payload.parts) {
33
- searchParts(message.payload.parts);
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
- // 3. Return plain text content or null
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 = undefined;
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(100, max - fetched)}`;
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 remaining = max - allMessages.length;
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
- Authorization: `Bearer ${authParams.authToken}`,
43
- },
66
+ headers: { Authorization: `Bearer ${authParams.authToken}` },
67
+ validateStatus: () => true,
44
68
  });
45
69
  const { id, threadId, snippet, labelIds, internalDate } = msgRes.data;
46
- const emailBody = getEmailContent(msgRes.data) || "";
47
- return {
48
- id,
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
- errorMessages.push(err instanceof Error ? err.message : "Failed to fetch message details");
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
- payload: {},
65
- error: err instanceof Error ? err.message : "Failed to fetch message details",
83
+ emailBody: "",
84
+ error: errorMessage,
66
85
  };
67
86
  }
68
87
  })));
69
88
  allMessages.push(...results);
70
89
  fetched = allMessages.length;
71
- if (!nextPageToken || allMessages.length >= max)
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 (error) {
100
+ catch (err) {
82
101
  return {
83
102
  success: false,
84
- error: error instanceof Error ? error.message : "Unknown error searching Gmail",
103
+ error: err instanceof Error ? err.message : "Unknown error searching Gmail",
85
104
  messages: [],
86
105
  };
87
106
  }
@@ -8,12 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { ApiError, axiosClient } from "../../util/axiosClient.js";
11
- const DEFAULT_LIMIT = 1000;
11
+ const DEFAULT_LIMIT = 100;
12
12
  const getJiraIssuesByQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ params, authParams, }) {
13
- const { authToken, cloudId } = authParams;
13
+ const { authToken, cloudId, baseUrl } = authParams;
14
14
  const { query, limit } = params;
15
- if (!cloudId || !authToken) {
16
- throw new Error("Valid Cloud ID and auth token are required to comment on Jira ticket");
15
+ if (!cloudId || !authToken || !baseUrl) {
16
+ throw new Error("Valid Cloud ID, base URL, and auth token are required to comment on Jira ticket");
17
17
  }
18
18
  const queryParams = new URLSearchParams();
19
19
  queryParams.set("jql", query);
@@ -77,6 +77,7 @@ const getJiraIssuesByQuery = (_a) => __awaiter(void 0, [_a], void 0, function* (
77
77
  updated: issue.fields.updated,
78
78
  resolution: ((_d = issue.fields.resolution) === null || _d === void 0 ? void 0 : _d.name) || null,
79
79
  dueDate: issue.fields.duedate || null,
80
+ url: `${baseUrl}/browse/${issue.key}`,
80
81
  });
81
82
  }),
82
83
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@credal/actions",
3
- "version": "0.2.75",
3
+ "version": "0.2.77",
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",