@evolvingmachines/modal 0.0.23 → 0.0.25

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 CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var modal=require('modal'),tarStream=require('tar-stream');var b={"evolve-all":"evolvingmachines/evolve-all:latest"},h=new Set([".xlsx",".xls",".docx",".doc",".pptx",".ppt",".pdf",".zip",".tar",".gz",".7z",".rar",".png",".jpg",".jpeg",".gif",".webp",".ico",".bmp",".mp3",".wav",".ogg",".flac",".aac",".mp4",".avi",".mov",".mkv",".webm",".woff",".woff2",".ttf",".otf",".eot",".exe",".dll",".so",".dylib",".sqlite",".db",".pickle",".pkl",".parquet"]);function y(m){let t=m.substring(m.lastIndexOf(".")).toLowerCase();return h.has(t)}var l=class{constructor(t){this.sandbox=t;}wrapAsUser(t,e,n){let r="";n&&Object.keys(n).length>0&&(r=Object.entries(n).filter(([,o])=>o!=null).map(([o,a])=>`export ${o}='${String(a).replace(/'/g,"'\\''")}'`).join("; ")+"; ");let s=e?`cd '${e.replace(/'/g,"'\\''")}' && ${r}${t}`:`${r}${t}`;return ["su","user","-c",`echo ${Buffer.from(s).toString("base64")} | base64 -d | bash`]}async run(t,e){let n=this.wrapAsUser(t,e?.cwd,e?.envs),r=await this.sandbox.exec(n,{timeoutMs:e?.timeoutMs}),{stdout:s,stderr:i}=await this.accumulateStreams(r,e?.onStdout,e?.onStderr);return {exitCode:await r.wait(),stdout:s,stderr:i}}async spawn(t,e){let n=this.wrapAsUser(t,e?.cwd,e?.envs),r=await this.sandbox.exec(n,{timeoutMs:e?.timeoutMs}),s="",i="",o=this.accumulateStreams(r,e?.onStdout?d=>{e.onStdout(d);}:void 0,e?.onStderr?d=>{e.onStderr(d);}:void 0).then(({stdout:d,stderr:c})=>{s=d,i=c;}).catch(()=>{});return {processId:`modal-${Date.now()}-${Math.random().toString(36).slice(2)}`,wait:async()=>(await o,{exitCode:await r.wait(),stdout:s,stderr:i}),kill:async()=>false}}async list(){let t=await this.sandbox.exec(["ps","-eo","pid,comm,args"],{timeoutMs:1e4});return await t.wait(),(await t.stdout.readText()).trim().split(`
2
- `).slice(1).map(r=>{let s=r.trim().split(/\s+/);return {processId:s[0],cmd:s[1]||"",args:s.slice(2),envs:{}}})}async connect(t,e){throw new Error("Modal does not support connecting to existing processes")}async sendStdin(t,e){throw new Error("Modal does not support sendStdin by process ID")}async kill(t){return await(await this.sandbox.exec(["kill","-9",t],{timeoutMs:1e4})).wait()===0}async accumulateStreams(t,e,n){let r="",s="",i=[];return i.push((async()=>{try{for await(let o of t.stdout){let a=typeof o=="string"?o:new TextDecoder().decode(o);r+=a,e?.(a);}}catch{}})()),i.push((async()=>{try{for await(let o of t.stderr){let a=typeof o=="string"?o:new TextDecoder().decode(o);s+=a,n?.(a);}}catch{}})()),await Promise.all(i),{stdout:r,stderr:s}}},f=class{constructor(t){this.sandbox=t;}async read(t){if(y(t)){let r=await this.sandbox.exec(["cat",t],{timeoutMs:3e5,mode:"binary"}),s=await r.wait();if(s!==0){let i=await r.stderr.readText();throw new Error(`Failed to read file ${t}: ${i||`exit code ${s}`}`)}return await r.stdout.readBytes()}let e=await this.sandbox.exec(["cat",t],{timeoutMs:3e5}),n=await e.wait();if(n!==0){let r=await e.stderr.readText();throw new Error(`Failed to read file ${t}: ${r||`exit code ${n}`}`)}return await e.stdout.readText()}async write(t,e){let n=this.toBuffer(e),r=t.substring(0,t.lastIndexOf("/"));r&&await this.makeDir(r);let s=t.replace(/'/g,"'\\''"),i=await this.sandbox.exec(["bash","-c",`cat > '${s}'`],{mode:"binary"});await i.stdin.writeBytes(new Uint8Array(n)),await i.stdin.getWriter().close(),await i.wait(),await(await this.sandbox.exec(["chown","user:user",t],{timeoutMs:1e4})).wait();}async writeBatch(t){let e=tarStream.pack(),n=[],r=new Set;for(let a of t){let d=this.toBuffer(a.data),c=a.path.startsWith("/")?a.path.slice(1):a.path;e.entry({name:c},d);let u=a.path.substring(0,a.path.lastIndexOf("/"));u&&r.add(u);}e.finalize();for await(let a of e)n.push(Buffer.from(a));let s=Buffer.concat(n),i=await this.sandbox.exec(["tar","-xf","-","-C","/"],{mode:"binary"});if(await i.stdin.writeBytes(new Uint8Array(s)),await i.stdin.getWriter().close(),await i.wait(),r.size>0){let a=Array.from(r),d=new Set(a.map(c=>c.split("/").slice(0,4).join("/")));for(let c of d)await(await this.sandbox.exec(["chown","-R","user:user",c],{timeoutMs:3e4})).wait();}}async makeDir(t){await(await this.sandbox.exec(["mkdir","-p",t],{timeoutMs:1e4})).wait(),await(await this.sandbox.exec(["chown","-R","user:user",t],{timeoutMs:1e4})).wait();}async exists(t){return await(await this.sandbox.exec(["test","-e",t],{timeoutMs:1e4})).wait()===0}async list(t){let e=t.replace(/'/g,"'\\''"),n=await this.sandbox.exec(["bash","-c",`ls -la '${e}' | tail -n +2`],{timeoutMs:3e4});await n.wait();let r=await n.stdout.readText(),s=[];for(let i of r.trim().split(`
3
- `)){if(!i)continue;let o=i.split(/\s+/);if(o.length<9)continue;let a=o[0],d=o.slice(8).join(" ");d==="."||d===".."||s.push({name:d,path:t.endsWith("/")?`${t}${d}`:`${t}/${d}`,type:a.startsWith("d")?"dir":"file"});}return s}async remove(t){await(await this.sandbox.exec(["rm","-rf",t],{timeoutMs:3e4})).wait();}async rename(t,e){await(await this.sandbox.exec(["mv",t,e],{timeoutMs:3e4})).wait();}async readStream(t){return (await this.sandbox.exec(["cat",t],{timeoutMs:3e5,mode:"binary"})).stdout}async writeStream(t,e){let n=t.substring(0,t.lastIndexOf("/"));n&&await this.makeDir(n);let r=t.replace(/'/g,"'\\''"),s=await this.sandbox.exec(["bash","-c",`cat > '${r}'`],{mode:"binary"}),i=e.getReader();try{for(;;){let{done:a,value:d}=await i.read();if(a)break;await s.stdin.writeBytes(d);}}finally{i.releaseLock();}await s.stdin.getWriter().close(),await s.wait();}async uploadUrl(t,e){throw new Error("Modal does not support pre-signed upload URLs")}async downloadUrl(t,e){throw new Error("Modal does not support pre-signed download URLs")}async watchDir(t,e,n){throw new Error("Modal does not support watchDir")}toBuffer(t){if(typeof t=="string")return Buffer.from(t,"utf-8");if(t instanceof Buffer)return t;if(t instanceof ArrayBuffer||t instanceof Uint8Array)return Buffer.from(t);throw new Error(`Unsupported data type: ${typeof t}`)}},p=class{constructor(t,e){this.sandbox=t;this.commands=new l(t),this.files=new f(t),this.image=e,this.startTime=new Date;}commands;files;image;startTime;get sandboxId(){return this.sandbox.sandboxId}async getHost(t){let n=(await this.sandbox.tunnels())[t];if(!n)throw new Error(`No tunnel found for port ${t}`);return n.url}async isRunning(){try{return await(await this.sandbox.exec(["echo","ping"],{timeoutMs:5e3})).wait(),!0}catch{return false}}async getInfo(){return {sandboxId:this.sandbox.sandboxId,image:this.image,metadata:{},startedAt:this.startTime.toISOString()}}async kill(){try{await this.sandbox.terminate();}catch{await new Promise(t=>setTimeout(t,500)),await this.sandbox.terminate();}}async pause(){throw new Error("Modal does not support pause. Use kill() instead.")}},w=class{providerType="modal";client;appName;defaultTimeoutMs;_app;constructor(t){!t.endpoint&&process.env.MODAL_SERVER_URL?.startsWith("unix:")?process.env.MODAL_SERVER_URL="https://api.modal.com:443":t.endpoint&&(process.env.MODAL_SERVER_URL=t.endpoint),this.client=new modal.ModalClient({tokenId:t.tokenId,tokenSecret:t.tokenSecret}),this.appName=t.appName??"evolve-sandbox",this.defaultTimeoutMs=t.defaultTimeoutMs??36e5;}async getApp(){return this._app||(this._app=await this.client.apps.fromName(this.appName,{createIfMissing:true})),this._app}async create(t){let e=await this.getApp(),n=t.timeoutMs??this.defaultTimeoutMs,r=t.image||"evolve-all",s=b[r]??r,i=this.client.images.fromRegistry(s),o=t.envs?Object.fromEntries(Object.entries(t.envs).filter(([,c])=>c!=null)):void 0,a=o&&Object.keys(o).length>0?o:void 0,d=await this.client.sandboxes.create(e,i,{cpu:4,memoryMiB:4096,timeoutMs:n,workdir:t.workingDirectory,env:a});return t.workingDirectory&&await(await d.exec(["chown","-R","user:user",t.workingDirectory],{timeoutMs:3e4})).wait(),new p(d,s)}async connect(t,e){let n=await this.client.sandboxes.fromId(t);return new p(n,"unknown")}async list(t){let e=[],n=t?.limit??100;try{for await(let r of this.client.sandboxes.list())if(e.push({sandboxId:r.sandboxId,image:"unknown",metadata:{},startedAt:new Date().toISOString()}),e.length>=n)break}catch{}return e}};function k(m={}){let t=m.tokenId??process.env.MODAL_TOKEN_ID,e=m.tokenSecret??process.env.MODAL_TOKEN_SECRET;if(!t||!e)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 w({...m,tokenId:t,tokenSecret:e})}
1
+ 'use strict';var modal=require('modal'),tarStream=require('tar-stream');var b={"evolve-all":"evolvingmachines/evolve-all"},h=new Set([".xlsx",".xls",".docx",".doc",".pptx",".ppt",".pdf",".zip",".tar",".gz",".7z",".rar",".png",".jpg",".jpeg",".gif",".webp",".ico",".bmp",".mp3",".wav",".ogg",".flac",".aac",".mp4",".avi",".mov",".mkv",".webm",".woff",".woff2",".ttf",".otf",".eot",".exe",".dll",".so",".dylib",".sqlite",".db",".pickle",".pkl",".parquet"]);function y(c){let t=c.substring(c.lastIndexOf(".")).toLowerCase();return h.has(t)}var u=class{constructor(t){this.sandbox=t;}wrapAsUser(t,e,n){let r="";n&&Object.keys(n).length>0&&(r=Object.entries(n).filter(([,o])=>o!=null).map(([o,i])=>`export ${o}='${String(i).replace(/'/g,"'\\''")}'`).join("; ")+"; ");let s=e?`cd '${e.replace(/'/g,"'\\''")}' && ${r}${t}`:`${r}${t}`;return ["su","user","-c",`echo ${Buffer.from(s).toString("base64")} | base64 -d | bash`]}async run(t,e){let n=this.wrapAsUser(t,e?.cwd,e?.envs),r=await this.sandbox.exec(n,{timeoutMs:e?.timeoutMs}),{stdout:s,stderr:a}=await this.accumulateStreams(r,e?.onStdout,e?.onStderr);return {exitCode:await r.wait(),stdout:s,stderr:a}}async spawn(t,e){let n=this.wrapAsUser(t,e?.cwd,e?.envs),r=await this.sandbox.exec(n,{timeoutMs:e?.timeoutMs}),s="",a="",o=this.accumulateStreams(r,e?.onStdout?d=>{e.onStdout(d);}:void 0,e?.onStderr?d=>{e.onStderr(d);}:void 0).then(({stdout:d,stderr:m})=>{s=d,a=m;}).catch(()=>{});return {processId:`modal-${Date.now()}-${Math.random().toString(36).slice(2)}`,wait:async()=>(await o,{exitCode:await r.wait(),stdout:s,stderr:a}),kill:async()=>false}}async list(){let t=await this.sandbox.exec(["ps","-eo","pid,comm,args"],{timeoutMs:1e4});return await t.wait(),(await t.stdout.readText()).trim().split(`
2
+ `).slice(1).map(r=>{let s=r.trim().split(/\s+/);return {processId:s[0],cmd:s[1]||"",args:s.slice(2),envs:{}}})}async connect(t,e){throw new Error("Modal does not support connecting to existing processes")}async sendStdin(t,e){throw new Error("Modal does not support sendStdin by process ID")}async kill(t){return await(await this.sandbox.exec(["kill","-9",t],{timeoutMs:1e4})).wait()===0}async accumulateStreams(t,e,n){let r="",s="",a=[];return a.push((async()=>{try{for await(let o of t.stdout){let i=typeof o=="string"?o:new TextDecoder().decode(o);r+=i,e?.(i);}}catch{}})()),a.push((async()=>{try{for await(let o of t.stderr){let i=typeof o=="string"?o:new TextDecoder().decode(o);s+=i,n?.(i);}}catch{}})()),await Promise.all(a),{stdout:r,stderr:s}}},g=class{constructor(t){this.sandbox=t;}async read(t){if(y(t)){let r=await this.sandbox.exec(["cat",t],{timeoutMs:3e5,mode:"binary"}),s=await r.wait();if(s!==0){let a=await r.stderr.readText();throw new Error(`Failed to read file ${t}: ${a||`exit code ${s}`}`)}return await r.stdout.readBytes()}let e=await this.sandbox.exec(["cat",t],{timeoutMs:3e5}),n=await e.wait();if(n!==0){let r=await e.stderr.readText();throw new Error(`Failed to read file ${t}: ${r||`exit code ${n}`}`)}return await e.stdout.readText()}async write(t,e){let n=this.toBuffer(e),r=t.substring(0,t.lastIndexOf("/"));r&&await this.makeDir(r);let s=t.replace(/'/g,"'\\''"),a=await this.sandbox.exec(["bash","-c",`cat > '${s}'`],{mode:"binary"});await a.stdin.writeBytes(new Uint8Array(n)),await a.stdin.getWriter().close(),await a.wait(),await(await this.sandbox.exec(["chown","user:user",t],{timeoutMs:1e4})).wait();}async writeBatch(t){let e=tarStream.pack(),n=[],r=new Set;for(let i of t){let d=this.toBuffer(i.data),m=i.path.startsWith("/")?i.path.slice(1):i.path;e.entry({name:m},d);let p=i.path.substring(0,i.path.lastIndexOf("/"));p&&r.add(p);}e.finalize();for await(let i of e)n.push(Buffer.from(i));let s=Buffer.concat(n),a=await this.sandbox.exec(["tar","-xf","-","-C","/"],{mode:"binary"});if(await a.stdin.writeBytes(new Uint8Array(s)),await a.stdin.getWriter().close(),await a.wait(),r.size>0){let i=Array.from(r),d=new Set(i.map(m=>m.split("/").slice(0,4).join("/")));for(let m of d)await(await this.sandbox.exec(["chown","-R","user:user",m],{timeoutMs:3e4})).wait();}}async makeDir(t){await(await this.sandbox.exec(["mkdir","-p",t],{timeoutMs:1e4})).wait(),await(await this.sandbox.exec(["chown","-R","user:user",t],{timeoutMs:1e4})).wait();}async exists(t){return await(await this.sandbox.exec(["test","-e",t],{timeoutMs:1e4})).wait()===0}async list(t){let e=t.replace(/'/g,"'\\''"),n=await this.sandbox.exec(["bash","-c",`ls -la '${e}' | tail -n +2`],{timeoutMs:3e4});await n.wait();let r=await n.stdout.readText(),s=[];for(let a of r.trim().split(`
3
+ `)){if(!a)continue;let o=a.split(/\s+/);if(o.length<9)continue;let i=o[0],d=o.slice(8).join(" ");d==="."||d===".."||s.push({name:d,path:t.endsWith("/")?`${t}${d}`:`${t}/${d}`,type:i.startsWith("d")?"dir":"file"});}return s}async remove(t){await(await this.sandbox.exec(["rm","-rf",t],{timeoutMs:3e4})).wait();}async rename(t,e){await(await this.sandbox.exec(["mv",t,e],{timeoutMs:3e4})).wait();}async readStream(t){return (await this.sandbox.exec(["cat",t],{timeoutMs:3e5,mode:"binary"})).stdout}async writeStream(t,e){let n=t.substring(0,t.lastIndexOf("/"));n&&await this.makeDir(n);let r=t.replace(/'/g,"'\\''"),s=await this.sandbox.exec(["bash","-c",`cat > '${r}'`],{mode:"binary"}),a=e.getReader();try{for(;;){let{done:i,value:d}=await a.read();if(i)break;await s.stdin.writeBytes(d);}}finally{a.releaseLock();}await s.stdin.getWriter().close(),await s.wait();}async uploadUrl(t,e){throw new Error("Modal does not support pre-signed upload URLs")}async downloadUrl(t,e){throw new Error("Modal does not support pre-signed download URLs")}async watchDir(t,e,n){throw new Error("Modal does not support watchDir")}toBuffer(t){if(typeof t=="string")return Buffer.from(t,"utf-8");if(t instanceof Buffer)return t;if(t instanceof ArrayBuffer||t instanceof Uint8Array)return Buffer.from(t);throw new Error(`Unsupported data type: ${typeof t}`)}},l=class{constructor(t,e){this.sandbox=t;this.commands=new u(t),this.files=new g(t),this.image=e,this.startTime=new Date;}commands;files;image;startTime;get sandboxId(){return this.sandbox.sandboxId}async getHost(t){let n=(await this.sandbox.tunnels())[t];if(!n)throw new Error(`No tunnel found for port ${t}`);return n.url}async isRunning(){try{return await(await this.sandbox.exec(["echo","ping"],{timeoutMs:5e3})).wait(),!0}catch{return false}}async getInfo(){return {sandboxId:this.sandbox.sandboxId,image:this.image,metadata:{},startedAt:this.startTime.toISOString()}}async kill(){try{await this.sandbox.terminate();}catch{await new Promise(t=>setTimeout(t,500)),await this.sandbox.terminate();}}async pause(){throw new Error("Modal does not support pause. Use kill() instead.")}},w=class{providerType="modal";name="Modal";client;appName;defaultTimeoutMs;imageName;_app;constructor(t){!t.endpoint&&process.env.MODAL_SERVER_URL?.startsWith("unix:")?process.env.MODAL_SERVER_URL="https://api.modal.com:443":t.endpoint&&(process.env.MODAL_SERVER_URL=t.endpoint),this.client=new modal.ModalClient({tokenId:t.tokenId,tokenSecret:t.tokenSecret}),this.appName=t.appName??"evolve-sandbox",this.defaultTimeoutMs=t.defaultTimeoutMs??36e5,this.imageName=t.imageName??"evolve-all";}async getApp(){return this._app||(this._app=await this.client.apps.fromName(this.appName,{createIfMissing:true})),this._app}async create(t){let e=await this.getApp(),n=t.timeoutMs??this.defaultTimeoutMs,r=t.image||this.imageName,s=b[r]??r,o=await this.client.images.fromRegistry(s).build(e),i=t.envs?Object.fromEntries(Object.entries(t.envs).filter(([,p])=>p!=null)):void 0,d=i&&Object.keys(i).length>0?i:void 0,m=await this.client.sandboxes.create(e,o,{cpu:4,memoryMiB:4096,timeoutMs:n,workdir:t.workingDirectory,env:d});return t.workingDirectory&&await(await m.exec(["chown","-R","user:user",t.workingDirectory],{timeoutMs:3e4})).wait(),new l(m,s)}async connect(t,e){let n=await this.client.sandboxes.fromId(t);return new l(n,"unknown")}async list(t){let e=[],n=t?.limit??100;try{for await(let r of this.client.sandboxes.list())if(e.push({sandboxId:r.sandboxId,image:"unknown",metadata:{},startedAt:new Date().toISOString()}),e.length>=n)break}catch{}return e}};function k(c={}){let t=c.tokenId??process.env.MODAL_TOKEN_ID,e=c.tokenSecret??process.env.MODAL_TOKEN_SECRET;if(!t||!e)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 w({...c,tokenId:t,tokenSecret:e})}
4
4
  exports.ModalProvider=w;exports.createModalProvider=k;
package/dist/index.d.cts CHANGED
@@ -187,6 +187,8 @@ interface ModalConfig {
187
187
  tokenSecret?: string;
188
188
  /** Modal API endpoint. Default: https://api.modal.com:443 */
189
189
  endpoint?: string;
190
+ /** Docker image name (default: 'evolve-all'). Resolved through IMAGE_MAP or used as-is for custom images. */
191
+ imageName?: string;
190
192
  }
191
193
  /** Internal resolved config with required credentials */
192
194
  interface ResolvedModalConfig {
@@ -195,12 +197,15 @@ interface ResolvedModalConfig {
195
197
  appName?: string;
196
198
  defaultTimeoutMs?: number;
197
199
  endpoint?: string;
200
+ imageName?: string;
198
201
  }
199
202
  declare class ModalProvider implements SandboxProvider {
200
203
  readonly providerType: "modal";
204
+ readonly name = "Modal";
201
205
  private readonly client;
202
206
  private readonly appName;
203
207
  private readonly defaultTimeoutMs;
208
+ private readonly imageName;
204
209
  private _app;
205
210
  constructor(config: ResolvedModalConfig);
206
211
  private getApp;
package/dist/index.d.ts CHANGED
@@ -187,6 +187,8 @@ interface ModalConfig {
187
187
  tokenSecret?: string;
188
188
  /** Modal API endpoint. Default: https://api.modal.com:443 */
189
189
  endpoint?: string;
190
+ /** Docker image name (default: 'evolve-all'). Resolved through IMAGE_MAP or used as-is for custom images. */
191
+ imageName?: string;
190
192
  }
191
193
  /** Internal resolved config with required credentials */
192
194
  interface ResolvedModalConfig {
@@ -195,12 +197,15 @@ interface ResolvedModalConfig {
195
197
  appName?: string;
196
198
  defaultTimeoutMs?: number;
197
199
  endpoint?: string;
200
+ imageName?: string;
198
201
  }
199
202
  declare class ModalProvider implements SandboxProvider {
200
203
  readonly providerType: "modal";
204
+ readonly name = "Modal";
201
205
  private readonly client;
202
206
  private readonly appName;
203
207
  private readonly defaultTimeoutMs;
208
+ private readonly imageName;
204
209
  private _app;
205
210
  constructor(config: ResolvedModalConfig);
206
211
  private getApp;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import {ModalClient}from'modal';import {pack}from'tar-stream';var b={"evolve-all":"evolvingmachines/evolve-all:latest"},h=new Set([".xlsx",".xls",".docx",".doc",".pptx",".ppt",".pdf",".zip",".tar",".gz",".7z",".rar",".png",".jpg",".jpeg",".gif",".webp",".ico",".bmp",".mp3",".wav",".ogg",".flac",".aac",".mp4",".avi",".mov",".mkv",".webm",".woff",".woff2",".ttf",".otf",".eot",".exe",".dll",".so",".dylib",".sqlite",".db",".pickle",".pkl",".parquet"]);function y(m){let t=m.substring(m.lastIndexOf(".")).toLowerCase();return h.has(t)}var l=class{constructor(t){this.sandbox=t;}wrapAsUser(t,e,n){let r="";n&&Object.keys(n).length>0&&(r=Object.entries(n).filter(([,o])=>o!=null).map(([o,a])=>`export ${o}='${String(a).replace(/'/g,"'\\''")}'`).join("; ")+"; ");let s=e?`cd '${e.replace(/'/g,"'\\''")}' && ${r}${t}`:`${r}${t}`;return ["su","user","-c",`echo ${Buffer.from(s).toString("base64")} | base64 -d | bash`]}async run(t,e){let n=this.wrapAsUser(t,e?.cwd,e?.envs),r=await this.sandbox.exec(n,{timeoutMs:e?.timeoutMs}),{stdout:s,stderr:i}=await this.accumulateStreams(r,e?.onStdout,e?.onStderr);return {exitCode:await r.wait(),stdout:s,stderr:i}}async spawn(t,e){let n=this.wrapAsUser(t,e?.cwd,e?.envs),r=await this.sandbox.exec(n,{timeoutMs:e?.timeoutMs}),s="",i="",o=this.accumulateStreams(r,e?.onStdout?d=>{e.onStdout(d);}:void 0,e?.onStderr?d=>{e.onStderr(d);}:void 0).then(({stdout:d,stderr:c})=>{s=d,i=c;}).catch(()=>{});return {processId:`modal-${Date.now()}-${Math.random().toString(36).slice(2)}`,wait:async()=>(await o,{exitCode:await r.wait(),stdout:s,stderr:i}),kill:async()=>false}}async list(){let t=await this.sandbox.exec(["ps","-eo","pid,comm,args"],{timeoutMs:1e4});return await t.wait(),(await t.stdout.readText()).trim().split(`
2
- `).slice(1).map(r=>{let s=r.trim().split(/\s+/);return {processId:s[0],cmd:s[1]||"",args:s.slice(2),envs:{}}})}async connect(t,e){throw new Error("Modal does not support connecting to existing processes")}async sendStdin(t,e){throw new Error("Modal does not support sendStdin by process ID")}async kill(t){return await(await this.sandbox.exec(["kill","-9",t],{timeoutMs:1e4})).wait()===0}async accumulateStreams(t,e,n){let r="",s="",i=[];return i.push((async()=>{try{for await(let o of t.stdout){let a=typeof o=="string"?o:new TextDecoder().decode(o);r+=a,e?.(a);}}catch{}})()),i.push((async()=>{try{for await(let o of t.stderr){let a=typeof o=="string"?o:new TextDecoder().decode(o);s+=a,n?.(a);}}catch{}})()),await Promise.all(i),{stdout:r,stderr:s}}},f=class{constructor(t){this.sandbox=t;}async read(t){if(y(t)){let r=await this.sandbox.exec(["cat",t],{timeoutMs:3e5,mode:"binary"}),s=await r.wait();if(s!==0){let i=await r.stderr.readText();throw new Error(`Failed to read file ${t}: ${i||`exit code ${s}`}`)}return await r.stdout.readBytes()}let e=await this.sandbox.exec(["cat",t],{timeoutMs:3e5}),n=await e.wait();if(n!==0){let r=await e.stderr.readText();throw new Error(`Failed to read file ${t}: ${r||`exit code ${n}`}`)}return await e.stdout.readText()}async write(t,e){let n=this.toBuffer(e),r=t.substring(0,t.lastIndexOf("/"));r&&await this.makeDir(r);let s=t.replace(/'/g,"'\\''"),i=await this.sandbox.exec(["bash","-c",`cat > '${s}'`],{mode:"binary"});await i.stdin.writeBytes(new Uint8Array(n)),await i.stdin.getWriter().close(),await i.wait(),await(await this.sandbox.exec(["chown","user:user",t],{timeoutMs:1e4})).wait();}async writeBatch(t){let e=pack(),n=[],r=new Set;for(let a of t){let d=this.toBuffer(a.data),c=a.path.startsWith("/")?a.path.slice(1):a.path;e.entry({name:c},d);let u=a.path.substring(0,a.path.lastIndexOf("/"));u&&r.add(u);}e.finalize();for await(let a of e)n.push(Buffer.from(a));let s=Buffer.concat(n),i=await this.sandbox.exec(["tar","-xf","-","-C","/"],{mode:"binary"});if(await i.stdin.writeBytes(new Uint8Array(s)),await i.stdin.getWriter().close(),await i.wait(),r.size>0){let a=Array.from(r),d=new Set(a.map(c=>c.split("/").slice(0,4).join("/")));for(let c of d)await(await this.sandbox.exec(["chown","-R","user:user",c],{timeoutMs:3e4})).wait();}}async makeDir(t){await(await this.sandbox.exec(["mkdir","-p",t],{timeoutMs:1e4})).wait(),await(await this.sandbox.exec(["chown","-R","user:user",t],{timeoutMs:1e4})).wait();}async exists(t){return await(await this.sandbox.exec(["test","-e",t],{timeoutMs:1e4})).wait()===0}async list(t){let e=t.replace(/'/g,"'\\''"),n=await this.sandbox.exec(["bash","-c",`ls -la '${e}' | tail -n +2`],{timeoutMs:3e4});await n.wait();let r=await n.stdout.readText(),s=[];for(let i of r.trim().split(`
3
- `)){if(!i)continue;let o=i.split(/\s+/);if(o.length<9)continue;let a=o[0],d=o.slice(8).join(" ");d==="."||d===".."||s.push({name:d,path:t.endsWith("/")?`${t}${d}`:`${t}/${d}`,type:a.startsWith("d")?"dir":"file"});}return s}async remove(t){await(await this.sandbox.exec(["rm","-rf",t],{timeoutMs:3e4})).wait();}async rename(t,e){await(await this.sandbox.exec(["mv",t,e],{timeoutMs:3e4})).wait();}async readStream(t){return (await this.sandbox.exec(["cat",t],{timeoutMs:3e5,mode:"binary"})).stdout}async writeStream(t,e){let n=t.substring(0,t.lastIndexOf("/"));n&&await this.makeDir(n);let r=t.replace(/'/g,"'\\''"),s=await this.sandbox.exec(["bash","-c",`cat > '${r}'`],{mode:"binary"}),i=e.getReader();try{for(;;){let{done:a,value:d}=await i.read();if(a)break;await s.stdin.writeBytes(d);}}finally{i.releaseLock();}await s.stdin.getWriter().close(),await s.wait();}async uploadUrl(t,e){throw new Error("Modal does not support pre-signed upload URLs")}async downloadUrl(t,e){throw new Error("Modal does not support pre-signed download URLs")}async watchDir(t,e,n){throw new Error("Modal does not support watchDir")}toBuffer(t){if(typeof t=="string")return Buffer.from(t,"utf-8");if(t instanceof Buffer)return t;if(t instanceof ArrayBuffer||t instanceof Uint8Array)return Buffer.from(t);throw new Error(`Unsupported data type: ${typeof t}`)}},p=class{constructor(t,e){this.sandbox=t;this.commands=new l(t),this.files=new f(t),this.image=e,this.startTime=new Date;}commands;files;image;startTime;get sandboxId(){return this.sandbox.sandboxId}async getHost(t){let n=(await this.sandbox.tunnels())[t];if(!n)throw new Error(`No tunnel found for port ${t}`);return n.url}async isRunning(){try{return await(await this.sandbox.exec(["echo","ping"],{timeoutMs:5e3})).wait(),!0}catch{return false}}async getInfo(){return {sandboxId:this.sandbox.sandboxId,image:this.image,metadata:{},startedAt:this.startTime.toISOString()}}async kill(){try{await this.sandbox.terminate();}catch{await new Promise(t=>setTimeout(t,500)),await this.sandbox.terminate();}}async pause(){throw new Error("Modal does not support pause. Use kill() instead.")}},w=class{providerType="modal";client;appName;defaultTimeoutMs;_app;constructor(t){!t.endpoint&&process.env.MODAL_SERVER_URL?.startsWith("unix:")?process.env.MODAL_SERVER_URL="https://api.modal.com:443":t.endpoint&&(process.env.MODAL_SERVER_URL=t.endpoint),this.client=new ModalClient({tokenId:t.tokenId,tokenSecret:t.tokenSecret}),this.appName=t.appName??"evolve-sandbox",this.defaultTimeoutMs=t.defaultTimeoutMs??36e5;}async getApp(){return this._app||(this._app=await this.client.apps.fromName(this.appName,{createIfMissing:true})),this._app}async create(t){let e=await this.getApp(),n=t.timeoutMs??this.defaultTimeoutMs,r=t.image||"evolve-all",s=b[r]??r,i=this.client.images.fromRegistry(s),o=t.envs?Object.fromEntries(Object.entries(t.envs).filter(([,c])=>c!=null)):void 0,a=o&&Object.keys(o).length>0?o:void 0,d=await this.client.sandboxes.create(e,i,{cpu:4,memoryMiB:4096,timeoutMs:n,workdir:t.workingDirectory,env:a});return t.workingDirectory&&await(await d.exec(["chown","-R","user:user",t.workingDirectory],{timeoutMs:3e4})).wait(),new p(d,s)}async connect(t,e){let n=await this.client.sandboxes.fromId(t);return new p(n,"unknown")}async list(t){let e=[],n=t?.limit??100;try{for await(let r of this.client.sandboxes.list())if(e.push({sandboxId:r.sandboxId,image:"unknown",metadata:{},startedAt:new Date().toISOString()}),e.length>=n)break}catch{}return e}};function k(m={}){let t=m.tokenId??process.env.MODAL_TOKEN_ID,e=m.tokenSecret??process.env.MODAL_TOKEN_SECRET;if(!t||!e)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 w({...m,tokenId:t,tokenSecret:e})}
1
+ import {ModalClient}from'modal';import {pack}from'tar-stream';var b={"evolve-all":"evolvingmachines/evolve-all"},h=new Set([".xlsx",".xls",".docx",".doc",".pptx",".ppt",".pdf",".zip",".tar",".gz",".7z",".rar",".png",".jpg",".jpeg",".gif",".webp",".ico",".bmp",".mp3",".wav",".ogg",".flac",".aac",".mp4",".avi",".mov",".mkv",".webm",".woff",".woff2",".ttf",".otf",".eot",".exe",".dll",".so",".dylib",".sqlite",".db",".pickle",".pkl",".parquet"]);function y(c){let t=c.substring(c.lastIndexOf(".")).toLowerCase();return h.has(t)}var u=class{constructor(t){this.sandbox=t;}wrapAsUser(t,e,n){let r="";n&&Object.keys(n).length>0&&(r=Object.entries(n).filter(([,o])=>o!=null).map(([o,i])=>`export ${o}='${String(i).replace(/'/g,"'\\''")}'`).join("; ")+"; ");let s=e?`cd '${e.replace(/'/g,"'\\''")}' && ${r}${t}`:`${r}${t}`;return ["su","user","-c",`echo ${Buffer.from(s).toString("base64")} | base64 -d | bash`]}async run(t,e){let n=this.wrapAsUser(t,e?.cwd,e?.envs),r=await this.sandbox.exec(n,{timeoutMs:e?.timeoutMs}),{stdout:s,stderr:a}=await this.accumulateStreams(r,e?.onStdout,e?.onStderr);return {exitCode:await r.wait(),stdout:s,stderr:a}}async spawn(t,e){let n=this.wrapAsUser(t,e?.cwd,e?.envs),r=await this.sandbox.exec(n,{timeoutMs:e?.timeoutMs}),s="",a="",o=this.accumulateStreams(r,e?.onStdout?d=>{e.onStdout(d);}:void 0,e?.onStderr?d=>{e.onStderr(d);}:void 0).then(({stdout:d,stderr:m})=>{s=d,a=m;}).catch(()=>{});return {processId:`modal-${Date.now()}-${Math.random().toString(36).slice(2)}`,wait:async()=>(await o,{exitCode:await r.wait(),stdout:s,stderr:a}),kill:async()=>false}}async list(){let t=await this.sandbox.exec(["ps","-eo","pid,comm,args"],{timeoutMs:1e4});return await t.wait(),(await t.stdout.readText()).trim().split(`
2
+ `).slice(1).map(r=>{let s=r.trim().split(/\s+/);return {processId:s[0],cmd:s[1]||"",args:s.slice(2),envs:{}}})}async connect(t,e){throw new Error("Modal does not support connecting to existing processes")}async sendStdin(t,e){throw new Error("Modal does not support sendStdin by process ID")}async kill(t){return await(await this.sandbox.exec(["kill","-9",t],{timeoutMs:1e4})).wait()===0}async accumulateStreams(t,e,n){let r="",s="",a=[];return a.push((async()=>{try{for await(let o of t.stdout){let i=typeof o=="string"?o:new TextDecoder().decode(o);r+=i,e?.(i);}}catch{}})()),a.push((async()=>{try{for await(let o of t.stderr){let i=typeof o=="string"?o:new TextDecoder().decode(o);s+=i,n?.(i);}}catch{}})()),await Promise.all(a),{stdout:r,stderr:s}}},g=class{constructor(t){this.sandbox=t;}async read(t){if(y(t)){let r=await this.sandbox.exec(["cat",t],{timeoutMs:3e5,mode:"binary"}),s=await r.wait();if(s!==0){let a=await r.stderr.readText();throw new Error(`Failed to read file ${t}: ${a||`exit code ${s}`}`)}return await r.stdout.readBytes()}let e=await this.sandbox.exec(["cat",t],{timeoutMs:3e5}),n=await e.wait();if(n!==0){let r=await e.stderr.readText();throw new Error(`Failed to read file ${t}: ${r||`exit code ${n}`}`)}return await e.stdout.readText()}async write(t,e){let n=this.toBuffer(e),r=t.substring(0,t.lastIndexOf("/"));r&&await this.makeDir(r);let s=t.replace(/'/g,"'\\''"),a=await this.sandbox.exec(["bash","-c",`cat > '${s}'`],{mode:"binary"});await a.stdin.writeBytes(new Uint8Array(n)),await a.stdin.getWriter().close(),await a.wait(),await(await this.sandbox.exec(["chown","user:user",t],{timeoutMs:1e4})).wait();}async writeBatch(t){let e=pack(),n=[],r=new Set;for(let i of t){let d=this.toBuffer(i.data),m=i.path.startsWith("/")?i.path.slice(1):i.path;e.entry({name:m},d);let p=i.path.substring(0,i.path.lastIndexOf("/"));p&&r.add(p);}e.finalize();for await(let i of e)n.push(Buffer.from(i));let s=Buffer.concat(n),a=await this.sandbox.exec(["tar","-xf","-","-C","/"],{mode:"binary"});if(await a.stdin.writeBytes(new Uint8Array(s)),await a.stdin.getWriter().close(),await a.wait(),r.size>0){let i=Array.from(r),d=new Set(i.map(m=>m.split("/").slice(0,4).join("/")));for(let m of d)await(await this.sandbox.exec(["chown","-R","user:user",m],{timeoutMs:3e4})).wait();}}async makeDir(t){await(await this.sandbox.exec(["mkdir","-p",t],{timeoutMs:1e4})).wait(),await(await this.sandbox.exec(["chown","-R","user:user",t],{timeoutMs:1e4})).wait();}async exists(t){return await(await this.sandbox.exec(["test","-e",t],{timeoutMs:1e4})).wait()===0}async list(t){let e=t.replace(/'/g,"'\\''"),n=await this.sandbox.exec(["bash","-c",`ls -la '${e}' | tail -n +2`],{timeoutMs:3e4});await n.wait();let r=await n.stdout.readText(),s=[];for(let a of r.trim().split(`
3
+ `)){if(!a)continue;let o=a.split(/\s+/);if(o.length<9)continue;let i=o[0],d=o.slice(8).join(" ");d==="."||d===".."||s.push({name:d,path:t.endsWith("/")?`${t}${d}`:`${t}/${d}`,type:i.startsWith("d")?"dir":"file"});}return s}async remove(t){await(await this.sandbox.exec(["rm","-rf",t],{timeoutMs:3e4})).wait();}async rename(t,e){await(await this.sandbox.exec(["mv",t,e],{timeoutMs:3e4})).wait();}async readStream(t){return (await this.sandbox.exec(["cat",t],{timeoutMs:3e5,mode:"binary"})).stdout}async writeStream(t,e){let n=t.substring(0,t.lastIndexOf("/"));n&&await this.makeDir(n);let r=t.replace(/'/g,"'\\''"),s=await this.sandbox.exec(["bash","-c",`cat > '${r}'`],{mode:"binary"}),a=e.getReader();try{for(;;){let{done:i,value:d}=await a.read();if(i)break;await s.stdin.writeBytes(d);}}finally{a.releaseLock();}await s.stdin.getWriter().close(),await s.wait();}async uploadUrl(t,e){throw new Error("Modal does not support pre-signed upload URLs")}async downloadUrl(t,e){throw new Error("Modal does not support pre-signed download URLs")}async watchDir(t,e,n){throw new Error("Modal does not support watchDir")}toBuffer(t){if(typeof t=="string")return Buffer.from(t,"utf-8");if(t instanceof Buffer)return t;if(t instanceof ArrayBuffer||t instanceof Uint8Array)return Buffer.from(t);throw new Error(`Unsupported data type: ${typeof t}`)}},l=class{constructor(t,e){this.sandbox=t;this.commands=new u(t),this.files=new g(t),this.image=e,this.startTime=new Date;}commands;files;image;startTime;get sandboxId(){return this.sandbox.sandboxId}async getHost(t){let n=(await this.sandbox.tunnels())[t];if(!n)throw new Error(`No tunnel found for port ${t}`);return n.url}async isRunning(){try{return await(await this.sandbox.exec(["echo","ping"],{timeoutMs:5e3})).wait(),!0}catch{return false}}async getInfo(){return {sandboxId:this.sandbox.sandboxId,image:this.image,metadata:{},startedAt:this.startTime.toISOString()}}async kill(){try{await this.sandbox.terminate();}catch{await new Promise(t=>setTimeout(t,500)),await this.sandbox.terminate();}}async pause(){throw new Error("Modal does not support pause. Use kill() instead.")}},w=class{providerType="modal";name="Modal";client;appName;defaultTimeoutMs;imageName;_app;constructor(t){!t.endpoint&&process.env.MODAL_SERVER_URL?.startsWith("unix:")?process.env.MODAL_SERVER_URL="https://api.modal.com:443":t.endpoint&&(process.env.MODAL_SERVER_URL=t.endpoint),this.client=new ModalClient({tokenId:t.tokenId,tokenSecret:t.tokenSecret}),this.appName=t.appName??"evolve-sandbox",this.defaultTimeoutMs=t.defaultTimeoutMs??36e5,this.imageName=t.imageName??"evolve-all";}async getApp(){return this._app||(this._app=await this.client.apps.fromName(this.appName,{createIfMissing:true})),this._app}async create(t){let e=await this.getApp(),n=t.timeoutMs??this.defaultTimeoutMs,r=t.image||this.imageName,s=b[r]??r,o=await this.client.images.fromRegistry(s).build(e),i=t.envs?Object.fromEntries(Object.entries(t.envs).filter(([,p])=>p!=null)):void 0,d=i&&Object.keys(i).length>0?i:void 0,m=await this.client.sandboxes.create(e,o,{cpu:4,memoryMiB:4096,timeoutMs:n,workdir:t.workingDirectory,env:d});return t.workingDirectory&&await(await m.exec(["chown","-R","user:user",t.workingDirectory],{timeoutMs:3e4})).wait(),new l(m,s)}async connect(t,e){let n=await this.client.sandboxes.fromId(t);return new l(n,"unknown")}async list(t){let e=[],n=t?.limit??100;try{for await(let r of this.client.sandboxes.list())if(e.push({sandboxId:r.sandboxId,image:"unknown",metadata:{},startedAt:new Date().toISOString()}),e.length>=n)break}catch{}return e}};function k(c={}){let t=c.tokenId??process.env.MODAL_TOKEN_ID,e=c.tokenSecret??process.env.MODAL_TOKEN_SECRET;if(!t||!e)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 w({...c,tokenId:t,tokenSecret:e})}
4
4
  export{w as ModalProvider,k as createModalProvider};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evolvingmachines/modal",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "keywords": [
5
5
  "ai",
6
6
  "agents",