UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

15 lines (14 loc) 7.03 kB
import{existsSync}from"node:fs";import{dirname,join}from"node:path";import{mkdir,readFile,rm,stat,writeFile}from"node:fs/promises";import{spawn}from"node:child_process";import{EVE_DEV_ENV_FLAG,isEveDevEnvironment}from"#internal/application/dev-environment.js";import{pathToFileURL}from"node:url";import{Worker}from"node:worker_threads";function detectProjectPackageManager(r){let i=r;for(;;){if(existsSync(join(i,`pnpm-lock.yaml`))||existsSync(join(i,`pnpm-workspace.yaml`)))return`pnpm`;if(existsSync(join(i,`yarn.lock`)))return`yarn`;if(existsSync(join(i,`bun.lock`))||existsSync(join(i,`bun.lockb`)))return`bun`;if(existsSync(join(i,`package-lock.json`)))return`npm`;let r=dirname(i);if(r===i)return`npm`;i=r}}const INSTALL_ARGUMENTS={bun:[`add`,`--dev`],npm:[`install`,`--save-dev`],pnpm:[`add`,`-D`],yarn:[`add`,`-D`]},OPTIONAL_PACKAGE_LOCK_TIMEOUT_MS=600*1e3,pendingOptionalPackageInstalls=new Map;async function installPackageIntoProject(e){let t=detectProjectPackageManager(e.appRoot),n=[...INSTALL_ARGUMENTS[t],e.packageName];console.info(`[eve:dev] installing optional dependency "${e.packageName}" via \`${t} ${n.join(` `)}\`...`);let r=spawn(t,n,{cwd:e.appRoot,shell:shouldSpawnPackageManagerWithShell(),stdio:[`ignore`,`pipe`,`pipe`]}),i=[];r.stdout?.on(`data`,e=>i.push(e)),r.stderr?.on(`data`,e=>i.push(e));let a=await new Promise((e,t)=>{r.on(`error`,t),r.on(`close`,t=>e(t??1))});if(a!==0){let n=Buffer.concat(i).toString(`utf8`).trim();throw Error(`Failed to install "${e.packageName}" with ${t} (exit ${a}).`+(n.length>0?`\n${n.slice(-2e3)}`:``))}console.info(`[eve:dev] installed "${e.packageName}".`)}async function loadOptionalEnginePackage(e){try{return await e.importModule()}catch(t){if(!e.autoInstall||!isEveDevEnvironment())throw Error(e.missingMessage,{cause:t});let n=e.importInstalledModule??(async()=>await importInstalledEnginePackage({appRoot:e.appRoot,packageName:e.packageName})),r=e.importInstalledModule===void 0?async()=>await isInstalledEnginePackageLoadable(e):async()=>{try{return await n(),!0}catch{return!1}};try{await withOptionalPackageInstallLock(e,async()=>{await r()||await installPackageIntoProject({appRoot:e.appRoot,packageName:e.packageName})})}catch(t){throw Error(`${e.missingMessage} Automatic installation failed: ${toMessage(t)}`,{cause:t})}try{return await n()}catch(t){throw Error(`${e.missingMessage} Automatic installation completed, but "${e.packageName}" still could not be loaded from "${e.appRoot}". This usually means the package manager installed into a different workspace, node_modules is unavailable, or the Node/package-manager environment changed while eve dev was running. Last load error: ${toMessage(t)}`,{cause:t})}}}async function importInstalledEnginePackage(e){return await import(await resolveInstalledEnginePackageEntrypointHref(e))}async function isInstalledEnginePackageLoadable(e){try{return await importEntrypointInWorker(await resolveInstalledEnginePackageEntrypointHref(e)),!0}catch{return!1}}async function resolveInstalledEnginePackageEntrypointHref(e){let t=findInstalledPackageRoot(e),r=join(t,`package.json`),a=resolvePackageEntryPoint(JSON.parse(await readFile(r,`utf8`)));if(a.startsWith(`/`))throw Error(`Invalid absolute entrypoint for optional package "${e.packageName}".`);return pathToFileURL(join(t,a)).href}async function importEntrypointInWorker(e){let t=new Worker(` const { parentPort, workerData } = require("node:worker_threads"); (async () => { try { await import(workerData.entrypointHref); parentPort.postMessage({ ok: true }); } catch (error) { parentPort.postMessage({ ok: false, message: error instanceof Error ? error.message : String(error), }); } })(); `,{eval:!0,workerData:{entrypointHref:e}});await new Promise((e,n)=>{let r=!1,settle=e=>{r||(r=!0,t.off(`error`,handleError),t.off(`exit`,handleExit),t.off(`message`,handleMessage),t.terminate().catch(()=>{}),e())},handleError=e=>settle(()=>n(e)),handleExit=e=>{e!==0&&settle(()=>n(Error(`Optional dependency worker exited before loading module (code ${e}).`)))},handleMessage=t=>{let r=t;settle(()=>{if(r.ok===!0){e();return}n(Error(typeof r.message==`string`?r.message:`Optional dependency worker failed to load module.`))})};t.once(`error`,handleError),t.once(`exit`,handleExit),t.once(`message`,handleMessage)})}function findInstalledPackageRoot(r){let i=r.packageName.split(`/`),a=[],o=r.appRoot;for(;;){let s=join(o,`node_modules`,...i);if(a.push(s),existsSync(join(s,`package.json`)))return s;let c=dirname(o);if(c===o)throw Error(`Could not find installed optional dependency "${r.packageName}". Checked: ${a.join(`, `)}`);o=c}}function resolvePackageEntryPoint(e){let t=readDotExport(e.exports);return t===void 0?typeof e.module==`string`&&e.module.length>0?e.module:typeof e.main==`string`&&e.main.length>0?e.main:`index.js`:t}function readDotExport(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e!=`object`||!e)return;let t=`.`in e?e[`.`]:e;if(typeof t==`string`&&t.length>0)return t;if(typeof t!=`object`||!t)return;let n=t;if(typeof n.import==`string`&&n.import.length>0)return n.import;if(typeof n.default==`string`&&n.default.length>0)return n.default}async function withOptionalPackageInstallLock(e,t){let n=`${e.appRoot}:${e.packageName}`,r=pendingOptionalPackageInstalls.get(n);if(r!==void 0){await r;return}let i=withOptionalPackageInstallFileLock(e,t).finally(()=>{pendingOptionalPackageInstalls.delete(n)});pendingOptionalPackageInstalls.set(n,i),await i}async function withOptionalPackageInstallFileLock(e,t){let r=join(e.appRoot,`.eve`,`optional-package-locks`,`${sanitizeLockName(e.packageName)}.lock`);await acquireLock(r);try{await t()}finally{await rm(r,{force:!0,recursive:!0}).catch(()=>{})}}async function acquireLock(e){let i=Date.now();for(;;){await mkdir(dirname(e),{recursive:!0});try{await mkdir(e),await writeFile(join(e,`owner.json`),`${JSON.stringify({createdAt:new Date().toISOString(),pid:process.pid})}\n`);return}catch(t){if(!isFileExistsError(t))throw t;await waitForExistingLock(e,i)}}}async function waitForExistingLock(e,t){let n=await stat(e).catch(e=>{if(isNotFoundError(e))return null;throw e});if(n!==null){if(Date.now()-n.mtimeMs>18e5){await rm(e,{force:!0,recursive:!0}).catch(()=>{});return}if(Date.now()-t>OPTIONAL_PACKAGE_LOCK_TIMEOUT_MS)throw Error(`Timed out waiting for optional package install lock "${e}" after ${OPTIONAL_PACKAGE_LOCK_TIMEOUT_MS}ms.`);await new Promise(e=>setTimeout(e,250))}}function sanitizeLockName(e){return e.replaceAll(/[^a-zA-Z0-9._-]+/g,`-`)}function shouldSpawnPackageManagerWithShell(e=process.platform){return e===`win32`}function isFileExistsError(e){return typeof e==`object`&&!!e&&`code`in e&&e.code===`EEXIST`}function isNotFoundError(e){return typeof e==`object`&&!!e&&`code`in e&&e.code===`ENOENT`}function toMessage(e){return e instanceof Error?e.message:String(e)}export{EVE_DEV_ENV_FLAG,detectProjectPackageManager,importInstalledEnginePackage,installPackageIntoProject,isEveDevEnvironment,loadOptionalEnginePackage};