@credal/actions 0.2.223 → 0.2.225

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.
Files changed (83) hide show
  1. package/dist/actions/actionMapper.js +29 -1
  2. package/dist/actions/autogen/templates.d.ts +4 -0
  3. package/dist/actions/autogen/templates.js +385 -4
  4. package/dist/actions/autogen/types.d.ts +379 -0
  5. package/dist/actions/autogen/types.js +161 -4
  6. package/dist/actions/providers/ashby/addCandidateToProject.d.ts +3 -0
  7. package/dist/actions/providers/ashby/addCandidateToProject.js +31 -0
  8. package/dist/actions/providers/ashby/createCandidate.d.ts +3 -0
  9. package/dist/actions/providers/ashby/createCandidate.js +38 -0
  10. package/dist/actions/providers/ashby/createNote.d.ts +3 -0
  11. package/dist/actions/providers/ashby/createNote.js +31 -0
  12. package/dist/actions/providers/ashby/getCandidateInfo.d.ts +3 -0
  13. package/dist/actions/providers/ashby/getCandidateInfo.js +33 -0
  14. package/dist/actions/providers/ashby/listCandidateNotes.d.ts +3 -0
  15. package/dist/actions/providers/ashby/listCandidateNotes.js +33 -0
  16. package/dist/actions/providers/ashby/listCandidates.d.ts +3 -0
  17. package/dist/actions/providers/ashby/listCandidates.js +30 -0
  18. package/dist/actions/providers/ashby/searchCandidates.d.ts +3 -0
  19. package/dist/actions/providers/ashby/searchCandidates.js +34 -0
  20. package/dist/actions/providers/ashby/updateCandidate.d.ts +3 -0
  21. package/dist/actions/providers/ashby/updateCandidate.js +56 -0
  22. package/dist/actions/providers/github/searchRepository.js +7 -7
  23. package/dist/actions/providers/gong/getGongTranscripts.d.ts +3 -0
  24. package/dist/actions/providers/gong/getGongTranscripts.js +315 -0
  25. package/dist/actions/providers/google-oauth/listFilesWithAssignedComments.d.ts +3 -0
  26. package/dist/actions/providers/google-oauth/listFilesWithAssignedComments.js +41 -0
  27. package/dist/actions/providers/jamf/getJamfComputerInventory.d.ts +3 -0
  28. package/dist/actions/providers/jamf/getJamfComputerInventory.js +37 -0
  29. package/dist/actions/providers/jamf/getJamfFileVaultRecoveryKey.d.ts +3 -0
  30. package/dist/actions/providers/jamf/getJamfFileVaultRecoveryKey.js +39 -0
  31. package/dist/actions/providers/jamf/getJamfUserComputerId.d.ts +3 -0
  32. package/dist/actions/providers/jamf/getJamfUserComputerId.js +45 -0
  33. package/dist/actions/providers/jamf/lockJamfComputerById.d.ts +3 -0
  34. package/dist/actions/providers/jamf/lockJamfComputerById.js +39 -0
  35. package/dist/actions/providers/jira/updateServiceDeskRequest.d.ts +3 -0
  36. package/dist/actions/providers/jira/updateServiceDeskRequest.js +72 -0
  37. package/dist/actions/providers/kandji/getFVRecoveryKeyForDevice.d.ts +3 -0
  38. package/dist/actions/providers/kandji/getFVRecoveryKeyForDevice.js +85 -0
  39. package/dist/actions/providers/microsoft/createDocument.js +3 -4
  40. package/dist/actions/providers/microsoft/getDocument.js +3 -6
  41. package/dist/actions/providers/microsoft/getSharepointItem.d.ts +3 -0
  42. package/dist/actions/providers/microsoft/getSharepointItem.js +61 -0
  43. package/dist/actions/providers/microsoft/listSharepointFolder.d.ts +3 -0
  44. package/dist/actions/providers/microsoft/listSharepointFolder.js +92 -0
  45. package/dist/actions/providers/microsoft/readSharepointContent.d.ts +3 -0
  46. package/dist/actions/providers/microsoft/readSharepointContent.js +157 -0
  47. package/dist/actions/providers/microsoft/searchSharepoint.d.ts +3 -0
  48. package/dist/actions/providers/microsoft/searchSharepoint.js +147 -0
  49. package/dist/actions/providers/microsoft/sendOutlookEmail.d.ts +3 -0
  50. package/dist/actions/providers/microsoft/sendOutlookEmail.js +48 -0
  51. package/dist/actions/providers/microsoft/sharepointUtils.d.ts +56 -0
  52. package/dist/actions/providers/microsoft/sharepointUtils.js +153 -0
  53. package/dist/actions/providers/microsoft/updateDocument.js +3 -6
  54. package/dist/actions/providers/microsoft/updateSpreadsheet.js +3 -4
  55. package/dist/actions/providers/microsoft/utils.d.ts +10 -0
  56. package/dist/actions/providers/microsoft/utils.js +9 -0
  57. package/dist/actions/providers/okta/addUserToGroup.d.ts +3 -0
  58. package/dist/actions/providers/okta/addUserToGroup.js +56 -0
  59. package/dist/actions/providers/okta/getOktaGroup.d.ts +3 -0
  60. package/dist/actions/providers/okta/getOktaGroup.js +73 -0
  61. package/dist/actions/providers/okta/getOktaUser.d.ts +3 -0
  62. package/dist/actions/providers/okta/getOktaUser.js +40 -0
  63. package/dist/actions/providers/okta/listMFA.d.ts +3 -0
  64. package/dist/actions/providers/okta/listMFA.js +52 -0
  65. package/dist/actions/providers/okta/listOktaGroupMembers.d.ts +3 -0
  66. package/dist/actions/providers/okta/listOktaGroupMembers.js +99 -0
  67. package/dist/actions/providers/okta/listOktaGroups.d.ts +3 -0
  68. package/dist/actions/providers/okta/listOktaGroups.js +92 -0
  69. package/dist/actions/providers/okta/listOktaUserGroups.d.ts +3 -0
  70. package/dist/actions/providers/okta/listOktaUserGroups.js +83 -0
  71. package/dist/actions/providers/okta/listOktaUsers.d.ts +3 -0
  72. package/dist/actions/providers/okta/listOktaUsers.js +100 -0
  73. package/dist/actions/providers/okta/removeUserFromGroup.d.ts +3 -0
  74. package/dist/actions/providers/okta/removeUserFromGroup.js +56 -0
  75. package/dist/actions/providers/okta/resetMFA.d.ts +3 -0
  76. package/dist/actions/providers/okta/resetMFA.js +62 -0
  77. package/dist/actions/providers/okta/resetPassword.d.ts +3 -0
  78. package/dist/actions/providers/okta/resetPassword.js +57 -0
  79. package/dist/actions/providers/okta/triggerOktaWorkflow.d.ts +3 -0
  80. package/dist/actions/providers/okta/triggerOktaWorkflow.js +44 -0
  81. package/dist/actions/providers/salesforce/fetchSalesforceSchema.d.ts +3 -0
  82. package/dist/actions/providers/salesforce/fetchSalesforceSchema.js +40 -0
  83. package/package.json +2 -2
