@evolvingmachines/daytona 0.0.51 → 0.0.52-project-sable.20260729.8632c49

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,2 +1,2 @@
1
- 'use strict';var sdk=require('@daytonaio/sdk');var h={"evolve-all":"evolvingmachines/evolve-all"},S=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 w(r){let e=r.substring(r.lastIndexOf(".")).toLowerCase();return S.has(e)}function v(r){let e=r.lastIndexOf("/");return e>0?r.substring(0,e):"/"}function P(r){let e=r.lastIndexOf("/");return e>=0?r.substring(e+1):r}function g(r,e,t){let s=r;return e&&(s=`cd '${e.replace(/'/g,"'\\''")}' && ${s}`),t&&Object.keys(t).length>0&&(s=`${Object.entries(t).filter(([,a])=>a!=null).map(([a,o])=>`export ${a}='${o.replace(/'/g,"'\\''")}'`).join("; ")}; ${s}`),s}var C=g,f=class{constructor(e){this.sandbox=e;}async run(e,t){let s=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,n=`run-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(n);try{let a=await this.sandbox.process.executeSessionCommand(n,{command:g(e,t?.cwd,t?.envs),runAsync:!1},s),o=a.cmdId;o&&(t?.onStdout||t?.onStderr)&&await this.sandbox.process.getSessionCommandLogs(n,o,t.onStdout||(()=>{}),t.onStderr||(()=>{}));let d=a.stdout??a.output??"",i=a.stderr??"";if(!d&&o&&!t?.onStdout)try{let m=await this.sandbox.process.getSessionCommandLogs(n,o);d=m.stdout??m.output??"",i=m.stderr??i;}catch{}return {exitCode:a.exitCode??0,stdout:d,stderr:i}}finally{try{await this.sandbox.process.deleteSession(n);}catch{}}}async spawn(e,t){let s=`evolve-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(s);let n=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,a=Date.now(),o=t?.timeoutMs,i=(await this.sandbox.process.executeSessionCommand(s,{command:g(e,t?.cwd,t?.envs),runAsync:true},n)).cmdId;i&&(t?.onStdout||t?.onStderr)&&this.sandbox.process.getSessionCommandLogs(s,i,t.onStdout||(()=>{}),t.onStderr||(()=>{})).catch(()=>{});let m=this.sandbox;return {processId:s,wait:async()=>{if(!i)return {exitCode:0,stdout:"",stderr:""};for(;;){if(o&&Date.now()-a>=o){try{await m.process.deleteSession(s);}catch{}return {exitCode:-1,stdout:"",stderr:"operation timed out"}}try{let c=await m.process.getSessionCommand(s,i);if(c.exitCode!==void 0)try{let l=await m.process.getSessionCommandLogs(s,i);return {exitCode:c.exitCode,stdout:l.stdout??l.output??"",stderr:l.stderr??""}}catch{return {exitCode:c.exitCode,stdout:"",stderr:""}}}catch(c){if((c instanceof Error?c.message.toLowerCase():String(c).toLowerCase()).includes("not found"))return {exitCode:-1,stdout:"",stderr:"session terminated"};throw c}await new Promise(c=>setTimeout(c,500));}},kill:async()=>{try{return await m.process.deleteSession(s),!0}catch{return false}}}}async list(){try{return (await this.sandbox.process.listSessions()).map(t=>({processId:t.sessionId||"",cmd:"",args:[],envs:{}}))}catch{return []}}async kill(e){try{return await this.sandbox.process.deleteSession(e),!0}catch{return false}}},p=class{constructor(e){this.sandbox=e;}async read(e){let t=await this.sandbox.fs.downloadFile(e);return w(e)?new Uint8Array(t):t.toString("utf-8")}async write(e,t){let s;if(typeof t=="string")s=Buffer.from(t,"utf-8");else if(Buffer.isBuffer(t))s=t;else if(t instanceof ArrayBuffer)s=Buffer.from(t);else if(t instanceof Uint8Array)s=Buffer.from(t);else throw new Error(`Unsupported content type: ${typeof t}`);await this.sandbox.fs.uploadFile(s,e);}async writeBatch(e){let t=e.map(s=>{let n;if(typeof s.data=="string")n=Buffer.from(s.data,"utf-8");else if(Buffer.isBuffer(s.data))n=s.data;else if(s.data instanceof ArrayBuffer)n=Buffer.from(s.data);else if(s.data instanceof Uint8Array)n=Buffer.from(s.data);else throw new Error(`Unsupported content type: ${typeof s.data}`);return {source:n,destination:s.path}});await this.sandbox.fs.uploadFiles(t);}async makeDir(e){await this.sandbox.fs.createFolder(e,"755");}async exists(e){try{let t=v(e),s=P(e);return (await this.sandbox.fs.listFiles(t)).some(a=>a.name===s)}catch{return false}}async list(e){return (await this.sandbox.fs.listFiles(e)).map(s=>({name:s.name,path:e.endsWith("/")?`${e}${s.name}`:`${e}/${s.name}`,type:s.isDir?"dir":"file"}))}async remove(e){await this.sandbox.fs.deleteFile(e,true);}async rename(e,t){await this.sandbox.fs.moveFiles(e,t);}},u=class{constructor(e){this.sandbox=e;this.commands=new f(e),this.files=new p(e);}commands;files;get sandboxId(){return this.sandbox.id}async getHost(e){return (await this.sandbox.getPreviewLink(e)).url}async isRunning(){return this.sandbox.state==="started"}async getInfo(){return {sandboxId:this.sandbox.id,image:this.sandbox.snapshot||"unknown",name:this.sandbox.name,metadata:this.sandbox.labels||{},startedAt:new Date().toISOString()}}async kill(){await this.sandbox.delete();}async pause(){await this.sandbox.stop();}},x=class{providerType="daytona";name="Daytona";client;defaultTimeoutMs;snapshotName;constructor(e){this.client=new sdk.Daytona({apiKey:e.apiKey,apiUrl:e.apiUrl,target:e.target}),this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.snapshotName=e.snapshotName??"evolve-all";}async create(e){let t=e.timeoutMs??this.defaultTimeoutMs,s=Math.max(1,Math.ceil(t/6e4)),n=e.image||this.snapshotName,a;try{let o=await this.client.snapshot.get(n);if(o&&o.state==="active")console.log(`[daytona] Using cached snapshot: ${n}`),a=await this.client.create({snapshot:n,envVars:e.envs,labels:e.metadata,autoStopInterval:s},{timeout:600});else throw new Error("Snapshot not active")}catch{let o=h[n]??n;console.log(`[daytona] Snapshot "${n}" not found, building from image: ${o}`),console.log("[daytona] First run will take a few minutes (this only happens once)...");try{await this.client.snapshot.create({name:n,image:sdk.Image.base(o),resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{onLogs:d=>console.log(`[daytona] ${d}`)}),console.log(`[daytona] Snapshot "${n}" ready.`),a=await this.client.create({snapshot:n,envVars:e.envs,labels:e.metadata,autoStopInterval:s},{timeout:600});}catch(d){console.warn(`[daytona] Snapshot creation failed, falling back to direct image: ${d instanceof Error?d.message:d}`),a=await this.client.create({image:o,envVars:e.envs,labels:e.metadata,autoStopInterval:s,resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{timeout:600,onSnapshotCreateLogs:i=>console.log(`[daytona] ${i}`)});}}return e.workingDirectory&&await a.fs.createFolder(e.workingDirectory,"755"),new u(a)}async connect(e,t){let s=await this.client.get(e);return s.state!=="started"&&await s.start(),new u(s)}async list(e){let t=e?.limit??100;return (await this.client.list(e?.metadata,1,t)).items.map(n=>({sandboxId:n.id,image:n.snapshot||"unknown",name:n.name,metadata:n.labels||{},startedAt:new Date().toISOString()}))}};function A(r={}){let e=r.apiKey??process.env.DAYTONA_API_KEY;if(!e)throw new Error("Daytona API key required. Set DAYTONA_API_KEY environment variable or pass apiKey in config. Get your key at https://app.daytona.io/dashboard/keys");return new x({...r,apiKey:e})}
2
- exports.DaytonaProvider=x;exports._testWrapCommand=C;exports.createDaytonaProvider=A;
1
+ 'use strict';var sdk=require('@daytonaio/sdk'),promises=require('dns/promises');var z={"evolve-all":"evolvingmachines/evolve-all"},q=10,L=10,T=100,y=100,W=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 X(s){let e=s.substring(s.lastIndexOf(".")).toLowerCase();return W.has(e)}function G(s){let e=s.lastIndexOf("/");return e>0?s.substring(0,e):"/"}function J(s){let e=s.lastIndexOf("/");return e>=0?s.substring(e+1):s}var f=class extends Error{reason;destination;constructor(e,t,n){super(t),this.name="DaytonaNetworkPolicyError",this.reason=e,this.destination=n;}},w=class extends Error{image;constructor(e,t){let n=t instanceof Error?t.message:String(t??"unknown error");super(`Failed to pull image "${e}" on Daytona: ${n}. Private registry images (e.g. AWS ECR) require registry credentials pre-registered in the Daytona dashboard (Registries page) before sandbox creation \u2014 Daytona has no per-call image pull secret. Register the registry at https://app.daytona.io, then retry. Also ensure the image is linux/amd64 and pinned to a tag or digest (floating tags like "latest" are rejected by Daytona's snapshot builder).`),this.name="DaytonaImagePullError",this.image=e;}},h=class extends Error{snapshot;constructor(e){super(`Daytona snapshot "${e}" already exists and pins its cpu/memory/disk sizing \u2014 create-from-snapshot cannot resize it, so the requested \`resources\` cannot be enforced. Pre-build a snapshot with the desired sizing under its own name (sizing-addressed), or omit \`resources\` to accept the snapshot's pinned sizing.`),this.name="DaytonaResourcesError",this.snapshot=e;}},S=class extends Error{constructor(){super("Daytona has no separate idle timeout: auto-stop IS its only clock and it already measures inactivity, so `timeoutMs` is what sets it (autoStopInterval). Set the inactivity bound with `timeoutMs`; a hard bound on the process itself is enforced in-box instead (see withInBoxTimeout)."),this.name="DaytonaIdleTimeoutError";}};function v(s,e){if(!e||e<=0)return s;let t=Buffer.from(s).toString("base64"),n=`/tmp/.evolve-cmd-${Date.now()}-${Math.random().toString(36).slice(2,8)}.sh`;return `sh -c '${`echo ${t} | base64 -d > ${n} && [ -s ${n} ] || { rm -f ${n}; exit 126; }; if command -v timeout >/dev/null 2>&1; then timeout -k 10 ${e} bash ${n}; else bash ${n}; fi; rc=$?; rm -f ${n}; exit $rc`}'`}function P(s,e,t,n){let r=s;return e&&(r=`cd '${e.replace(/'/g,"'\\''")}' && ${r}`),t&&Object.keys(t).length>0&&(r=`${Object.entries(t).filter(([,o])=>o!=null).map(([o,a])=>`export ${o}='${String(a).replace(/'/g,"'\\''")}'`).join("; ")}; ${r}`),n==="root"&&(r=`echo ${Buffer.from(r).toString("base64")} | base64 -d | sudo -n bash`),r}var V=s=>promises.resolve4(s);function E(s){let e=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(?:\/(\d{1,3}))?$/.exec(s);return !(!e||[e[1],e[2],e[3],e[4]].some(t=>Number(t)>255)||e[5]!==void 0&&Number(e[5])>32)}function Z(s){return /^\d{1,3}(\.\d{1,3}){3}(\/\d+)?$/.test(s)&&!E(s)}function Q(s){return s.startsWith("[")?true:(s.match(/:/g)?.length??0)>=2}function ee(s){return /^[^:]+:\d+$/.test(s)}async function B(s,e=V){if(!s||s.outbound==="open"){if(s?.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");return s?{networkBlockAll:false}:{}}let t=s.allowedDestinations??[];if(t.length===0)return {networkBlockAll:true};let n=[],r=[];for(let o of t)if(E(o))n.push(o.includes("/")?o:`${o}/32`);else {if(Z(o))throw new f("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(Q(o))throw new f("ipv6-unsupported",`Daytona's network allowlist supports IPv4 CIDRs only; cannot allow IPv6 destination "${o}".`,o);if(ee(o))throw new f("port-unsupported",`Daytona'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);if(o.includes("*"))throw new f("wildcard-hostname",`Daytona cannot enforce wildcard hostname "${o}": its allowlist is kernel-level IPv4 CIDR filtering with no DNS/domain layer. List concrete hostnames or IPv4 CIDRs instead.`,o);r.push(o);}for(let o of r){let a=[];try{a=await e(o);}catch(i){throw new f("unresolvable-hostname",`Cannot pin hostname "${o}" to stable IPv4 addresses at create time (${i instanceof Error?i.message:String(i)}). Daytona enforces IPv4 CIDRs only; use an IPv4 CIDR for this destination instead.`,o)}if(a.length===0)throw new f("unresolvable-hostname",`Hostname "${o}" resolved to no IPv4 addresses. Daytona enforces IPv4 CIDRs only; use an IPv4 CIDR for this destination instead.`,o);console.warn(`[daytona] Network allowlist: pinning "${o}" to [${a.join(", ")}] (DNS resolved at create time). Daytona enforces IPv4 CIDRs only \u2014 if this host rotates DNS (CDNs and cloud APIs often do), traffic to its new IPs will be BLOCKED for the sandbox's lifetime.`);for(let i of a)n.push(`${i}/32`);}let l=[...new Set(n)];if(l.length>L)throw new f("allowlist-too-large",`Daytona allows at most ${L} entries in its network allowlist; this policy resolved to ${l.length} CIDRs (${l.join(", ")}). Aggregate destinations into broader CIDRs or reduce the list.`);return {networkBlockAll:false,networkAllowList:l.join(",")}}function O(s){let e=s.indexOf("/");if(e<0)return;let t=s.substring(0,e);return t.includes(".")||t.includes(":")||t==="localhost"?t:void 0}function k(s){return {sandboxId:s.id,image:s.snapshot??"",name:s.name,metadata:s.labels??{},startedAt:s.createdAt??""}}function _(s){if(s==="started")return "running";if(s==="stopped"||s==="archived")return "paused"}var I=class extends sdk.Daytona{constructor(t,n){super(t);this.managedToolboxUrl=n;}async getProxyToolboxUrl(){return this.managedToolboxUrl}};function te(s,e){let t=new Uint8Array(new ArrayBuffer(s.length+e.length));return t.set(s,0),t.set(e,s.length),t}function U(s,e,t){e:for(let n=t;n+e.length<=s.length;n++){for(let r=0;r<e.length;r++)if(s[n+r]!==e[r])continue e;return n}return -1}function N(s,e){let t=new TextDecoder("utf-8"),n=new TextDecoder("utf-8"),r=new Uint8Array(0),l="stdout",o=a=>{a.length!==0&&(l==="stdout"?s(t.decode(a,{stream:true})):e(n.decode(a,{stream:true})));};return {push(a){for(r=te(r,a);;){let i=U(r,sdk.STDOUT_PREFIX_BYTES,0),d=U(r,sdk.STDERR_PREFIX_BYTES,0),c=-1,u="stdout",m=0;if(i!==-1&&(d===-1||i<d)?(c=i,u="stdout",m=sdk.STDOUT_PREFIX_BYTES.length):d!==-1&&(c=d,u="stderr",m=sdk.STDERR_PREFIX_BYTES.length),c===-1){let g=Math.max(0,r.length-(sdk.MAX_PREFIX_LEN-1));o(r.subarray(0,g)),r=r.slice(g);return}o(r.subarray(0,c)),l=u,r=r.slice(c+m);}},flush(){o(r),r=new Uint8Array(0);let a=t.decode();a&&s(a);let i=n.decode();i&&e(i);}}}async function F(s,e,t,n,r,l){let o=`${s.toolboxUrl.replace(/\/+$/,"")}/${encodeURIComponent(e)}/process/session/${encodeURIComponent(t)}/command/${encodeURIComponent(n)}/logs?follow=true`,a=await fetch(o,{headers:{Authorization:`Bearer ${s.apiKey}`,accept:"application/octet-stream"}});if(!a.ok)throw new Error(`Daytona managed log follow failed: ${a.status} ${await a.text()}`);if(!a.body)return;let i=N(r,l),d=a.body.getReader();try{for(;;){let{done:c,value:u}=await d.read();if(c)break;u&&i.push(u);}}finally{i.flush(),await d.cancel().catch(()=>{});}}function x(s){let e=s.stdout||"",t=s.stderr||"",n=s.output||"";return !e&&!t&&n?{stdout:n,stderr:""}:{stdout:e,stderr:t}}var D=class{constructor(e,t,n){this.sandbox=e;this.user=t;this.managedStream=n;}followLogs(e,t,n,r){return this.managedStream?F(this.managedStream,this.sandbox.id,e,t,n,r):this.sandbox.process.getSessionCommandLogs(e,t,n,r)}async run(e,t){let n=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,r=`run-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(r);try{let l=await this.sandbox.process.executeSessionCommand(r,{command:v(P(e,t?.cwd,t?.envs,this.user),n),runAsync:!1},n),o=l.cmdId;o&&(t?.onStdout||t?.onStderr)&&await this.followLogs(r,o,t.onStdout||(()=>{}),t.onStderr||(()=>{}));let{stdout:a,stderr:i}=x(l);if(!a&&!i&&o&&!t?.onStdout)try{let d=await this.sandbox.process.getSessionCommandLogs(r,o),c=x(d);a=c.stdout,i=c.stderr||i;}catch{}return {exitCode:l.exitCode??0,stdout:a,stderr:i}}finally{try{await this.sandbox.process.deleteSession(r);}catch{}}}async spawn(e,t){let n=`evolve-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(n);let r=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,l=Date.now(),o=t?.timeoutMs,i=(await this.sandbox.process.executeSessionCommand(n,{command:v(P(e,t?.cwd,t?.envs,this.user),r),runAsync:true},r)).cmdId;i&&(t?.onStdout||t?.onStderr)&&this.followLogs(n,i,t.onStdout||(()=>{}),t.onStderr||(()=>{})).catch(()=>{});let d=this.sandbox;return {processId:n,wait:async()=>{if(!i)return {exitCode:0,stdout:"",stderr:""};for(;;){if(o&&Date.now()-l>=o){try{await d.process.deleteSession(n);}catch{}return {exitCode:124,stdout:"",stderr:"operation timed out"}}try{let c=await d.process.getSessionCommand(n,i);if(c.exitCode!==void 0)try{let u=await d.process.getSessionCommandLogs(n,i);return {exitCode:c.exitCode,...x(u)}}catch{return {exitCode:c.exitCode,stdout:"",stderr:""}}}catch(c){let u=c instanceof Error?c.message.toLowerCase():String(c).toLowerCase();if(u.includes("not found")){let m=u.includes("sandbox");try{await d.refreshData(),m=!1;}catch(g){(g instanceof Error?g.message.toLowerCase():String(g).toLowerCase()).includes("not found")&&(m=true);}return {exitCode:-1,stdout:"",stderr:m?"sandbox deleted during run":"session terminated"}}throw c}await new Promise(c=>setTimeout(c,500));}},kill:async()=>{try{return await d.process.deleteSession(n),!0}catch{return false}}}}async list(){try{return (await this.sandbox.process.listSessions()).map(t=>({processId:t.sessionId||"",cmd:"",args:[],envs:{}}))}catch{return []}}async kill(e){try{return await this.sandbox.process.deleteSession(e),!0}catch{return false}}},A=class{constructor(e){this.sandbox=e;}async read(e){let t=await this.sandbox.fs.downloadFile(e);return X(e)?new Uint8Array(t):t.toString("utf-8")}async write(e,t){let n;if(typeof t=="string")n=Buffer.from(t,"utf-8");else if(Buffer.isBuffer(t))n=t;else if(t instanceof ArrayBuffer)n=Buffer.from(t);else if(t instanceof Uint8Array)n=Buffer.from(t);else throw new Error(`Unsupported content type: ${typeof t}`);await this.sandbox.fs.uploadFile(n,e);}async writeBatch(e){let t=e.map(n=>{let r;if(typeof n.data=="string")r=Buffer.from(n.data,"utf-8");else if(Buffer.isBuffer(n.data))r=n.data;else if(n.data instanceof ArrayBuffer)r=Buffer.from(n.data);else if(n.data instanceof Uint8Array)r=Buffer.from(n.data);else throw new Error(`Unsupported content type: ${typeof n.data}`);return {source:r,destination:n.path}});await this.sandbox.fs.uploadFiles(t);}async writeFromPath(e,t){await this.sandbox.fs.uploadFile(t,e);}async makeDir(e){await this.sandbox.fs.createFolder(e,"755");}async exists(e){try{let t=G(e),n=J(e);return (await this.sandbox.fs.listFiles(t)).some(l=>l.name===n)}catch{return false}}async list(e){return (await this.sandbox.fs.listFiles(e)).map(n=>({name:n.name,path:e.endsWith("/")?`${e}${n.name}`:`${e}/${n.name}`,type:n.isDir?"dir":"file"}))}async remove(e){await this.sandbox.fs.deleteFile(e,true);}async rename(e,t){await this.sandbox.fs.moveFiles(e,t);}},b=class{constructor(e,t,n){this.sandbox=e;this.commands=new D(e,t,n),this.files=new A(e);}commands;files;get sandboxId(){return this.sandbox.id}async getHost(e){return (await this.sandbox.getPreviewLink(e)).url}async isRunning(){try{await this.sandbox.refreshData();}catch{return false}return this.sandbox.state==="started"}async getInfo(){return await this.sandbox.refreshData(),k(this.sandbox)}async kill(){await this.sandbox.delete();}async pause(){await this.sandbox.stop();}},C=class{providerType="daytona";name="Daytona";client;defaultTimeoutMs;snapshotName;sandboxUsers=new Map;managedStream;constructor(e){let t={apiKey:e.apiKey,apiUrl:e.apiUrl,target:e.target};this.client=e.managedToolboxUrl?new I(t,e.managedToolboxUrl):new sdk.Daytona(t),e.managedToolboxUrl&&(this.managedStream={toolboxUrl:e.managedToolboxUrl,apiKey:e.apiKey}),this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.snapshotName=e.snapshotName??"evolve-all";}async create(e){if(e.idleTimeoutMs!==void 0)throw new S;let t=await B(e.network),n=e.user,r=n&&n!=="root"?n:void 0,l=e.timeoutMs??this.defaultTimeoutMs,o=Math.max(1,Math.ceil(l/6e4)),a=e.image||this.snapshotName,i={envVars:e.envs,labels:e.metadata,autoStopInterval:o,autoDeleteInterval:q,...r?{user:r}:{},...t},d,c=e.resources!==void 0&&(e.resources.cpu!==void 0||e.resources.memory!==void 0||e.resources.disk!==void 0);if(this.managedStream){if(c)throw new h(a);let u=await this.client.create({snapshot:a,...i},{timeout:600});return e.workingDirectory&&await u.fs.createFolder(e.workingDirectory,"755"),n&&this.sandboxUsers.set(u.id,n),new b(u,n,this.managedStream)}try{let u=await this.client.snapshot.get(a);if(u&&u.state==="active"){if(c)throw new h(a);console.log(`[daytona] Using cached snapshot: ${a}`),d=await this.client.create({snapshot:a,...i},{timeout:600});}else throw new Error("Snapshot not active")}catch(u){if(u instanceof h)throw u;let m=z[a]??a;console.log(`[daytona] Snapshot "${a}" not found, building from image: ${m}`),console.log("[daytona] First run will take a few minutes (this only happens once)...");try{await this.client.snapshot.create({name:a,image:sdk.Image.base(m),resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{onLogs:g=>console.log(`[daytona] ${g}`)}),console.log(`[daytona] Snapshot "${a}" ready.`),d=await this.client.create({snapshot:a,...i},{timeout:600});}catch(g){console.warn(`[daytona] Snapshot creation failed, falling back to direct image: ${g instanceof Error?g.message:g}`);try{d=await this.client.create({image:m,...i,resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{timeout:600,onSnapshotCreateLogs:p=>console.log(`[daytona] ${p}`)});}catch(p){throw O(m)?new w(m,p):p}}}return e.workingDirectory&&await d.fs.createFolder(e.workingDirectory,"755"),n&&this.sandboxUsers.set(d.id,n),new b(d,n,this.managedStream)}async connect(e,t){let n=await this.client.get(e);return n.state!=="started"&&await n.start(),new b(n,this.sandboxUsers.get(e),this.managedStream)}async list(e){let t=await this.paginate(e);if(t.error&&!t.stoppedAtLimit)throw new Error(t.error);return t.sandboxes}async listAll(e){let{stoppedAtLimit:t,...n}=await this.paginate(e);return n}async paginate(e){return K(t=>this.listPage(e,t),e)}async listPage(e,t){let n=e?.state?ne(e.state):void 0;if(n&&n.length>0){let r=this.client.sandboxApi;if(typeof r?.listSandboxesPaginated=="function")try{let l=await r.listSandboxesPaginated(void 0,t,T,void 0,void 0,e?.metadata?JSON.stringify(e.metadata):void 0,void 0,n);return {items:l.data.items,totalPages:l.data.totalPages}}catch{}}return this.client.list(e?.metadata,t,T)}};function ne(s){let e=[];for(let t of s)t==="running"&&e.push("started"),t==="paused"&&e.push("stopped","archived");return e}async function K(s,e){let t=e?.limit,n=[],r=0,l=new Set;for(let o=1;o<=y;o+=1){let a;try{a=await s(o);}catch(d){return {sandboxes:n,complete:false,pagesFetched:r,stoppedAtLimit:false,error:`sandbox list failed: ${d instanceof Error?d.message:String(d)}`}}r+=1;for(let d of a.items){if(e?.state){let c=_(d.state);if(c===void 0||!e.state.includes(c))continue}if(!l.has(d.id)){if(t!==void 0&&n.length>=t)return {sandboxes:n,complete:false,pagesFetched:r,stoppedAtLimit:true,error:`stopped at the requested limit of ${t} with more sandboxes available`};l.add(d.id),n.push(k(d));}}let i=a.items.length===0||typeof a.totalPages=="number"&&o>=a.totalPages;if(t!==void 0&&n.length>=t&&!i)return {sandboxes:n,complete:false,pagesFetched:r,stoppedAtLimit:true,error:`stopped at the requested limit of ${t} with more sandboxes available`};if(i)break;if(o===y)return {sandboxes:n,complete:false,pagesFetched:r,stoppedAtLimit:false,error:`sandbox list exceeded ${y} pages`}}return {sandboxes:n,complete:true,pagesFetched:r,stoppedAtLimit:false}}var oe=K;function ae(s={}){let e=s.apiKey??process.env.DAYTONA_API_KEY;if(!e)throw new Error("Daytona API key required. Set DAYTONA_API_KEY environment variable or pass apiKey in config. Get your key at https://app.daytona.io/dashboard/keys");return new C({...s,apiKey:e})}var ie=P,de=v,le=B,ce=O,ue=k,me=_,ge=N,fe=F,pe=x;
2
+ exports.DAYTONA_AUTO_DELETE_GRACE_MINUTES=q;exports.DAYTONA_LIST_PAGE_SIZE=T;exports.DAYTONA_MAX_LIST_PAGES=y;exports.DAYTONA_MAX_NETWORK_ALLOWLIST=L;exports.DaytonaCommands=D;exports.DaytonaFiles=A;exports.DaytonaIdleTimeoutError=S;exports.DaytonaImagePullError=w;exports.DaytonaNetworkPolicyError=f;exports.DaytonaProvider=C;exports.DaytonaResourcesError=h;exports._testCollectSandboxPages=oe;exports._testCreateLogDemuxer=ge;exports._testDaytonaStateToEvolveState=me;exports._testFollowManagedSessionLogs=fe;exports._testImageRegistryHost=ce;exports._testMapNetworkPolicy=le;exports._testReadCommandStreams=pe;exports._testToSandboxInfo=ue;exports._testWithInBoxTimeout=de;exports._testWrapCommand=ie;exports.createDaytonaProvider=ae;
package/dist/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Sandbox } from '@daytonaio/sdk';
2
+
1
3
  /**
2
4
  * Daytona Sandbox Provider
3
5
  *
@@ -7,17 +9,215 @@
7
9
  * - Mirror E2B provider interface for SDK compatibility
8
10
  * - Uses public Docker images via IMAGE_MAP
9
11
  * - Parallel structure to E2B provider
12
+ *
13
+ * Daytona-specific notes:
14
+ * - Network policy maps to Daytona's networkBlockAll / networkAllowList,
15
+ * enforced by kernel iptables on the runner. The allowlist is IPv4 CIDR
16
+ * ONLY (max 10 entries, DAYTONA_MAX_NETWORK_ALLOWLIST). Hostname
17
+ * destinations are pinned to their IPv4 addresses by a DNS lookup at
18
+ * create time — see the DNS-ROTATION CAVEAT on SandboxCreateOptions.network.
19
+ * Destinations Daytona can never enforce (wildcards, IPv6, unresolvable
20
+ * hostnames, >10 CIDRs) throw DaytonaNetworkPolicyError instead of
21
+ * silently weakening the policy.
22
+ * - The `user` option is a CREATE-TIME OS user (Daytona's osUser field);
23
+ * there is no per-exec user switch — the Daytona daemon runs commands as
24
+ * the container's user (governed by the image's USER directive; default
25
+ * images use "daytona" with passwordless sudo). `user: "root"` keeps the
26
+ * image's default user and elevates every command through a `sudo -n`
27
+ * wrapper instead (mirrors the Modal provider's su wrapper). File
28
+ * operations always go through the Daytona daemon and are NOT elevated.
29
+ * - Private registry images (AWS ECR, GHCR, GCP Artifact Registry, ...)
30
+ * require registry credentials pre-registered in the Daytona dashboard
31
+ * (Registries page) — Daytona has no per-call image pull secret. Pull
32
+ * failures for such images throw DaytonaImagePullError.
33
+ * - getInfo()/list() report the API's real createdAt timestamp (never a
34
+ * fabricated client-side date); Daytona exposes no end timestamp, so
35
+ * endAt is always undefined.
36
+ */
37
+
38
+ /**
39
+ * Daytona's hard cap on network allowlist size (validated server-side too).
40
+ * Policies that resolve to more CIDRs throw DaytonaNetworkPolicyError.
41
+ */
42
+ /**
43
+ * Minutes a STOPPED sandbox is kept before Daytona deletes it. Long enough that
44
+ * a stop nobody ordered leaves something to look at and something to collect
45
+ * from; short enough that it is never a way to hold billable state.
46
+ */
47
+ declare const DAYTONA_AUTO_DELETE_GRACE_MINUTES = 10;
48
+ declare const DAYTONA_MAX_NETWORK_ALLOWLIST = 10;
49
+ /**
50
+ * Sandboxes requested per list page. Held at the value every provider in this
51
+ * lineup accepts, so one number is valid everywhere a fleet is enumerated.
10
52
  */
53
+ declare const DAYTONA_LIST_PAGE_SIZE = 100;
11
54
  /**
12
- * Wrap command with cwd and envs shell prefixes.
55
+ * Page ceiling for one enumeration 10,000 sandboxes at the page size above.
56
+ * A walk that reaches it stops and reports itself INCOMPLETE, because the one
57
+ * thing a fleet enumeration may never do is return a short list that reads like
58
+ * a whole one.
59
+ */
60
+ declare const DAYTONA_MAX_LIST_PAGES = 100;
61
+ /** Why a network policy cannot be enforced by Daytona. */
62
+ type DaytonaNetworkPolicyReason = "wildcard-hostname" | "ipv6-unsupported" | "port-unsupported" | "invalid-ipv4" | "unresolvable-hostname" | "allowlist-too-large";
63
+ /**
64
+ * Typed error for network policies Daytona cannot enforce.
65
+ *
66
+ * Daytona's allowlist is kernel-level IPv4 CIDR filtering only (max 10
67
+ * entries) — no DNS/domain layer. Anything that cannot be pinned to stable
68
+ * IPv4 CIDRs at create time is rejected loudly instead of silently
69
+ * weakening the sandbox's egress policy.
70
+ */
71
+ declare class DaytonaNetworkPolicyError extends Error {
72
+ readonly reason: DaytonaNetworkPolicyReason;
73
+ /** The offending destination (absent for list-level violations). */
74
+ readonly destination?: string;
75
+ constructor(reason: DaytonaNetworkPolicyReason, message: string, destination?: string);
76
+ }
77
+ /**
78
+ * Typed error for image pull failures on private registries.
79
+ *
80
+ * Daytona has no per-call image pull secret: credentials for private
81
+ * registries (AWS ECR, GHCR, GCP Artifact Registry, private Docker Hub)
82
+ * must be pre-registered in the Daytona dashboard BEFORE creating the
83
+ * sandbox (dashboard → Registries → Add Registry).
84
+ */
85
+ declare class DaytonaImagePullError extends Error {
86
+ /** The image reference that failed to pull. */
87
+ readonly image: string;
88
+ constructor(image: string, cause?: unknown);
89
+ }
90
+ /**
91
+ * Typed error for create-time sizing requests an EXISTING snapshot cannot
92
+ * honor. Daytona pins cpu/memory/disk on the SNAPSHOT at build time;
93
+ * create-from-snapshot has no resources parameter, so a `resources` request
94
+ * against a cached snapshot would be silently ignored. Per the provider law
95
+ * (reject what you cannot enforce) it is refused loudly instead: pre-build a
96
+ * snapshot with the desired sizing under its own name, or drop `resources`.
97
+ */
98
+ declare class DaytonaResourcesError extends Error {
99
+ /** The existing snapshot whose pinned sizing cannot be overridden. */
100
+ readonly snapshot: string;
101
+ constructor(snapshot: string);
102
+ }
103
+ /**
104
+ * Typed error for an idle timeout Daytona cannot take as a SEPARATE bound.
105
+ * Daytona has no absolute lifetime at all: auto-stop is an inactivity clock and
106
+ * it is the only one there is, so `timeoutMs` is already mapped onto it. A
107
+ * second option pointing at the same knob could only contradict the first.
108
+ */
109
+ declare class DaytonaIdleTimeoutError extends Error {
110
+ constructor();
111
+ }
112
+ /**
113
+ * Wrap command with cwd and envs shell prefixes, and (for user "root") a
114
+ * `sudo -n` elevation wrapper.
13
115
  *
14
116
  * Daytona's executeSessionCommand doesn't support cwd or envs natively,
15
117
  * so we inline them as shell commands. Values are single-quoted to handle
16
118
  * spaces and special characters safely.
119
+ *
120
+ * Daytona has no per-exec user switch: commands run as the container's OS
121
+ * user (image USER directive; default "daytona" with passwordless sudo).
122
+ * When the sandbox user is "root", the fully wrapped command is base64
123
+ * encoded and piped through `sudo -n bash` — base64 avoids escaping issues
124
+ * and `-n` fails fast (typed non-zero exit) instead of hanging on a
125
+ * password prompt if the image lacks passwordless sudo.
17
126
  */
18
- declare function wrapCommand(command: string, cwd?: string, envs?: Record<string, string>): string;
19
- /** @internal Test-only export for unit testing wrapCommand logic. */
20
- declare const _testWrapCommand: typeof wrapCommand;
127
+ /**
128
+ * Enforce the timeout INSIDE the box, on BOTH execution paths. Daytona has no
129
+ * fixed sandbox lifetime, and its auto-stop timer measures INACTIVITY over SDK
130
+ * interactions ("no new events ... state changes or interactions with the
131
+ * Sandbox through the sdk"), so it bounds nothing that matters here: while a
132
+ * client is polling, that polling is itself an interaction and resets the clock,
133
+ * and once the client is gone the box still runs for the whole remaining
134
+ * interval before it merely STOPS. e2b and Modal both take an absolute,
135
+ * server-side lifetime; Daytona takes none.
136
+ *
137
+ * The session API's timeout argument does not close the hole on either path.
138
+ * spawn launches with runAsync:true, where that argument bounds the *call*, not
139
+ * the process, and the only deadline is a client-side poll in wait(). run blocks
140
+ * with runAsync:false, where the argument is documented as how long to WAIT for
141
+ * the command — and it is this process that waits, so a wait that ends because
142
+ * this process died is not a kill.
143
+ *
144
+ * coreutils `timeout` closes it for both: the kernel kills the command whether
145
+ * or not anything is still watching, and exits 124 — the same code wait()'s own
146
+ * deadline and the e2b adapter return, so a timeout means one thing across every
147
+ * provider and both paths. The script is passed base64 -> file so no quoting of
148
+ * the caller's command is ever attempted, and a box without coreutils degrades
149
+ * to the un-timed run rather than failing outright (the client-side deadlines
150
+ * still cover the case where a client is alive to enforce them).
151
+ *
152
+ * IT FAILS CLOSED ON A BAD DECODE, and that is not a nicety. `>` creates the
153
+ * file before the pipeline runs, so on an image with no `base64` the decode
154
+ * fails and leaves a ZERO-BYTE script — and `bash <empty>` exits 0 having
155
+ * printed nothing. This wrapper would then have turned a command that used to
156
+ * fail loudly into one that reports success with empty output, on exactly the
157
+ * images where it breaks: the eval artifact listing is itself a
158
+ * `find ... | base64 -w0`, so a box missing base64 failed that listing outright
159
+ * before this wrapper existed, and would afterwards have returned an empty
160
+ * listing instead — no files, no patch, and a clean exit all the way up. So the
161
+ * decode is chained with `&&` and the script is size-checked before bash is
162
+ * handed it; either way out is exit 126, which is nonzero and therefore visible
163
+ * to every caller that checks.
164
+ */
165
+ declare function withInBoxTimeout(wrapped: string, timeoutSec?: number): string;
166
+ declare function wrapCommand(command: string, cwd?: string, envs?: Record<string, string>, user?: string): string;
167
+ /** Daytona create() params derived from Evolve's provider-neutral network policy. */
168
+ interface DaytonaNetworkCreateParams {
169
+ networkBlockAll?: boolean;
170
+ networkAllowList?: string;
171
+ }
172
+ /** Resolves a hostname to its IPv4 addresses (injectable for tests). */
173
+ type HostnameResolver = (hostname: string) => Promise<string[]>;
174
+ /**
175
+ * Map Evolve's provider-neutral network policy onto Daytona create() params.
176
+ *
177
+ * - outbound "open" (or no policy) → no restrictions
178
+ * - outbound "blocked", no allowlist → networkBlockAll: true (kernel DROP of all egress)
179
+ * - outbound "blocked" with allowlist → networkAllowList (comma-separated IPv4
180
+ * CIDRs; bare IPv4 gets /32). Daytona enforces the allowlist with kernel
181
+ * iptables and supports IPv4 CIDRs ONLY, max 10 entries.
182
+ *
183
+ * DNS-ROTATION CAVEAT (load-bearing): hostname destinations are resolved to
184
+ * their IPv4 A records ONCE, at create time, and pinned as /32 CIDRs. If the
185
+ * host rotates DNS afterwards (CDNs and cloud APIs often do), traffic to the
186
+ * new IPs is BLOCKED for the sandbox's lifetime. Prefer stable IPs/CIDRs for
187
+ * anything long-running.
188
+ *
189
+ * Anything that cannot be pinned to stable IPv4 CIDRs throws
190
+ * DaytonaNetworkPolicyError (wildcard hostnames, IPv6, unresolvable
191
+ * hostnames, >10 resolved CIDRs) — never silently weakened.
192
+ */
193
+ declare function mapNetworkPolicy(network?: SandboxCreateOptions["network"], resolveHostname?: HostnameResolver): Promise<DaytonaNetworkCreateParams>;
194
+ /**
195
+ * Registry host of an image reference, or undefined for Docker Hub images.
196
+ * A reference carries a registry host only when its first path segment
197
+ * contains "." or ":" or is "localhost" (Docker's own heuristic).
198
+ */
199
+ declare function imageRegistryHost(image: string): string | undefined;
200
+ /** Fields we read off a Daytona SDK sandbox to build a SandboxInfo. */
201
+ interface DaytonaSandboxLike {
202
+ id: string;
203
+ name?: string;
204
+ snapshot?: string;
205
+ labels?: Record<string, string>;
206
+ createdAt?: string;
207
+ }
208
+ /**
209
+ * Build a SandboxInfo from a Daytona sandbox entity. Timestamps come from
210
+ * the API's createdAt (never fabricated client-side; empty string when the
211
+ * API omits it). Daytona exposes no end timestamp, so endAt is always
212
+ * undefined.
213
+ */
214
+ declare function toSandboxInfo(sandbox: DaytonaSandboxLike): SandboxInfo;
215
+ /**
216
+ * Map a Daytona sandbox state onto Evolve's list() filter states.
217
+ * "started" → running; "stopped"/"archived" → paused (our pause() stops the
218
+ * sandbox); transitional and terminal states match no filter.
219
+ */
220
+ declare function daytonaStateToEvolveState(state?: string): "running" | "paused" | undefined;
21
221
  /** Result of a completed sandbox command */
22
222
  interface SandboxCommandResult {
23
223
  exitCode: number;
@@ -46,6 +246,7 @@ interface SandboxInfo {
46
246
  name?: string;
47
247
  metadata: Record<string, string>;
48
248
  startedAt: string;
249
+ /** End time (undefined for running sandboxes; Daytona never exposes one) */
49
250
  endAt?: string;
50
251
  }
51
252
  /** File or directory entry info */
@@ -81,16 +282,72 @@ interface SandboxCreateOptions {
81
282
  envs?: Record<string, string>;
82
283
  metadata?: Record<string, string>;
83
284
  timeoutMs?: number;
285
+ /**
286
+ * REJECTED with DaytonaIdleTimeoutError — not for want of an idle clock, but
287
+ * because auto-stop is the ONLY clock Daytona has and `timeoutMs` is already
288
+ * mapped onto it (autoStopInterval). Two options, one knob.
289
+ */
290
+ idleTimeoutMs?: number;
84
291
  workingDirectory?: string;
85
- /** Resource allocation for the sandbox */
292
+ /**
293
+ * Resource allocation (cpu cores, memory GiB, disk GiB), applied when a
294
+ * SNAPSHOT IS BUILT for the image (Daytona pins sizing on the snapshot).
295
+ * When the named snapshot ALREADY exists it cannot be resized at create —
296
+ * declaring resources then throws DaytonaResourcesError rather than
297
+ * silently ignoring them (pre-build a sizing-addressed snapshot instead).
298
+ */
86
299
  resources?: SandboxResources;
300
+ /**
301
+ * Provider-neutral outbound network policy, enforced by kernel iptables on
302
+ * the Daytona runner. "blocked" with no allowedDestinations drops all
303
+ * egress. With allowedDestinations, Daytona supports IPv4 CIDRs ONLY (max
304
+ * 10): IPs/CIDRs pass through; hostnames are DNS-resolved ONCE at create
305
+ * time and pinned as /32 CIDRs — if the host rotates DNS later (CDNs and
306
+ * cloud APIs often do), its new IPs are BLOCKED for the sandbox's
307
+ * lifetime. Wildcards, IPv6, and unresolvable hostnames throw
308
+ * DaytonaNetworkPolicyError rather than silently weakening the policy.
309
+ * Note: on Daytona orgs below Tier 3, org network policy overrides
310
+ * per-sandbox settings server-side.
311
+ */
312
+ network?: {
313
+ outbound: "open" | "blocked";
314
+ allowedDestinations?: string[];
315
+ };
316
+ /**
317
+ * OS user for the sandbox, applied at CREATE time (Daytona's osUser field)
318
+ * — Daytona has no per-exec user switch, so the user commands actually run
319
+ * as is governed by the sandbox image (USER directive; default Daytona
320
+ * images use "daytona" with passwordless sudo). A non-root value must
321
+ * exist in the image. Pass "root" to keep the image's default user and
322
+ * elevate every command through a `sudo -n` wrapper instead (requires
323
+ * passwordless sudo in the image; default images have it). File operations
324
+ * go through the Daytona daemon and are NOT elevated.
325
+ */
326
+ user?: string;
327
+ /** Home directory used by the SDK for agent config paths; not consumed by the provider. */
328
+ homeDir?: string;
87
329
  }
88
330
  /** Options for listing sandboxes */
89
331
  interface SandboxListOptions {
332
+ /** "running" matches Daytona "started"; "paused" matches "stopped"/"archived". */
90
333
  state?: ("running" | "paused")[];
91
334
  metadata?: Record<string, string>;
92
335
  limit?: number;
93
336
  }
337
+ /**
338
+ * A COMPLETE (or admittedly incomplete) enumeration of the organization's fleet.
339
+ *
340
+ * `complete` is the load-bearing field. Callers that need a whole fleet —
341
+ * orphan sweeps, lifecycle reconciliation — read a sandbox's ABSENCE from the
342
+ * list as evidence it is gone, so a truncated page and a small fleet must never
343
+ * be the same answer. `complete: false` means leave every row alone.
344
+ */
345
+ interface SandboxListPage {
346
+ sandboxes: SandboxInfo[];
347
+ complete: boolean;
348
+ pagesFetched: number;
349
+ error?: string;
350
+ }
94
351
  /** Command execution capabilities */
95
352
  interface SandboxCommands {
96
353
  run(command: string, options?: SandboxRunOptions): Promise<SandboxCommandResult>;
@@ -106,6 +363,8 @@ interface SandboxFiles {
106
363
  path: string;
107
364
  data: string | Buffer | ArrayBuffer | Uint8Array;
108
365
  }>): Promise<void>;
366
+ /** Upload a local file by path, without buffering it whole */
367
+ writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
109
368
  makeDir(path: string): Promise<void>;
110
369
  exists(path: string): Promise<boolean>;
111
370
  list(path: string): Promise<FileInfo[]>;
@@ -129,7 +388,10 @@ interface SandboxProvider {
129
388
  readonly name?: string;
130
389
  create(options: SandboxCreateOptions): Promise<SandboxInstance>;
131
390
  connect(sandboxId: string, timeoutMs?: number): Promise<SandboxInstance>;
391
+ /** List sandboxes, paginating to exhaustion. `limit` bounds items returned. */
132
392
  list(options?: SandboxListOptions): Promise<SandboxInfo[]>;
393
+ /** The same enumeration for fleet bookkeeping: never throws, reports completeness. */
394
+ listAll(options?: SandboxListOptions): Promise<SandboxListPage>;
133
395
  }
134
396
  interface DaytonaConfig {
135
397
  /** Daytona API key. Default: reads from DAYTONA_API_KEY env var */
@@ -142,6 +404,20 @@ interface DaytonaConfig {
142
404
  defaultTimeoutMs?: number;
143
405
  /** Daytona snapshot name (default: 'evolve-all'). Create custom snapshots via `cd assets && ./build.sh daytona` */
144
406
  snapshotName?: string;
407
+ /**
408
+ * Evolve-managed toolbox base URL. Setting it puts the provider in MANAGED
409
+ * mode, where `apiKey` is an Evolve API key rather than a Daytona one and
410
+ * both planes ride the Dashboard.
411
+ *
412
+ * One field rather than a separate `managed: true` because the two effects
413
+ * have one cause: if the toolbox belongs to the platform, so do the
414
+ * snapshots behind it, so managed creates name an existing platform snapshot
415
+ * and never build one. Resolved by the Evolve SDK; direct/BYO callers leave
416
+ * it unset.
417
+ *
418
+ * @internal
419
+ */
420
+ managedToolboxUrl?: string;
145
421
  }
146
422
  interface ResolvedDaytonaConfig {
147
423
  apiKey: string;
@@ -149,6 +425,110 @@ interface ResolvedDaytonaConfig {
149
425
  target?: string;
150
426
  defaultTimeoutMs?: number;
151
427
  snapshotName?: string;
428
+ managedToolboxUrl?: string;
429
+ }
430
+ /** What a managed sandbox's streaming log follow needs to reach the Dashboard. */
431
+ interface ManagedStreamContext {
432
+ toolboxUrl: string;
433
+ apiKey: string;
434
+ }
435
+ /**
436
+ * Split Daytona's multiplexed command log into stdout and stderr as the bytes
437
+ * arrive.
438
+ *
439
+ * The wire format is one byte stream with 3-byte markers announcing which
440
+ * stream the following bytes belong to (STDOUT_PREFIX_BYTES /
441
+ * STDERR_PREFIX_BYTES, both exported by the Daytona SDK — this reads their
442
+ * framing, it does not invent one). Two things make a streaming demux
443
+ * different from the SDK's whole-buffer one: a marker can be split across two
444
+ * chunks, so the last MAX_PREFIX_LEN-1 bytes are always held back rather than
445
+ * emitted; and a multi-byte UTF-8 character can be split too, so each stream
446
+ * keeps its own decoder in streaming mode.
447
+ */
448
+ declare function createLogDemuxer(onStdout: (chunk: string) => void, onStderr: (chunk: string) => void): {
449
+ push(chunk: Uint8Array): void;
450
+ flush(): void;
451
+ };
452
+ /**
453
+ * Follow a session command's logs over plain HTTP.
454
+ *
455
+ * The Daytona SDK follows logs over a WEBSOCKET (Process.js:289 rewrites the
456
+ * toolbox base to ws:// and opens a socket). A managed sandbox's toolbox base
457
+ * is a Next.js route handler, and a Next route handler never sees a websocket
458
+ * upgrade — the codebase's one websocket proxy lives in a separate custom
459
+ * server for exactly that reason. So managed mode cannot use that transport.
460
+ *
461
+ * It does not have to. MEASURED 2026-07-26 against a live sandbox: the same
462
+ * endpoint with `?follow=true` over ordinary HTTP answers 200 with
463
+ * `transfer-encoding: chunked` and `content-type: application/octet-stream`,
464
+ * and chunks arrive as the command produces them — 95 ms, 1045 ms, 2127 ms,
465
+ * 2984 ms, 3976 ms for a command printing one line per second. That is real
466
+ * streaming through a plain response body, which is what the Dashboard route
467
+ * pipes through unbuffered.
468
+ */
469
+ declare function followManagedSessionLogs(context: ManagedStreamContext, sandboxId: string, sessionId: string, commandId: string, onStdout: (chunk: string) => void, onStderr: (chunk: string) => void): Promise<void>;
470
+ /**
471
+ * Read a command's streams out of whatever shape Daytona returned.
472
+ *
473
+ * Session logs are USUALLY framed with per-stream markers, and the Daytona
474
+ * SDK's demux returns "" for a stream whose marker never appears. An empty
475
+ * string is not "this command printed nothing" — it means "this daemon build
476
+ * did not frame the output" — and `??` does not fall through an empty string,
477
+ * so reading `stdout ?? output` silently reports every such command as silent.
478
+ *
479
+ * Measured 2026-07-26 on a live sandbox booted from ubuntu:22.04: a command
480
+ * printing one line to each stream came back as
481
+ * {output: "hello-managed\noops\n", stdout: null, exitCode: 0} with no marker
482
+ * bytes anywhere in the log body, and the provider reported exit 0 with empty
483
+ * stdout. Unframed output goes to stdout, because that is what the combined
484
+ * `output` field is; a framed response is untouched.
485
+ */
486
+ declare function readCommandStreams(source: {
487
+ stdout?: string | null;
488
+ stderr?: string | null;
489
+ output?: string | null;
490
+ }): {
491
+ stdout: string;
492
+ stderr: string;
493
+ };
494
+ declare class DaytonaCommands implements SandboxCommands {
495
+ private sandbox;
496
+ private user?;
497
+ private managedStream?;
498
+ constructor(sandbox: Sandbox, user?: string | undefined, managedStream?: ManagedStreamContext | undefined);
499
+ /**
500
+ * Stream a command's output to callbacks. Direct mode uses the Daytona
501
+ * SDK's own follow, which is a websocket; managed mode uses the HTTP
502
+ * chunked follow, because a Dashboard route handler cannot terminate a
503
+ * websocket upgrade (see followManagedSessionLogs).
504
+ */
505
+ private followLogs;
506
+ run(command: string, options?: SandboxRunOptions): Promise<SandboxCommandResult>;
507
+ spawn(command: string, options?: SandboxSpawnOptions): Promise<SandboxCommandHandle>;
508
+ list(): Promise<ProcessInfo[]>;
509
+ kill(processId: string): Promise<boolean>;
510
+ }
511
+ declare class DaytonaFiles implements SandboxFiles {
512
+ private sandbox;
513
+ constructor(sandbox: Sandbox);
514
+ read(path: string): Promise<string | Uint8Array>;
515
+ write(path: string, content: string | Buffer | ArrayBuffer | Uint8Array): Promise<void>;
516
+ writeBatch(files: Array<{
517
+ path: string;
518
+ data: string | Buffer | ArrayBuffer | Uint8Array;
519
+ }>): Promise<void>;
520
+ /**
521
+ * Upload a local file by PATH. Daytona's own uploadFile has a local-path
522
+ * overload (FileSystem.d.ts: `uploadFile(localPath: string, remotePath:
523
+ * string, timeout?)`), so the bytes never pass through this process's heap —
524
+ * which is what makes a large artifact safe to upload under concurrency.
525
+ */
526
+ writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
527
+ makeDir(path: string): Promise<void>;
528
+ exists(path: string): Promise<boolean>;
529
+ list(path: string): Promise<FileInfo[]>;
530
+ remove(path: string): Promise<void>;
531
+ rename(oldPath: string, newPath: string): Promise<void>;
152
532
  }
153
533
  declare class DaytonaProvider implements SandboxProvider {
154
534
  readonly providerType: "daytona";
@@ -156,11 +536,85 @@ declare class DaytonaProvider implements SandboxProvider {
156
536
  private readonly client;
157
537
  private readonly defaultTimeoutMs;
158
538
  private readonly snapshotName;
539
+ /**
540
+ * Sandbox user configured at create time, reapplied on connect() so the
541
+ * root sudo wrapper keeps applying. In-memory only: a connect() from a
542
+ * fresh process falls back to the sandbox's create-time OS user without
543
+ * the wrapper — callers reconnecting across processes must recreate the
544
+ * provider and sandbox with the same user, or root-only operations fail
545
+ * loudly.
546
+ */
547
+ private readonly sandboxUsers;
548
+ /** Set only in Evolve-managed mode; see the EVOLVE-MANAGED MODE section. */
549
+ private readonly managedStream?;
159
550
  constructor(config: ResolvedDaytonaConfig);
160
551
  create(options: SandboxCreateOptions): Promise<SandboxInstance>;
161
552
  connect(sandboxId: string, _timeoutMs?: number): Promise<SandboxInstance>;
553
+ /**
554
+ * List sandboxes, walking every page.
555
+ *
556
+ * This used to request page 1 and stop, discarding `totalPages` — an
557
+ * organization with more than one page of sandboxes was silently truncated,
558
+ * and nothing in the return value said so. For any caller that reads absence
559
+ * from the list as "this sandbox is gone", that is a correctness bug.
560
+ *
561
+ * ORDER OF OPERATIONS, because it is observable: `limit` bounds the sandboxes
562
+ * RETURNED, and Daytona has no server-side state filter, so the state filter
563
+ * runs client-side on each page BEFORE the limit is counted. Asking for 10
564
+ * running sandboxes therefore keeps paging until ten running ones have been
565
+ * found, rather than filtering ten arbitrary rows down to whatever survives.
566
+ */
162
567
  list(options?: SandboxListOptions): Promise<SandboxInfo[]>;
568
+ /**
569
+ * The fleet-bookkeeping enumeration: same walk, never throws.
570
+ *
571
+ * The distinction from `list()` is what a failure MEANS to the caller. An
572
+ * orphan sweep treats a missing sandbox as a terminated one, so it must be
573
+ * able to tell "the organization has no sandboxes" from "the enumeration
574
+ * stopped early".
575
+ */
576
+ listAll(options?: SandboxListOptions): Promise<SandboxListPage>;
577
+ private paginate;
578
+ /**
579
+ * One page, asking the API to do the state filtering when it can.
580
+ *
581
+ * Daytona's REST endpoint takes a `states` array
582
+ * (api-client sandbox-api.d.ts listSandboxesPaginated), but the SDK wrapper
583
+ * this provider is built on does not expose it — `Daytona.list(labels, page,
584
+ * limit)` is the whole public surface, and `sandboxApi` is declared private.
585
+ * Without the server-side filter, `state: ['running']` on an organization
586
+ * full of archived boxes pages through all of them client-side looking for a
587
+ * handful of live ones.
588
+ *
589
+ * So the server filter is applied OPPORTUNISTICALLY, through the private
590
+ * field when it is shaped the way we expect, and the client-side filter in
591
+ * the walk stays the authority regardless. That ordering is the whole point:
592
+ * if a future SDK refactor renames or removes the field, this degrades to the
593
+ * request count it has today, and can never degrade to admitting states the
594
+ * caller excluded.
595
+ */
596
+ private listPage;
597
+ }
598
+ /** One page as Daytona's `list(labels?, page?, limit?)` returns it. */
599
+ interface DaytonaSandboxPage {
600
+ items: Sandbox[];
601
+ totalPages?: number;
163
602
  }
603
+ /**
604
+ * Walk every page into one answer, with an honest completeness verdict.
605
+ *
606
+ * Separate from the provider because everything worth getting wrong lives here
607
+ * and none of it needs a network: the ways a walk can fail to terminate, the
608
+ * difference between "the caller asked for ten" and "the provider ran out", and
609
+ * the rule that a failure mid-walk yields the sandboxes seen so far marked
610
+ * INCOMPLETE rather than either an exception or a short complete list.
611
+ *
612
+ * Exported for its test (`_testCollectSandboxPages`).
613
+ */
614
+ declare function collectSandboxPages(fetchPage: (page: number) => Promise<DaytonaSandboxPage>, options?: SandboxListOptions): Promise<SandboxListPage & {
615
+ stoppedAtLimit: boolean;
616
+ }>;
617
+ declare const _testCollectSandboxPages: typeof collectSandboxPages;
164
618
  /**
165
619
  * Create Daytona sandbox provider.
166
620
  *
@@ -168,5 +622,15 @@ declare class DaytonaProvider implements SandboxProvider {
168
622
  * @throws Error if apiKey cannot be resolved
169
623
  */
170
624
  declare function createDaytonaProvider(config?: DaytonaConfig): SandboxProvider;
625
+ /** @internal Test-only export for unit testing wrapCommand logic. */
626
+ declare const _testWrapCommand: typeof wrapCommand;
627
+ declare const _testWithInBoxTimeout: typeof withInBoxTimeout;
628
+ declare const _testMapNetworkPolicy: typeof mapNetworkPolicy;
629
+ declare const _testImageRegistryHost: typeof imageRegistryHost;
630
+ declare const _testToSandboxInfo: typeof toSandboxInfo;
631
+ declare const _testDaytonaStateToEvolveState: typeof daytonaStateToEvolveState;
632
+ declare const _testCreateLogDemuxer: typeof createLogDemuxer;
633
+ declare const _testFollowManagedSessionLogs: typeof followManagedSessionLogs;
634
+ declare const _testReadCommandStreams: typeof readCommandStreams;
171
635
 
172
- export { type DaytonaConfig, DaytonaProvider, type FileInfo, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxProvider, type SandboxResources, type SandboxRunOptions, type SandboxSpawnOptions, _testWrapCommand, createDaytonaProvider };
636
+ export { DAYTONA_AUTO_DELETE_GRACE_MINUTES, DAYTONA_LIST_PAGE_SIZE, DAYTONA_MAX_LIST_PAGES, DAYTONA_MAX_NETWORK_ALLOWLIST, DaytonaCommands, type DaytonaConfig, DaytonaFiles, DaytonaIdleTimeoutError, DaytonaImagePullError, DaytonaNetworkPolicyError, type DaytonaNetworkPolicyReason, DaytonaProvider, DaytonaResourcesError, type DaytonaSandboxPage, type FileInfo, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxListPage, type SandboxProvider, type SandboxResources, type SandboxRunOptions, type SandboxSpawnOptions, _testCollectSandboxPages, _testCreateLogDemuxer, _testDaytonaStateToEvolveState, _testFollowManagedSessionLogs, _testImageRegistryHost, _testMapNetworkPolicy, _testReadCommandStreams, _testToSandboxInfo, _testWithInBoxTimeout, _testWrapCommand, createDaytonaProvider };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Sandbox } from '@daytonaio/sdk';
2
+
1
3
  /**
2
4
  * Daytona Sandbox Provider
3
5
  *
@@ -7,17 +9,215 @@
7
9
  * - Mirror E2B provider interface for SDK compatibility
8
10
  * - Uses public Docker images via IMAGE_MAP
9
11
  * - Parallel structure to E2B provider
12
+ *
13
+ * Daytona-specific notes:
14
+ * - Network policy maps to Daytona's networkBlockAll / networkAllowList,
15
+ * enforced by kernel iptables on the runner. The allowlist is IPv4 CIDR
16
+ * ONLY (max 10 entries, DAYTONA_MAX_NETWORK_ALLOWLIST). Hostname
17
+ * destinations are pinned to their IPv4 addresses by a DNS lookup at
18
+ * create time — see the DNS-ROTATION CAVEAT on SandboxCreateOptions.network.
19
+ * Destinations Daytona can never enforce (wildcards, IPv6, unresolvable
20
+ * hostnames, >10 CIDRs) throw DaytonaNetworkPolicyError instead of
21
+ * silently weakening the policy.
22
+ * - The `user` option is a CREATE-TIME OS user (Daytona's osUser field);
23
+ * there is no per-exec user switch — the Daytona daemon runs commands as
24
+ * the container's user (governed by the image's USER directive; default
25
+ * images use "daytona" with passwordless sudo). `user: "root"` keeps the
26
+ * image's default user and elevates every command through a `sudo -n`
27
+ * wrapper instead (mirrors the Modal provider's su wrapper). File
28
+ * operations always go through the Daytona daemon and are NOT elevated.
29
+ * - Private registry images (AWS ECR, GHCR, GCP Artifact Registry, ...)
30
+ * require registry credentials pre-registered in the Daytona dashboard
31
+ * (Registries page) — Daytona has no per-call image pull secret. Pull
32
+ * failures for such images throw DaytonaImagePullError.
33
+ * - getInfo()/list() report the API's real createdAt timestamp (never a
34
+ * fabricated client-side date); Daytona exposes no end timestamp, so
35
+ * endAt is always undefined.
36
+ */
37
+
38
+ /**
39
+ * Daytona's hard cap on network allowlist size (validated server-side too).
40
+ * Policies that resolve to more CIDRs throw DaytonaNetworkPolicyError.
41
+ */
42
+ /**
43
+ * Minutes a STOPPED sandbox is kept before Daytona deletes it. Long enough that
44
+ * a stop nobody ordered leaves something to look at and something to collect
45
+ * from; short enough that it is never a way to hold billable state.
46
+ */
47
+ declare const DAYTONA_AUTO_DELETE_GRACE_MINUTES = 10;
48
+ declare const DAYTONA_MAX_NETWORK_ALLOWLIST = 10;
49
+ /**
50
+ * Sandboxes requested per list page. Held at the value every provider in this
51
+ * lineup accepts, so one number is valid everywhere a fleet is enumerated.
10
52
  */
53
+ declare const DAYTONA_LIST_PAGE_SIZE = 100;
11
54
  /**
12
- * Wrap command with cwd and envs shell prefixes.
55
+ * Page ceiling for one enumeration 10,000 sandboxes at the page size above.
56
+ * A walk that reaches it stops and reports itself INCOMPLETE, because the one
57
+ * thing a fleet enumeration may never do is return a short list that reads like
58
+ * a whole one.
59
+ */
60
+ declare const DAYTONA_MAX_LIST_PAGES = 100;
61
+ /** Why a network policy cannot be enforced by Daytona. */
62
+ type DaytonaNetworkPolicyReason = "wildcard-hostname" | "ipv6-unsupported" | "port-unsupported" | "invalid-ipv4" | "unresolvable-hostname" | "allowlist-too-large";
63
+ /**
64
+ * Typed error for network policies Daytona cannot enforce.
65
+ *
66
+ * Daytona's allowlist is kernel-level IPv4 CIDR filtering only (max 10
67
+ * entries) — no DNS/domain layer. Anything that cannot be pinned to stable
68
+ * IPv4 CIDRs at create time is rejected loudly instead of silently
69
+ * weakening the sandbox's egress policy.
70
+ */
71
+ declare class DaytonaNetworkPolicyError extends Error {
72
+ readonly reason: DaytonaNetworkPolicyReason;
73
+ /** The offending destination (absent for list-level violations). */
74
+ readonly destination?: string;
75
+ constructor(reason: DaytonaNetworkPolicyReason, message: string, destination?: string);
76
+ }
77
+ /**
78
+ * Typed error for image pull failures on private registries.
79
+ *
80
+ * Daytona has no per-call image pull secret: credentials for private
81
+ * registries (AWS ECR, GHCR, GCP Artifact Registry, private Docker Hub)
82
+ * must be pre-registered in the Daytona dashboard BEFORE creating the
83
+ * sandbox (dashboard → Registries → Add Registry).
84
+ */
85
+ declare class DaytonaImagePullError extends Error {
86
+ /** The image reference that failed to pull. */
87
+ readonly image: string;
88
+ constructor(image: string, cause?: unknown);
89
+ }
90
+ /**
91
+ * Typed error for create-time sizing requests an EXISTING snapshot cannot
92
+ * honor. Daytona pins cpu/memory/disk on the SNAPSHOT at build time;
93
+ * create-from-snapshot has no resources parameter, so a `resources` request
94
+ * against a cached snapshot would be silently ignored. Per the provider law
95
+ * (reject what you cannot enforce) it is refused loudly instead: pre-build a
96
+ * snapshot with the desired sizing under its own name, or drop `resources`.
97
+ */
98
+ declare class DaytonaResourcesError extends Error {
99
+ /** The existing snapshot whose pinned sizing cannot be overridden. */
100
+ readonly snapshot: string;
101
+ constructor(snapshot: string);
102
+ }
103
+ /**
104
+ * Typed error for an idle timeout Daytona cannot take as a SEPARATE bound.
105
+ * Daytona has no absolute lifetime at all: auto-stop is an inactivity clock and
106
+ * it is the only one there is, so `timeoutMs` is already mapped onto it. A
107
+ * second option pointing at the same knob could only contradict the first.
108
+ */
109
+ declare class DaytonaIdleTimeoutError extends Error {
110
+ constructor();
111
+ }
112
+ /**
113
+ * Wrap command with cwd and envs shell prefixes, and (for user "root") a
114
+ * `sudo -n` elevation wrapper.
13
115
  *
14
116
  * Daytona's executeSessionCommand doesn't support cwd or envs natively,
15
117
  * so we inline them as shell commands. Values are single-quoted to handle
16
118
  * spaces and special characters safely.
119
+ *
120
+ * Daytona has no per-exec user switch: commands run as the container's OS
121
+ * user (image USER directive; default "daytona" with passwordless sudo).
122
+ * When the sandbox user is "root", the fully wrapped command is base64
123
+ * encoded and piped through `sudo -n bash` — base64 avoids escaping issues
124
+ * and `-n` fails fast (typed non-zero exit) instead of hanging on a
125
+ * password prompt if the image lacks passwordless sudo.
17
126
  */
18
- declare function wrapCommand(command: string, cwd?: string, envs?: Record<string, string>): string;
19
- /** @internal Test-only export for unit testing wrapCommand logic. */
20
- declare const _testWrapCommand: typeof wrapCommand;
127
+ /**
128
+ * Enforce the timeout INSIDE the box, on BOTH execution paths. Daytona has no
129
+ * fixed sandbox lifetime, and its auto-stop timer measures INACTIVITY over SDK
130
+ * interactions ("no new events ... state changes or interactions with the
131
+ * Sandbox through the sdk"), so it bounds nothing that matters here: while a
132
+ * client is polling, that polling is itself an interaction and resets the clock,
133
+ * and once the client is gone the box still runs for the whole remaining
134
+ * interval before it merely STOPS. e2b and Modal both take an absolute,
135
+ * server-side lifetime; Daytona takes none.
136
+ *
137
+ * The session API's timeout argument does not close the hole on either path.
138
+ * spawn launches with runAsync:true, where that argument bounds the *call*, not
139
+ * the process, and the only deadline is a client-side poll in wait(). run blocks
140
+ * with runAsync:false, where the argument is documented as how long to WAIT for
141
+ * the command — and it is this process that waits, so a wait that ends because
142
+ * this process died is not a kill.
143
+ *
144
+ * coreutils `timeout` closes it for both: the kernel kills the command whether
145
+ * or not anything is still watching, and exits 124 — the same code wait()'s own
146
+ * deadline and the e2b adapter return, so a timeout means one thing across every
147
+ * provider and both paths. The script is passed base64 -> file so no quoting of
148
+ * the caller's command is ever attempted, and a box without coreutils degrades
149
+ * to the un-timed run rather than failing outright (the client-side deadlines
150
+ * still cover the case where a client is alive to enforce them).
151
+ *
152
+ * IT FAILS CLOSED ON A BAD DECODE, and that is not a nicety. `>` creates the
153
+ * file before the pipeline runs, so on an image with no `base64` the decode
154
+ * fails and leaves a ZERO-BYTE script — and `bash <empty>` exits 0 having
155
+ * printed nothing. This wrapper would then have turned a command that used to
156
+ * fail loudly into one that reports success with empty output, on exactly the
157
+ * images where it breaks: the eval artifact listing is itself a
158
+ * `find ... | base64 -w0`, so a box missing base64 failed that listing outright
159
+ * before this wrapper existed, and would afterwards have returned an empty
160
+ * listing instead — no files, no patch, and a clean exit all the way up. So the
161
+ * decode is chained with `&&` and the script is size-checked before bash is
162
+ * handed it; either way out is exit 126, which is nonzero and therefore visible
163
+ * to every caller that checks.
164
+ */
165
+ declare function withInBoxTimeout(wrapped: string, timeoutSec?: number): string;
166
+ declare function wrapCommand(command: string, cwd?: string, envs?: Record<string, string>, user?: string): string;
167
+ /** Daytona create() params derived from Evolve's provider-neutral network policy. */
168
+ interface DaytonaNetworkCreateParams {
169
+ networkBlockAll?: boolean;
170
+ networkAllowList?: string;
171
+ }
172
+ /** Resolves a hostname to its IPv4 addresses (injectable for tests). */
173
+ type HostnameResolver = (hostname: string) => Promise<string[]>;
174
+ /**
175
+ * Map Evolve's provider-neutral network policy onto Daytona create() params.
176
+ *
177
+ * - outbound "open" (or no policy) → no restrictions
178
+ * - outbound "blocked", no allowlist → networkBlockAll: true (kernel DROP of all egress)
179
+ * - outbound "blocked" with allowlist → networkAllowList (comma-separated IPv4
180
+ * CIDRs; bare IPv4 gets /32). Daytona enforces the allowlist with kernel
181
+ * iptables and supports IPv4 CIDRs ONLY, max 10 entries.
182
+ *
183
+ * DNS-ROTATION CAVEAT (load-bearing): hostname destinations are resolved to
184
+ * their IPv4 A records ONCE, at create time, and pinned as /32 CIDRs. If the
185
+ * host rotates DNS afterwards (CDNs and cloud APIs often do), traffic to the
186
+ * new IPs is BLOCKED for the sandbox's lifetime. Prefer stable IPs/CIDRs for
187
+ * anything long-running.
188
+ *
189
+ * Anything that cannot be pinned to stable IPv4 CIDRs throws
190
+ * DaytonaNetworkPolicyError (wildcard hostnames, IPv6, unresolvable
191
+ * hostnames, >10 resolved CIDRs) — never silently weakened.
192
+ */
193
+ declare function mapNetworkPolicy(network?: SandboxCreateOptions["network"], resolveHostname?: HostnameResolver): Promise<DaytonaNetworkCreateParams>;
194
+ /**
195
+ * Registry host of an image reference, or undefined for Docker Hub images.
196
+ * A reference carries a registry host only when its first path segment
197
+ * contains "." or ":" or is "localhost" (Docker's own heuristic).
198
+ */
199
+ declare function imageRegistryHost(image: string): string | undefined;
200
+ /** Fields we read off a Daytona SDK sandbox to build a SandboxInfo. */
201
+ interface DaytonaSandboxLike {
202
+ id: string;
203
+ name?: string;
204
+ snapshot?: string;
205
+ labels?: Record<string, string>;
206
+ createdAt?: string;
207
+ }
208
+ /**
209
+ * Build a SandboxInfo from a Daytona sandbox entity. Timestamps come from
210
+ * the API's createdAt (never fabricated client-side; empty string when the
211
+ * API omits it). Daytona exposes no end timestamp, so endAt is always
212
+ * undefined.
213
+ */
214
+ declare function toSandboxInfo(sandbox: DaytonaSandboxLike): SandboxInfo;
215
+ /**
216
+ * Map a Daytona sandbox state onto Evolve's list() filter states.
217
+ * "started" → running; "stopped"/"archived" → paused (our pause() stops the
218
+ * sandbox); transitional and terminal states match no filter.
219
+ */
220
+ declare function daytonaStateToEvolveState(state?: string): "running" | "paused" | undefined;
21
221
  /** Result of a completed sandbox command */
22
222
  interface SandboxCommandResult {
23
223
  exitCode: number;
@@ -46,6 +246,7 @@ interface SandboxInfo {
46
246
  name?: string;
47
247
  metadata: Record<string, string>;
48
248
  startedAt: string;
249
+ /** End time (undefined for running sandboxes; Daytona never exposes one) */
49
250
  endAt?: string;
50
251
  }
51
252
  /** File or directory entry info */
@@ -81,16 +282,72 @@ interface SandboxCreateOptions {
81
282
  envs?: Record<string, string>;
82
283
  metadata?: Record<string, string>;
83
284
  timeoutMs?: number;
285
+ /**
286
+ * REJECTED with DaytonaIdleTimeoutError — not for want of an idle clock, but
287
+ * because auto-stop is the ONLY clock Daytona has and `timeoutMs` is already
288
+ * mapped onto it (autoStopInterval). Two options, one knob.
289
+ */
290
+ idleTimeoutMs?: number;
84
291
  workingDirectory?: string;
85
- /** Resource allocation for the sandbox */
292
+ /**
293
+ * Resource allocation (cpu cores, memory GiB, disk GiB), applied when a
294
+ * SNAPSHOT IS BUILT for the image (Daytona pins sizing on the snapshot).
295
+ * When the named snapshot ALREADY exists it cannot be resized at create —
296
+ * declaring resources then throws DaytonaResourcesError rather than
297
+ * silently ignoring them (pre-build a sizing-addressed snapshot instead).
298
+ */
86
299
  resources?: SandboxResources;
300
+ /**
301
+ * Provider-neutral outbound network policy, enforced by kernel iptables on
302
+ * the Daytona runner. "blocked" with no allowedDestinations drops all
303
+ * egress. With allowedDestinations, Daytona supports IPv4 CIDRs ONLY (max
304
+ * 10): IPs/CIDRs pass through; hostnames are DNS-resolved ONCE at create
305
+ * time and pinned as /32 CIDRs — if the host rotates DNS later (CDNs and
306
+ * cloud APIs often do), its new IPs are BLOCKED for the sandbox's
307
+ * lifetime. Wildcards, IPv6, and unresolvable hostnames throw
308
+ * DaytonaNetworkPolicyError rather than silently weakening the policy.
309
+ * Note: on Daytona orgs below Tier 3, org network policy overrides
310
+ * per-sandbox settings server-side.
311
+ */
312
+ network?: {
313
+ outbound: "open" | "blocked";
314
+ allowedDestinations?: string[];
315
+ };
316
+ /**
317
+ * OS user for the sandbox, applied at CREATE time (Daytona's osUser field)
318
+ * — Daytona has no per-exec user switch, so the user commands actually run
319
+ * as is governed by the sandbox image (USER directive; default Daytona
320
+ * images use "daytona" with passwordless sudo). A non-root value must
321
+ * exist in the image. Pass "root" to keep the image's default user and
322
+ * elevate every command through a `sudo -n` wrapper instead (requires
323
+ * passwordless sudo in the image; default images have it). File operations
324
+ * go through the Daytona daemon and are NOT elevated.
325
+ */
326
+ user?: string;
327
+ /** Home directory used by the SDK for agent config paths; not consumed by the provider. */
328
+ homeDir?: string;
87
329
  }
88
330
  /** Options for listing sandboxes */
89
331
  interface SandboxListOptions {
332
+ /** "running" matches Daytona "started"; "paused" matches "stopped"/"archived". */
90
333
  state?: ("running" | "paused")[];
91
334
  metadata?: Record<string, string>;
92
335
  limit?: number;
93
336
  }
337
+ /**
338
+ * A COMPLETE (or admittedly incomplete) enumeration of the organization's fleet.
339
+ *
340
+ * `complete` is the load-bearing field. Callers that need a whole fleet —
341
+ * orphan sweeps, lifecycle reconciliation — read a sandbox's ABSENCE from the
342
+ * list as evidence it is gone, so a truncated page and a small fleet must never
343
+ * be the same answer. `complete: false` means leave every row alone.
344
+ */
345
+ interface SandboxListPage {
346
+ sandboxes: SandboxInfo[];
347
+ complete: boolean;
348
+ pagesFetched: number;
349
+ error?: string;
350
+ }
94
351
  /** Command execution capabilities */
95
352
  interface SandboxCommands {
96
353
  run(command: string, options?: SandboxRunOptions): Promise<SandboxCommandResult>;
@@ -106,6 +363,8 @@ interface SandboxFiles {
106
363
  path: string;
107
364
  data: string | Buffer | ArrayBuffer | Uint8Array;
108
365
  }>): Promise<void>;
366
+ /** Upload a local file by path, without buffering it whole */
367
+ writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
109
368
  makeDir(path: string): Promise<void>;
110
369
  exists(path: string): Promise<boolean>;
111
370
  list(path: string): Promise<FileInfo[]>;
@@ -129,7 +388,10 @@ interface SandboxProvider {
129
388
  readonly name?: string;
130
389
  create(options: SandboxCreateOptions): Promise<SandboxInstance>;
131
390
  connect(sandboxId: string, timeoutMs?: number): Promise<SandboxInstance>;
391
+ /** List sandboxes, paginating to exhaustion. `limit` bounds items returned. */
132
392
  list(options?: SandboxListOptions): Promise<SandboxInfo[]>;
393
+ /** The same enumeration for fleet bookkeeping: never throws, reports completeness. */
394
+ listAll(options?: SandboxListOptions): Promise<SandboxListPage>;
133
395
  }
134
396
  interface DaytonaConfig {
135
397
  /** Daytona API key. Default: reads from DAYTONA_API_KEY env var */
@@ -142,6 +404,20 @@ interface DaytonaConfig {
142
404
  defaultTimeoutMs?: number;
143
405
  /** Daytona snapshot name (default: 'evolve-all'). Create custom snapshots via `cd assets && ./build.sh daytona` */
144
406
  snapshotName?: string;
407
+ /**
408
+ * Evolve-managed toolbox base URL. Setting it puts the provider in MANAGED
409
+ * mode, where `apiKey` is an Evolve API key rather than a Daytona one and
410
+ * both planes ride the Dashboard.
411
+ *
412
+ * One field rather than a separate `managed: true` because the two effects
413
+ * have one cause: if the toolbox belongs to the platform, so do the
414
+ * snapshots behind it, so managed creates name an existing platform snapshot
415
+ * and never build one. Resolved by the Evolve SDK; direct/BYO callers leave
416
+ * it unset.
417
+ *
418
+ * @internal
419
+ */
420
+ managedToolboxUrl?: string;
145
421
  }
146
422
  interface ResolvedDaytonaConfig {
147
423
  apiKey: string;
@@ -149,6 +425,110 @@ interface ResolvedDaytonaConfig {
149
425
  target?: string;
150
426
  defaultTimeoutMs?: number;
151
427
  snapshotName?: string;
428
+ managedToolboxUrl?: string;
429
+ }
430
+ /** What a managed sandbox's streaming log follow needs to reach the Dashboard. */
431
+ interface ManagedStreamContext {
432
+ toolboxUrl: string;
433
+ apiKey: string;
434
+ }
435
+ /**
436
+ * Split Daytona's multiplexed command log into stdout and stderr as the bytes
437
+ * arrive.
438
+ *
439
+ * The wire format is one byte stream with 3-byte markers announcing which
440
+ * stream the following bytes belong to (STDOUT_PREFIX_BYTES /
441
+ * STDERR_PREFIX_BYTES, both exported by the Daytona SDK — this reads their
442
+ * framing, it does not invent one). Two things make a streaming demux
443
+ * different from the SDK's whole-buffer one: a marker can be split across two
444
+ * chunks, so the last MAX_PREFIX_LEN-1 bytes are always held back rather than
445
+ * emitted; and a multi-byte UTF-8 character can be split too, so each stream
446
+ * keeps its own decoder in streaming mode.
447
+ */
448
+ declare function createLogDemuxer(onStdout: (chunk: string) => void, onStderr: (chunk: string) => void): {
449
+ push(chunk: Uint8Array): void;
450
+ flush(): void;
451
+ };
452
+ /**
453
+ * Follow a session command's logs over plain HTTP.
454
+ *
455
+ * The Daytona SDK follows logs over a WEBSOCKET (Process.js:289 rewrites the
456
+ * toolbox base to ws:// and opens a socket). A managed sandbox's toolbox base
457
+ * is a Next.js route handler, and a Next route handler never sees a websocket
458
+ * upgrade — the codebase's one websocket proxy lives in a separate custom
459
+ * server for exactly that reason. So managed mode cannot use that transport.
460
+ *
461
+ * It does not have to. MEASURED 2026-07-26 against a live sandbox: the same
462
+ * endpoint with `?follow=true` over ordinary HTTP answers 200 with
463
+ * `transfer-encoding: chunked` and `content-type: application/octet-stream`,
464
+ * and chunks arrive as the command produces them — 95 ms, 1045 ms, 2127 ms,
465
+ * 2984 ms, 3976 ms for a command printing one line per second. That is real
466
+ * streaming through a plain response body, which is what the Dashboard route
467
+ * pipes through unbuffered.
468
+ */
469
+ declare function followManagedSessionLogs(context: ManagedStreamContext, sandboxId: string, sessionId: string, commandId: string, onStdout: (chunk: string) => void, onStderr: (chunk: string) => void): Promise<void>;
470
+ /**
471
+ * Read a command's streams out of whatever shape Daytona returned.
472
+ *
473
+ * Session logs are USUALLY framed with per-stream markers, and the Daytona
474
+ * SDK's demux returns "" for a stream whose marker never appears. An empty
475
+ * string is not "this command printed nothing" — it means "this daemon build
476
+ * did not frame the output" — and `??` does not fall through an empty string,
477
+ * so reading `stdout ?? output` silently reports every such command as silent.
478
+ *
479
+ * Measured 2026-07-26 on a live sandbox booted from ubuntu:22.04: a command
480
+ * printing one line to each stream came back as
481
+ * {output: "hello-managed\noops\n", stdout: null, exitCode: 0} with no marker
482
+ * bytes anywhere in the log body, and the provider reported exit 0 with empty
483
+ * stdout. Unframed output goes to stdout, because that is what the combined
484
+ * `output` field is; a framed response is untouched.
485
+ */
486
+ declare function readCommandStreams(source: {
487
+ stdout?: string | null;
488
+ stderr?: string | null;
489
+ output?: string | null;
490
+ }): {
491
+ stdout: string;
492
+ stderr: string;
493
+ };
494
+ declare class DaytonaCommands implements SandboxCommands {
495
+ private sandbox;
496
+ private user?;
497
+ private managedStream?;
498
+ constructor(sandbox: Sandbox, user?: string | undefined, managedStream?: ManagedStreamContext | undefined);
499
+ /**
500
+ * Stream a command's output to callbacks. Direct mode uses the Daytona
501
+ * SDK's own follow, which is a websocket; managed mode uses the HTTP
502
+ * chunked follow, because a Dashboard route handler cannot terminate a
503
+ * websocket upgrade (see followManagedSessionLogs).
504
+ */
505
+ private followLogs;
506
+ run(command: string, options?: SandboxRunOptions): Promise<SandboxCommandResult>;
507
+ spawn(command: string, options?: SandboxSpawnOptions): Promise<SandboxCommandHandle>;
508
+ list(): Promise<ProcessInfo[]>;
509
+ kill(processId: string): Promise<boolean>;
510
+ }
511
+ declare class DaytonaFiles implements SandboxFiles {
512
+ private sandbox;
513
+ constructor(sandbox: Sandbox);
514
+ read(path: string): Promise<string | Uint8Array>;
515
+ write(path: string, content: string | Buffer | ArrayBuffer | Uint8Array): Promise<void>;
516
+ writeBatch(files: Array<{
517
+ path: string;
518
+ data: string | Buffer | ArrayBuffer | Uint8Array;
519
+ }>): Promise<void>;
520
+ /**
521
+ * Upload a local file by PATH. Daytona's own uploadFile has a local-path
522
+ * overload (FileSystem.d.ts: `uploadFile(localPath: string, remotePath:
523
+ * string, timeout?)`), so the bytes never pass through this process's heap —
524
+ * which is what makes a large artifact safe to upload under concurrency.
525
+ */
526
+ writeFromPath(sandboxPath: string, localPath: string): Promise<void>;
527
+ makeDir(path: string): Promise<void>;
528
+ exists(path: string): Promise<boolean>;
529
+ list(path: string): Promise<FileInfo[]>;
530
+ remove(path: string): Promise<void>;
531
+ rename(oldPath: string, newPath: string): Promise<void>;
152
532
  }
153
533
  declare class DaytonaProvider implements SandboxProvider {
154
534
  readonly providerType: "daytona";
@@ -156,11 +536,85 @@ declare class DaytonaProvider implements SandboxProvider {
156
536
  private readonly client;
157
537
  private readonly defaultTimeoutMs;
158
538
  private readonly snapshotName;
539
+ /**
540
+ * Sandbox user configured at create time, reapplied on connect() so the
541
+ * root sudo wrapper keeps applying. In-memory only: a connect() from a
542
+ * fresh process falls back to the sandbox's create-time OS user without
543
+ * the wrapper — callers reconnecting across processes must recreate the
544
+ * provider and sandbox with the same user, or root-only operations fail
545
+ * loudly.
546
+ */
547
+ private readonly sandboxUsers;
548
+ /** Set only in Evolve-managed mode; see the EVOLVE-MANAGED MODE section. */
549
+ private readonly managedStream?;
159
550
  constructor(config: ResolvedDaytonaConfig);
160
551
  create(options: SandboxCreateOptions): Promise<SandboxInstance>;
161
552
  connect(sandboxId: string, _timeoutMs?: number): Promise<SandboxInstance>;
553
+ /**
554
+ * List sandboxes, walking every page.
555
+ *
556
+ * This used to request page 1 and stop, discarding `totalPages` — an
557
+ * organization with more than one page of sandboxes was silently truncated,
558
+ * and nothing in the return value said so. For any caller that reads absence
559
+ * from the list as "this sandbox is gone", that is a correctness bug.
560
+ *
561
+ * ORDER OF OPERATIONS, because it is observable: `limit` bounds the sandboxes
562
+ * RETURNED, and Daytona has no server-side state filter, so the state filter
563
+ * runs client-side on each page BEFORE the limit is counted. Asking for 10
564
+ * running sandboxes therefore keeps paging until ten running ones have been
565
+ * found, rather than filtering ten arbitrary rows down to whatever survives.
566
+ */
162
567
  list(options?: SandboxListOptions): Promise<SandboxInfo[]>;
568
+ /**
569
+ * The fleet-bookkeeping enumeration: same walk, never throws.
570
+ *
571
+ * The distinction from `list()` is what a failure MEANS to the caller. An
572
+ * orphan sweep treats a missing sandbox as a terminated one, so it must be
573
+ * able to tell "the organization has no sandboxes" from "the enumeration
574
+ * stopped early".
575
+ */
576
+ listAll(options?: SandboxListOptions): Promise<SandboxListPage>;
577
+ private paginate;
578
+ /**
579
+ * One page, asking the API to do the state filtering when it can.
580
+ *
581
+ * Daytona's REST endpoint takes a `states` array
582
+ * (api-client sandbox-api.d.ts listSandboxesPaginated), but the SDK wrapper
583
+ * this provider is built on does not expose it — `Daytona.list(labels, page,
584
+ * limit)` is the whole public surface, and `sandboxApi` is declared private.
585
+ * Without the server-side filter, `state: ['running']` on an organization
586
+ * full of archived boxes pages through all of them client-side looking for a
587
+ * handful of live ones.
588
+ *
589
+ * So the server filter is applied OPPORTUNISTICALLY, through the private
590
+ * field when it is shaped the way we expect, and the client-side filter in
591
+ * the walk stays the authority regardless. That ordering is the whole point:
592
+ * if a future SDK refactor renames or removes the field, this degrades to the
593
+ * request count it has today, and can never degrade to admitting states the
594
+ * caller excluded.
595
+ */
596
+ private listPage;
597
+ }
598
+ /** One page as Daytona's `list(labels?, page?, limit?)` returns it. */
599
+ interface DaytonaSandboxPage {
600
+ items: Sandbox[];
601
+ totalPages?: number;
163
602
  }
603
+ /**
604
+ * Walk every page into one answer, with an honest completeness verdict.
605
+ *
606
+ * Separate from the provider because everything worth getting wrong lives here
607
+ * and none of it needs a network: the ways a walk can fail to terminate, the
608
+ * difference between "the caller asked for ten" and "the provider ran out", and
609
+ * the rule that a failure mid-walk yields the sandboxes seen so far marked
610
+ * INCOMPLETE rather than either an exception or a short complete list.
611
+ *
612
+ * Exported for its test (`_testCollectSandboxPages`).
613
+ */
614
+ declare function collectSandboxPages(fetchPage: (page: number) => Promise<DaytonaSandboxPage>, options?: SandboxListOptions): Promise<SandboxListPage & {
615
+ stoppedAtLimit: boolean;
616
+ }>;
617
+ declare const _testCollectSandboxPages: typeof collectSandboxPages;
164
618
  /**
165
619
  * Create Daytona sandbox provider.
166
620
  *
@@ -168,5 +622,15 @@ declare class DaytonaProvider implements SandboxProvider {
168
622
  * @throws Error if apiKey cannot be resolved
169
623
  */
170
624
  declare function createDaytonaProvider(config?: DaytonaConfig): SandboxProvider;
625
+ /** @internal Test-only export for unit testing wrapCommand logic. */
626
+ declare const _testWrapCommand: typeof wrapCommand;
627
+ declare const _testWithInBoxTimeout: typeof withInBoxTimeout;
628
+ declare const _testMapNetworkPolicy: typeof mapNetworkPolicy;
629
+ declare const _testImageRegistryHost: typeof imageRegistryHost;
630
+ declare const _testToSandboxInfo: typeof toSandboxInfo;
631
+ declare const _testDaytonaStateToEvolveState: typeof daytonaStateToEvolveState;
632
+ declare const _testCreateLogDemuxer: typeof createLogDemuxer;
633
+ declare const _testFollowManagedSessionLogs: typeof followManagedSessionLogs;
634
+ declare const _testReadCommandStreams: typeof readCommandStreams;
171
635
 
172
- export { type DaytonaConfig, DaytonaProvider, type FileInfo, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxProvider, type SandboxResources, type SandboxRunOptions, type SandboxSpawnOptions, _testWrapCommand, createDaytonaProvider };
636
+ export { DAYTONA_AUTO_DELETE_GRACE_MINUTES, DAYTONA_LIST_PAGE_SIZE, DAYTONA_MAX_LIST_PAGES, DAYTONA_MAX_NETWORK_ALLOWLIST, DaytonaCommands, type DaytonaConfig, DaytonaFiles, DaytonaIdleTimeoutError, DaytonaImagePullError, DaytonaNetworkPolicyError, type DaytonaNetworkPolicyReason, DaytonaProvider, DaytonaResourcesError, type DaytonaSandboxPage, type FileInfo, type ProcessInfo, type SandboxCommandHandle, type SandboxCommandResult, type SandboxCommands, type SandboxCreateOptions, type SandboxFiles, type SandboxInfo, type SandboxInstance, type SandboxListOptions, type SandboxListPage, type SandboxProvider, type SandboxResources, type SandboxRunOptions, type SandboxSpawnOptions, _testCollectSandboxPages, _testCreateLogDemuxer, _testDaytonaStateToEvolveState, _testFollowManagedSessionLogs, _testImageRegistryHost, _testMapNetworkPolicy, _testReadCommandStreams, _testToSandboxInfo, _testWithInBoxTimeout, _testWrapCommand, createDaytonaProvider };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {Daytona,Image}from'@daytonaio/sdk';var h={"evolve-all":"evolvingmachines/evolve-all"},S=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 w(r){let e=r.substring(r.lastIndexOf(".")).toLowerCase();return S.has(e)}function v(r){let e=r.lastIndexOf("/");return e>0?r.substring(0,e):"/"}function P(r){let e=r.lastIndexOf("/");return e>=0?r.substring(e+1):r}function g(r,e,t){let s=r;return e&&(s=`cd '${e.replace(/'/g,"'\\''")}' && ${s}`),t&&Object.keys(t).length>0&&(s=`${Object.entries(t).filter(([,a])=>a!=null).map(([a,o])=>`export ${a}='${o.replace(/'/g,"'\\''")}'`).join("; ")}; ${s}`),s}var C=g,f=class{constructor(e){this.sandbox=e;}async run(e,t){let s=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,n=`run-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(n);try{let a=await this.sandbox.process.executeSessionCommand(n,{command:g(e,t?.cwd,t?.envs),runAsync:!1},s),o=a.cmdId;o&&(t?.onStdout||t?.onStderr)&&await this.sandbox.process.getSessionCommandLogs(n,o,t.onStdout||(()=>{}),t.onStderr||(()=>{}));let d=a.stdout??a.output??"",i=a.stderr??"";if(!d&&o&&!t?.onStdout)try{let m=await this.sandbox.process.getSessionCommandLogs(n,o);d=m.stdout??m.output??"",i=m.stderr??i;}catch{}return {exitCode:a.exitCode??0,stdout:d,stderr:i}}finally{try{await this.sandbox.process.deleteSession(n);}catch{}}}async spawn(e,t){let s=`evolve-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(s);let n=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,a=Date.now(),o=t?.timeoutMs,i=(await this.sandbox.process.executeSessionCommand(s,{command:g(e,t?.cwd,t?.envs),runAsync:true},n)).cmdId;i&&(t?.onStdout||t?.onStderr)&&this.sandbox.process.getSessionCommandLogs(s,i,t.onStdout||(()=>{}),t.onStderr||(()=>{})).catch(()=>{});let m=this.sandbox;return {processId:s,wait:async()=>{if(!i)return {exitCode:0,stdout:"",stderr:""};for(;;){if(o&&Date.now()-a>=o){try{await m.process.deleteSession(s);}catch{}return {exitCode:-1,stdout:"",stderr:"operation timed out"}}try{let c=await m.process.getSessionCommand(s,i);if(c.exitCode!==void 0)try{let l=await m.process.getSessionCommandLogs(s,i);return {exitCode:c.exitCode,stdout:l.stdout??l.output??"",stderr:l.stderr??""}}catch{return {exitCode:c.exitCode,stdout:"",stderr:""}}}catch(c){if((c instanceof Error?c.message.toLowerCase():String(c).toLowerCase()).includes("not found"))return {exitCode:-1,stdout:"",stderr:"session terminated"};throw c}await new Promise(c=>setTimeout(c,500));}},kill:async()=>{try{return await m.process.deleteSession(s),!0}catch{return false}}}}async list(){try{return (await this.sandbox.process.listSessions()).map(t=>({processId:t.sessionId||"",cmd:"",args:[],envs:{}}))}catch{return []}}async kill(e){try{return await this.sandbox.process.deleteSession(e),!0}catch{return false}}},p=class{constructor(e){this.sandbox=e;}async read(e){let t=await this.sandbox.fs.downloadFile(e);return w(e)?new Uint8Array(t):t.toString("utf-8")}async write(e,t){let s;if(typeof t=="string")s=Buffer.from(t,"utf-8");else if(Buffer.isBuffer(t))s=t;else if(t instanceof ArrayBuffer)s=Buffer.from(t);else if(t instanceof Uint8Array)s=Buffer.from(t);else throw new Error(`Unsupported content type: ${typeof t}`);await this.sandbox.fs.uploadFile(s,e);}async writeBatch(e){let t=e.map(s=>{let n;if(typeof s.data=="string")n=Buffer.from(s.data,"utf-8");else if(Buffer.isBuffer(s.data))n=s.data;else if(s.data instanceof ArrayBuffer)n=Buffer.from(s.data);else if(s.data instanceof Uint8Array)n=Buffer.from(s.data);else throw new Error(`Unsupported content type: ${typeof s.data}`);return {source:n,destination:s.path}});await this.sandbox.fs.uploadFiles(t);}async makeDir(e){await this.sandbox.fs.createFolder(e,"755");}async exists(e){try{let t=v(e),s=P(e);return (await this.sandbox.fs.listFiles(t)).some(a=>a.name===s)}catch{return false}}async list(e){return (await this.sandbox.fs.listFiles(e)).map(s=>({name:s.name,path:e.endsWith("/")?`${e}${s.name}`:`${e}/${s.name}`,type:s.isDir?"dir":"file"}))}async remove(e){await this.sandbox.fs.deleteFile(e,true);}async rename(e,t){await this.sandbox.fs.moveFiles(e,t);}},u=class{constructor(e){this.sandbox=e;this.commands=new f(e),this.files=new p(e);}commands;files;get sandboxId(){return this.sandbox.id}async getHost(e){return (await this.sandbox.getPreviewLink(e)).url}async isRunning(){return this.sandbox.state==="started"}async getInfo(){return {sandboxId:this.sandbox.id,image:this.sandbox.snapshot||"unknown",name:this.sandbox.name,metadata:this.sandbox.labels||{},startedAt:new Date().toISOString()}}async kill(){await this.sandbox.delete();}async pause(){await this.sandbox.stop();}},x=class{providerType="daytona";name="Daytona";client;defaultTimeoutMs;snapshotName;constructor(e){this.client=new Daytona({apiKey:e.apiKey,apiUrl:e.apiUrl,target:e.target}),this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.snapshotName=e.snapshotName??"evolve-all";}async create(e){let t=e.timeoutMs??this.defaultTimeoutMs,s=Math.max(1,Math.ceil(t/6e4)),n=e.image||this.snapshotName,a;try{let o=await this.client.snapshot.get(n);if(o&&o.state==="active")console.log(`[daytona] Using cached snapshot: ${n}`),a=await this.client.create({snapshot:n,envVars:e.envs,labels:e.metadata,autoStopInterval:s},{timeout:600});else throw new Error("Snapshot not active")}catch{let o=h[n]??n;console.log(`[daytona] Snapshot "${n}" not found, building from image: ${o}`),console.log("[daytona] First run will take a few minutes (this only happens once)...");try{await this.client.snapshot.create({name:n,image:Image.base(o),resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{onLogs:d=>console.log(`[daytona] ${d}`)}),console.log(`[daytona] Snapshot "${n}" ready.`),a=await this.client.create({snapshot:n,envVars:e.envs,labels:e.metadata,autoStopInterval:s},{timeout:600});}catch(d){console.warn(`[daytona] Snapshot creation failed, falling back to direct image: ${d instanceof Error?d.message:d}`),a=await this.client.create({image:o,envVars:e.envs,labels:e.metadata,autoStopInterval:s,resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{timeout:600,onSnapshotCreateLogs:i=>console.log(`[daytona] ${i}`)});}}return e.workingDirectory&&await a.fs.createFolder(e.workingDirectory,"755"),new u(a)}async connect(e,t){let s=await this.client.get(e);return s.state!=="started"&&await s.start(),new u(s)}async list(e){let t=e?.limit??100;return (await this.client.list(e?.metadata,1,t)).items.map(n=>({sandboxId:n.id,image:n.snapshot||"unknown",name:n.name,metadata:n.labels||{},startedAt:new Date().toISOString()}))}};function A(r={}){let e=r.apiKey??process.env.DAYTONA_API_KEY;if(!e)throw new Error("Daytona API key required. Set DAYTONA_API_KEY environment variable or pass apiKey in config. Get your key at https://app.daytona.io/dashboard/keys");return new x({...r,apiKey:e})}
2
- export{x as DaytonaProvider,C as _testWrapCommand,A as createDaytonaProvider};
1
+ import {STDOUT_PREFIX_BYTES,STDERR_PREFIX_BYTES,MAX_PREFIX_LEN,Daytona,Image}from'@daytonaio/sdk';import {resolve4}from'dns/promises';var z={"evolve-all":"evolvingmachines/evolve-all"},q=10,L=10,T=100,y=100,W=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 X(s){let e=s.substring(s.lastIndexOf(".")).toLowerCase();return W.has(e)}function G(s){let e=s.lastIndexOf("/");return e>0?s.substring(0,e):"/"}function J(s){let e=s.lastIndexOf("/");return e>=0?s.substring(e+1):s}var f=class extends Error{reason;destination;constructor(e,t,n){super(t),this.name="DaytonaNetworkPolicyError",this.reason=e,this.destination=n;}},w=class extends Error{image;constructor(e,t){let n=t instanceof Error?t.message:String(t??"unknown error");super(`Failed to pull image "${e}" on Daytona: ${n}. Private registry images (e.g. AWS ECR) require registry credentials pre-registered in the Daytona dashboard (Registries page) before sandbox creation \u2014 Daytona has no per-call image pull secret. Register the registry at https://app.daytona.io, then retry. Also ensure the image is linux/amd64 and pinned to a tag or digest (floating tags like "latest" are rejected by Daytona's snapshot builder).`),this.name="DaytonaImagePullError",this.image=e;}},h=class extends Error{snapshot;constructor(e){super(`Daytona snapshot "${e}" already exists and pins its cpu/memory/disk sizing \u2014 create-from-snapshot cannot resize it, so the requested \`resources\` cannot be enforced. Pre-build a snapshot with the desired sizing under its own name (sizing-addressed), or omit \`resources\` to accept the snapshot's pinned sizing.`),this.name="DaytonaResourcesError",this.snapshot=e;}},S=class extends Error{constructor(){super("Daytona has no separate idle timeout: auto-stop IS its only clock and it already measures inactivity, so `timeoutMs` is what sets it (autoStopInterval). Set the inactivity bound with `timeoutMs`; a hard bound on the process itself is enforced in-box instead (see withInBoxTimeout)."),this.name="DaytonaIdleTimeoutError";}};function v(s,e){if(!e||e<=0)return s;let t=Buffer.from(s).toString("base64"),n=`/tmp/.evolve-cmd-${Date.now()}-${Math.random().toString(36).slice(2,8)}.sh`;return `sh -c '${`echo ${t} | base64 -d > ${n} && [ -s ${n} ] || { rm -f ${n}; exit 126; }; if command -v timeout >/dev/null 2>&1; then timeout -k 10 ${e} bash ${n}; else bash ${n}; fi; rc=$?; rm -f ${n}; exit $rc`}'`}function P(s,e,t,n){let r=s;return e&&(r=`cd '${e.replace(/'/g,"'\\''")}' && ${r}`),t&&Object.keys(t).length>0&&(r=`${Object.entries(t).filter(([,o])=>o!=null).map(([o,a])=>`export ${o}='${String(a).replace(/'/g,"'\\''")}'`).join("; ")}; ${r}`),n==="root"&&(r=`echo ${Buffer.from(r).toString("base64")} | base64 -d | sudo -n bash`),r}var V=s=>resolve4(s);function E(s){let e=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(?:\/(\d{1,3}))?$/.exec(s);return !(!e||[e[1],e[2],e[3],e[4]].some(t=>Number(t)>255)||e[5]!==void 0&&Number(e[5])>32)}function Z(s){return /^\d{1,3}(\.\d{1,3}){3}(\/\d+)?$/.test(s)&&!E(s)}function Q(s){return s.startsWith("[")?true:(s.match(/:/g)?.length??0)>=2}function ee(s){return /^[^:]+:\d+$/.test(s)}async function B(s,e=V){if(!s||s.outbound==="open"){if(s?.allowedDestinations?.length)throw new Error("network.allowedDestinations is only valid when outbound is blocked");return s?{networkBlockAll:false}:{}}let t=s.allowedDestinations??[];if(t.length===0)return {networkBlockAll:true};let n=[],r=[];for(let o of t)if(E(o))n.push(o.includes("/")?o:`${o}/32`);else {if(Z(o))throw new f("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(Q(o))throw new f("ipv6-unsupported",`Daytona's network allowlist supports IPv4 CIDRs only; cannot allow IPv6 destination "${o}".`,o);if(ee(o))throw new f("port-unsupported",`Daytona'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);if(o.includes("*"))throw new f("wildcard-hostname",`Daytona cannot enforce wildcard hostname "${o}": its allowlist is kernel-level IPv4 CIDR filtering with no DNS/domain layer. List concrete hostnames or IPv4 CIDRs instead.`,o);r.push(o);}for(let o of r){let a=[];try{a=await e(o);}catch(i){throw new f("unresolvable-hostname",`Cannot pin hostname "${o}" to stable IPv4 addresses at create time (${i instanceof Error?i.message:String(i)}). Daytona enforces IPv4 CIDRs only; use an IPv4 CIDR for this destination instead.`,o)}if(a.length===0)throw new f("unresolvable-hostname",`Hostname "${o}" resolved to no IPv4 addresses. Daytona enforces IPv4 CIDRs only; use an IPv4 CIDR for this destination instead.`,o);console.warn(`[daytona] Network allowlist: pinning "${o}" to [${a.join(", ")}] (DNS resolved at create time). Daytona enforces IPv4 CIDRs only \u2014 if this host rotates DNS (CDNs and cloud APIs often do), traffic to its new IPs will be BLOCKED for the sandbox's lifetime.`);for(let i of a)n.push(`${i}/32`);}let l=[...new Set(n)];if(l.length>L)throw new f("allowlist-too-large",`Daytona allows at most ${L} entries in its network allowlist; this policy resolved to ${l.length} CIDRs (${l.join(", ")}). Aggregate destinations into broader CIDRs or reduce the list.`);return {networkBlockAll:false,networkAllowList:l.join(",")}}function O(s){let e=s.indexOf("/");if(e<0)return;let t=s.substring(0,e);return t.includes(".")||t.includes(":")||t==="localhost"?t:void 0}function k(s){return {sandboxId:s.id,image:s.snapshot??"",name:s.name,metadata:s.labels??{},startedAt:s.createdAt??""}}function _(s){if(s==="started")return "running";if(s==="stopped"||s==="archived")return "paused"}var I=class extends Daytona{constructor(t,n){super(t);this.managedToolboxUrl=n;}async getProxyToolboxUrl(){return this.managedToolboxUrl}};function te(s,e){let t=new Uint8Array(new ArrayBuffer(s.length+e.length));return t.set(s,0),t.set(e,s.length),t}function U(s,e,t){e:for(let n=t;n+e.length<=s.length;n++){for(let r=0;r<e.length;r++)if(s[n+r]!==e[r])continue e;return n}return -1}function N(s,e){let t=new TextDecoder("utf-8"),n=new TextDecoder("utf-8"),r=new Uint8Array(0),l="stdout",o=a=>{a.length!==0&&(l==="stdout"?s(t.decode(a,{stream:true})):e(n.decode(a,{stream:true})));};return {push(a){for(r=te(r,a);;){let i=U(r,STDOUT_PREFIX_BYTES,0),d=U(r,STDERR_PREFIX_BYTES,0),c=-1,u="stdout",m=0;if(i!==-1&&(d===-1||i<d)?(c=i,u="stdout",m=STDOUT_PREFIX_BYTES.length):d!==-1&&(c=d,u="stderr",m=STDERR_PREFIX_BYTES.length),c===-1){let g=Math.max(0,r.length-(MAX_PREFIX_LEN-1));o(r.subarray(0,g)),r=r.slice(g);return}o(r.subarray(0,c)),l=u,r=r.slice(c+m);}},flush(){o(r),r=new Uint8Array(0);let a=t.decode();a&&s(a);let i=n.decode();i&&e(i);}}}async function F(s,e,t,n,r,l){let o=`${s.toolboxUrl.replace(/\/+$/,"")}/${encodeURIComponent(e)}/process/session/${encodeURIComponent(t)}/command/${encodeURIComponent(n)}/logs?follow=true`,a=await fetch(o,{headers:{Authorization:`Bearer ${s.apiKey}`,accept:"application/octet-stream"}});if(!a.ok)throw new Error(`Daytona managed log follow failed: ${a.status} ${await a.text()}`);if(!a.body)return;let i=N(r,l),d=a.body.getReader();try{for(;;){let{done:c,value:u}=await d.read();if(c)break;u&&i.push(u);}}finally{i.flush(),await d.cancel().catch(()=>{});}}function x(s){let e=s.stdout||"",t=s.stderr||"",n=s.output||"";return !e&&!t&&n?{stdout:n,stderr:""}:{stdout:e,stderr:t}}var D=class{constructor(e,t,n){this.sandbox=e;this.user=t;this.managedStream=n;}followLogs(e,t,n,r){return this.managedStream?F(this.managedStream,this.sandbox.id,e,t,n,r):this.sandbox.process.getSessionCommandLogs(e,t,n,r)}async run(e,t){let n=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,r=`run-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(r);try{let l=await this.sandbox.process.executeSessionCommand(r,{command:v(P(e,t?.cwd,t?.envs,this.user),n),runAsync:!1},n),o=l.cmdId;o&&(t?.onStdout||t?.onStderr)&&await this.followLogs(r,o,t.onStdout||(()=>{}),t.onStderr||(()=>{}));let{stdout:a,stderr:i}=x(l);if(!a&&!i&&o&&!t?.onStdout)try{let d=await this.sandbox.process.getSessionCommandLogs(r,o),c=x(d);a=c.stdout,i=c.stderr||i;}catch{}return {exitCode:l.exitCode??0,stdout:a,stderr:i}}finally{try{await this.sandbox.process.deleteSession(r);}catch{}}}async spawn(e,t){let n=`evolve-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;await this.sandbox.process.createSession(n);let r=t?.timeoutMs?Math.floor(t.timeoutMs/1e3):void 0,l=Date.now(),o=t?.timeoutMs,i=(await this.sandbox.process.executeSessionCommand(n,{command:v(P(e,t?.cwd,t?.envs,this.user),r),runAsync:true},r)).cmdId;i&&(t?.onStdout||t?.onStderr)&&this.followLogs(n,i,t.onStdout||(()=>{}),t.onStderr||(()=>{})).catch(()=>{});let d=this.sandbox;return {processId:n,wait:async()=>{if(!i)return {exitCode:0,stdout:"",stderr:""};for(;;){if(o&&Date.now()-l>=o){try{await d.process.deleteSession(n);}catch{}return {exitCode:124,stdout:"",stderr:"operation timed out"}}try{let c=await d.process.getSessionCommand(n,i);if(c.exitCode!==void 0)try{let u=await d.process.getSessionCommandLogs(n,i);return {exitCode:c.exitCode,...x(u)}}catch{return {exitCode:c.exitCode,stdout:"",stderr:""}}}catch(c){let u=c instanceof Error?c.message.toLowerCase():String(c).toLowerCase();if(u.includes("not found")){let m=u.includes("sandbox");try{await d.refreshData(),m=!1;}catch(g){(g instanceof Error?g.message.toLowerCase():String(g).toLowerCase()).includes("not found")&&(m=true);}return {exitCode:-1,stdout:"",stderr:m?"sandbox deleted during run":"session terminated"}}throw c}await new Promise(c=>setTimeout(c,500));}},kill:async()=>{try{return await d.process.deleteSession(n),!0}catch{return false}}}}async list(){try{return (await this.sandbox.process.listSessions()).map(t=>({processId:t.sessionId||"",cmd:"",args:[],envs:{}}))}catch{return []}}async kill(e){try{return await this.sandbox.process.deleteSession(e),!0}catch{return false}}},A=class{constructor(e){this.sandbox=e;}async read(e){let t=await this.sandbox.fs.downloadFile(e);return X(e)?new Uint8Array(t):t.toString("utf-8")}async write(e,t){let n;if(typeof t=="string")n=Buffer.from(t,"utf-8");else if(Buffer.isBuffer(t))n=t;else if(t instanceof ArrayBuffer)n=Buffer.from(t);else if(t instanceof Uint8Array)n=Buffer.from(t);else throw new Error(`Unsupported content type: ${typeof t}`);await this.sandbox.fs.uploadFile(n,e);}async writeBatch(e){let t=e.map(n=>{let r;if(typeof n.data=="string")r=Buffer.from(n.data,"utf-8");else if(Buffer.isBuffer(n.data))r=n.data;else if(n.data instanceof ArrayBuffer)r=Buffer.from(n.data);else if(n.data instanceof Uint8Array)r=Buffer.from(n.data);else throw new Error(`Unsupported content type: ${typeof n.data}`);return {source:r,destination:n.path}});await this.sandbox.fs.uploadFiles(t);}async writeFromPath(e,t){await this.sandbox.fs.uploadFile(t,e);}async makeDir(e){await this.sandbox.fs.createFolder(e,"755");}async exists(e){try{let t=G(e),n=J(e);return (await this.sandbox.fs.listFiles(t)).some(l=>l.name===n)}catch{return false}}async list(e){return (await this.sandbox.fs.listFiles(e)).map(n=>({name:n.name,path:e.endsWith("/")?`${e}${n.name}`:`${e}/${n.name}`,type:n.isDir?"dir":"file"}))}async remove(e){await this.sandbox.fs.deleteFile(e,true);}async rename(e,t){await this.sandbox.fs.moveFiles(e,t);}},b=class{constructor(e,t,n){this.sandbox=e;this.commands=new D(e,t,n),this.files=new A(e);}commands;files;get sandboxId(){return this.sandbox.id}async getHost(e){return (await this.sandbox.getPreviewLink(e)).url}async isRunning(){try{await this.sandbox.refreshData();}catch{return false}return this.sandbox.state==="started"}async getInfo(){return await this.sandbox.refreshData(),k(this.sandbox)}async kill(){await this.sandbox.delete();}async pause(){await this.sandbox.stop();}},C=class{providerType="daytona";name="Daytona";client;defaultTimeoutMs;snapshotName;sandboxUsers=new Map;managedStream;constructor(e){let t={apiKey:e.apiKey,apiUrl:e.apiUrl,target:e.target};this.client=e.managedToolboxUrl?new I(t,e.managedToolboxUrl):new Daytona(t),e.managedToolboxUrl&&(this.managedStream={toolboxUrl:e.managedToolboxUrl,apiKey:e.apiKey}),this.defaultTimeoutMs=e.defaultTimeoutMs??36e5,this.snapshotName=e.snapshotName??"evolve-all";}async create(e){if(e.idleTimeoutMs!==void 0)throw new S;let t=await B(e.network),n=e.user,r=n&&n!=="root"?n:void 0,l=e.timeoutMs??this.defaultTimeoutMs,o=Math.max(1,Math.ceil(l/6e4)),a=e.image||this.snapshotName,i={envVars:e.envs,labels:e.metadata,autoStopInterval:o,autoDeleteInterval:q,...r?{user:r}:{},...t},d,c=e.resources!==void 0&&(e.resources.cpu!==void 0||e.resources.memory!==void 0||e.resources.disk!==void 0);if(this.managedStream){if(c)throw new h(a);let u=await this.client.create({snapshot:a,...i},{timeout:600});return e.workingDirectory&&await u.fs.createFolder(e.workingDirectory,"755"),n&&this.sandboxUsers.set(u.id,n),new b(u,n,this.managedStream)}try{let u=await this.client.snapshot.get(a);if(u&&u.state==="active"){if(c)throw new h(a);console.log(`[daytona] Using cached snapshot: ${a}`),d=await this.client.create({snapshot:a,...i},{timeout:600});}else throw new Error("Snapshot not active")}catch(u){if(u instanceof h)throw u;let m=z[a]??a;console.log(`[daytona] Snapshot "${a}" not found, building from image: ${m}`),console.log("[daytona] First run will take a few minutes (this only happens once)...");try{await this.client.snapshot.create({name:a,image:Image.base(m),resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{onLogs:g=>console.log(`[daytona] ${g}`)}),console.log(`[daytona] Snapshot "${a}" ready.`),d=await this.client.create({snapshot:a,...i},{timeout:600});}catch(g){console.warn(`[daytona] Snapshot creation failed, falling back to direct image: ${g instanceof Error?g.message:g}`);try{d=await this.client.create({image:m,...i,resources:{cpu:e.resources?.cpu??4,memory:e.resources?.memory??4,disk:e.resources?.disk??10}},{timeout:600,onSnapshotCreateLogs:p=>console.log(`[daytona] ${p}`)});}catch(p){throw O(m)?new w(m,p):p}}}return e.workingDirectory&&await d.fs.createFolder(e.workingDirectory,"755"),n&&this.sandboxUsers.set(d.id,n),new b(d,n,this.managedStream)}async connect(e,t){let n=await this.client.get(e);return n.state!=="started"&&await n.start(),new b(n,this.sandboxUsers.get(e),this.managedStream)}async list(e){let t=await this.paginate(e);if(t.error&&!t.stoppedAtLimit)throw new Error(t.error);return t.sandboxes}async listAll(e){let{stoppedAtLimit:t,...n}=await this.paginate(e);return n}async paginate(e){return K(t=>this.listPage(e,t),e)}async listPage(e,t){let n=e?.state?ne(e.state):void 0;if(n&&n.length>0){let r=this.client.sandboxApi;if(typeof r?.listSandboxesPaginated=="function")try{let l=await r.listSandboxesPaginated(void 0,t,T,void 0,void 0,e?.metadata?JSON.stringify(e.metadata):void 0,void 0,n);return {items:l.data.items,totalPages:l.data.totalPages}}catch{}}return this.client.list(e?.metadata,t,T)}};function ne(s){let e=[];for(let t of s)t==="running"&&e.push("started"),t==="paused"&&e.push("stopped","archived");return e}async function K(s,e){let t=e?.limit,n=[],r=0,l=new Set;for(let o=1;o<=y;o+=1){let a;try{a=await s(o);}catch(d){return {sandboxes:n,complete:false,pagesFetched:r,stoppedAtLimit:false,error:`sandbox list failed: ${d instanceof Error?d.message:String(d)}`}}r+=1;for(let d of a.items){if(e?.state){let c=_(d.state);if(c===void 0||!e.state.includes(c))continue}if(!l.has(d.id)){if(t!==void 0&&n.length>=t)return {sandboxes:n,complete:false,pagesFetched:r,stoppedAtLimit:true,error:`stopped at the requested limit of ${t} with more sandboxes available`};l.add(d.id),n.push(k(d));}}let i=a.items.length===0||typeof a.totalPages=="number"&&o>=a.totalPages;if(t!==void 0&&n.length>=t&&!i)return {sandboxes:n,complete:false,pagesFetched:r,stoppedAtLimit:true,error:`stopped at the requested limit of ${t} with more sandboxes available`};if(i)break;if(o===y)return {sandboxes:n,complete:false,pagesFetched:r,stoppedAtLimit:false,error:`sandbox list exceeded ${y} pages`}}return {sandboxes:n,complete:true,pagesFetched:r,stoppedAtLimit:false}}var oe=K;function ae(s={}){let e=s.apiKey??process.env.DAYTONA_API_KEY;if(!e)throw new Error("Daytona API key required. Set DAYTONA_API_KEY environment variable or pass apiKey in config. Get your key at https://app.daytona.io/dashboard/keys");return new C({...s,apiKey:e})}var ie=P,de=v,le=B,ce=O,ue=k,me=_,ge=N,fe=F,pe=x;
2
+ export{q as DAYTONA_AUTO_DELETE_GRACE_MINUTES,T as DAYTONA_LIST_PAGE_SIZE,y as DAYTONA_MAX_LIST_PAGES,L as DAYTONA_MAX_NETWORK_ALLOWLIST,D as DaytonaCommands,A as DaytonaFiles,S as DaytonaIdleTimeoutError,w as DaytonaImagePullError,f as DaytonaNetworkPolicyError,C as DaytonaProvider,h as DaytonaResourcesError,oe as _testCollectSandboxPages,ge as _testCreateLogDemuxer,me as _testDaytonaStateToEvolveState,fe as _testFollowManagedSessionLogs,ce as _testImageRegistryHost,le as _testMapNetworkPolicy,pe as _testReadCommandStreams,ue as _testToSandboxInfo,de as _testWithInBoxTimeout,ie as _testWrapCommand,ae as createDaytonaProvider};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evolvingmachines/daytona",
3
- "version": "0.0.51",
3
+ "version": "0.0.52-project-sable.20260729.8632c49",
4
4
  "keywords": [
5
5
  "ai",
6
6
  "agents",
@@ -33,7 +33,7 @@
33
33
  "build": "tsup --minify",
34
34
  "dev": "tsup --watch",
35
35
  "type-check": "tsc --noEmit",
36
- "test:unit": "tsx tests/unit/daytona-commands.test.ts"
36
+ "test:unit": "tsx tests/unit/daytona-commands.test.ts && tsx tests/unit/daytona-provider.test.ts && tsx tests/unit/daytona-managed.test.ts && tsx tests/unit/daytona-list-pagination.test.ts"
37
37
  },
38
38
  "dependencies": {
39
39
  "@daytonaio/sdk": "^0.134.0"