eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
1 lines • 4.07 kB
JavaScript
import{readdir,rm,stat}from"node:fs/promises";import{createLogger,formatError}from"#internal/logging.js";import{join}from"node:path";import{resolveLocalTraceSchemaDirectory,resolveLocalTraceSegmentsDirectory}from"#tracing/local-trace-span-processor.js";const log=createLogger(`harness.local-trace-retention`),LOCAL_TRACE_QUIESCENCE_MS=3e5,LOCAL_TRACE_MAX_AGE_MS=6048e5,LOCAL_TRACE_MAX_TOTAL_BYTES=536870912;function resolveLocalTraceRetentionSettings(e=process.env){return{enabled:!isDisabled(e.EVE_TRACES),maxAgeMs:readBound(e,`EVE_TRACES_MAX_AGE_MS`,LOCAL_TRACE_MAX_AGE_MS),maxTotalBytes:readBound(e,`EVE_TRACES_MAX_TOTAL_BYTES`,LOCAL_TRACE_MAX_TOTAL_BYTES),retainCount:readBound(e,`EVE_TRACES_RETAIN_COUNT`,20)}}async function pruneLocalTraceStore(e){let t=e.now??Date.now(),n=clampBound(e.maxAgeMs??LOCAL_TRACE_MAX_AGE_MS),r=clampBound(e.maxTotalBytes??LOCAL_TRACE_MAX_TOTAL_BYTES),i=clampCount(e.retainCount??20),a=await readStoredTraces(e.appRoot,t),o=[...a].sort((e,t)=>t.modifiedAtMs-e.modifiedAtMs),s=new Set(o.slice(0,i===!1?0:i)),c=new Set,l=a.reduce((e,t)=>e+t.byteSize,0),u=0,d=0,f=0;for(let i of o.toReversed()){let a=t-i.modifiedAtMs,o=e.activeTraceIds.has(i.traceId)||s.has(i)||a<=LOCAL_TRACE_QUIESCENCE_MS?void 0:n!==!1&&a>n?`maxAgeMs`:r!==!1&&l>r?`maxTotalBytes`:void 0;if(o===void 0){f+=1;continue}await rm(i.path,{force:!0,recursive:!0}),l-=i.byteSize,u+=i.byteSize,d+=1,c.add(o)}return{reasons:[...c],reclaimedBytes:u,removedTraces:d,retainedTraces:f}}const pruneRequest={input:{activeTraceIds:new Set,appRoot:``},requested:!1,running:void 0};function requestLocalTraceStorePrune(e){pruneRequest.input=e,pruneRequest.requested=!0,pruneRequest.running===void 0&&startPruning()}function startPruning(){pruneRequest.running=(async()=>{for(;pruneRequest.requested;)pruneRequest.requested=!1,report(await pruneLocalTraceStore(pruneRequest.input))})().catch(e=>{log.warn(`local trace retention failed`,{error:formatError(e)})}).finally(()=>{pruneRequest.running=void 0,pruneRequest.requested&&startPruning()})}function report(e){e.removedTraces!==0&&log.info(`pruned local traces`,{reasons:e.reasons.join(`,`),reclaimedBytes:e.reclaimedBytes,removedTraces:e.removedTraces,retainedTraces:e.retainedTraces})}async function readStoredTraces(e,t){let n=resolveLocalTraceSchemaDirectory(e),r=await readDirectoryEntries(n);return r===void 0?[]:(await Promise.all(r.filter(e=>e.isDirectory()).map(async r=>{let i=join(n,r.name),a=resolveLocalTraceSegmentsDirectory(e,r.name);await reapAbandonedTemporaryFiles(a,t);let o=await directoryMtime(a)??await directoryMtime(i);if(o!==void 0)return{byteSize:await directorySize(i),modifiedAtMs:o,path:i,traceId:r.name}}))).filter(e=>e!==void 0)}async function reapAbandonedTemporaryFiles(e,t){let n=await readDirectoryEntries(e);if(n!==void 0)for(let r of n){if(!r.isFile()||!r.name.includes(`.tmp-`))continue;let n=join(e,r.name);try{if(t-(await stat(n)).mtimeMs<=LOCAL_TRACE_QUIESCENCE_MS)continue;await rm(n,{force:!0})}catch{}}}async function directoryMtime(e){try{return(await stat(e)).mtimeMs}catch{return}}async function directorySize(e){let t=await readDirectoryEntries(e);if(t===void 0)return 0;let n=0;for(let r of t){let t=join(e,r.name);if(r.isDirectory()){n+=await directorySize(t);continue}try{n+=(await stat(t)).size}catch{}}return n}async function readDirectoryEntries(e){try{return await readdir(e,{withFileTypes:!0})}catch(e){if(e instanceof Error&&`code`in e&&(e.code===`ENOENT`||e.code===`ENOTDIR`))return;throw e}}function isDisabled(e){if(e===void 0)return!1;let t=e.trim().toLowerCase();return t===`off`||t===`false`||t===`0`}function readBound(e,t,n){let r=e[t];if(r===void 0)return n;let i=r.trim().toLowerCase();if(i===`off`||i===`false`)return!1;let a=Number(i);return!Number.isFinite(a)||a<0?(log.warn(`ignoring invalid local trace retention value`,{fallback:n,name:t,value:r}),n):a}function clampBound(e){return e!==!1&&Math.max(0,e)}function clampCount(e){return e!==!1&&Math.max(0,Math.trunc(e))}export{pruneLocalTraceStore,requestLocalTraceStorePrune,resolveLocalTraceRetentionSettings};