@evolvingmachines/modal 0.0.59 → 0.0.60
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.
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var modal=require('modal'),tarStream=require('tar-stream');var p="c-
|
|
1
|
+
'use strict';var modal=require('modal'),tarStream=require('tar-stream');var p="c-4bfce70a407f";var D={[`evolve-all-${p}`]:`evolvingmachines/evolve-all:${p}`,"evolve-all":"evolvingmachines/evolve-all"},$=1440*60*1e3,g=8*1024*1024;async function*Q(r){let e=r.getReader();try{for(;;){let{done:t,value:n}=await e.read();if(t)break;n&&(yield n);}}finally{e.releaseLock();}}var N="user",T="evolve.image",B="evolve.startedAt",v=class extends Error{requestedTimeoutMs;constructor(e){let t=(e/36e5).toFixed(1);super(`Modal sandboxes have a hard 24h lifetime cap; requested timeout was ${t}h. For sessions longer than 24h, persist progress with Evolve checkpoints and resume in a fresh sandbox instead of extending the timeout.`),this.name="ModalSandboxLifetimeError",this.requestedTimeoutMs=e;}};function L(r){if(r>$)throw new v(r)}var f=class extends Error{requestedIdleTimeoutMs;constructor(e,t){super(`Modal idleTimeoutMs of ${e}ms is invalid: ${t}`),this.name="ModalIdleTimeoutError",this.requestedIdleTimeoutMs=e;}};function U(r){if(r===void 0)return {};if(!Number.isFinite(r)||r<=0)throw new f(r,"it must be a positive number of milliseconds");if(r>$)throw new f(r,"it exceeds Modal's 24h lifetime cap, so the sandbox would always die of the lifetime first");return {idleTimeoutMs:r}}var P=class extends Error{constructor(e){super(`Modal bootCommand is invalid: ${e}`),this.name="ModalBootCommandError";}};function V(r){if(r===void 0)return {};if(r.length===0)throw new P(`an empty argv is Modal's own "no args" wire shape and would boot the image's ENTRYPOINT/CMD \u2014 omit bootCommand to boot the image's command`);return {command:[...r]}}var m=class extends Error{reason;mountPath;volumeName;constructor(e,t,n,o,s){super(o,s===void 0?void 0:{cause:s}),this.name="ModalVolumeError",this.reason=e,this.mountPath=t,this.volumeName=n;}};function F(r){if(!r)return [];let e=[];for(let[t,n]of Object.entries(r)){if(!t.startsWith("/")||t==="/")throw new m("invalid-mount-path",t,n.name,`Modal Volume mount path ${JSON.stringify(t)} is invalid: it must be an absolute in-box path other than "/"`);let o=n.name?.trim()??"";if(o.length===0)throw new m("invalid-name",t,n.name??"",`Modal Volume mounted at ${t} has a blank name`);e.push({mountPath:t,name:o,readOnly:n.readOnly===true,createIfMissing:n.createIfMissing===true});}return e}function I(r,e,t,n){let o="";n&&Object.keys(n).length>0&&(o=Object.entries(n).filter(([,a])=>a!=null).map(([a,d])=>`export ${a}='${String(d).replace(/'/g,"'\\''")}'`).join("; ")+"; ");let s=t?`cd '${t.replace(/'/g,"'\\''")}' && ${o}${r}`:`${o}${r}`;if(e==="root")return ["bash","-c",s];let i=Buffer.from(s).toString("base64");return ["su",e,"-c",`echo ${i} | base64 -d | bash`]}var h=class extends Error{constructor(e){super(e),this.name="ModalResourcesError";}},Z=4,ee=4096;function W(r){if(r?.disk!==void 0)throw new h(`Modal's JS SDK has no create-time disk-size parameter, so a ${r.disk} GiB disk request cannot be enforced. Drop \`resources.disk\` (containers get Modal's default disk quota) or run on a provider that sizes disk.`);if((r?.gpu===void 0||r.gpu<=0)&&r?.gpuTypes?.length)throw new h("resources.gpuTypes was set without a positive resources.gpu count \u2014 declare how many GPUs to reserve, or drop gpuTypes.");let e=r?.gpu!==void 0&&r.gpu>0?`${r.gpuTypes?.length?r.gpuTypes[0]:"any"}:${r.gpu}`:void 0;return {cpu:r?.cpu??Z,memoryMiB:r?.memory!==void 0?Math.ceil(r.memory*1024):ee,...e!==void 0?{gpu:e}:{}}}var w=class extends Error{reason;destination;constructor(e,t,n){super(t),this.name="ModalNetworkPolicyError",this.reason=e,this.destination=n;}};function q(r){let e=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(?:\/(\d{1,3}))?$/.exec(r);return !(!e||[e[1],e[2],e[3],e[4]].some(t=>Number(t)>255)||e[5]!==void 0&&Number(e[5])>32)}function te(r){return /^\d{1,3}(\.\d{1,3}){3}(\/\d+)?$/.test(r)&&!q(r)}function ne(r){return r.startsWith("[")?true:(r.match(/:/g)?.length??0)>=2}function re(r){return /^[^:]+:\d+$/.test(r)}function E(r){if(!r||r.outbound==="open"){if(r?.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");return {}}let e=r.allowedDestinations??[];if(e.length===0)return {blockNetwork:true};let t=[],n=[];for(let o of e)if(q(o))t.push(o.includes("/")?o:`${o}/32`);else {if(te(o))throw new w("invalid-ipv4",`"${o}" is not a valid IPv4 address or CIDR (octets must be 0-255, prefix 0-32). Fix the address or list a hostname instead.`,o);if(ne(o))t.push(o.includes("/")?o:`${o}/128`);else {if(re(o))throw new w("port-unsupported",`Modal's network allowlist filters hosts and IPs only and cannot match a port; drop the ":<port>" from "${o}" and list just the host or IP.`,o);n.push(o);}}return {outboundCidrAllowlist:t,outboundDomainAllowlist:n}}function b(r){if(!r||r.outbound==="open"){if(r?.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");return {outboundCidrAllowlist:["0.0.0.0/0"],outboundDomainAllowlist:["*"]}}let e=E(r);return {outboundCidrAllowlist:e.outboundCidrAllowlist??[],outboundDomainAllowlist:se(e.outboundDomainAllowlist??[])}}var oe="sealed.invalid";function se(r){return r.length>0?r:[oe]}function G(r,e){if(!e||e.length===0)return false;let t=o=>o===void 0?"open|":`${o.outbound}|${[...new Set(o.allowedDestinations??[])].sort().join(",")}`,n=t(r);return e.some(o=>t(o)!==n)}function H(r){let e=r.split("/")[0];return /^\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com$/.test(e)?"aws-ecr":e==="gcr.io"||e.endsWith(".gcr.io")||e.endsWith("-docker.pkg.dev")?"gcp-artifact-registry":"registry"}function R(r,e,t){let{[T]:n,[B]:o,...s}=e;return {sandboxId:r,image:n??t??"",metadata:s,startedAt:o??""}}var O=1e4,M=class{constructor(e,t){this.sandbox=e;this.user=t;}async run(e,t){let n=I(e,this.user,t?.cwd,t?.envs),o=await this.sandbox.exec(n,{timeoutMs:t?.timeoutMs}),{stdout:s,stderr:i}=await this.accumulateStreams(o,t?.onStdout,t?.onStderr);return {exitCode:await o.wait(),stdout:s,stderr:i}}async spawn(e,t){let n=I(e,this.user,t?.cwd,t?.envs),o=await this.sandbox.exec(n,{timeoutMs:t?.timeoutMs}),s="",i="",a=this.accumulateStreams(o,t?.onStdout?c=>{t.onStdout(c);}:void 0,t?.onStderr?c=>{t.onStderr(c);}:void 0).then(({stdout:c,stderr:u})=>{s=c,i=u;}).catch(()=>{});return {processId:`modal-${Date.now()}-${Math.random().toString(36).slice(2)}`,wait:async()=>(await a,{exitCode:await o.wait(),stdout:s,stderr:i}),kill:async()=>false}}async list(){let e=await this.sandbox.exec(["ps","-eo","pid,comm,args"],{timeoutMs:1e4});return await e.wait(),(await e.stdout.readText()).trim().split(`
|
|
2
2
|
`).slice(1).map(o=>{let s=o.trim().split(/\s+/);return {processId:s[0],cmd:s[1]||"",args:s.slice(2),envs:{}}})}async connect(e,t){throw new Error("Modal does not support connecting to existing processes")}async sendStdin(e,t){throw new Error("Modal does not support sendStdin by process ID")}async kill(e){return await(await this.sandbox.exec(["kill","-9",e],{timeoutMs:1e4})).wait()===0}async accumulateStreams(e,t,n){let o="",s="",i=[];return i.push((async()=>{try{for await(let a of e.stdout){let d=typeof a=="string"?a:new TextDecoder().decode(a);o+=d,t?.(d);}}catch{}})()),i.push((async()=>{try{for await(let a of e.stderr){let d=typeof a=="string"?a:new TextDecoder().decode(a);s+=d,n?.(d);}}catch{}})()),await Promise.all(i),{stdout:o,stderr:s}}},A=class{constructor(e,t){this.sandbox=e;this.user=t;}async chownToUser(e,t=false){if(this.user==="root")return;let n=t?["chown","-R",`${this.user}:${this.user}`,e]:["chown",`${this.user}:${this.user}`,e];await(await this.sandbox.exec(n,{timeoutMs:t?3e4:1e4})).wait();}async writeStdinChunked(e,t){for(let n=0;n<t.length;n+=g)await e.writeBytes(t.subarray(n,n+g));}async writeStdinCoalesced(e,t){let n=[],o=0,s=async()=>{if(o===0)return;let i=n.length===1?n[0]:Buffer.concat(n,o);n=[],o=0,await this.writeStdinChunked(e,i);};for await(let i of t)n.push(i),o+=i.length,o>=g&&await s();await s();}async read(e){let t=await this.sandbox.exec(["cat",e],{timeoutMs:3e5,mode:"binary"}),n=await t.wait();if(n!==0){let s=await t.stderr.readText();throw new Error(`Failed to read file ${e}: ${s||`exit code ${n}`}`)}let o=await t.stdout.readBytes();if(!o.includes(0))try{return new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}).decode(o)}catch{}return o}async write(e,t){let n=this.toBuffer(t),o=e.substring(0,e.lastIndexOf("/"));o&&await this.makeDir(o);let s=e.replace(/'/g,"'\\''"),i=await this.sandbox.exec(["bash","-c",`cat > '${s}'`],{mode:"binary"});await this.writeStdinChunked(i.stdin,new Uint8Array(n)),await i.stdin.getWriter().close(),await i.wait(),await this.chownToUser(e);}async writeBatch(e){let t=tarStream.pack(),n=new Set;for(let i of e){let a=this.toBuffer(i.data),d=i.path.startsWith("/")?i.path.slice(1):i.path;t.entry({name:d},a);let c=i.path.substring(0,i.path.lastIndexOf("/"));c&&n.add(c);}t.finalize();let o=await this.sandbox.exec(["tar","-xf","-","-C","/"],{mode:"binary"});if(await this.writeStdinCoalesced(o.stdin,t),await o.stdin.getWriter().close(),await o.wait(),n.size>0){let i=Array.from(n),a=new Set(i.map(d=>d.split("/").slice(0,4).join("/")));for(let d of a)await this.chownToUser(d,true);}}async makeDir(e){await(await this.sandbox.exec(["mkdir","-p",e],{timeoutMs:1e4})).wait(),await this.chownToUser(e,true);}async exists(e){return await(await this.sandbox.exec(["test","-e",e],{timeoutMs:1e4})).wait()===0}async list(e){let t=e.replace(/'/g,"'\\''"),n=await this.sandbox.exec(["bash","-c",`ls -la '${t}' | tail -n +2`],{timeoutMs:3e4});await n.wait();let o=await n.stdout.readText(),s=[];for(let i of o.trim().split(`
|
|
3
3
|
`)){if(!i)continue;let a=i.split(/\s+/);if(a.length<9)continue;let d=a[0],c=a.slice(8).join(" ");c==="."||c===".."||s.push({name:c,path:e.endsWith("/")?`${e}${c}`:`${e}/${c}`,type:d.startsWith("d")?"dir":"file"});}return s}async remove(e){await(await this.sandbox.exec(["rm","-rf",e],{timeoutMs:3e4})).wait();}async rename(e,t){await(await this.sandbox.exec(["mv",e,t],{timeoutMs:3e4})).wait();}async readStream(e){return (await this.sandbox.exec(["cat",e],{timeoutMs:3e5,mode:"binary"})).stdout}async writeStream(e,t){let n=e.substring(0,e.lastIndexOf("/"));n&&await this.makeDir(n);let o=e.replace(/'/g,"'\\''"),s=await this.sandbox.exec(["bash","-c",`cat > '${o}'`],{mode:"binary"});await this.writeStdinCoalesced(s.stdin,Q(t)),await s.stdin.getWriter().close(),await s.wait(),await this.chownToUser(e);}async writeFromPath(e,t){let{createReadStream:n}=await import('fs'),{Readable:o}=await import('stream'),s=o.toWeb(n(t,{highWaterMark:g}));await this.writeStream(e,s);}async uploadUrl(e,t){throw new Error("Modal does not support pre-signed upload URLs")}async downloadUrl(e,t){throw new Error("Modal does not support pre-signed download URLs")}async watchDir(e,t,n){throw new Error("Modal does not support watchDir")}toBuffer(e){if(typeof e=="string")return Buffer.from(e,"utf-8");if(e instanceof Buffer)return e;if(e instanceof ArrayBuffer||e instanceof Uint8Array)return Buffer.from(e);throw new Error(`Unsupported data type: ${typeof e}`)}},x=class{constructor(e,t,n){this.sandbox=e;this.commands=new M(e,n),this.files=new A(e,n),this.image=t;}commands;files;image;get sandboxId(){return this.sandbox.sandboxId}async getHost(e){let n=(await this.sandbox.tunnels())[e];if(!n)throw new Error(`No tunnel found for port ${e}`);return n.url}async isRunning(){try{return await this.sandbox.poll()===null}catch{return false}}async getInfo(){let e=await this.sandbox.getTags();return R(this.sandbox.sandboxId,e,this.image)}async updateNetwork(e){await this.sandbox.updateNetworkPolicy(b(e));}async kill(){try{await this.sandbox.terminate();}catch{await new Promise(e=>setTimeout(e,500)),await this.sandbox.terminate();}}async pause(){throw new Error("Modal does not support pause/resume. Persist progress with Evolve checkpoints and resume in a fresh sandbox, or use kill() to terminate.")}},k=class{providerType="modal";name="Modal";supportsBootCommand=true;supportsVolumes=true;client;appName;defaultTimeoutMs;imageName;imageSecretName;_app;sandboxUsers=new Map;constructor(e){!e.endpoint&&process.env.MODAL_SERVER_URL?.startsWith("unix:")?process.env.MODAL_SERVER_URL="https://api.modal.com:443":e.endpoint&&(process.env.MODAL_SERVER_URL=e.endpoint),this.client=new modal.ModalClient({tokenId:e.tokenId,tokenSecret:e.tokenSecret}),this.appName=e.appName??"evolve-sandbox",this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.imageName=e.imageName??`evolve-all-${p}`,this.imageSecretName=e.imageSecretName;}async getApp(){return this._app||(this._app=await this.client.apps.fromName(this.appName,{createIfMissing:true})),this._app}async resolveImage(e){let t=H(e);if(t==="registry"){let o=this.imageSecretName?await this.client.secrets.fromName(this.imageSecretName):void 0;return this.client.images.fromRegistry(e,o)}if(!this.imageSecretName)throw new Error(`Private registry image "${e}" requires config.imageSecretName \u2014 the name of a Modal Secret holding registry credentials (AWS ECR: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION with read-only ECR IAM). Create one at https://modal.com/secrets`);let n=t==="aws-ecr"?await this.client.secrets.fromName(this.imageSecretName,{requiredKeys:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"]}):await this.client.secrets.fromName(this.imageSecretName);return t==="aws-ecr"?this.client.images.fromAwsEcr(e,n):this.client.images.fromGcpArtifactRegistry(e,n)}async resolveAndBuildImage(e){let t=D[e]??e;if(!/[/:@]/.test(t))try{return {tag:t,image:await this.client.images.fromName(t)}}catch(s){if(!(s instanceof modal.NotFoundError))throw s}let n=await this.getApp(),o=await this.resolveImage(t);return {tag:t,image:await o.build(n)}}async publishImageAs(e,t){if(!e.trim())throw new Error("publishImageAs requires a non-empty alias to publish under");let{image:n}=await this.resolveAndBuildImage(t||this.imageName);return await n.publish(e),e}async prepareImage(e){await this.resolveAndBuildImage(e||this.imageName);}async create(e){let t=e.timeoutMs??this.defaultTimeoutMs;L(t);let n=U(e.idleTimeoutMs),o=V(e.bootCommand),s=F(e.volumes);for(let l of e.phaseNetworkPolicies??[])b(l);let i=G(e.network,e.phaseNetworkPolicies)?b(e.network):E(e.network),a=W(e.resources),d=e.user??N,c=await this.getApp(),{tag:u,image:j}=await this.resolveAndBuildImage(e.image||this.imageName),C=await this.resolveVolumes(s),y=e.envs?Object.fromEntries(Object.entries(e.envs).filter(([,l])=>l!=null)):void 0,z=y&&Object.keys(y).length>0?y:void 0,Y={...e.metadata,[T]:u,[B]:new Date().toISOString()},S=await this.client.sandboxes.create(c,j,{cpu:a.cpu,memoryMiB:a.memoryMiB,...a.gpu!==void 0?{gpu:a.gpu}:{},timeoutMs:t,...n,...o,workdir:e.workingDirectory,env:z,tags:Y,...i,...C!==void 0?{volumes:C}:{}});return e.workingDirectory&&d!=="root"&&await(await S.exec(["chown","-R",`${d}:${d}`,e.workingDirectory],{timeoutMs:3e4})).wait(),this.sandboxUsers.set(S.sandboxId,d),new x(S,u,d)}async resolveVolumes(e){if(e.length===0)return;let t={};for(let n of e){let o;try{o=await this.client.volumes.fromName(n.name,{createIfMissing:n.createIfMissing});}catch(s){let i=s instanceof Error?s.message:String(s),a=s instanceof modal.NotFoundError||s instanceof Error&&s.name==="NotFoundError";throw new m(a?"not-found":"resolve-failed",n.mountPath,n.name,a?`Modal Volume "${n.name}" (to mount at ${n.mountPath}) does not exist and createIfMissing was not set: ${i}`:`Modal Volume "${n.name}" (to mount at ${n.mountPath}) could not be resolved: ${i}`,s)}t[n.mountPath]=o.withMountOptions({readOnly:n.readOnly});}return t}async connect(e,t){let n=await this.client.sandboxes.fromId(e),o=this.sandboxUsers.get(e)??N;return new x(n,void 0,o)}async list(e){let t=await this.walk(e);if(t.error&&!t.stoppedAtLimit)throw new Error(t.error);return t.sandboxes}async listAll(e){let{stoppedAtLimit:t,...n}=await this.walk(e);return n}async walk(e){if(e?.state&&!e.state.includes("running"))return {sandboxes:[],complete:true,pagesFetched:0,stoppedAtLimit:false};let t;try{t=await this.getApp();}catch(n){return {sandboxes:[],complete:false,pagesFetched:0,stoppedAtLimit:false,error:`sandbox list failed: ${n instanceof Error?n.message:String(n)}`}}return K(()=>this.client.sandboxes.list({appId:t.appId,tags:e?.metadata}),e?.limit)}async listSandboxIds(){let e=new Set;try{let t=await this.getApp();for await(let n of this.client.sandboxes.list({appId:t.appId}))e.add(n.sandboxId);return {ids:e,complete:!0}}catch{return {ids:new Set,complete:false}}}};async function K(r,e){let t=[];try{for await(let n of r()){if(e!==void 0&&t.length>=e)return {sandboxes:t,complete:!1,pagesFetched:t.length,stoppedAtLimit:!0,error:`stopped at the requested limit of ${e} with more sandboxes available`};if(t.length>=O)return {sandboxes:t,complete:!1,pagesFetched:t.length,stoppedAtLimit:!1,error:`sandbox list exceeded ${O} sandboxes`};let o=await n.getTags();t.push(R(n.sandboxId,o));}}catch(n){return {sandboxes:t,complete:false,pagesFetched:t.length,stoppedAtLimit:false,error:`sandbox list failed: ${n instanceof Error?n.message:String(n)}`}}return {sandboxes:t,complete:true,pagesFetched:t.length,stoppedAtLimit:false}}function ge(r={}){let e=r.tokenId??process.env.MODAL_TOKEN_ID,t=r.tokenSecret??process.env.MODAL_TOKEN_SECRET;if(!e||!t)throw new Error("Modal credentials required. Set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables, or pass tokenId/tokenSecret in config. Get your token at https://modal.com/settings/tokens");return new k({...r,tokenId:e,tokenSecret:t})}var fe=I,he=D,we=E,be=b,xe=G,ye=W,Se=H,ve=R,Pe=K,Ie=L,Me=U,Ae=V,ke=F;
|
|
4
4
|
exports.EVOLVE_IMAGE_VERSION=p;exports.MODAL_MAX_LIFETIME_MS=$;exports.MODAL_MAX_LIST_SANDBOXES=O;exports.MODAL_STDIN_CHUNK_BYTES=g;exports.ModalBootCommandError=P;exports.ModalCommands=M;exports.ModalFiles=A;exports.ModalIdleTimeoutError=f;exports.ModalNetworkPolicyError=w;exports.ModalProvider=k;exports.ModalResourcesError=h;exports.ModalSandboxLifetimeError=v;exports.ModalVolumeError=m;exports._testBuildSandboxInfo=ve;exports._testCollectSandboxes=Pe;exports._testDynamicNetworkPolicyParams=be;exports._testImageMap=he;exports._testMapBootCommand=Ae;exports._testMapIdleTimeout=Me;exports._testMapNetworkPolicy=we;exports._testMapResources=ye;exports._testMapVolumeMounts=ke;exports._testRequiresDynamicNetwork=xe;exports._testResolveImageRegistry=Se;exports._testValidateTimeout=Ie;exports._testWrapCommand=fe;exports.createModalProvider=ge;
|
package/dist/index.d.cts
CHANGED
|
@@ -9,7 +9,7 @@ import { Sandbox } from 'modal';
|
|
|
9
9
|
* packages/daytona/tests/unit/daytona-image-version.test.ts fails the
|
|
10
10
|
* suite whenever this checked-in copy is stale.
|
|
11
11
|
*/
|
|
12
|
-
declare const EVOLVE_IMAGE_VERSION = "c-
|
|
12
|
+
declare const EVOLVE_IMAGE_VERSION = "c-4bfce70a407f";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Modal Sandbox Provider - Clean Architecture
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { Sandbox } from 'modal';
|
|
|
9
9
|
* packages/daytona/tests/unit/daytona-image-version.test.ts fails the
|
|
10
10
|
* suite whenever this checked-in copy is stale.
|
|
11
11
|
*/
|
|
12
|
-
declare const EVOLVE_IMAGE_VERSION = "c-
|
|
12
|
+
declare const EVOLVE_IMAGE_VERSION = "c-4bfce70a407f";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Modal Sandbox Provider - Clean Architecture
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {ModalClient,NotFoundError}from'modal';import {pack}from'tar-stream';var p="c-
|
|
1
|
+
import {ModalClient,NotFoundError}from'modal';import {pack}from'tar-stream';var p="c-4bfce70a407f";var D={[`evolve-all-${p}`]:`evolvingmachines/evolve-all:${p}`,"evolve-all":"evolvingmachines/evolve-all"},$=1440*60*1e3,g=8*1024*1024;async function*Q(r){let e=r.getReader();try{for(;;){let{done:t,value:n}=await e.read();if(t)break;n&&(yield n);}}finally{e.releaseLock();}}var N="user",T="evolve.image",B="evolve.startedAt",v=class extends Error{requestedTimeoutMs;constructor(e){let t=(e/36e5).toFixed(1);super(`Modal sandboxes have a hard 24h lifetime cap; requested timeout was ${t}h. For sessions longer than 24h, persist progress with Evolve checkpoints and resume in a fresh sandbox instead of extending the timeout.`),this.name="ModalSandboxLifetimeError",this.requestedTimeoutMs=e;}};function L(r){if(r>$)throw new v(r)}var f=class extends Error{requestedIdleTimeoutMs;constructor(e,t){super(`Modal idleTimeoutMs of ${e}ms is invalid: ${t}`),this.name="ModalIdleTimeoutError",this.requestedIdleTimeoutMs=e;}};function U(r){if(r===void 0)return {};if(!Number.isFinite(r)||r<=0)throw new f(r,"it must be a positive number of milliseconds");if(r>$)throw new f(r,"it exceeds Modal's 24h lifetime cap, so the sandbox would always die of the lifetime first");return {idleTimeoutMs:r}}var P=class extends Error{constructor(e){super(`Modal bootCommand is invalid: ${e}`),this.name="ModalBootCommandError";}};function V(r){if(r===void 0)return {};if(r.length===0)throw new P(`an empty argv is Modal's own "no args" wire shape and would boot the image's ENTRYPOINT/CMD \u2014 omit bootCommand to boot the image's command`);return {command:[...r]}}var m=class extends Error{reason;mountPath;volumeName;constructor(e,t,n,o,s){super(o,s===void 0?void 0:{cause:s}),this.name="ModalVolumeError",this.reason=e,this.mountPath=t,this.volumeName=n;}};function F(r){if(!r)return [];let e=[];for(let[t,n]of Object.entries(r)){if(!t.startsWith("/")||t==="/")throw new m("invalid-mount-path",t,n.name,`Modal Volume mount path ${JSON.stringify(t)} is invalid: it must be an absolute in-box path other than "/"`);let o=n.name?.trim()??"";if(o.length===0)throw new m("invalid-name",t,n.name??"",`Modal Volume mounted at ${t} has a blank name`);e.push({mountPath:t,name:o,readOnly:n.readOnly===true,createIfMissing:n.createIfMissing===true});}return e}function I(r,e,t,n){let o="";n&&Object.keys(n).length>0&&(o=Object.entries(n).filter(([,a])=>a!=null).map(([a,d])=>`export ${a}='${String(d).replace(/'/g,"'\\''")}'`).join("; ")+"; ");let s=t?`cd '${t.replace(/'/g,"'\\''")}' && ${o}${r}`:`${o}${r}`;if(e==="root")return ["bash","-c",s];let i=Buffer.from(s).toString("base64");return ["su",e,"-c",`echo ${i} | base64 -d | bash`]}var h=class extends Error{constructor(e){super(e),this.name="ModalResourcesError";}},Z=4,ee=4096;function W(r){if(r?.disk!==void 0)throw new h(`Modal's JS SDK has no create-time disk-size parameter, so a ${r.disk} GiB disk request cannot be enforced. Drop \`resources.disk\` (containers get Modal's default disk quota) or run on a provider that sizes disk.`);if((r?.gpu===void 0||r.gpu<=0)&&r?.gpuTypes?.length)throw new h("resources.gpuTypes was set without a positive resources.gpu count \u2014 declare how many GPUs to reserve, or drop gpuTypes.");let e=r?.gpu!==void 0&&r.gpu>0?`${r.gpuTypes?.length?r.gpuTypes[0]:"any"}:${r.gpu}`:void 0;return {cpu:r?.cpu??Z,memoryMiB:r?.memory!==void 0?Math.ceil(r.memory*1024):ee,...e!==void 0?{gpu:e}:{}}}var w=class extends Error{reason;destination;constructor(e,t,n){super(t),this.name="ModalNetworkPolicyError",this.reason=e,this.destination=n;}};function q(r){let e=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(?:\/(\d{1,3}))?$/.exec(r);return !(!e||[e[1],e[2],e[3],e[4]].some(t=>Number(t)>255)||e[5]!==void 0&&Number(e[5])>32)}function te(r){return /^\d{1,3}(\.\d{1,3}){3}(\/\d+)?$/.test(r)&&!q(r)}function ne(r){return r.startsWith("[")?true:(r.match(/:/g)?.length??0)>=2}function re(r){return /^[^:]+:\d+$/.test(r)}function E(r){if(!r||r.outbound==="open"){if(r?.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");return {}}let e=r.allowedDestinations??[];if(e.length===0)return {blockNetwork:true};let t=[],n=[];for(let o of e)if(q(o))t.push(o.includes("/")?o:`${o}/32`);else {if(te(o))throw new w("invalid-ipv4",`"${o}" is not a valid IPv4 address or CIDR (octets must be 0-255, prefix 0-32). Fix the address or list a hostname instead.`,o);if(ne(o))t.push(o.includes("/")?o:`${o}/128`);else {if(re(o))throw new w("port-unsupported",`Modal's network allowlist filters hosts and IPs only and cannot match a port; drop the ":<port>" from "${o}" and list just the host or IP.`,o);n.push(o);}}return {outboundCidrAllowlist:t,outboundDomainAllowlist:n}}function b(r){if(!r||r.outbound==="open"){if(r?.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");return {outboundCidrAllowlist:["0.0.0.0/0"],outboundDomainAllowlist:["*"]}}let e=E(r);return {outboundCidrAllowlist:e.outboundCidrAllowlist??[],outboundDomainAllowlist:se(e.outboundDomainAllowlist??[])}}var oe="sealed.invalid";function se(r){return r.length>0?r:[oe]}function G(r,e){if(!e||e.length===0)return false;let t=o=>o===void 0?"open|":`${o.outbound}|${[...new Set(o.allowedDestinations??[])].sort().join(",")}`,n=t(r);return e.some(o=>t(o)!==n)}function H(r){let e=r.split("/")[0];return /^\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com$/.test(e)?"aws-ecr":e==="gcr.io"||e.endsWith(".gcr.io")||e.endsWith("-docker.pkg.dev")?"gcp-artifact-registry":"registry"}function R(r,e,t){let{[T]:n,[B]:o,...s}=e;return {sandboxId:r,image:n??t??"",metadata:s,startedAt:o??""}}var O=1e4,M=class{constructor(e,t){this.sandbox=e;this.user=t;}async run(e,t){let n=I(e,this.user,t?.cwd,t?.envs),o=await this.sandbox.exec(n,{timeoutMs:t?.timeoutMs}),{stdout:s,stderr:i}=await this.accumulateStreams(o,t?.onStdout,t?.onStderr);return {exitCode:await o.wait(),stdout:s,stderr:i}}async spawn(e,t){let n=I(e,this.user,t?.cwd,t?.envs),o=await this.sandbox.exec(n,{timeoutMs:t?.timeoutMs}),s="",i="",a=this.accumulateStreams(o,t?.onStdout?c=>{t.onStdout(c);}:void 0,t?.onStderr?c=>{t.onStderr(c);}:void 0).then(({stdout:c,stderr:u})=>{s=c,i=u;}).catch(()=>{});return {processId:`modal-${Date.now()}-${Math.random().toString(36).slice(2)}`,wait:async()=>(await a,{exitCode:await o.wait(),stdout:s,stderr:i}),kill:async()=>false}}async list(){let e=await this.sandbox.exec(["ps","-eo","pid,comm,args"],{timeoutMs:1e4});return await e.wait(),(await e.stdout.readText()).trim().split(`
|
|
2
2
|
`).slice(1).map(o=>{let s=o.trim().split(/\s+/);return {processId:s[0],cmd:s[1]||"",args:s.slice(2),envs:{}}})}async connect(e,t){throw new Error("Modal does not support connecting to existing processes")}async sendStdin(e,t){throw new Error("Modal does not support sendStdin by process ID")}async kill(e){return await(await this.sandbox.exec(["kill","-9",e],{timeoutMs:1e4})).wait()===0}async accumulateStreams(e,t,n){let o="",s="",i=[];return i.push((async()=>{try{for await(let a of e.stdout){let d=typeof a=="string"?a:new TextDecoder().decode(a);o+=d,t?.(d);}}catch{}})()),i.push((async()=>{try{for await(let a of e.stderr){let d=typeof a=="string"?a:new TextDecoder().decode(a);s+=d,n?.(d);}}catch{}})()),await Promise.all(i),{stdout:o,stderr:s}}},A=class{constructor(e,t){this.sandbox=e;this.user=t;}async chownToUser(e,t=false){if(this.user==="root")return;let n=t?["chown","-R",`${this.user}:${this.user}`,e]:["chown",`${this.user}:${this.user}`,e];await(await this.sandbox.exec(n,{timeoutMs:t?3e4:1e4})).wait();}async writeStdinChunked(e,t){for(let n=0;n<t.length;n+=g)await e.writeBytes(t.subarray(n,n+g));}async writeStdinCoalesced(e,t){let n=[],o=0,s=async()=>{if(o===0)return;let i=n.length===1?n[0]:Buffer.concat(n,o);n=[],o=0,await this.writeStdinChunked(e,i);};for await(let i of t)n.push(i),o+=i.length,o>=g&&await s();await s();}async read(e){let t=await this.sandbox.exec(["cat",e],{timeoutMs:3e5,mode:"binary"}),n=await t.wait();if(n!==0){let s=await t.stderr.readText();throw new Error(`Failed to read file ${e}: ${s||`exit code ${n}`}`)}let o=await t.stdout.readBytes();if(!o.includes(0))try{return new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}).decode(o)}catch{}return o}async write(e,t){let n=this.toBuffer(t),o=e.substring(0,e.lastIndexOf("/"));o&&await this.makeDir(o);let s=e.replace(/'/g,"'\\''"),i=await this.sandbox.exec(["bash","-c",`cat > '${s}'`],{mode:"binary"});await this.writeStdinChunked(i.stdin,new Uint8Array(n)),await i.stdin.getWriter().close(),await i.wait(),await this.chownToUser(e);}async writeBatch(e){let t=pack(),n=new Set;for(let i of e){let a=this.toBuffer(i.data),d=i.path.startsWith("/")?i.path.slice(1):i.path;t.entry({name:d},a);let c=i.path.substring(0,i.path.lastIndexOf("/"));c&&n.add(c);}t.finalize();let o=await this.sandbox.exec(["tar","-xf","-","-C","/"],{mode:"binary"});if(await this.writeStdinCoalesced(o.stdin,t),await o.stdin.getWriter().close(),await o.wait(),n.size>0){let i=Array.from(n),a=new Set(i.map(d=>d.split("/").slice(0,4).join("/")));for(let d of a)await this.chownToUser(d,true);}}async makeDir(e){await(await this.sandbox.exec(["mkdir","-p",e],{timeoutMs:1e4})).wait(),await this.chownToUser(e,true);}async exists(e){return await(await this.sandbox.exec(["test","-e",e],{timeoutMs:1e4})).wait()===0}async list(e){let t=e.replace(/'/g,"'\\''"),n=await this.sandbox.exec(["bash","-c",`ls -la '${t}' | tail -n +2`],{timeoutMs:3e4});await n.wait();let o=await n.stdout.readText(),s=[];for(let i of o.trim().split(`
|
|
3
3
|
`)){if(!i)continue;let a=i.split(/\s+/);if(a.length<9)continue;let d=a[0],c=a.slice(8).join(" ");c==="."||c===".."||s.push({name:c,path:e.endsWith("/")?`${e}${c}`:`${e}/${c}`,type:d.startsWith("d")?"dir":"file"});}return s}async remove(e){await(await this.sandbox.exec(["rm","-rf",e],{timeoutMs:3e4})).wait();}async rename(e,t){await(await this.sandbox.exec(["mv",e,t],{timeoutMs:3e4})).wait();}async readStream(e){return (await this.sandbox.exec(["cat",e],{timeoutMs:3e5,mode:"binary"})).stdout}async writeStream(e,t){let n=e.substring(0,e.lastIndexOf("/"));n&&await this.makeDir(n);let o=e.replace(/'/g,"'\\''"),s=await this.sandbox.exec(["bash","-c",`cat > '${o}'`],{mode:"binary"});await this.writeStdinCoalesced(s.stdin,Q(t)),await s.stdin.getWriter().close(),await s.wait(),await this.chownToUser(e);}async writeFromPath(e,t){let{createReadStream:n}=await import('fs'),{Readable:o}=await import('stream'),s=o.toWeb(n(t,{highWaterMark:g}));await this.writeStream(e,s);}async uploadUrl(e,t){throw new Error("Modal does not support pre-signed upload URLs")}async downloadUrl(e,t){throw new Error("Modal does not support pre-signed download URLs")}async watchDir(e,t,n){throw new Error("Modal does not support watchDir")}toBuffer(e){if(typeof e=="string")return Buffer.from(e,"utf-8");if(e instanceof Buffer)return e;if(e instanceof ArrayBuffer||e instanceof Uint8Array)return Buffer.from(e);throw new Error(`Unsupported data type: ${typeof e}`)}},x=class{constructor(e,t,n){this.sandbox=e;this.commands=new M(e,n),this.files=new A(e,n),this.image=t;}commands;files;image;get sandboxId(){return this.sandbox.sandboxId}async getHost(e){let n=(await this.sandbox.tunnels())[e];if(!n)throw new Error(`No tunnel found for port ${e}`);return n.url}async isRunning(){try{return await this.sandbox.poll()===null}catch{return false}}async getInfo(){let e=await this.sandbox.getTags();return R(this.sandbox.sandboxId,e,this.image)}async updateNetwork(e){await this.sandbox.updateNetworkPolicy(b(e));}async kill(){try{await this.sandbox.terminate();}catch{await new Promise(e=>setTimeout(e,500)),await this.sandbox.terminate();}}async pause(){throw new Error("Modal does not support pause/resume. Persist progress with Evolve checkpoints and resume in a fresh sandbox, or use kill() to terminate.")}},k=class{providerType="modal";name="Modal";supportsBootCommand=true;supportsVolumes=true;client;appName;defaultTimeoutMs;imageName;imageSecretName;_app;sandboxUsers=new Map;constructor(e){!e.endpoint&&process.env.MODAL_SERVER_URL?.startsWith("unix:")?process.env.MODAL_SERVER_URL="https://api.modal.com:443":e.endpoint&&(process.env.MODAL_SERVER_URL=e.endpoint),this.client=new ModalClient({tokenId:e.tokenId,tokenSecret:e.tokenSecret}),this.appName=e.appName??"evolve-sandbox",this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.imageName=e.imageName??`evolve-all-${p}`,this.imageSecretName=e.imageSecretName;}async getApp(){return this._app||(this._app=await this.client.apps.fromName(this.appName,{createIfMissing:true})),this._app}async resolveImage(e){let t=H(e);if(t==="registry"){let o=this.imageSecretName?await this.client.secrets.fromName(this.imageSecretName):void 0;return this.client.images.fromRegistry(e,o)}if(!this.imageSecretName)throw new Error(`Private registry image "${e}" requires config.imageSecretName \u2014 the name of a Modal Secret holding registry credentials (AWS ECR: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION with read-only ECR IAM). Create one at https://modal.com/secrets`);let n=t==="aws-ecr"?await this.client.secrets.fromName(this.imageSecretName,{requiredKeys:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"]}):await this.client.secrets.fromName(this.imageSecretName);return t==="aws-ecr"?this.client.images.fromAwsEcr(e,n):this.client.images.fromGcpArtifactRegistry(e,n)}async resolveAndBuildImage(e){let t=D[e]??e;if(!/[/:@]/.test(t))try{return {tag:t,image:await this.client.images.fromName(t)}}catch(s){if(!(s instanceof NotFoundError))throw s}let n=await this.getApp(),o=await this.resolveImage(t);return {tag:t,image:await o.build(n)}}async publishImageAs(e,t){if(!e.trim())throw new Error("publishImageAs requires a non-empty alias to publish under");let{image:n}=await this.resolveAndBuildImage(t||this.imageName);return await n.publish(e),e}async prepareImage(e){await this.resolveAndBuildImage(e||this.imageName);}async create(e){let t=e.timeoutMs??this.defaultTimeoutMs;L(t);let n=U(e.idleTimeoutMs),o=V(e.bootCommand),s=F(e.volumes);for(let l of e.phaseNetworkPolicies??[])b(l);let i=G(e.network,e.phaseNetworkPolicies)?b(e.network):E(e.network),a=W(e.resources),d=e.user??N,c=await this.getApp(),{tag:u,image:j}=await this.resolveAndBuildImage(e.image||this.imageName),C=await this.resolveVolumes(s),y=e.envs?Object.fromEntries(Object.entries(e.envs).filter(([,l])=>l!=null)):void 0,z=y&&Object.keys(y).length>0?y:void 0,Y={...e.metadata,[T]:u,[B]:new Date().toISOString()},S=await this.client.sandboxes.create(c,j,{cpu:a.cpu,memoryMiB:a.memoryMiB,...a.gpu!==void 0?{gpu:a.gpu}:{},timeoutMs:t,...n,...o,workdir:e.workingDirectory,env:z,tags:Y,...i,...C!==void 0?{volumes:C}:{}});return e.workingDirectory&&d!=="root"&&await(await S.exec(["chown","-R",`${d}:${d}`,e.workingDirectory],{timeoutMs:3e4})).wait(),this.sandboxUsers.set(S.sandboxId,d),new x(S,u,d)}async resolveVolumes(e){if(e.length===0)return;let t={};for(let n of e){let o;try{o=await this.client.volumes.fromName(n.name,{createIfMissing:n.createIfMissing});}catch(s){let i=s instanceof Error?s.message:String(s),a=s instanceof NotFoundError||s instanceof Error&&s.name==="NotFoundError";throw new m(a?"not-found":"resolve-failed",n.mountPath,n.name,a?`Modal Volume "${n.name}" (to mount at ${n.mountPath}) does not exist and createIfMissing was not set: ${i}`:`Modal Volume "${n.name}" (to mount at ${n.mountPath}) could not be resolved: ${i}`,s)}t[n.mountPath]=o.withMountOptions({readOnly:n.readOnly});}return t}async connect(e,t){let n=await this.client.sandboxes.fromId(e),o=this.sandboxUsers.get(e)??N;return new x(n,void 0,o)}async list(e){let t=await this.walk(e);if(t.error&&!t.stoppedAtLimit)throw new Error(t.error);return t.sandboxes}async listAll(e){let{stoppedAtLimit:t,...n}=await this.walk(e);return n}async walk(e){if(e?.state&&!e.state.includes("running"))return {sandboxes:[],complete:true,pagesFetched:0,stoppedAtLimit:false};let t;try{t=await this.getApp();}catch(n){return {sandboxes:[],complete:false,pagesFetched:0,stoppedAtLimit:false,error:`sandbox list failed: ${n instanceof Error?n.message:String(n)}`}}return K(()=>this.client.sandboxes.list({appId:t.appId,tags:e?.metadata}),e?.limit)}async listSandboxIds(){let e=new Set;try{let t=await this.getApp();for await(let n of this.client.sandboxes.list({appId:t.appId}))e.add(n.sandboxId);return {ids:e,complete:!0}}catch{return {ids:new Set,complete:false}}}};async function K(r,e){let t=[];try{for await(let n of r()){if(e!==void 0&&t.length>=e)return {sandboxes:t,complete:!1,pagesFetched:t.length,stoppedAtLimit:!0,error:`stopped at the requested limit of ${e} with more sandboxes available`};if(t.length>=O)return {sandboxes:t,complete:!1,pagesFetched:t.length,stoppedAtLimit:!1,error:`sandbox list exceeded ${O} sandboxes`};let o=await n.getTags();t.push(R(n.sandboxId,o));}}catch(n){return {sandboxes:t,complete:false,pagesFetched:t.length,stoppedAtLimit:false,error:`sandbox list failed: ${n instanceof Error?n.message:String(n)}`}}return {sandboxes:t,complete:true,pagesFetched:t.length,stoppedAtLimit:false}}function ge(r={}){let e=r.tokenId??process.env.MODAL_TOKEN_ID,t=r.tokenSecret??process.env.MODAL_TOKEN_SECRET;if(!e||!t)throw new Error("Modal credentials required. Set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables, or pass tokenId/tokenSecret in config. Get your token at https://modal.com/settings/tokens");return new k({...r,tokenId:e,tokenSecret:t})}var fe=I,he=D,we=E,be=b,xe=G,ye=W,Se=H,ve=R,Pe=K,Ie=L,Me=U,Ae=V,ke=F;
|
|
4
4
|
export{p as EVOLVE_IMAGE_VERSION,$ as MODAL_MAX_LIFETIME_MS,O as MODAL_MAX_LIST_SANDBOXES,g as MODAL_STDIN_CHUNK_BYTES,P as ModalBootCommandError,M as ModalCommands,A as ModalFiles,f as ModalIdleTimeoutError,w as ModalNetworkPolicyError,k as ModalProvider,h as ModalResourcesError,v as ModalSandboxLifetimeError,m as ModalVolumeError,ve as _testBuildSandboxInfo,Pe as _testCollectSandboxes,be as _testDynamicNetworkPolicyParams,he as _testImageMap,Ae as _testMapBootCommand,Me as _testMapIdleTimeout,we as _testMapNetworkPolicy,ye as _testMapResources,ke as _testMapVolumeMounts,xe as _testRequiresDynamicNetwork,Se as _testResolveImageRegistry,Ie as _testValidateTimeout,fe as _testWrapCommand,ge as createModalProvider};
|