@@ -0,0 +1,85 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { axiosClient } from "../../util/axiosClient.js";
11
+ const getFVRecoveryKeyForDevice = (_a) => __awaiter(void 0, [_a], void 0, function* ({ params, authParams, }) {
12
+ const { userEmail, subdomain } = params;
13
+ const { apiKey } = authParams;
14
+ if (!apiKey) {
15
+ throw new Error("Missing API key in auth parameters");
16
+ }
17
+ try {
18
+ // First list all devices to get the device for the specific device
19
+ const device = yield getDeviceByEmail({
20
+ apiKey,
21
+ userEmail,
22
+ subdomain,
23
+ });
24
+ if (!device) {
25
+ return {
26
+ success: false,
27
+ error: "Device not found for the given serial number",
28
+ };
29
+ }
30
+ // Then get the FV recovery key for that device
31
+ const fvRecoveryKey = yield axiosClient.get(`https://${subdomain}.api.kandji.io/api/v1/devices/${device.device_id}/secrets/filevaultkey`, {
32
+ headers: {
33
+ "Content-Type": "application/json",
34
+ Authorization: `Bearer ${apiKey}`,
35
+ },
36
+ });
37
+ if (!fvRecoveryKey || !fvRecoveryKey.data || !fvRecoveryKey.data.key) {
38
+ return {
39
+ success: false,
40
+ error: "FireVault recovery key not found",
41
+ };
42
+ }
43
+ return {
44
+ success: true,
45
+ recoveryKey: fvRecoveryKey.data.key,
46
+ };
47
+ }
48
+ catch (error) {
49
+ return {
50
+ success: false,
51
+ error: error instanceof Error ? error.message : "Unknown error",
52
+ };
53
+ }
54
+ });
55
+ function getDeviceByEmail(input) {
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ const limit = 300;
58
+ let offset = 0;
59
+ const { apiKey, userEmail, subdomain } = input;
60
+ while (true) {
61
+ // Update params
62
+ const params = { limit, offset };
63
+ const endpoint = `https://${subdomain}.api.kandji.io/api/v1/devices`;
64
+ const response = yield axiosClient.get(endpoint, {
65
+ params: Object.assign({}, params),
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ Authorization: `Bearer ${apiKey}`,
69
+ },
70
+ });
71
+ for (const device of response.data) {
72
+ if (device.user && device.user.email === userEmail) {
73
+ // If the device user email matches, return the device
74
+ return device;
75
+ }
76
+ }
77
+ offset += limit;
78
+ if (response.data.length === 0) {
79
+ break;
80
+ }
81
+ }
82
+ return null;
83
+ });
84
+ }
85
+ export default getFVRecoveryKeyForDevice;
@@ -1,6 +1,6 @@
1
- import { getGraphClient, validateAndSanitizeFileName } from "./utils.js";
1
+ import { getDrivePath, getGraphClient, validateAndSanitizeFileName } from "./utils.js";
2
2
  const createDocument = async ({ params, authParams, }) => {
3
- const { folderId, name, content, siteId } = params;
3
+ const { folderId, name, content, siteId, driveId } = params;
4
4
  let client = undefined;
5
5
  try {
6
6
  client = await getGraphClient(authParams);
@@ -11,9 +11,8 @@ const createDocument = async ({ params, authParams, }) => {
11
11
  error: "Error while authorizing: " + (error instanceof Error ? error.message : "Unknown error"),
12
12
  };
13
13
  }
14
- const apiEndpointPrefix = siteId ? `/sites/${siteId}` : "/me";
15
14
  const sanitizedFileName = validateAndSanitizeFileName(name);
16
- const endpoint = `${apiEndpointPrefix}/drive/items/${folderId || "root"}:/${sanitizedFileName}:/content`;
15
+ const endpoint = `${getDrivePath({ driveId, siteId })}/items/${folderId || "root"}:/${sanitizedFileName}:/content`;
17
16
  try {
18
17
  // Create or update the document
19
18
  const response = await client.api(endpoint).put(content);
@@ -1,6 +1,6 @@
1
- import { getGraphClient } from "./utils.js";
1
+ import { getDrivePath, getGraphClient } from "./utils.js";
2
2
  const getDocument = async ({ params, authParams, }) => {
3
- const { siteId, documentId } = params;
3
+ const { siteId, driveId, documentId } = params;
4
4
  let client;
5
5
  try {
6
6
  client = await getGraphClient(authParams);
@@ -12,10 +12,7 @@ const getDocument = async ({ params, authParams, }) => {
12
12
  };
13
13
  }
14
14
  try {
15
- // Construct the API endpoint
16
- const endpoint = siteId
17
- ? `/sites/${siteId}/drive/items/${documentId}/content`
18
- : `/me/drive/items/${documentId}/content`;
15
+ const endpoint = `${getDrivePath({ driveId, siteId })}/items/${documentId}/content`;
19
16
  // Fetch the document content
20
17
  const response = await client.api(endpoint).get();
21
18
  return {
@@ -0,0 +1,3 @@
1
+ import type { microsoftGetSharepointItemFunction } from "../../autogen/types.js";
2
+ declare const getSharepointItem: microsoftGetSharepointItemFunction;
3
+ export default getSharepointItem;
@@ -0,0 +1,61 @@
1
+ import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
2
+ import { axiosClient } from "../../util/axiosClient.js";
3
+ import { MICROSOFT_GRAPH_API_URL } from "./utils.js";
4
+ import { resolveItemFromUrl } from "./sharepointUtils.js";
5
+ const getSharepointItem = async ({ params, authParams, }) => {
6
+ if (!authParams.authToken) {
7
+ return { success: false, error: MISSING_AUTH_TOKEN };
8
+ }
9
+ try {
10
+ const resolved = await resolveItemFromUrl(authParams.authToken, params.url);
11
+ if (resolved.itemType === "site") {
12
+ const drivesResponse = await axiosClient.get(`${MICROSOFT_GRAPH_API_URL}/sites/${resolved.siteId}/drives`, {
13
+ headers: { Authorization: `Bearer ${authParams.authToken}` },
14
+ });
15
+ const drives = drivesResponse.data.value ?? [];
16
+ return {
17
+ success: true,
18
+ item: {
19
+ itemType: "site",
20
+ siteId: resolved.siteId,
21
+ name: resolved.name,
22
+ webUrl: resolved.webUrl,
23
+ drives: drives.map(drive => ({ driveId: drive.id, name: drive.name, webUrl: drive.webUrl })),
24
+ },
25
+ };
26
+ }
27
+ if (resolved.itemType === "page") {
28
+ return {
29
+ success: true,
30
+ item: {
31
+ itemType: "page",
32
+ siteId: resolved.siteId,
33
+ itemId: resolved.pageId,
34
+ name: resolved.name,
35
+ webUrl: resolved.webUrl,
36
+ },
37
+ };
38
+ }
39
+ return {
40
+ success: true,
41
+ item: {
42
+ itemType: resolved.itemType,
43
+ name: resolved.name,
44
+ webUrl: resolved.webUrl,
45
+ driveId: resolved.driveId,
46
+ itemId: resolved.itemId,
47
+ siteId: resolved.siteId,
48
+ mimeType: resolved.mimeType,
49
+ sizeBytes: resolved.sizeBytes,
50
+ },
51
+ };
52
+ }
53
+ catch (error) {
54
+ console.error("Error resolving SharePoint item from URL", error);
55
+ return {
56
+ success: false,
57
+ error: error instanceof Error ? error.message : "Unknown error",
58
+ };
59
+ }
60
+ };
61
+ export default getSharepointItem;
@@ -0,0 +1,3 @@
1
+ import type { microsoftListSharepointFolderFunction } from "../../autogen/types.js";
2
+ declare const listSharepointFolder: microsoftListSharepointFolderFunction;
3
+ export default listSharepointFolder;
@@ -0,0 +1,92 @@
1
+ import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
2
+ import { axiosClient } from "../../util/axiosClient.js";
3
+ import { MICROSOFT_GRAPH_API_URL } from "./utils.js";
4
+ import { resolveItemFromUrl } from "./sharepointUtils.js";
5
+ const DEFAULT_MAX_ITEMS = 200;
6
+ const PAGE_SIZE = 200;
7
+ function buildChildrenUrl(driveId, itemId) {
8
+ const base = itemId === "root"
9
+ ? `${MICROSOFT_GRAPH_API_URL}/drives/${driveId}/root/children`
10
+ : `${MICROSOFT_GRAPH_API_URL}/drives/${driveId}/items/${itemId}/children`;
11
+ return `${base}?$top=${PAGE_SIZE}`;
12
+ }
13
+ /**
14
+ * Resolve a pasted URL to the folder to enumerate. Throws when the URL points at
15
+ * something that cannot be listed (file, page); the action's catch maps the message
16
+ * onto the error output.
17
+ */
18
+ async function resolveRootFolderFromUrl(authToken, url, headers) {
19
+ const resolved = await resolveItemFromUrl(authToken, url);
20
+ if (resolved.itemType === "file") {
21
+ throw new Error("The URL points to a file, not a folder. Use readSharepointContent instead.");
22
+ }
23
+ if (resolved.itemType === "page") {
24
+ throw new Error("The URL points to a site page, not a folder. Use readSharepointContent instead.");
25
+ }
26
+ if (resolved.itemType === "site") {
27
+ // A site URL lists the root of the site's default document library
28
+ const driveResponse = await axiosClient.get(`${MICROSOFT_GRAPH_API_URL}/sites/${resolved.siteId}/drive`, {
29
+ headers,
30
+ });
31
+ return { driveId: driveResponse.data.id, itemId: "root" };
32
+ }
33
+ return { driveId: resolved.driveId, itemId: resolved.itemId };
34
+ }
35
+ const listSharepointFolder = async ({ params, authParams, }) => {
36
+ if (!authParams.authToken) {
37
+ return { success: false, error: MISSING_AUTH_TOKEN };
38
+ }
39
+ const { url, driveItem, recursive = false } = params;
40
+ if (params.maxItems != null && (!Number.isFinite(params.maxItems) || params.maxItems <= 0)) {
41
+ return { success: false, error: "maxItems must be a positive number" };
42
+ }
43
+ const maxItems = params.maxItems ?? DEFAULT_MAX_ITEMS;
44
+ if (!url && !driveItem) {
45
+ return { success: false, error: "Either url or driveItem must be provided" };
46
+ }
47
+ const headers = { Authorization: `Bearer ${authParams.authToken}` };
48
+ try {
49
+ const rootFolder = driveItem ?? (await resolveRootFolderFromUrl(authParams.authToken, url, headers));
50
+ const items = [];
51
+ let truncated = false;
52
+ const queue = [rootFolder];
53
+ outer: while (queue.length > 0) {
54
+ const folder = queue.shift();
55
+ let nextUrl = buildChildrenUrl(folder.driveId, folder.itemId);
56
+ while (nextUrl) {
57
+ const response = await axiosClient.get(nextUrl, { headers });
58
+ const children = response.data.value ?? [];
59
+ for (const child of children) {
60
+ if (items.length >= maxItems) {
61
+ truncated = true;
62
+ break outer;
63
+ }
64
+ const childDriveId = child.parentReference?.driveId ?? folder.driveId;
65
+ items.push({
66
+ itemId: child.id ?? "",
67
+ driveId: childDriveId,
68
+ name: child.name ?? "",
69
+ itemType: child.folder ? "folder" : "file",
70
+ mimeType: child.file?.mimeType,
71
+ sizeBytes: child.size,
72
+ webUrl: child.webUrl,
73
+ lastModified: child.lastModifiedDateTime,
74
+ });
75
+ if (recursive && child.folder && child.id) {
76
+ queue.push({ driveId: childDriveId, itemId: child.id });
77
+ }
78
+ }
79
+ nextUrl = response.data["@odata.nextLink"];
80
+ }
81
+ }
82
+ return { success: true, items, truncated };
83
+ }
84
+ catch (error) {
85
+ console.error("Error listing SharePoint folder", error);
86
+ return {
87
+ success: false,
88
+ error: error instanceof Error ? error.message : "Unknown error",
89
+ };
90
+ }
91
+ };
92
+ export default listSharepointFolder;
@@ -0,0 +1,3 @@
1
+ import type { microsoftReadSharepointContentFunction } from "../../autogen/types.js";
2
+ declare const readSharepointContent: microsoftReadSharepointContentFunction;
3
+ export default readSharepointContent;
@@ -0,0 +1,157 @@
1
+ import mammoth from "mammoth";
2
+ import officeParser from "officeparser";
3
+ import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
4
+ import { ApiError, axiosClient } from "../../util/axiosClient.js";
5
+ import { extractTextFromPdf } from "../../../utils/pdf.js";
6
+ import { parseWorkbookBufferToPlainText } from "../../../utils/google.js";
7
+ import { MICROSOFT_GRAPH_API_URL } from "./utils.js";
8
+ import { MISSING_SITES_SCOPE_MESSAGE, resolveItemFromUrl } from "./sharepointUtils.js";
9
+ const DEFAULT_FILE_SIZE_LIMIT_MB = 50;
10
+ const TEXT_MIME_TYPES = new Set([
11
+ "text/plain",
12
+ "text/html",
13
+ "text/csv",
14
+ "text/tab-separated-values",
15
+ "application/rtf",
16
+ "application/json",
17
+ ]);
18
+ function stripHtml(html) {
19
+ return html
20
+ .replace(/<(script|style)[^>]*>[\s\S]*?<\/\1>/gi, " ")
21
+ .replace(/<[^>]+>/g, " ")
22
+ .replace(/&nbsp;/gi, " ")
23
+ .replace(/&amp;/gi, "&")
24
+ .replace(/&lt;/gi, "<")
25
+ .replace(/&gt;/gi, ">")
26
+ .replace(/&quot;/gi, '"')
27
+ .replace(/&#39;/gi, "'")
28
+ .replace(/[ \t]+/g, " ")
29
+ .replace(/\s*\n\s*/g, "\n")
30
+ .trim();
31
+ }
32
+ function buildResult(name, url, content, charLimit) {
33
+ const originalLength = content.length;
34
+ let finalContent = content.trim();
35
+ if (charLimit && charLimit > 0 && finalContent.length > charLimit) {
36
+ finalContent = `${finalContent.slice(0, charLimit)}\n\n[Content truncated to ${charLimit} characters]`;
37
+ }
38
+ return {
39
+ success: true,
40
+ results: [{ name, url, contents: { content: finalContent, fileName: name, fileLength: originalLength } }],
41
+ };
42
+ }
43
+ async function convertFileBuffer(mimeType, buffer, charLimit) {
44
+ if (mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ||
45
+ mimeType === "application/msword") {
46
+ const result = await mammoth.extractRawText({ buffer });
47
+ return result.value ?? "";
48
+ }
49
+ if (mimeType === "application/pdf") {
50
+ return await extractTextFromPdf(buffer);
51
+ }
52
+ if (mimeType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
53
+ mimeType === "application/vnd.ms-excel") {
54
+ return parseWorkbookBufferToPlainText(buffer, charLimit);
55
+ }
56
+ if (mimeType === "application/vnd.openxmlformats-officedocument.presentationml.presentation") {
57
+ return await officeParser.parseOfficeAsync(buffer);
58
+ }
59
+ if (TEXT_MIME_TYPES.has(mimeType) || mimeType.startsWith("text/")) {
60
+ return buffer.toString("utf-8");
61
+ }
62
+ throw new Error(`Unsupported file type: ${mimeType}`);
63
+ }
64
+ async function readFileContent(file, headers, charLimit, fileSizeLimit) {
65
+ const maxMegabytes = fileSizeLimit && fileSizeLimit > 0 ? fileSizeLimit : DEFAULT_FILE_SIZE_LIMIT_MB;
66
+ const maxFileSize = maxMegabytes * 1024 * 1024;
67
+ if (file.sizeBytes !== undefined && file.sizeBytes > maxFileSize) {
68
+ const actualMegabytes = (file.sizeBytes / (1024 * 1024)).toFixed(1);
69
+ return { success: false, error: `File too large: ${actualMegabytes}MB exceeds the ${maxMegabytes}MB limit` };
70
+ }
71
+ const downloadResponse = await axiosClient.get(`${MICROSOFT_GRAPH_API_URL}/drives/${file.driveId}/items/${file.itemId}/content`, { headers, responseType: "arraybuffer" });
72
+ const buffer = Buffer.from(downloadResponse.data);
73
+ const content = await convertFileBuffer(file.mimeType ?? "", buffer, charLimit);
74
+ return buildResult(file.name ?? "", file.webUrl ?? "", content, charLimit);
75
+ }
76
+ async function readPageContent(page, headers, charLimit) {
77
+ if (!page.pageId) {
78
+ return { success: false, error: "Could not identify the site page from the URL" };
79
+ }
80
+ try {
81
+ const response = await axiosClient.get(`${MICROSOFT_GRAPH_API_URL}/sites/${page.siteId}/pages/${page.pageId}/microsoft.graph.sitePage/webparts`, { headers });
82
+ const webparts = response.data.value ?? [];
83
+ const content = webparts
84
+ .map(webpart => (webpart.innerHtml ? stripHtml(webpart.innerHtml) : ""))
85
+ .filter(Boolean)
86
+ .join("\n\n");
87
+ return buildResult(page.name ?? "", page.webUrl ?? "", content, charLimit);
88
+ }
89
+ catch (error) {
90
+ if (error instanceof ApiError && (error.status === 401 || error.status === 403)) {
91
+ return { success: false, error: MISSING_SITES_SCOPE_MESSAGE };
92
+ }
93
+ throw error;
94
+ }
95
+ }
96
+ /**
97
+ * Resolve the target document from either a driveItem reference or a URL. The driveItem
98
+ * takes precedence over url when both are provided (it is an exact round-trip token; a
99
+ * url would be re-resolved and could name a different item).
100
+ */
101
+ async function resolveTargetItem(authToken, params, headers) {
102
+ const { url, driveItem } = params;
103
+ if (driveItem) {
104
+ const { driveId, itemId } = driveItem;
105
+ const metadataResponse = await axiosClient.get(`${MICROSOFT_GRAPH_API_URL}/drives/${driveId}/items/${itemId}`, {
106
+ headers,
107
+ });
108
+ const item = metadataResponse.data;
109
+ return {
110
+ itemType: item.folder ? "folder" : "file",
111
+ driveId,
112
+ itemId,
113
+ name: item.name,
114
+ webUrl: item.webUrl,
115
+ mimeType: item.file?.mimeType,
116
+ sizeBytes: item.size,
117
+ };
118
+ }
119
+ return resolveItemFromUrl(authToken, url);
120
+ }
121
+ const readSharepointContent = async ({ params, authParams, }) => {
122
+ if (!authParams.authToken) {
123
+ return { success: false, error: MISSING_AUTH_TOKEN };
124
+ }
125
+ const { url, driveItem, charLimit, fileSizeLimit } = params;
126
+ if (charLimit != null && (!Number.isFinite(charLimit) || charLimit <= 0)) {
127
+ return { success: false, error: "charLimit must be a positive number" };
128
+ }
129
+ if (fileSizeLimit != null && (!Number.isFinite(fileSizeLimit) || fileSizeLimit <= 0)) {
130
+ return { success: false, error: "fileSizeLimit must be a positive number" };
131
+ }
132
+ if (!url && !driveItem) {
133
+ return { success: false, error: "Either url or driveItem must be provided" };
134
+ }
135
+ const headers = { Authorization: `Bearer ${authParams.authToken}` };
136
+ try {
137
+ const resolved = await resolveTargetItem(authParams.authToken, { url, driveItem }, headers);
138
+ if (resolved.itemType === "folder" || resolved.itemType === "site") {
139
+ return {
140
+ success: false,
141
+ error: `The URL points to a ${resolved.itemType}, not a document. Use listSharepointFolder to enumerate it and read files individually.`,
142
+ };
143
+ }
144
+ if (resolved.itemType === "page") {
145
+ return await readPageContent(resolved, headers, charLimit);
146
+ }
147
+ return await readFileContent(resolved, headers, charLimit, fileSizeLimit);
148
+ }
149
+ catch (error) {
150
+ console.error("Error reading SharePoint content", error);
151
+ return {
152
+ success: false,
153
+ error: error instanceof Error ? error.message : "Unknown error",
154
+ };
155
+ }
156
+ };
157
+ export default readSharepointContent;
@@ -0,0 +1,3 @@
1
+ import type { microsoftSearchSharepointFunction } from "../../autogen/types.js";
2
+ declare const searchSharepoint: microsoftSearchSharepointFunction;
3
+ export default searchSharepoint;
@@ -0,0 +1,147 @@
1
+ import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
2
+ import { axiosClient } from "../../util/axiosClient.js";
3
+ import { MICROSOFT_GRAPH_API_URL } from "./utils.js";
4
+ import { MISSING_SITES_SCOPE_MESSAGE, isSiteScopeError, resolveItemFromUrl } from "./sharepointUtils.js";
5
+ const DEFAULT_LIMIT = 25;
6
+ const SEARCH_PAGE_SIZE = 100;
7
+ function mapDriveItem(item, snippet) {
8
+ return {
9
+ itemId: item.id ?? "",
10
+ driveId: item.parentReference?.driveId,
11
+ name: item.name ?? "",
12
+ mimeType: item.file?.mimeType,
13
+ webUrl: item.webUrl,
14
+ snippet,
15
+ lastModified: item.lastModifiedDateTime,
16
+ };
17
+ }
18
+ function dedupeByItemIdKeepFirst(files) {
19
+ const seen = new Set();
20
+ return files.filter(file => {
21
+ if (seen.has(file.itemId))
22
+ return false;
23
+ seen.add(file.itemId);
24
+ return true;
25
+ });
26
+ }
27
+ /** POST /search/query, optionally KQL path-scoped — requires Sites.Read.All. */
28
+ async function searchWithSearchApi(query, pathScope, limit, headers) {
29
+ const queryString = pathScope ? `${query} path:"${pathScope}"` : query;
30
+ const files = [];
31
+ let truncated = false;
32
+ let from = 0;
33
+ let moreResultsAvailable = false;
34
+ do {
35
+ const response = await axiosClient.post(`${MICROSOFT_GRAPH_API_URL}/search/query`, {
36
+ requests: [
37
+ {
38
+ entityTypes: ["driveItem"],
39
+ query: { queryString },
40
+ from,
41
+ size: SEARCH_PAGE_SIZE,
42
+ },
43
+ ],
44
+ }, { headers });
45
+ const container = response.data?.value?.[0]?.hitsContainers?.[0];
46
+ const hits = container?.hits ?? [];
47
+ moreResultsAvailable = Boolean(container?.moreResultsAvailable);
48
+ if (hits.length === 0)
49
+ break;
50
+ // Truncation is detected in two places because each sees a case the other can't:
51
+ // here, stopping mid-page with unconsumed hits in hand (moreResultsAvailable can be
52
+ // false then — it only signals more pages on the server, not leftovers in this page);
53
+ for (const hit of hits) {
54
+ if (files.length >= limit) {
55
+ truncated = true;
56
+ break;
57
+ }
58
+ files.push(mapDriveItem(hit.resource ?? {}, hit.summary));
59
+ }
60
+ from += hits.length;
61
+ } while (!truncated && moreResultsAvailable && files.length < limit);
62
+ // and here, the limit exactly consumed the page (no hit was encountered past the
63
+ // limit, so the in-loop check never fired) while the server still has more pages.
64
+ if (files.length >= limit && moreResultsAvailable) {
65
+ truncated = true;
66
+ }
67
+ return { success: true, files: dedupeByItemIdKeepFirst(files), truncated };
68
+ }
69
+ /** Item-scoped GET /drives/{driveId}/items/{itemId}/search — works with Files.Read.All alone. */
70
+ async function searchWithinFolder(driveId, itemId, query, limit, headers) {
71
+ const escapedQuery = encodeURIComponent(query.replace(/'/g, "''"));
72
+ const base = itemId === "root"
73
+ ? `${MICROSOFT_GRAPH_API_URL}/drives/${driveId}/root`
74
+ : `${MICROSOFT_GRAPH_API_URL}/drives/${driveId}/items/${itemId}`;
75
+ const files = [];
76
+ let truncated = false;
77
+ let nextUrl = `${base}/search(q='${escapedQuery}')?$top=${SEARCH_PAGE_SIZE}`;
78
+ while (!truncated && nextUrl) {
79
+ const response = await axiosClient.get(nextUrl, { headers });
80
+ const items = response.data.value ?? [];
81
+ for (const item of items) {
82
+ if (files.length >= limit) {
83
+ truncated = true;
84
+ break;
85
+ }
86
+ files.push(mapDriveItem(item));
87
+ }
88
+ nextUrl = response.data["@odata.nextLink"];
89
+ }
90
+ if (files.length >= limit && nextUrl) {
91
+ truncated = true;
92
+ }
93
+ return { success: true, files: dedupeByItemIdKeepFirst(files), truncated };
94
+ }
95
+ const searchSharepoint = async ({ params, authParams, }) => {
96
+ if (!authParams.authToken) {
97
+ return { success: false, error: MISSING_AUTH_TOKEN };
98
+ }
99
+ const { query, scopeUrl } = params;
100
+ if (params.limit != null && (!Number.isFinite(params.limit) || params.limit <= 0)) {
101
+ return { success: false, error: "limit must be a positive number" };
102
+ }
103
+ const limit = params.limit ?? DEFAULT_LIMIT;
104
+ const headers = { Authorization: `Bearer ${authParams.authToken}` };
105
+ try {
106
+ if (!scopeUrl) {
107
+ try {
108
+ return await searchWithSearchApi(query, undefined, limit, headers);
109
+ }
110
+ catch (error) {
111
+ if (isSiteScopeError(error)) {
112
+ return { success: false, error: MISSING_SITES_SCOPE_MESSAGE };
113
+ }
114
+ throw error;
115
+ }
116
+ }
117
+ // Resolving the scopeUrl decides the search mode: folders and library roots use an
118
+ // item-scoped drive search (Files.Read.All alone), sites use the path-filtered Search API
119
+ const resolved = await resolveItemFromUrl(authParams.authToken, scopeUrl);
120
+ if (resolved.itemType === "folder") {
121
+ return await searchWithinFolder(resolved.driveId, resolved.itemId, query, limit, headers);
122
+ }
123
+ if (resolved.itemType === "site") {
124
+ try {
125
+ return await searchWithSearchApi(query, resolved.webUrl ?? scopeUrl, limit, headers);
126
+ }
127
+ catch (error) {
128
+ if (isSiteScopeError(error)) {
129
+ return { success: false, error: MISSING_SITES_SCOPE_MESSAGE };
130
+ }
131
+ throw error;
132
+ }
133
+ }
134
+ return {
135
+ success: false,
136
+ error: `scopeUrl points to a ${resolved.itemType === "page" ? "site page" : "file"} — provide a folder, document library, or site URL`,
137
+ };
138
+ }
139
+ catch (error) {
140
+ console.error("Error searching SharePoint", error);
141
+ return {
142
+ success: false,
143
+ error: error instanceof Error ? error.message : "Unknown error",
144
+ };
145
+ }
146
+ };
147
+ export default searchSharepoint;
@@ -0,0 +1,3 @@
1
+ import type { microsoftSendOutlookEmailFunction } from "../../autogen/types.js";
2
+ declare const sendEmail: microsoftSendOutlookEmailFunction;
3
+ export default sendEmail;
@@ -0,0 +1,48 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { getGraphClient } from "./utils.js";
11
+ const sendEmail = (_a) => __awaiter(void 0, [_a], void 0, function* ({ params, authParams, }) {
12
+ const { toRecipients, subject, body, ccRecipients, bccRecipients } = params;
13
+ let client = undefined;
14
+ try {
15
+ client = yield getGraphClient(authParams);
16
+ }
17
+ catch (error) {
18
+ return {
19
+ success: false,
20
+ error: "Error while authorizing: " + (error instanceof Error ? error.message : "Unknown error"),
21
+ };
22
+ }
23
+ try {
24
+ const message = {
25
+ message: Object.assign(Object.assign({ subject, body: {
26
+ contentType: "HTML",
27
+ content: body,
28
+ }, toRecipients: toRecipients.map(email => ({ emailAddress: { address: email } })) }, (ccRecipients && ccRecipients.length > 0
29
+ ? { ccRecipients: ccRecipients.map(email => ({ emailAddress: { address: email } })) }
30
+ : {})), (bccRecipients && bccRecipients.length > 0
31
+ ? { bccRecipients: bccRecipients.map(email => ({ emailAddress: { address: email } })) }
32
+ : {})),
33
+ saveToSentItems: true,
34
+ };
35
+ yield client.api("/me/sendMail").post(message);
36
+ return {
37
+ success: true,
38
+ };
39
+ }
40
+ catch (error) {
41
+ console.error(error);
42
+ return {
43
+ success: false,
44
+ error: "Error sending email: " + (error instanceof Error ? error.message : "Unknown error"),
45
+ };
46
+ }
47
+ });
48
+ export default sendEmail;