@credal/actions 0.2.216 → 0.2.218
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 +51 -47
- package/dist/actions/autogen/types.d.ts +30 -38
- package/dist/actions/autogen/types.js +305 -269
- package/dist/actions/parse.js +10 -2
- package/dist/actions/providers/google-oauth/appendRowsToSpreadsheet.js +8 -7
- package/dist/actions/providers/google-oauth/updateRowsInSpreadsheet.js +18 -12
- package/dist/actions/providers/jira/getJiraDCIssuesByQuery.d.ts +2 -3
- package/dist/actions/providers/jira/getJiraDCIssuesByQuery.js +11 -9
- package/dist/actions/providers/jira/getJiraIssuesByQuery.js +31 -57
- package/package.json +1 -1
package/dist/actions/parse.js
CHANGED
|
@@ -125,8 +125,16 @@ async function addActionTypes({ file, prefix, action }) {
|
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
127
|
async function addTypesToFile({ file, obj, fallback, name, }) {
|
|
128
|
-
// Tool calling framework currently having trouble filling in records as opposed to objects
|
|
129
|
-
|
|
128
|
+
// Tool calling framework currently having trouble filling in records as opposed to objects.
|
|
129
|
+
// Also coerce numeric types from strings: LLMs often emit numbers as JSON strings (e.g. "2"
|
|
130
|
+
// instead of 2) even when the schema specifies integer/number, causing Zod validation failures.
|
|
131
|
+
// z.coerce.number() is a no-op for actual numbers, so this is fully backward-compatible.
|
|
132
|
+
const zodSchema = obj
|
|
133
|
+
? convert(obj)
|
|
134
|
+
.replace(/z\.record\(z\.any\(\)\)/g, "z.object({}).catchall(z.any())")
|
|
135
|
+
.replace(/z\.number\(\)\.int\(\)/g, "z.coerce.number().int()")
|
|
136
|
+
.replace(/z\.number\(\)/g, "z.coerce.number()")
|
|
137
|
+
: fallback;
|
|
130
138
|
const zodName = `${name}Schema`;
|
|
131
139
|
file.addVariableStatement({
|
|
132
140
|
declarationKind: VariableDeclarationKind.Const,
|
|
@@ -9,16 +9,17 @@ const appendRowsToSpreadsheet = async ({ params, authParams, }) => {
|
|
|
9
9
|
throw new Error(MISSING_AUTH_TOKEN);
|
|
10
10
|
}
|
|
11
11
|
const { spreadsheetId, sheetName, rows } = params;
|
|
12
|
-
// Transform rows from schema format to Google Sheets API format
|
|
13
|
-
// Schema: [[{ stringValue: "cell1" }, { stringValue: "cell2" }], ...]
|
|
14
|
-
// API expects: [["cell1", "cell2"], ...]
|
|
15
|
-
const values = rows.map(row => row.map(cell => cell.stringValue));
|
|
16
|
-
const appendUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/'${sheetName ?? "Sheet1"}':append`;
|
|
17
12
|
try {
|
|
13
|
+
if (rows.length === 0) {
|
|
14
|
+
throw new Error("rows array cannot be empty");
|
|
15
|
+
}
|
|
16
|
+
const sheet = sheetName ?? "Sheet1";
|
|
17
|
+
const quotedSheet = /[\s'!]/.test(sheet) ? `'${sheet.replace(/'/g, "''")}'` : sheet;
|
|
18
|
+
const appendUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(quotedSheet)}:append`;
|
|
18
19
|
const response = await axiosClient.post(appendUrl, {
|
|
19
|
-
values,
|
|
20
|
+
values: rows,
|
|
20
21
|
majorDimension: "ROWS",
|
|
21
|
-
range:
|
|
22
|
+
range: quotedSheet,
|
|
22
23
|
}, {
|
|
23
24
|
headers: {
|
|
24
25
|
Authorization: `Bearer ${authParams.authToken}`,
|
|
@@ -8,20 +8,26 @@ const updateRowsInSpreadsheet = async ({ params, authParams, }) => {
|
|
|
8
8
|
if (!authParams.authToken) {
|
|
9
9
|
throw new Error(MISSING_AUTH_TOKEN);
|
|
10
10
|
}
|
|
11
|
-
const { spreadsheetId, sheetName, startRow, rows } = params;
|
|
12
|
-
if (rows.length === 0) {
|
|
13
|
-
throw new Error("rows array cannot be empty");
|
|
14
|
-
}
|
|
15
|
-
const values = rows.map(row => row.map(cell => cell.stringValue));
|
|
16
|
-
if (startRow < 1) {
|
|
17
|
-
throw new Error("startRow must be >= 1");
|
|
18
|
-
}
|
|
19
|
-
const endRow = startRow + rows.length - 1;
|
|
20
|
-
const range = `'${sheetName ?? "Sheet1"}'!A${startRow}:ZZ${endRow}`;
|
|
21
|
-
const updateUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}`;
|
|
11
|
+
const { spreadsheetId, sheetName, startRow, startColumn, rows } = params;
|
|
22
12
|
try {
|
|
13
|
+
if (rows.length === 0) {
|
|
14
|
+
throw new Error("rows array cannot be empty");
|
|
15
|
+
}
|
|
16
|
+
if (startRow < 1) {
|
|
17
|
+
throw new Error("startRow must be >= 1");
|
|
18
|
+
}
|
|
19
|
+
const col = startColumn ?? "A";
|
|
20
|
+
if (!/^[A-Za-z]+$/.test(col)) {
|
|
21
|
+
throw new Error(`startColumn must be a column letter (e.g. "A", "BE"), got: "${col}"`);
|
|
22
|
+
}
|
|
23
|
+
const endRow = startRow + rows.length - 1;
|
|
24
|
+
const sheet = sheetName ?? "Sheet1";
|
|
25
|
+
// Only quote sheet names that contain spaces or special characters
|
|
26
|
+
const quotedSheet = /[\s'!]/.test(sheet) ? `'${sheet.replace(/'/g, "''")}'` : sheet;
|
|
27
|
+
const range = `${quotedSheet}!${col}${startRow}:ZZ${endRow}`;
|
|
28
|
+
const updateUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}`;
|
|
23
29
|
const response = await axiosClient.put(updateUrl, {
|
|
24
|
-
values,
|
|
30
|
+
values: rows,
|
|
25
31
|
majorDimension: "ROWS",
|
|
26
32
|
range,
|
|
27
33
|
}, {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { jiraGetJiraIssuesByQueryFunction } from "../../autogen/types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Get Jira issues from Jira Data Center using offset-based pagination (startAt).
|
|
4
|
-
* Returns `
|
|
5
|
-
*
|
|
6
|
-
* (Contrast with the Cloud implementation which returns `truncated` but not `total`.)
|
|
4
|
+
* Returns `itemsReturned` and `truncated` so agents can page through results by
|
|
5
|
+
* incrementing startAt by itemsReturned each call until truncated is false.
|
|
7
6
|
*/
|
|
8
7
|
declare const getJiraDCIssuesByQuery: jiraGetJiraIssuesByQueryFunction;
|
|
9
8
|
export default getJiraDCIssuesByQuery;
|
|
@@ -3,13 +3,12 @@ import { getJiraApiConfig, getErrorMessage, extractPlainText } from "./utils.js"
|
|
|
3
3
|
const DEFAULT_LIMIT = 100;
|
|
4
4
|
/**
|
|
5
5
|
* Get Jira issues from Jira Data Center using offset-based pagination (startAt).
|
|
6
|
-
* Returns `
|
|
7
|
-
*
|
|
8
|
-
* (Contrast with the Cloud implementation which returns `truncated` but not `total`.)
|
|
6
|
+
* Returns `itemsReturned` and `truncated` so agents can page through results by
|
|
7
|
+
* incrementing startAt by itemsReturned each call until truncated is false.
|
|
9
8
|
*/
|
|
10
9
|
const getJiraDCIssuesByQuery = async ({ params, authParams, }) => {
|
|
11
10
|
const { authToken } = authParams;
|
|
12
|
-
const { query, limit } = params;
|
|
11
|
+
const { query, limit, startAt: paramStartAt } = params;
|
|
13
12
|
const { apiUrl, browseUrl, strategy } = getJiraApiConfig(authParams);
|
|
14
13
|
if (!authToken) {
|
|
15
14
|
throw new Error("Auth token is required");
|
|
@@ -36,7 +35,7 @@ const getJiraDCIssuesByQuery = async ({ params, authParams, }) => {
|
|
|
36
35
|
const searchEndpoint = strategy.getSearchEndpoint();
|
|
37
36
|
const requestedLimit = limit ?? DEFAULT_LIMIT;
|
|
38
37
|
const allIssues = [];
|
|
39
|
-
let
|
|
38
|
+
let currentStartAt = paramStartAt ?? 0;
|
|
40
39
|
let jiraTotal = undefined;
|
|
41
40
|
try {
|
|
42
41
|
// Keep fetching pages until we have all requested issues
|
|
@@ -47,7 +46,7 @@ const getJiraDCIssuesByQuery = async ({ params, authParams, }) => {
|
|
|
47
46
|
const queryParams = new URLSearchParams();
|
|
48
47
|
queryParams.set("jql", query);
|
|
49
48
|
queryParams.set("maxResults", String(maxResults));
|
|
50
|
-
queryParams.set("startAt", String(
|
|
49
|
+
queryParams.set("startAt", String(currentStartAt));
|
|
51
50
|
queryParams.set("fields", fields.join(","));
|
|
52
51
|
const fullApiUrl = `${apiUrl}${searchEndpoint}?${queryParams.toString()}`;
|
|
53
52
|
const response = await axiosClient.get(fullApiUrl, {
|
|
@@ -59,13 +58,16 @@ const getJiraDCIssuesByQuery = async ({ params, authParams, }) => {
|
|
|
59
58
|
const { issues, total } = response.data;
|
|
60
59
|
jiraTotal = total;
|
|
61
60
|
allIssues.push(...issues);
|
|
62
|
-
if (allIssues.length >= total || issues.length === 0) {
|
|
61
|
+
if ((paramStartAt ?? 0) + allIssues.length >= total || issues.length === 0) {
|
|
63
62
|
break;
|
|
64
63
|
}
|
|
65
|
-
|
|
64
|
+
currentStartAt += issues.length;
|
|
66
65
|
}
|
|
66
|
+
const absoluteEnd = (paramStartAt ?? 0) + allIssues.length;
|
|
67
|
+
const truncated = jiraTotal !== undefined && absoluteEnd < jiraTotal;
|
|
67
68
|
return {
|
|
68
|
-
|
|
69
|
+
itemsReturned: allIssues.length,
|
|
70
|
+
truncated,
|
|
69
71
|
results: allIssues.map(issue => {
|
|
70
72
|
const { id, key, fields } = issue;
|
|
71
73
|
const { summary, description, project, issuetype, status, assignee, reporter, creator, created, updated, resolution, duedate, } = fields;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { Version3Client } from "jira.js";
|
|
2
|
+
import { axiosClient } from "../../util/axiosClient.js";
|
|
2
3
|
import { getJiraApiConfig, getErrorMessage, extractPlainText, getUserInfoFromAccountId } from "./utils.js";
|
|
3
4
|
const DEFAULT_LIMIT = 100;
|
|
4
|
-
// Jira Cloud implementation using the
|
|
5
|
-
//
|
|
6
|
-
// Note: the enhanced API does not expose a total count, so `total` is never returned here.
|
|
7
|
-
// Use `jiraDataCenter` provider if you need the exact total count.
|
|
5
|
+
// Jira Cloud implementation using the legacy offset-based search API (/rest/api/3/search).
|
|
6
|
+
// Uses startAt for pagination so agents can resume from any offset even when app-layer truncation occurs.
|
|
8
7
|
const getJiraIssuesByQuery = async ({ params, authParams, }) => {
|
|
9
8
|
const { authToken, cloudId } = authParams;
|
|
10
|
-
const { query, limit } = params;
|
|
9
|
+
const { query, limit, startAt: paramStartAt } = params;
|
|
11
10
|
const { browseUrl } = getJiraApiConfig(authParams);
|
|
12
11
|
if (!authToken)
|
|
13
12
|
throw new Error("Auth token is required");
|
|
@@ -34,47 +33,36 @@ const getJiraIssuesByQuery = async ({ params, authParams, }) => {
|
|
|
34
33
|
];
|
|
35
34
|
const requestedLimit = limit ?? DEFAULT_LIMIT;
|
|
36
35
|
const allIssues = [];
|
|
37
|
-
let
|
|
38
|
-
let
|
|
36
|
+
let currentStartAt = paramStartAt ?? 0;
|
|
37
|
+
let jiraTotal = undefined;
|
|
38
|
+
// jira.js client is kept solely for getUserInfoFromAccountId (user email lookups)
|
|
39
|
+
const client = new Version3Client({
|
|
40
|
+
host: `https://api.atlassian.com/ex/jira/${cloudId}`,
|
|
41
|
+
authentication: { oauth2: { accessToken: authToken } },
|
|
42
|
+
});
|
|
39
43
|
try {
|
|
40
|
-
// Initialize jira.js client with OAuth 2.0 authentication
|
|
41
|
-
const client = new Version3Client({
|
|
42
|
-
host: `https://api.atlassian.com/ex/jira/${cloudId}`,
|
|
43
|
-
authentication: {
|
|
44
|
-
oauth2: {
|
|
45
|
-
accessToken: authToken,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
// Keep fetching pages until we have all requested issues
|
|
50
44
|
while (allIssues.length < requestedLimit) {
|
|
51
|
-
// Calculate how many results to fetch in this request
|
|
52
45
|
const remainingIssues = requestedLimit - allIssues.length;
|
|
53
46
|
const maxResults = Math.min(remainingIssues, DEFAULT_LIMIT);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
allIssues.push(...searchResults.issues);
|
|
65
|
-
// Check if we've reached the end or have enough results
|
|
66
|
-
if (allIssues.length >= requestedLimit || !searchResults.nextPageToken || searchResults.issues.length === 0) {
|
|
67
|
-
// Truncated when we hit the limit but Jira still has more pages
|
|
68
|
-
truncated = allIssues.length >= requestedLimit && !!searchResults.nextPageToken;
|
|
47
|
+
const queryParams = new URLSearchParams();
|
|
48
|
+
queryParams.set("jql", query);
|
|
49
|
+
queryParams.set("maxResults", String(maxResults));
|
|
50
|
+
queryParams.set("startAt", String(currentStartAt));
|
|
51
|
+
queryParams.set("fields", fields.join(","));
|
|
52
|
+
const response = await axiosClient.get(`https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/search?${queryParams.toString()}`, { headers: { Authorization: `Bearer ${authToken}`, Accept: "application/json" } });
|
|
53
|
+
const { issues, total } = response.data;
|
|
54
|
+
jiraTotal = total;
|
|
55
|
+
allIssues.push(...issues);
|
|
56
|
+
if ((paramStartAt ?? 0) + allIssues.length >= total || issues.length === 0) {
|
|
69
57
|
break;
|
|
70
58
|
}
|
|
71
|
-
|
|
59
|
+
currentStartAt += issues.length;
|
|
72
60
|
}
|
|
73
|
-
|
|
61
|
+
const absoluteEnd = (paramStartAt ?? 0) + allIssues.length;
|
|
62
|
+
const truncated = jiraTotal !== undefined && absoluteEnd < jiraTotal;
|
|
74
63
|
const results = await Promise.all(allIssues.map(async ({ id, key, fields }) => {
|
|
75
64
|
const ticketUrl = `${browseUrl}/browse/${key}`;
|
|
76
65
|
const { summary, description, project, issuetype, status, assignee, reporter, creator, created, updated, resolution, duedate, } = fields;
|
|
77
|
-
// Fetch user info in parallel
|
|
78
66
|
const [assigneeInfo, reporterInfo, creatorInfo] = await Promise.all([
|
|
79
67
|
getUserInfoFromAccountId(assignee?.accountId, client),
|
|
80
68
|
getUserInfoFromAccountId(reporter?.accountId, client),
|
|
@@ -88,39 +76,25 @@ const getJiraIssuesByQuery = async ({ params, authParams, }) => {
|
|
|
88
76
|
key,
|
|
89
77
|
summary,
|
|
90
78
|
description: extractPlainText(description),
|
|
91
|
-
project: {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
name: project?.name,
|
|
95
|
-
},
|
|
96
|
-
issueType: {
|
|
97
|
-
id: issuetype?.id,
|
|
98
|
-
name: issuetype?.name,
|
|
99
|
-
},
|
|
100
|
-
status: {
|
|
101
|
-
id: status?.id,
|
|
102
|
-
name: status?.name,
|
|
103
|
-
category: status?.statusCategory?.name,
|
|
104
|
-
},
|
|
79
|
+
project: { id: project?.id, key: project?.key, name: project?.name },
|
|
80
|
+
issueType: { id: issuetype?.id, name: issuetype?.name },
|
|
81
|
+
status: { id: status?.id, name: status?.name, category: status?.statusCategory?.name },
|
|
105
82
|
assignee: assigneeInfo,
|
|
106
83
|
reporter: reporterInfo,
|
|
107
84
|
creator: creatorInfo,
|
|
108
|
-
created
|
|
109
|
-
updated
|
|
85
|
+
created,
|
|
86
|
+
updated,
|
|
110
87
|
resolution: resolution?.name,
|
|
111
88
|
dueDate: duedate,
|
|
112
89
|
url: ticketUrl,
|
|
113
90
|
},
|
|
114
91
|
};
|
|
115
92
|
}));
|
|
116
|
-
return {
|
|
93
|
+
return { itemsReturned: allIssues.length, truncated, results };
|
|
117
94
|
}
|
|
118
95
|
catch (error) {
|
|
119
96
|
console.error("Error retrieving Jira issues:", error);
|
|
120
|
-
return {
|
|
121
|
-
results: [],
|
|
122
|
-
error: getErrorMessage(error),
|
|
123
|
-
};
|
|
97
|
+
return { results: [], error: getErrorMessage(error) };
|
|
124
98
|
}
|
|
125
99
|
};
|
|
126
100
|
export default getJiraIssuesByQuery;
|