eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
1 lines • 7.04 kB
JavaScript
import{randomUUID}from"node:crypto";import{expectDockerSuccess}from"#execution/sandbox/bindings/docker-utils.js";import{assertDockerDaemonAvailable,createDockerCli}from"#execution/sandbox/bindings/docker-cli.js";import{DOCKER_SANDBOX_LABEL,runDockerBaseSetup,startDockerContainer,stopDockerContainerIfRunning}from"#execution/sandbox/bindings/docker-container.js";import{setDockerNetworkPolicy}from"#execution/sandbox/bindings/docker-network.js";import{createDockerSandboxOptionsHash,resolveDockerSandboxOptions}from"#execution/sandbox/bindings/docker-options.js";import{createDockerInternalSession}from"#execution/sandbox/bindings/docker-session.js";import{DOCKER_TEMPLATE_IMAGE_REPOSITORY,dockerImageExists,dockerTemplateImageReference,ensureDockerBaseImage,pruneDockerSandboxTemplates,resolveDockerTemplateMarkerPath,touchDockerTemplateMarker}from"#execution/sandbox/bindings/docker-templates.js";import{buildSandboxDockerfile,dockerfileImageReference,materializeSandboxDockerfile}from"#execution/sandbox/dockerfile.js";import{hydrateSandboxProviderResources,prepareImmutableResources}from"#execution/sandbox/bindings/immutable-resources.js";import{createLoggingSandboxSession}from"#execution/sandbox/logging-session.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{createSandboxProviderIdentity}from"#execution/sandbox/provider-identity.js";import{isSandboxPreparedArtifactRecord,sandboxProviderResourceIdentity}from"#shared/sandbox-provider.js";import{SandboxTemplateNotProvisionedError}from"#shared/sandbox-template-error.js";const DOCKER_PROVIDER_NAME=`docker`;function createDockerSandboxProvider(e,t){let n=t??createDockerCli(),r=e??{},i=resolveDockerSandboxOptions(r),a=createDockerSandboxOptionsHash(i),o;function ensureDaemon(){return o??=assertDockerDaemonAvailable(n).catch(e=>{throw o=void 0,e}),o}async function openDockerSession(e,t,r,a,o){await ensureDaemon();let s=requirePreparedDockerArtifact(r);if(!await dockerImageExists(n,s.imageReference))throw new SandboxTemplateNotProvisionedError({providerName:DOCKER_PROVIDER_NAME,templateKey:s.imageReference});let c=await n.run([`container`,`inspect`,`--format`,`{{.State.Running}}`,a]);if(c.exitCode===0)c.stdout.trim()!==`true`&&expectDockerSuccess(await n.run([`start`,a]),`restart sandbox session container "${a}"`);else{if(!o)throw Error(`Docker sandbox session container "${a}" no longer exists.`);await startDockerContainer({cli:n,containerName:a,image:s.imageReference,initialNetworkPolicy:t?.networkPolicy??`allow-all`,options:i,role:`session`,tags:{agent:e.session.id,sessionId:e.session.id}})}let l=await resolveDockerHandleIdentity(n,a);return{sandbox:buildSandboxSession(createDockerInternalSession({cli:n,containerIdentity:l}),e=>setDockerNetworkPolicy(n,l,e)),async onSessionDelete(){await stopDockerContainerIfRunning(n,l),expectDockerSuccess(await n.run([`rm`,`-f`,l]),`delete sandbox session container "${a}"`)},async onSessionStop(){await stopDockerContainerIfRunning(n,l)},async onRuntimeShutdown(){await stopDockerContainerIfRunning(n,l)}}}return{async prepare(e){e.log?.(`checking Docker daemon`),await ensureDaemon();let t=await materializeSandboxDockerfile({files:e.files,storagePath:e.storagePath}),o=createSandboxProviderIdentity({dockerfile:t?.contentHash,optionsHash:a,resources:sandboxProviderResourceIdentity(e.resources),sourceRevision:e.sourceRevision,version:2}).slice(0,24),s={optionsHash:a,templateKey:o},c=dockerTemplateImageReference(s),l=e.resources.source,u=l.kind===`materialized`?await prepareImmutableResources({storagePath:e.storagePath,provider:DOCKER_PROVIDER_NAME,resourcesKey:l.key,sourcePath:l.path}):void 0,d=resolveDockerTemplateMarkerPath(e.storagePath,s);if(e.log?.(`checking cached template image "${c}"`),await dockerImageExists(n,c))return e.log?.(`reusing cached template image`),await touchDockerTemplateMarker(d,c),{imageReference:c};let f=i.image;t===void 0?(e.log?.(`checking base image "${i.image}"`),await ensureDockerBaseImage(n,i)):(f=dockerfileImageReference({dockerfile:t,templateKey:o}),e.log?.(`building sandbox Dockerfile "${t.path}"`),await buildSandboxDockerfile({cli:n,dockerfile:t,imageReference:f}));let p=`${o}-build-${randomUUID().slice(0,8)}`;e.log?.(`starting template build container`),await startDockerContainer({cli:n,containerName:p,image:f,initialNetworkPolicy:`allow-all`,options:i,resourcesPath:u,role:`template-build`});try{let t=await resolveDockerHandleIdentity(n,p);e.log?.(`preparing base runtime inside container`),await runDockerBaseSetup(n,t);let i=buildSandboxSession(createDockerInternalSession({cli:n,containerIdentity:t}),e=>setDockerNetworkPolicy(n,t,e));await hydrateSandboxProviderResources({copySkills:!0,log:e.log,resources:e.resources,session:i}),r.prepare!==void 0&&(e.log?.(`running sandbox preparation`),await r.prepare(createLoggingSandboxSession({log:e.log,session:i}))),e.log?.(`stopping template build container`),expectDockerSuccess(await n.run([`stop`,`-t`,`0`,t]),`stop template build container "${p}"`),e.log?.(`committing template image "${c}"`),expectDockerSuccess(await n.run([`commit`,`--change`,`LABEL ${DOCKER_SANDBOX_LABEL}=1`,`--change`,`LABEL ${DOCKER_SANDBOX_LABEL}.role=template`,`--change`,`LABEL ${DOCKER_SANDBOX_LABEL}.template-key=${o}`,t,c]),`commit sandbox template image "${c}"`),await touchDockerTemplateMarker(d,c)}finally{await n.run([`rm`,`-f`,p]).catch(()=>{})}return{imageReference:c}},async resume(e,t,n){let r=requireDockerSessionState(n);if(r.generation!==dockerGeneration(t,a))throw Error(`Docker sandbox session state is incompatible with this environment.`);return await openDockerSession(e,void 0,t,r.containerName,!1)},async start(e,t,n){let r=dockerSessionName(e.session.id,t,n,a);return{handle:await openDockerSession(e,t,n,r,!0),state:{containerName:r,generation:dockerGeneration(n,a),version:2}}}}}function dockerGeneration(e,t){return createSandboxProviderIdentity({artifact:e,environment:t,version:1})}function dockerSessionName(e,t,n,r){return`eve-sbx-${createSandboxProviderIdentity({artifact:n,environment:r,open:{networkPolicy:t?.networkPolicy},sessionId:e,version:1}).slice(0,32)}`}function requirePreparedDockerArtifact(e){if(!isSandboxPreparedArtifactRecord(e)||typeof e.imageReference!=`string`)throw Error(`Invalid prepared Docker artifact.`);return{imageReference:e.imageReference}}function requireDockerSessionState(e){if(!isSandboxPreparedArtifactRecord(e)||e.version!==2||typeof e.containerName!=`string`||typeof e.generation!=`string`)throw Error(`Invalid Docker sandbox session state.`);return{containerName:e.containerName,generation:e.generation,version:2}}async function resolveDockerHandleIdentity(e,t){let n=await e.run([`container`,`inspect`,`--format`,`{{.Id}}`,t]);expectDockerSuccess(n,`resolve sandbox container identity for "${t}"`);let r=n.stdout.trim();if(r.length===0)throw Error(`Docker returned an empty identity for sandbox container "${t}".`);return r}export{DOCKER_PROVIDER_NAME,DOCKER_TEMPLATE_IMAGE_REPOSITORY,createDockerSandboxProvider,pruneDockerSandboxTemplates};