eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
1 lines • 3.49 kB
JavaScript
import{createLogger}from"#internal/logging.js";import{isObject}from"#shared/guards.js";import{evaluateFilePart,formatUploadPolicyViolation,isUploadsDisabled,mergeUploadPolicy}from"#public/channels/upload-policy.js";import{EveAttachmentError}from"#internal/attachments/errors.js";import{resolveTeamsAccessToken}from"#public/channels/teams/api.js";const log=createLogger(`teams.attachments`),BOT_CONNECTOR_HOSTS=new Set([`smba.trafficmanager.net`,`smba.infra.gcc.teams.microsoft.com`,`smba.infra.gov.teams.microsoft.us`,`smba.infra.dod.teams.microsoft.us`]),REDIRECT_STATUSES=new Set([301,302,303,307,308]);function normalizeTeamsFilesPolicy(e){return{allowedHosts:e?.allowedHosts??[],enabled:e?.enabled===!0,uploadPolicy:mergeUploadPolicy(e?.uploadPolicy)}}function collectTeamsFileParts(e,t){if(!t.enabled||isUploadsDisabled(t.uploadPolicy))return[];let n=[];for(let r of e){let e=toTeamsFilePart(r,n.length,t);if(e===null)continue;let i=evaluateFilePart(e,t.uploadPolicy);if(i!==null){log.warn(`dropped Teams attachment — ${formatUploadPolicyViolation(i)}`,{name:r.name});continue}n.push(e)}return n}function buildTeamsTurnMessage(e,t){return t.length===0?e:e.trim().length===0?[...t]:[{type:`text`,text:e},...t]}function createTeamsFetchFile(e,t={}){return async n=>{if(!e.enabled||!isAllowedUrl(n,e.allowedHosts))return null;let{response:r,finalUrl:i}=await fetchTeamsFile(n,e.allowedHosts,t);if(!r.ok)throw attachmentError(`Teams file fetch returned HTTP ${r.status} for host ${i.hostname}.`);return{bytes:Buffer.from(await r.arrayBuffer()),mediaType:r.headers.get(`content-type`)??void 0}}}async function fetchTeamsFile(e,t,n){let r=n.fetch??fetch,i=new URL(e),a;for(let e=0;e<=5;e+=1){let o=i.port===``&&BOT_CONNECTOR_HOSTS.has(i.hostname),s=new Headers;o&&(a??=await resolveTeamsAccessToken(n),s.set(`authorization`,`Bearer ${a}`));let c=await r(i,{headers:s,redirect:`manual`});if(!REDIRECT_STATUSES.has(c.status))return{finalUrl:i,response:c};if(e===5)throw attachmentError(`Teams file fetch exceeded 5 redirects.`);let l=c.headers.get(`location`);if(l===null)throw attachmentError(`Teams file fetch redirect from host ${i.hostname} had no location.`);let u=new URL(l,i);if(!isAllowedUrl(u.href,t))throw attachmentError(`Teams file fetch redirect to host ${u.hostname} is not allowed.`);i=u}throw attachmentError(`Teams file fetch exceeded 5 redirects.`)}function attachmentError(e){return new EveAttachmentError({adapterKind:`teams`,kind:`resolver-threw`,message:e})}function toTeamsFilePart(e,t,n){let r=readAttachmentUrl(e);return!r||!isAllowedUrl(r,n.allowedHosts)?null:{data:new URL(r),filename:e.name??`teams-attachment-${t}`,mediaType:inferMediaType(e),type:`file`}}function readAttachmentUrl(e){return e.contentType===`application/vnd.microsoft.teams.file.download.info`&&isObject(e.content)&&typeof e.content.downloadUrl==`string`?e.content.downloadUrl:e.contentUrl&&!e.contentType.startsWith(`application/vnd.microsoft.card.`)?e.contentUrl:null}function inferMediaType(e){return e.contentType===`application/vnd.microsoft.teams.file.download.info`&&(isObject(e.content)&&typeof e.content.fileType==`string`?e.content.fileType:void 0)===`txt`?`text/plain`:e.contentType||`application/octet-stream`}function isAllowedUrl(e,t){let n;try{n=new URL(e)}catch{return!1}return n.protocol===`https:`?t===`*`||t.some(e=>e.includes(`:`)?n.host===e:n.port===``&&n.hostname===e):!1}export{buildTeamsTurnMessage,collectTeamsFileParts,createTeamsFetchFile,normalizeTeamsFilesPolicy};